Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/282.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用MSIEnumRelatedProducts和MSIGetProductInfo的MSI互操作_C#_C++_Winapi_Windows Installer - Fatal编程技术网

C# 使用MSIEnumRelatedProducts和MSIGetProductInfo的MSI互操作

C# 使用MSIEnumRelatedProducts和MSIGetProductInfo的MSI互操作,c#,c++,winapi,windows-installer,C#,C++,Winapi,Windows Installer,在使用MSI互操作API时,我遇到了一些异常行为,导致我的应用程序崩溃。“处理”这个问题很简单,但我想知道更多关于“为什么”会发生这种情况的信息 我对MSIEnumRelatedProducts的第一次调用返回值0,并将字符串缓冲区正确设置为productcode。我的理解是,只有当给定的升级代码(作为parm传递给方法)当前安装了“相关系列产品”时,才会发生这种情况,否则它将返回259个错误项 但是,当我随后使用相同的productcode调用MSIGetProductInfo时,我得到了返回

在使用MSI互操作API时,我遇到了一些异常行为,导致我的应用程序崩溃。“处理”这个问题很简单,但我想知道更多关于“为什么”会发生这种情况的信息

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

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

有人知道在什么情况下会发生这种情况吗?它在一台机器上100%可重复,但我还没有在另一台机器上获得复制步骤

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

任何想法/建议都将不胜感激

谢谢 本

更新: 我注意到,当运行带有日志记录的问题msi包时,会显示以下行:

MSI(88:68)[12:15:50:235]:FindRelatedProducts:无法读取产品“{840C…etc…96}”的分配类型信息。跳过

有人知道这意味着什么吗

更新:代码示例。

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);

我假设您尝试使用获取文档中描述的其他属性。例如,您可以毫无问题地获取
“PackageCode”
属性(
INSTALLPROPERTY\u PackageCode
)的值,但是您无法获取与1605错误(
error\u UNKNOWN\u PRODUCT
)相关的
“UpgradeCode”
属性的值并接收该错误

更新:好的,现在我明白你的问题了。你如何在互联网上发现有一个漏洞,所以它并不总是有效的。有时它会返回1605(
ERROR\u UNKNOWN\u PRODUCT
)或1608(
ERROR\u UNKNOWN\u PROPERTY
)。在这种情况下,唯一的解决方法是手动获取version属性。我可以用Microsoft Office Outlook 2010 MUI(UpgradeCode=“{00140000-001A-0000-0000-0000000 FF1CE}”)重现您在我的计算机上描述的问题,并编写一个解决方法,从注册表获取产品版本。在本例中,我仅从
HKEY\U LOCAL\U 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);
            }
        }
    }
}

与之前的
ProductVersion
中的错误不同。

我假设您尝试使用获取文档中描述的其他属性。例如,您可以毫无问题地获取
“PackageCode”
属性(
INSTALLPROPERTY\u PackageCode
)的值,但是您无法获取与1605错误(
error\u UNKNOWN\u PRODUCT
)相关的
“UpgradeCode”
属性的值并接收该错误

更新:好的,现在我明白你的问题了。你如何在互联网上发现有一个漏洞,所以它并不总是有效的。有时它会返回1605(
ERROR\u UNKNOWN\u PRODUCT
)或1608(
ERROR\u UNKNOWN\u PROPERTY
)。在这种情况下,唯一的解决方法是手动获取version属性。我可以用Microsoft Office Outlook 2010 MUI(UpgradeCode=“{00140000-001A-0000-0000-0000000 FF1CE}”)重现您在我的计算机上描述的问题,并编写一个解决方法,从注册表获取产品版本。在本例中,我仅从
HKEY\U LOCAL\U 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);
            }
        }
    }
}

而不是在<代码>产品版本之前的错误。

< P>您应该查看Windows安装程序XML的部署工具基础。它有一个非常成熟的MSI互操作(Microsoft.Deployment.WindowsInstaller),这将使编写和测试此代码更加容易


<>我看到你已经有了WIX(希望V3+),所以在C:\程序文件\ Windows安装程序XML V3\SDK文件夹中查找它。

< P>你应该查看Windows安装程序XML的部署工具基础。它有一个非常成熟的MSI互操作(Microsoft.Deployment.WindowsInstaller),这将使编写和测试此代码更加容易


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

尝试获取PackageCode时返回相同的1605。这似乎没有什么不同,如果API认为产品没有安装,那么我认为更改属性不会有什么不同。不过谢谢你的建议。@Ben Cawley:我可以给你发一些工作代码示例。我想知道你到底想要什么。你知道升级代码并尝试获取相关产品的信息吗?或者你想获取只有ProductCode的升级代码的信息吗?嗨,Oleg,我确实有适用于大多数情况的工作代码。不幸的是,由于msi包或我试图安装它的机器的某些奇怪状态,似乎正是这个“一个”msi导致了问题。我正在使用已知的“升级代码”获取产品代码。我将用代码示例更新我的问题。嗨,Oleg,谢谢你提供的信息。我确实试着先用谷歌搜索这个问题,但什么也没找到。你能发布关于MSIGetProductInfo中已知错误的链接吗?你知道MSIGetProductInfoEx是否也有同样的问题吗?我可以先试试。谢谢同时,提姆