如何以编程方式卸载我的 MSIX python 应用程序?

How can I programmatically uninstall my MSIX python app?

我刚刚在 python 3 中编写了我的第一个 MSIX 应用程序。我使用 pyinstaller to generate an EXE. Then I use WiX Toolset to generate an MSI. I then use MSIX Packaging Tool 创建 MSIX。从代码到 MSIX 可能有更简单的方法,但这就是我目前所做的工作。

理想情况下,我想捕获一个 onuninstall 事件并抛出一个 GUI 提示询问用户他们为什么要卸载。我可以在 MSI 中做到这一点。不过,我的理解是MSIX offers no onuninstall event。如果您有不同的看法,请告诉我!

由于我显然无法捕获 MSIX 卸载事件,我的下一个首选是为用户提供一种从托盘图标卸载应用程序的方法。用户从我的应用程序图标中选择一个卸载托盘菜单按钮,该按钮会弹出一个 window,然后应用程序会询问他们为什么要卸载。他们输入答案,然后单击提交按钮。然后,该应用程序应完全自行卸载。这在 MSI 中也很有效。但是,我无法在 MSIX 中使用它。

安装 MSI 后 python 中的工作原理如下:

subprocess.call('msiexec.exe /x {' + myguid + '}', shell=True)

但是,从 MSI 构建的 MSIX 在 运行 行时抛出此弹出错误消息,并且从未真正卸载应用程序:

This action is only valid for products that are currently installed.

我试过使用我的 WXS 文件的 <Product> 条目中的 GUID,硬编码的,只是为了看看它是否有效。那一个适用于卸载 MSI,但不适用于 MSIX。我还尝试动态获取 GUID,但这对 MSI 或 MSIX 都不起作用,两者都会产生与上述相同的错误。这是我动态获取 GUID 的方法:

from System.Runtime.InteropServices import Marshal
from System import Reflection

myguid = str(Marshal.GetTypeLibGuidForAssembly(
    Reflection.Assembly.GetExecutingAssembly()
)).upper()

虽然 运行 正在使用 MSI(我的日志记录比在 MSIX 中好得多),但似乎 GetExecutingAssembly() 得到了一个 FullName 为 [=17] 的程序集=],这当然是我不想卸载的东西。 GetCallingAssembly() 产生相同的结果。 GetEntryAssembly() 产生 null 值。

我遍历 AppDomain.CurrentDomain.GetAssemblies() 以查看列出的内容,但没有列出我的应用程序,尽管我看到它使用了很多库。

那么,关于如何让应用程序以编程方式卸载有什么想法吗?如果这是问题所在,也许是关于如何为 MSIX 应用程序获取正确 GUID 的建议? DotNet 代码应该没问题。我大概可以弄清楚如何将其翻译成 python.

或者更好的是,知道如何捕获 MSIX 卸载事件和 运行 一些自定义代码吗?

提前致谢!

据我所知,目前无法捕获卸载事件。我不建议实施您建议的方法(托盘图标),但要提出您的问题,您可以使用 MSIX PowerShell commandlets 以编程方式安装和卸载 MSIX 包。

此外,我注意到您在创建 MSIX 程序包时确实在折磨自己。

MSIX 打包工具是由 Microsoft 为无法访问源代码的 IT 专业人员创建的。开发人员可以使用 Windows Application Packaging Project 项目模板(如果他们使用 Visual Studio)或其他第三方工具,例如 Advanced Installer 或 Wix(据我所知有一个 Wix extension that can be used to build MSIX packages ).

这里是关于如何从头开始创建 MSIX 的快速教程,使用 Advanced Installer 更容易: https://www.advancedinstaller.com/create-msi-python-executable.html

免责声明:我在 Advanced Installer 团队中工作。

我使用了 Bogdan 的 Powershell 建议并提出了以下代码,它似乎运行良好:

import subprocess

powershellLocation = "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"
try:
    # Use the name of the app (which I found manually using Get-AppPackage)
    # to get the full name of the app, which seems to have some random numbers
    # on the end of it.
    powershell_tuple = subprocess.Popen([
        powershellLocation,
        "Get-AppPackage",
        "-name",
        '"' + myAppPackageName + '"'
    ],shell=True,stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE).communicate()
    appStrings = powershell_tuple[0].decode('utf-8').strip()
except Exception as e:
    pass
for appStr in appStrings.splitlines():
    # Find the full name of the app
    if appStr.startswith('PackageFullName'):
        colonIndex = appStr.index(':') + 1
        fullName = appStr[colonIndex:].strip()
if fullName:
    # The MSIX package was found, and I now have its full name
    subprocess.call(powershellLocation + ' Remove-AppPackage -Package "' + fullName + '"', shell=True)