msiexec скриптинг в питоне - PullRequest
0 голосов
/ 14 марта 2011

В основном это фон, пропустите следующие 3 абзаца для вопроса:

Я разработал инструмент, который вызывает некоторые установщики, изменяет элементы реестра и перемещает файлы, чтобы помочь мне протестировать продукт, который имеетдовольно быстрый цикл обновления.Пока все хорошо, у меня есть графический интерфейс, который выполняется в отдельном процессе и бизнес-логике, чтобы предотвратить его блокировку из-за GIL, все работает и т. Д., Но у меня есть проблемы с частью моего кода, где я делаю вызовы msiexec.

В частности, это часть удаления, которая вызывает у меня беспокойство.В настоящее время GUID не изменяется, поэтому я могу удалить продукт с помощью os.system('msiexec /x "{GUID}" /passive').На самом деле все немного сложнее, так как я использую subprocess.Popen и опрашиваю его до тех пор, пока он не завершится из цикла событий, чтобы обеспечить параллелизм с другими шагами.

Меня беспокоит, что в случае изменения GUID, очевидно, это не будет работать.Я не хочу указывать msiexec непосредственно на источник установки, так как это означало бы, что это не сработало бы, если бы я «потерял» файл msi, который я храню во временном каталоге.

То, что я ищу, - это способ запроса по имени программы, чтобы получить GUID, или даже обертка для msiexec, которая сделала бы все это, включая удаление, для меня.Я думал о сканировании через реестр, но модуль _winreg кажется очень медленным, поэтому я предпочел бы избежать этого, если это вообще возможно.Если есть лучший способ сканирования реестра, я на все уши, так как это ускорит и другие части инструмента.


Update0

Производительность на этом критически важна, так какодна из целей разработки - сделать процесс, которому инструмент следует, быстрее любого другого метода, ручного или иного, чтобы добиться массового внедрения.


Update1

Я пробовалнебольшое изменение версии реестра ниже, однако оно последовательно возвращает None.Я не совсем уверен, как это происходит - кажется, что он не может открыть соответствующий ключ, поскольку я вставил точку останова после оператора with, который никогда не достигается ...

def get_guid_by_name(name):
  from _winreg import (OpenKey,
                       QueryInfoKey,
                       EnumKey,
                       QueryValueEx,
                       HKEY_LOCAL_MACHINE,
                       )
  with OpenKey(HKEY_LOCAL_MACHINE, 
               r'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall') as key:
      subkeys, _0, _1 = QueryInfoKey(key) # The breakpoint here is never reached
      del _0, _1
      for i in range(subkeys):
        subkey = EnumKey(key, i)
        if subkey[0] != '{' or subkey[-1] != '}':
          continue
        with OpenKey(key, subkey) as _subkey:
          if name in QueryValueEx(_subkey, 'DisplayName')[0]:
            return subkey
  return None

  print get_guid_by_name('Microsoft Visual Studio')

Обновление2

Ударьте это - я дурак, который недостаточно тщательно проверяет свои отступы - print get_guid_by_name('Microsoft Visual Studio') был в пределах get_guid_by_name ...

1 Ответ

1 голос
/ 14 марта 2011

Я не уверен, что модуль _winreg работает так медленно. Я полагаю, что если вы пытаетесь перечислить весь реестр, чтобы найти все экземпляры строки, которые могут занять некоторое время, но при правильно ориентированном запросе это кажется достаточно быстрым.

Вот пример:

from _winreg import *

def get_guid_by_name(name):
    # Open the uninstaller key
    with OpenKey(HKEY_LOCAL_MACHINE, r'Software\Microsoft\Windows\CurrentVersion\Uninstall') as key:
        # We only care about subkeys of the installer key
        subkeys, _, _ = QueryInfoKey(key)
        for i in range(subkeys):
            subkey = EnumKey(key, i)
            # Since we're looking for uninstallers for MSI products,
            # the key name will always be the GUID. We assume that any
            # key starting with '{' and ending with '}' is a GUID, but
            # if not the name won't match.
            if subkey[0] != '{' or subkey[-1] != '}':
                 continue
            # Query the display name or other property of the key to
            # see if it's the one we want
            with OpenKey(key, subkey) as _subkey:
                if QueryValueEx(_subkey, 'DisplayName')[0] == name:
                    return subkey
     return None

На моей машине, запрашивая ActiveState Komodo Edit (я фактически использовал регулярное выражение, а не сравнение с прямыми значениями), 1000 итераций этого заняли 8,18 секунды (по времени, используя timeit), что кажется мне незначительным количеством времени , Еще лучше, вы можете извлечь ключ UninstallString из реестра и передать его прямо в ваш подпроцесс (хотя вы можете добавить ключ /passive в конец.


Редактировать

Microsoft, конечно, предоставляет класс WMI ( Win32_Product ), который предоставляет довольно удобный интерфейс для всего этого. Используя превосходную оболочку WMI Тима Голдена , можно начать установку следующим образом:

import wmi
c = wmi.WMI()
c.Win32_Product(Name = 'ProductName')[0].Uninstall()

Однако, как отмечалось в этом сообщении в блоге , класс Win32_Product чрезвычайно, очень болезненно медленен в использовании.

...