我创建了一个非常简单的MSI,它将一些文件复制到ProgramFiles目录,同时安装调用在C#编写的二进制文件中找到的自定义操作。
安装时,我可以轻松调用我想要的任何自定义操作。例如,我创建了一个安装步骤,用户应该输入许可证,在确认许可证后,使用C#自定义操作中编写的逻辑对服务器进行检查。
但是,在卸载时,每次添加自定义操作时(即使它只执行返回成功),我会收到安装失败的错误。
这是我使用卸载步骤的方式:
<InstallExecuteSequence>
<Custom Action='TestUninstallation' After='MsiUnpublishAssemblies'>REMOVE="ALL"</Custom>
</InstallExecuteSequence>
其中TestUninstallation定义如下:
<CustomAction Id="TestUninstallation" Return="check" Execute="deferred" Impersonate="no" BinaryKey="TestCustomAction" DllEntry="Uninstall" />
属性DllEntry等于Uninstall,这是一个只返回Success的C#方法。
安装完成后,我正在尝试卸载,我正在使用属性OnExit在AdminUISequence中定义UserExit对话框。
知道我错过了什么吗?
调试:托管代码相对容易调试(本机代码实际上更容易)。以下是一些指示:
建议:我认为你只有一个
broken reference to the dll export function
- 换句话说,一个错误的DLL函数名称/参考:
<CustomAction Id="TestUninstallation" Return="check" Execute="deferred" Impersonate="no"
BinaryKey="CustomActions" DllEntry="__ERRONEOUS FUNCTION REFERENCE__" />
只需检查dll实际导出和匹配的内容如下:
<CustomAction Id="CustomAction1" BinaryKey="CustomActions" DllEntry="CustomAction1"/>
像往常一样真正的McCoy是检查dll本身,看看你是否有正确的功能名称(the below screen shot from this prior answer, recommended read)。
This is a native code C++ dll
:
This is a DTF-packaged managed code dll
:
请注意,这是一个嵌入了托管代码的本机dll。它会生成一个非常不同的函数列表,但您仍需要在其中找到您所引用的函数名称。
This is a straight-up managed code dll (no native wrapping)
:
最后:这是直接托管代码DLL,不包含在本机DLL shell中。
不可卸载的安装程序:当自定义操作在卸载过程中崩溃或失败时,您将无法摆脱安装(它只是回滚而且您已经安装了它)。有几个修复或解决方法。
总体修复 - 在我看来 - 是在卸载时不会使自定义操作失败,或者至少调整它们的条件,以便您可以通过命令行设置属性来强制卸载:
在MSI属性表中设置:SUPPRESSERROR = 0
。然后 - 在需要时 - 在命令行集上:
msiexec.exe /x {PRODUCT-GUID} SUPPRESSERROR="1"
在MSI中,您可以使用以下命令调整卸载自定义操作:
REMOVE="ALL" AND SUPPRESSERROR="0"
现在,如果SUPPRESSERROR不是0,则自定义操作将不会运行。
还有一个较旧的答案还有几个选项:I screwed up, how can I uninstall my program?(由Wim Coenen提供,我用更多的建议搞砸了他的答案)。
Boilerplate:为了快速使用,让我在这里转储一个样板ad-hoc自定义动作测试项目。这假定在同一个Visual Studio解决方案中有一个名为"CustomAction1"
的C#托管代码自定义操作项目,并且在您的WiX源中添加了一个引用 - 就像您已经明显看到的那样(这是为了以后我们都忘记了问题所在并且需要再次测试):
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*" Name="WiXCustomActionsTesting" Language="1033" Version="1.0.0.0"
Manufacturer="test" UpgradeCode="PUT-GUID-HERE">
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
<UIRef Id="WixUI_Mondo" />
<Property Id="SUPPRESSERROR" Value="0" Secure="yes" />
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<MediaTemplate EmbedCab="yes" />
<Feature Id="ProductFeature" Title="WiXCustomActionsTesting" Level="1">
<ComponentGroupRef Id="ProductComponents" />
</Feature>
<!--BEGIN CUSTOM ACTION SECTION-->
<Binary Id="CustomActions" SourceFile="$(var.CustomAction1.TargetDir)\$(var.CustomAction1.TargetName).CA.dll" />
<CustomAction Id="TestUninstallation" Return="check" Execute="deferred" Impersonate="no" BinaryKey="CustomActions" DllEntry="CustomAction1" />
<InstallUISequence></InstallUISequence>
<InstallExecuteSequence>
<Custom Action='TestUninstallation' After='InstallInitialize'></Custom>
</InstallExecuteSequence>
<!--END CUSTOM ACTION SECTION-->
</Product>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLFOLDER" Name="WiXCustomActionsTesting" />
</Directory>
</Directory>
</Fragment>
<Fragment>
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
<Component>
<File Source="C:\Projects\MySetup\MyApp.exe">
</File>
</Component>
</ComponentGroup>
</Fragment>
</Wix>