使用MSIEnumRelatedProducts和MSIGetProductInfo的MSI Interop

时间:2010-10-25 09:49:41

标签: c# c++ winapi windows-installer

在使用MSI Interop API时,我遇到了一些导致应用程序崩溃的异常行为。这很简单,可以“处理”问题,但我想更多地了解“为什么”这种情况发生。

我对MSIEnumRelatedProducts的第一次调用返回值0并正确地将我的字符串缓冲区设置为productcode。我的理解是,只有当给定的升级代码(作为parm传递给方法)当前安装了“相关的系列产品”时才会发生这种情况,否则它将返回259 ERROR_NO_MORE_ITEMS。

然而,当我随后使用相同的产品代码调用MSIGetProductInfo时,我得到返回值1605,“此操作仅对当前安装的产品有效。”。

在这种情况下,有没有人有任何想法?它在一台机器上是100%可重复的,但我还没有设法在另一台机器上进行复制步骤。

我们所有产品都使用Wix属性“AllUsers = 1”构建,因此应为所有用户安装产品,而不仅仅是一个。

赞赏任何想法/建议。

由于 本

更新 我注意到,当运行问题msi包时,会显示以下行:

MSI(88:68)[12:15:50:235]:FindRelatedProducts:无法读取产品'{840C ... etc ..... 96}'的ASSIGNMENTTYPE信息。跳过...

有谁知道这可能意味着什么?

更新:代码示例。

do
{
   result = _MSIApi.EnumRelatedProducts(upgradeCode.ToString("B"), 0, 
                                        productIndex, productCode);
   if (result == MSIApi.ERROR_BAD_CONFIGURATION ||
       result == MSIApi.ERROR_INVALID_PARAMETER ||
       result == MSIApi.ERROR_NOT_ENOUGH_MEMORY)
   {
      throw new MSIInteropException("Failed to check for related products", 
                                     new Win32Exception((Int32)result));
   }

   if(!String.IsNullOrEmpty(productCode.ToString()))
   {
      Int32 size = 255;
      StringBuilder buffer = new StringBuilder(size);
      Int32 result = (Int32)_MSIApi.GetProductInfo(productCode, 
                             MSIApi.INSTALLPROPERTY_VERSIONSTRING, 
                             buffer, 
                             ref size);

      if (result != MSIApi.ERROR_SUCCESS)
      {               
         throw new MSIInteropException("Failed to get installed version", 
                                        new Win32Exception(result));
      }

      version = new Version(buffer.ToString());
   }

   productCode = new StringBuilder(39);
   productIndex++;
}
while (result == MSIApi.ERROR_SUCCESS);

2 个答案:

答案 0 :(得分:5)

我想您尝试使用MsiGetProductInfo获取其他属性,如文档中所述。例如,您可以不受任何问题影响"PackageCode"属性(INSTALLPROPERTY_PACKAGECODE)的值,但是您无法获得"UpgradeCode"属性的值ERROR_UNKNOWN_PRODUCT 3}}并收到错误1605(ERROR_UNKNOWN_PRODUCT)。

更新:好的,现在我明白了你的问题。你如何在互联网上找到MsiGetProductInfo中的错误,所以它并不总是如此。有时它会回到1605(ERROR_UNKNOWN_PROPERTY)或1608(HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products)。在这种情况下,唯一的解决方法是手动获取版本属性。我可以使用Microsoft Office Outlook 2010 MUI(UpgradeCode =“{00140000-001A-0000-0000-0000000FF1CE}”)重现您在我的计算机上描述的问题,并编写了一个解决方法,我从注册表中获取产品版本。在示例中,我仅从using System; using System.Text; using System.Runtime.InteropServices; using Microsoft.Win32; namespace EnumInstalledMsiProducts { internal static class NativeMethods { internal const int MaxGuidChars = 38; internal const int NoError = 0; internal const int ErrorNoMoreItems = 259; internal const int ErrorUnknownProduct = 1605; internal const int ErrorUnknownProperty = 1608; internal const int ErrorMoreData = 234; [DllImport ("msi.dll", CharSet = CharSet.Unicode, SetLastError = true)] internal static extern int MsiEnumRelatedProducts (string lpUpgradeCode, int dwReserved, int iProductIndex, //The zero-based index into the registered products. StringBuilder lpProductBuf); // A buffer to receive the product code GUID. // This buffer must be 39 characters long. // The first 38 characters are for the GUID, and the last character is for // the terminating null character. [DllImport ("msi.dll", CharSet = CharSet.Unicode, SetLastError = true)] internal static extern Int32 MsiGetProductInfo (string product, string property, StringBuilder valueBuf, ref Int32 cchValueBuf); } class Program { static int GetProperty(string productCode, string propertyName, StringBuilder sbBuffer) { int len = sbBuffer.Capacity; sbBuffer.Length = 0; int status = NativeMethods.MsiGetProductInfo (productCode, propertyName, sbBuffer, ref len); if (status == NativeMethods.ErrorMoreData) { len++; sbBuffer.EnsureCapacity (len); status = NativeMethods.MsiGetProductInfo (productCode, propertyName, sbBuffer, ref len); } if ((status == NativeMethods.ErrorUnknownProduct || status == NativeMethods.ErrorUnknownProperty) && (String.Compare (propertyName, "ProductVersion", StringComparison.Ordinal) == 0 || String.Compare (propertyName, "ProductName", StringComparison.Ordinal) == 0)) { // try to get vesrion manually StringBuilder sbKeyName = new StringBuilder (); sbKeyName.Append ("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\S-1-5-18\\Products\\"); Guid guid = new Guid (productCode); byte[] buidAsBytes = guid.ToByteArray (); foreach (byte b in buidAsBytes) { int by = ((b & 0xf) << 4) + ((b & 0xf0) >> 4); // swap hex digits in the byte sbKeyName.AppendFormat ("{0:X2}", by); } sbKeyName.Append ("\\InstallProperties"); RegistryKey key = Registry.LocalMachine.OpenSubKey (sbKeyName.ToString ()); if (key != null) { string valueName = "DisplayName"; if (String.Compare (propertyName, "ProductVersion", StringComparison.Ordinal) == 0) valueName = "DisplayVersion"; string val = key.GetValue (valueName) as string; if (!String.IsNullOrEmpty (val)) { sbBuffer.Length = 0; sbBuffer.Append (val); status = NativeMethods.NoError; } } } return status; } static void Main () { string upgradeCode = "{00140000-001A-0000-0000-0000000FF1CE}"; StringBuilder sbProductCode = new StringBuilder (39); StringBuilder sbProductName = new StringBuilder (); StringBuilder sbProductVersion = new StringBuilder (1024); for (int iProductIndex = 0; ; iProductIndex++) { int iRes = NativeMethods.MsiEnumRelatedProducts (upgradeCode, 0, iProductIndex, sbProductCode); if (iRes != NativeMethods.NoError) { // NativeMethods.ErrorNoMoreItems=259 break; } string productCode = sbProductCode.ToString(); int status = GetProperty (productCode, "ProductVersion", sbProductVersion); if (status != NativeMethods.NoError) { Console.WriteLine ("Can't get 'ProductVersion' for {0}", productCode); } status = GetProperty (productCode, "ProductName", sbProductName); if (status != NativeMethods.NoError) { Console.WriteLine ("Can't get 'ProductName' for {0}", productCode); } Console.WriteLine ("ProductCode: {0}{3}ProductName:'{1}'{3}ProductVersion:'{2}'{3}", productCode, sbProductName, sbProductVersion, Environment.NewLine); } } } } 获取信息。如果您对所有用户安装的产品感兴趣,则必须修改该程序。这是代码

ProductCode: {90140000-001A-0407-0000-0000000FF1CE}
ProductName:'Microsoft Office Outlook MUI (German) 2010'
ProductVersion:'14.0.4763.1000'

ProductCode: {90140000-001A-0419-0000-0000000FF1CE}
ProductName:'Microsoft Office Outlook MUI (Russian) 2010'
ProductVersion:'14.0.4763.1000'

在我的电脑上产生正确的输出

ProductVersion

而不是{{1}}之前的错误。

答案 1 :(得分:4)

您应该查看Windows Installer XML的部署工具基础。它有一个非常成熟的MSI Interop(Microsoft.Deployment.WindowsInstaller),它将使编写和测试此代码变得更加容易。

我看到你已经有了WiX(希望是v3 +),所以在C:\ Program Files \ Windows Installer XML v3 \ SDK文件夹中查找它。