自定义操作 returns 错误时中断安装

Interrupt installation when custom action returns error

我有一种方法可以在 .dll 中验证密码,失败时它应该 return 一个错误代码。它有以下原型:

#define DllExport __declspec( dllexport )
extern "C" DllExport UINT TestPassword(MSIHANDLE hInstall);

我希望当此方法 return 出现错误代码(如 ERROR_INSTALL_USEREXIT = 1602)时,整个安装过程将终止(不会执行此方法之后的其他自定义操作),但它不会't.

此外,在 Wix 中我有以下片段:

 <CustomAction Id='TestPassword' BinaryKey='TestPassword' DllEntry='TestPassword' Execute='immediate'
              Return='check'/>

<Binary Id='TestPassword' SourceFile='DummyDll.dll'/>

Quick Link: (below is for C++ native custom actions).


UPDATE: Some helpful links.


怀疑原因:

在此处顶部列出一些建议。

  • 1) 错误的C++自定义动作代码配置,经常忘记创建一个CA.def文件来定义 dll 导出。我也一直用__stdcall(微星是老妹子)
  • 2) WiX 标记中自定义操作 dll 的路径错误(MSI 中的 dll 无效)。
  • 3) 忘记在 WiX 标记 (*.WXS) 中启用错误代码检查,然后 CA 不会结束设置。这在你的情况下看起来是正确的 (Return='check').
  • 4) 忘记在安装顺序中插入自定义操作。
  • 还有几个,暂时想不起来了。稍后可能会添加...想到的一个是位数问题 (x86/64)...
  • 文件和 运行时间依赖性 是典型的故障转移原因。
  • 尽可能地静态地link
  • 部署 DLL 应该是最小依赖性,因为它们需要 运行 在任何系统、任何语言、任何状态、任何 OS 版本等...
  • 真正推荐使用静态 linking 并且始终是正确选择的少数情况之一。

Heads-Up: Avoid Licensing In Setup? I would recommend you put the license validation in your application instead of your setup. Here are some thoughts on the matter: Installer with Online Registration for Windows Application (recommended read).


技术问题:

FileName.def:我不是C++专家,但是你有FileName.def 文件来声明 dll 的导出函数?如果没有 - 添加一个(下面的步骤/过程)。确保它的格式正确(通过 Visual Studio 添加,我认为它是没有 BOM 的 UTF8)。编译并检查 Dependency Walker 是否所有导出都是正确的:

验证 MSI 文件 DLL:您应该检查已编译的 MSI 以验证其内部是否包含正确的 DLL 以及可用的正确导出.因此;验证 DLL 是否已安全地进入 MSI:

的二进制 table
  1. Orca 打开 你编译的 MSI ().
  2. 二进制table,双击数据列作为您的DLL条目。
  3. Select“将二进制文件写入文件名”并保存到桌面(或其他地方)。
  4. 使用 Dependency Walker (depends.exe) 来验证您是否具有如上图所示的有效 DLL。 常见问题是根本看不到任何导出MyImmediateCAMyTestFailMyTestSuccessetc...)。
  5. 验证文件-产品版本以及文件属性。

错误处理: 可以设置自定义操作来抑制错误。您的标记看起来与“Return 属性”设置正确:(Return='check')。您的代码段:

<CustomAction Id='TestPassword' BinaryKey='TestPassword' 
              DllEntry='TestPassword' Execute='immediate' Return='check'/>

Sequencing:还要检查你的排序是否OK。总之,您需要指向二进制 table DLL,声明自定义操作,然后将其插入正确的序列中。模型 WiX 标记:

<!--<Binary Id="CustomActions" SourceFile="$(var.TestDll.TargetPath)" />-->
<Binary Id="CustomActions" SourceFile="C:\TestDll.dll" />

<CustomAction Id="MyTestFail" BinaryKey="CustomActions" DllEntry="MyTestFail"/>
<CustomAction Id="MyTestSuccess" BinaryKey="CustomActions" DllEntry="MyTestSuccess"/>

<InstallExecuteSequence>
  <Custom Action="MyTestSuccess" After="CostFinalize" />
  <Custom Action="MyTestFail" After="MyTestSuccess" />
</InstallExecuteSequence>

C++ DLL:以及实际的 C++ DLL 本身(记住 *.def 文件)。 MSI API Custom Action Security:

底部代码段中的片段

Visual Studio 2017 的建议步骤:

  1. 创建新的 VC+ DLL 项目 - 动态-Link 库 (DLL)。
  2. 将下面的代码转储到主 *.cpp 文件中(我避免使用 dllmain.cpp)。
  3. 添加 *.def 文件!
  • Right Click Source Files => Add => New Item... => Code => Module-Definition File (.def) => 任何名字都应该...(只有一个允许 def 文件)
  • 添加导出函数名称:

模型:

LIBRARY

EXPORTS
     MyTestFail
     MyTestSuccess
     MyImmediateCA

关闭并重新打开文件以验证是否存在任何格式错误。 Select 修复出现警告的问题。 UTF8 没有 BOM 我想是必需的。

#include "stdafx.h"

#include <windows.h>
#include <Msiquery.h>
#pragma comment(lib, "msi.lib")

UINT __stdcall MyTestFail(MSIHANDLE hInstall)
{
    MessageBox(NULL, L"MyTestFail", L"MyTestFail", MB_OK);    
    return ERROR_INSTALL_FAILURE;
}

UINT __stdcall MyTestSuccess(MSIHANDLE hInstall)
{
    MessageBox(NULL, L"MyTestSuccess", L"MyTestSuccess", MB_OK);    
    return ERROR_SUCCESS;
}

// I will leave in the below snippet from the MSI API - section "Custom Action Security". Above two test methods will do though... 
UINT __stdcall MyImmediateCA(MSIHANDLE hInstall)
{
    MessageBox(NULL, L"Test", L"Test", MB_OK);

    // set up information for deferred custom action called MyDeferredCA
    const TCHAR szValue[] = TEXT("data");
    UINT uiStat = ERROR_INSTALL_FAILURE;
    if (ERROR_SUCCESS == MsiSetProperty(hInstall, TEXT("MyDeferredCA"), szValue))
    {
        uiStat = MsiDoAction(hInstall, TEXT("MyDeferredCA"));

        // clear CustomActionData property
        if (ERROR_SUCCESS != MsiSetProperty(hInstall, TEXT("MyDeferredCA"), TEXT("")))
            return ERROR_INSTALL_FAILURE;
    }

    return (uiStat == ERROR_SUCCESS) ? uiStat : ERROR_INSTALL_FAILURE;
}

(检索 属性 值)。这在 C++ 中稍微复杂一些——有缓冲区等等。向下滚动查看源代码。

Minimal Dependencies: In order to minimize dependencies you should eliminate the Visual C / C++ Runtime dependencies and any MFC dependencies (don't use MFC if you can help it for file size and performance reasons). If you use MFC, set it to use static linking - also for ATL. And finally for the C/C++ runtime, see here: Visual Studio 2010 MSVCR dependency removal? (there are better links, but all I could find that I have time for right now - just want to get this in there so it is not forgotten).

发布模式 C++ 二进制文件现在不应依赖于任何 MSVC 运行time dlls:

这是 DLL 如何依赖 MSVC 运行time dll 的屏幕截图,没有这个调整 - 不要介意红色图标 - 这是古老的 dependency walker 工具,它不是针对现代依赖项进行了更新,但完美地显示了旧式依赖项:

请注意,调试模式 DLL 可能依赖于不同于发布模式二进制文件的文件。发布模式二进制文件是重要的。显然永远不要分发调试模式的二进制文件!