使用MSIEnumRelatedProducts和MSIGetProductInfo的MSI Interop

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

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

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

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

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

任何想法/建议表示赞赏。

谢谢本

更新:我注意到在运行问题msi软件包时记录了以下行:

MSI(s)(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); 

我想您尝试使用MsiGetProductInfo获取其他属性,如文档中所述。 例如,您可以顺利地获取"PackageCode"属性( INSTALLPROPERTY_PACKAGECODE )的值而没有任何问题,但是您无法获得关于MsiGetProductInfo的"UpgradeCode"属性的值并收到错误1605( ERROR_UNKNOWN_PRODUCT )。

更新 :好的,现在我明白了你的问题。 你如何在互联网上找到MsiGetProductInfo中的一个错误,所以它并不总是如此。 有时它会返回1605( ERROR_UNKNOWN_PRODUCT )或1608( ERROR_UNKNOWN_PROPERTY )。 在这种情况下,唯一的解决方法是手动获取版本属性。 我可以使用Microsoft Office Outlook 2010 MUI(UpgradeCode =“{00140000-001A-0000-0000-0000000FF1CE}”)重现您在我的计算机上描述的问题,并编写了一个解决方法,我从注册表中获取产品版本。 在示例中,我仅从HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products 。 如果您对所有用户安装的产品感兴趣,则必须修改该程序。 这是代码

 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的错误。

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

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