无法在 UPGRADINGPRODUCTCODE 中获取 MSI 属性,WIX_UPGRADE_DETECTED

Failed to get MSI property in UPGRADINGPRODUCTCODE, WIX_UPGRADE_DETECTED

我想在升级过程中跳过我的一些自定义操作,uninstallDriver,为此我尝试检索 属性 WIX_UPGRADE_DETECTEDUPGRADINGPRODUCTCODE,但两者都没有设置。

示例代码:

UninstallDriver(MSIHANDLE hInstall)  
{
   char szBuff[1024]; DWORD dwValue = 0;

   OutputDebugStringA("UninstallDriver");
   MsiGetPropertyA(hInstall, "UPGRADINGPRODUCTCODE", szBuff, &dwValue);
   OutputDebugStringA("UPGRADINGPRODUCTCODE");OutputDebugStringA(szBuff);

   MsiGetPropertyA(hInstall, "WIX_UPGRADE_DETECTED", szBuff, &dwValue);
   OutputDebugStringA("WIX_UPGRADE_DETECTED");OutputDebugStringA(szBuff);

   result = UninstallDriver();

   return result;
}

而我的 customAction 是,

<Custom Action='UninstallMYDriverAction'
 After='InstallInitialize'>
        REMOVE~="ALL" OR REINSTALL</Custom>

Custom Action Condition: You should be able to condition that custom action outright without going via a custom action? MSI conditions are notorious for being difficult to get just right since there are so many installation modes to deal with: install, repair, self-repair, modify and minor upgrade patching, uninstall, major upgrade initiated uninstalls, etc... There are several more.

In your case it might be sufficient to add to the existing condition:

AND (NOT UPGRADINGPRODUCTCODE)

This will make the condition invalid if an upgrade is detected.

I would actually try: (REMOVE~="ALL") AND (NOT UPGRADINGPRODUCTCODE) ().


Top Tip: You can debug MSI conditions pretty efficiently by the approach described here: (see bottom section). You use VBScript and a few MSI API calls to determine what conditions are true, and then you can change the sequencing to check the conditions at different times. They might change.

Note: This is just a sorry-for-itself and poor man's MSI debugger, but it works.


属性 字符串大小:我不是 C++ 专家,但这里来自 the MSI SDK: You need to call MsiGetProperty首先是一个空字符串,然后为 null termination 添加 1。与 C++ 一样高 "line noise"。大量的管道和手续收效甚微 - 但让我们处理一下 :-):

The below C++ sample is from the MSI SDK:

UINT __stdcall MyCustomAction(MSIHANDLE hInstall)
{
    TCHAR* szValueBuf = NULL;
    DWORD cchValueBuf = 0;
    UINT uiStat =  MsiGetProperty(hInstall, TEXT("MyProperty"), TEXT(""), &cchValueBuf);
    //cchValueBuf now contains the size of the property's string, without null termination
    if (ERROR_MORE_DATA == uiStat)
    {
        ++cchValueBuf; // add 1 for null termination
        szValueBuf = new TCHAR[cchValueBuf];
        if (szValueBuf)
        {
            uiStat = MsiGetProperty(hInstall, TEXT("MyProperty"), szValueBuf, &cchValueBuf);
        }
    }
    if (ERROR_SUCCESS != uiStat)
    {
        if (szValueBuf != NULL) 
           delete[] szValueBuf;
        return ERROR_INSTALL_FAILURE;
    }   

// custom action uses MyProperty
// ...

delete[] szValueBuf;

return ERROR_SUCCESS;

}

VBScript:出于测试目的,可以轻松使用 VBScript(因此您可以确定是否存在编码问题):

MsgBox Session.Property("PROPERTYNAME")

一些进一步的链接(仅供参考和方便检索,我认为您不需要):

  • MSI Conditions
  • How to execute custom action only in install (not uninstall)
  • How to add a WiX custom action that happens only on uninstall (via MSI)?

忽略调试字符串,更容易看出缓冲区处理不正确。我建议还输出 MsiGetPropertyA() 中的 return 值和 dwValue 中的值来确认,但这是我认为正在发生的事情(评论参考 dwValue):

char szBuff[1024]; DWORD dwValue = 0;
MsiGetPropertyA(hInstall, "UPGRADINGPRODUCTCODE", szBuff, &dwValue); // passes 0, updated to ?x?
MsiGetPropertyA(hInstall, "WIX_UPGRADE_DETECTED", szBuff, &dwValue); // passes ?x?, updated to ?y?

当请求 UPGRADINGPRODUCTCODE 属性 并声明缓冲区长度为零时,提取永远不会成功,因为它必须始终接受至少一个空字符。因此,这将 return ERROR_MORE_DATA 并将 dwValue 设置为不包括空字符 (?x?) 的长度。

然后它将请求 WIX_UPGRADE_DETECTED 的值,要求的缓冲区长度为 (?x?)。如果新长度(?y?)小于旧长度(?x?),您将在缓冲区中获得它的内容;否则它也将只查询这个新的 属性.

的长度

因为 WIX_UPGRADE_DETECTED 包含一个或多个 GUID 的列表,而 UPGRADINGPRODUCTCODE 只包含一个,并且此代码从不递增 dwValue 来解释空值,它只可能如果后者 ?y? 为 0(空)且 ?x? 非空,则成功。但请注意,第二次调用传递了一个未经验证的值作为缓冲区的长度,这种模式是等待发生的缓冲区溢出。

所以修复你的缓冲区处理。我喜欢使用的模式(如下)类似于 Stein 所描述的模式,但如果我知道缓冲区的默认大小,则可以避免第二次调用。在您的情况下,听起来您对 1024 元素缓冲区很满意,但请考虑是否需要处理超过 1024 / len(GUID) 的相关升级代码。

(我猜你没问题。但至少要考虑清楚。即使 GUID 是 ASCII,所以内容无关紧要,请这些天构建 UNICODE...)

WCHAR szBuf[1024];
DWORD cchBuf = 1024; // or _countof(szBuf);
DWORD dwErr = MsiGetPropertyW(hInstall, L"UPGRADINGPRODUCTCODE", szBuf, &cchBuf);
if (dwErr != ERROR_MORE_DATA) {
    // exercise: increment cchBuf for null, adjust buffer, call MsiGetPropertyW again
}
if (dwErr != ERROR_SUCCESS) {
    // per https://docs.microsoft.com/en-us/windows/desktop/msi/custom-action-return-values
    return ERROR_INSTALL_FAILURE;
}

// reset buffer length for next call, in case second property is longer than first
cchBuf = 1024;
dwErr = MsiGetPropertyW(hInstall, L"WIX_UPGRADE_DETECTED", szBuf, &cchBuf);
// : : :