MsiOpenProduct是从正确的产品中读取属性的正确方法吗?

时间:2010-06-23 13:32:23

标签: c++ windows windows-installer

鉴于MSI产品代码,我希望从已安装的产品中获取升级代码(以及其他属性)。我通过调用MsiOpenProduct方法,然后调用MsiGetProductProperty()来尝试这个。 (缩写)示例如下所示:

MSIHANDLE handle = NULL;
MsiOpenProduct(strProductCode,&handle);
CString strUpgradeCode;
MsiGetProductProperty(handle,_T("UpgradeCode"), strUpgradeCode.GetBuffer(GUID_LENGTH), &dwSize);
strUpgradeCode.ReleaseBuffer();
MsiCloseHandle(handle);

这让我得到了理想的值,从MSDN文档来看,这似乎是一种有效的方法:

  

MsiOpenProduct函数打开一个   与功能一起使用的产品   访问产品数据库。该   必须调用MsiCloseHandle函数   当手柄没有时用手柄   需要更长时间。

但是,调用MsiOpenProduct()会弹出“Windows安装程序正在准备安装...”对话框。对MsiCloseHandle()的调用使它再次消失。

这让我想知道:

  • 对MsiOpenProduct()的调用是什么?我不想触发任何动作,我只想读取属性。
  • 我不介意弹出对话框,因为这只适用于单元测试代码,只要这没有副作用。由于有许多单元测试可以执行此操作,因此在快速连续打开和关闭句柄时它仍然可以正常工作。
  • 虽然我偶然发现了MsiGetProductInfo方法,但似乎无法获得升级代码。我是对的吗?
  • MsiOpenProduct是读取升级代码等属性的正确方法吗?

3 个答案:

答案 0 :(得分:3)

MsiOpenProduct应该没问题只要你不运行任何序列或动作,它就不会做任何事情。如果要使对话框静音,可以小心使用MsiSetInternalUI()或MsiSetExternalUI()。

您可以采取的另一种方法,只要ProductCodeUpgradeCode安全静态(即只要它们不被变换更改),就是使用MsiGetProductInfo()定位数据库并在其上调用MsiOpenDatabase()。区别在于MsiOpenProduct()(或类似的MsiOpenPackage)应用在安装时使用的转换并准备会话,而MsiOpenDatabase()则不应用。

答案 1 :(得分:1)

对于您想要的信息,听起来您可以调用:: MsiGetProductInfo()。 :: MsiOpenDatabase()是一个非常慢的操作,而:: MsiGetProductInfo()是(IIRC)更符合注册表查找。

答案 2 :(得分:1)

有一个全面的答案,其中提供了有关如何使用 Powershell VBScript WMI 获取UpgradeCode的信息:How can I find the Upgrade Code for an installed MSI file?

下面是一个使用VBScript / COM自动化( MSI API ,而不是WMI)的快速基本示例以及OP讨论的方法(使用OpenProduct方法 - 相当于Win32安装程序功能的COM)

正如我在上面的评论中所讨论的,我将添加这个小VBScript,就像OP在C ++中一样。请注意,可以通过Windows Installer(Win32_Product对象),WMICOM automation访问Win32 C++ installer functions

由于某些原因,程序包的UpgradeCode似乎无法直接从COM API或Win32 API中获得。确实非常奇怪,特别是因为它是Installer.RelatedProducts等函数的输入参数 - 文档中不清楚实际调用应该是RelatedProducts(UpgradeCode),而是查看msi.IDL你看到的:StringList* RelatedProducts([in] BSTR UpgradeCode);

WMI选项有效,但下面显示的这个OpenProduct调用也是如此(显然更快,看起来很安全 - 据我所知,WMI完全是只读的 - 但天知道他们在做什么“ “它们是否会启动会话对象?或者它们是否正在从WMI数据库中读取?WMI确实”感觉“更安全”。

以下方法的优点在于它将应用在安装时应用于相关产品的所有变换。如果你想写入磁盘而不是显示消息框而且无法查找文档,这里有一个类似的VBScript,它将包信息写入桌面文本文件:How can I find the product GUID of an installed MSI setup? - 相当多的页面,只需复制几行,你就可以免费留言了。)

  

注意!如果在系统上启用automatic logging,则下面的脚本将为每个打开的MSI创建一个日志文件。目前,脚本只会在它存在之前打开一个MSI(Exit For构造)。

On Error Resume Next ' This "tersified" script has no error handling

Const msiUILevelNone = 2
Set installer = CreateObject("WindowsInstaller.Installer")
Set products = installer.ProductsEx("", "", 7)
installer.UILevel = msiUILevelNone ' Suppress GUI (MSI progress dialog)

'Iterate over all MSI packages on the box
For Each product In products
   ' productcode = product.ProductCode
   ' name = product.InstallProperty("ProductName")
   ' version = product.InstallProperty("VersionString")
   ' pkgcode = product.InstallProperty("PackageCode")

   Set session = installer.OpenProduct(product.ProductCode)
   upgradecode = session.ProductProperty("UpgradeCode")
   MsgBox upgradecode
   Set session = Nothing ' Important, close the session (doesn't work in Javascript btw)

   Exit For   ' End after one iteration (so you don't get a million message boxes)
              ' Alternatively something like: If i > 4 Then Exit For
Next

Set installer = Nothing

MsgBox "Finished"

我试图查看C ++ Win32安装程序函数以获取任何其他方法来检索UpgradeCode,但我看不到任何明显的东西。会话方法也应该在C ++中工作,但我对句柄和资源的释放有点担心。我没有用C ++进行过适当的训练,但知道的不仅仅是危险的。 在洞中射击。等...

我想知道OP是否检索了盒子上的所有包,或者只是一个包。我想知道用Javascript看到的时序问题和并发会话对象问题是否会在C ++中出现?我想 - 有一天我会试一试。

相关问题