Является ли MsiOpenProduct правильным способом считывания свойств с установленного продукта? - PullRequest
3 голосов
/ 23 июня 2010

Учитывая код продукта 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 ]

3 голосов
/ 24 июня 2010

MsiOpenProduct должно быть в порядке. Пока вы не выполняете никаких последовательностей или действий, он ничего не будет делать.Если вы хотите заставить диалог замолчать, вы можете осторожно использовать MsiSetInternalUI () или MsiSetExternalUI ().

Другой подход, который вы можете использовать, если ProductCode и UpgradeCode безопасно статичны (т. е. до тех пор, пока они не изменяются преобразованиями), это найти базу данных с помощью MsiGetProductInfo () и вызвать MsiOpenDatabase () для этого.Разница в том, что MsiOpenProduct () (или аналогично MsiOpenPackage) применяет преобразования, которые использовались во время установки, и подготавливает сеанс, тогда как MsiOpenDatabase () не выполняет ни того, ни другого.

1 голос
/ 05 февраля 2018

Подробный ответ с информацией о том, как получить код UpgradeCode с использованием Powershell или VBScript и WMI здесь: Как найтиКод обновления для установленного файла MSI?

Ниже приведен краткий базовый пример с использованием автоматизации VBScript / COM ( MSI API , а не WMI) и подходом, обсужденным OP (с использованиемметод OpenProduct - COM, эквивалентный функции установщика Win32.


Как обсуждалось в моем комментарии выше, я просто добавлю этот небольшой VBScript, чтобы сделать то же самое, что делает OP в C ++.Обратите внимание, что Установщик Windows может быть доступен через WMI (объект Win32_Product), Автоматизация COM * и Функции установщика Win32 C ++ .

По какой-то причине UpgradeCode для пакета, по-видимому, недоступен непосредственно из COM API или Win32 API.Очень странно, тем более, что это входной параметр для таких функций, как Installer.RelatedProducts - в документации не ясно, что фактический вызов должен быть RelatedProducts(UpgradeCode), но, глядя на msi.IDL, вы видите: StringList* RelatedProducts([in] BSTR UpgradeCode);

Опция WMI работает, но также работает этот вызов OpenProduct, показанный ниже (который значительно быстрее и выглядит безопасным - насколько я знаю, WMI полностью доступен только для чтения - но небеса знаютчто они делают «под капотом». Они раскручивают объект сеанса? Или они читают из базы данных WMI? WMI каким-то образом «чувствует себя безопаснее».

Прелесть метода, приведенного ниже, заключается в том, чтоон будет применять все преобразования, которые были применены к рассматриваемому продукту во время установки.Если вы хотите записать на диск вместо того, чтобы показывать окна сообщений и не можете потрудиться, просматривая документы, вот аналогичный VBScript, который записывает информацию о пакете в текстовый файл рабочего стола: Как мне найти GUID продукта дляустановка MSI? - совсем немного вниз по странице, просто скопируйте пару строк, и вы свободны от окна сообщения).

Примечание! Сценарий нижесоздайте один файл журнала для каждого открытого 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"

Я попытался найти в функциях установки C32 Win32 любой другой способполучить код UpgradeCode, но я не вижу ничего очевидного.Сеансовый подход должен работать и в C ++, но я немного опасаюсь за выпуск дескрипторов и ресурсов.Я недостаточно хорошо обучен на C ++, но знаю более чем достаточно, чтобы быть опасным. Огонь в дыре .И т.д. ...

Интересно, OP извлек все пакеты на коробке или только один.Интересно, возникнут ли проблемы синхронизации и одновременных проблем с объектами сеанса в Javascript в C ++?Я попробую, думаю, когда-нибудь.

1 голос
/ 11 июля 2010

Для информации, которую вы хотите, звучит так, как будто вы можете просто позвонить :: MsiGetProductInfo (). :: MsiOpenDatabase () - очень медленная операция, в то время как :: MsiGetProductInfo () - это (IIRC) больше по сравнению с поиском в реестре.

...