Python 

Python | AdobeアプリのバージョンをPythonで調べてみた

2024/09/26

DCCツールのランチャーを開発することがあり、SubstancePainterなどのAdobeアプリのバージョン情報をAPIで参照できないか?と色々調べたところ、色々面白い知見が得られたので共有しようと思います。

レジストリキーを調べる

アドビの公式のヘルプによると、アドビアプリのバージョンを調べるには「シェルを叩いてレジストリキーを調べろ」と書いてありました。▼

https://helpx.adobe.com/jp/enterprise/kb/how-to-determine-which-versions-of-adobe-applications-are-instal.html

実際にPowerShellでコマンドを実行してみると…

PS > Get-ItemProperty HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* | Select-Object DisplayName, DisplayVersion, Publisher, InstallDate | Format-Table –AutoSize | findstr Adobe
                                               Adobe Inc.
Adobe Creative Cloud           6.4.0.361       Adobe Inc.
                                               Adobe Inc.
                                               Adobe Inc.
                                               Adobe Inc.
                                               Adobe Inc.
                                               Adobe Inc.
                                               Adobe Inc.
                                               Adobe Inc.
                                               Adobe Inc.
Adobe Photoshop 2024           25.5.1.408      Adobe Inc.
Adobe Substance 3D Designer    13.1.1          Adobe Inc.
Adobe Substance 3D Painter     9.1.2           Adobe Inc.
UXP WebView Support            1.1.0           Adobe Inc.

Adobeアプリとバージョン情報の一覧が返ってきました。
レジストリエディタでキーの場所を見てみると、こんな感じになっていました。▼


ボーンデジタルさんの記事によると、レジストリでは一意のGUIDでアプリケーションを識別しているようです。▼

https://support.borndigital.co.jp/hc/ja/articles/360006691854-%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%
BC%E3%83%AB%E3%83%91%E3%82%B9%E3%81%AE%E5%8F%96%E5%BE%97

つまり、SubstanceアプリのGUIDが分かれば、そのキーの DisplayVersion の値を調べてアプリのバージョンを特定できるということです。

レジストリキーって?

そもそもレジストリキーって何でしょう?

レジストリキーは、WindowsがOSやアプリケーション、ハードウェアなどの 設定や構成を記録するためのデータベース です。

データの構成

レジストリは、キー のペアで情報を保存しています。
キーは固有の値と、別のキー (サブキー) を格納することができます。

サブキーは入れ子のようにさらにサブキーを格納して、階層構造を作ることができます。

キーはディレクトリやファイルと同じように、¥ でパス指定して値を参照します。

Uninstallキー

先ほどシェルコマンドで参照した Uninstall キーは、インストールされているソフトウェアの情報を格納しているキーです。

https://learn.microsoft.com/ja-jp/windows/win32/msi/uninstall-registry-key

Uninstallキーには、アプリケーションを識別するための一意のIDがつけられたサブキーが格納されていて、
そのサブキーには DisplayNameDisplayVersion などの、それぞれのアプリケーション固有の情報が値として保存されています。

AdobeアプリのGUIDを特定する

では、まずはAdobeアプリのGUIDを特定するために、GUIDとDisplayNameを一覧化してみます。

winreg という、レジストリを操作するための標準モジュールを使います。
(winregについての解説はネット上に他の記事はたくさんあるので割愛します。)

winreg --- Windows レジストリへのアクセス — Python 3.12.6 ドキュメント https://docs.python.org/ja/3/library/winreg.html


サンプルコード ▼

import winreg
import os
import pprint

ROOT_KEY = "Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall"
GUID_DICT = {}

with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, ROOT_KEY) as key:
    # サブキーの数でループ
    for i in range(winreg.QueryInfoKey(key)[0]):
        key_name = winreg.EnumKey(key, i)

        # サブキーを開く
        with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE,
                "{}\\{}".format(ROOT_KEY, key_name)) as subkey:
            value_dict = {} # 値の名前とデータのペアを格納

            # 値の数でループ
            for j in range(winreg.QueryInfoKey(subkey)[1]):
                curr_value = winreg.EnumValue(subkey, j)
                value_dict[curr_value[0]] = curr_value[1]
        
        GUID_DICT[key_name] = value_dict

# 外部ファイルに出力
log_file = os.path.join(os.getcwd(), "GUID.log")
with open(log_file, "w", encoding="utf-8") as f:
    format_dict = pprint.pformat(GUID_DICT, indent=2)
    print(format_dict, file=f)

出力した結果、こんな感じになりました。(一部) ▼

'Unity 2022.3.17f1': {
    'DisplayIcon': 'C:\\Program '
    'Files\\Unity\\Hub\\Editor\\2022.3.17f1\\Editor\\Unity.exe',
    'DisplayName': 'Unity 2022.3.17f1',
    'DisplayVersion': '2022.3.17f1',
    'Publisher': 'Unity Technologies ApS',
    'URLInfoAbout': 'http://www.unity3d.com',
    'UninstallString': 'C:\\Program '
    'Files\\Unity\\Hub\\Editor\\2022.3.17f1\\Editor\\Uninstall.exe'},

    ・・・

GUIDと値が出力できたので、この中から DisplayName「Adobe」 が含まれているキーを抽出すればAdobeアプリケーションのGUIDを特定できそうです。

# 外部ファイルに出力
log_file = os.path.join(os.getcwd(), "GUID.log")
with open(log_file, "w", encoding="utf-8") as f:
    for k, v in GUID_DICT.items():
        if "DisplayName" in v.keys():
            if "Adobe" in v["DisplayName"]:
                print("{} : {}".format(k, v["DisplayName"]), file=f)

出力の部分を変えてみたところ、こんな感じになりました。▼

Adobe Creative Cloud : Adobe Creative Cloud
PHSP_25_5_1 : Adobe Photoshop 2024
SBSTD_13_1_1 : Adobe Substance 3D Designer
SBSTP_9_1_2 : Adobe Substance 3D Painter

これでGUIDが特定できました。

レジストリエディタでキーを見てみると、確かにAdobeアプリケーションのバージョン情報が参照できるのが分かります。▼

レジストリキーはバージョン番号が接尾語として付いているみたいなので、GUIDを決め打ちの文字列で指定するのはできなさそうですね。。

バージョン情報を取得する

では、レジストリの情報から実際にバージョン情報を取り出してみます。
サンプルとして、Adobeアプリケーションのランチャーを作ってみました。▼

import sys
import winreg

from PySide6 import QtWidgets


ROOT_KEY = "Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall"
GUID_DICT = {}

with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, ROOT_KEY) as key:
    # サブキーの数でループ
    for i in range(winreg.QueryInfoKey(key)[0]):
        key_name = winreg.EnumKey(key, i)
        # サブキーを開く
        with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE,
                            "{}\\{}".format(ROOT_KEY, key_name)
                            ) as subkey:
            value_dict = {} # 値の名前とデータのペアを格納
            # 値の数でループ
            for j in range(winreg.QueryInfoKey(subkey)[1]):
                curr_value = winreg.EnumValue(subkey, j)
                value_dict[curr_value[0]] = curr_value[1]
        
        GUID_DICT[key_name] = value_dict

def get_adobe_apps_versions() -> dict:
    version_dict = {}
    for v in GUID_DICT.values():
        if "DisplayName" in v.keys():
            if "Photoshop" in v["DisplayName"]:
                version_dict["Photoshop"] = v["DisplayVersion"]
            elif "Substance 3D Painter" in v["DisplayName"]:
                version_dict["Substance 3D Painter"] =
                v["DisplayVersion"]
            elif "Substance 3D Designer" in v["DisplayName"]:
                version_dict["Substance 3D Designer"] =
                v["DisplayVersion"]
    return version_dict

class AppLauncher(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.version_dict = get_adobe_apps_versions()
        self.setWindowTitle("AdobeAppLauncher")
        self.setMinimumWidth(400)
        self.setMinimumHeight(200)
        self._init_ui()
    
    def _init_ui(self):
        self.main_widget = QtWidgets.QWidget()
        self.setCentralWidget(self.main_widget)
        self.main_grid = QtWidgets.QGridLayout()
        self.main_widget.setLayout(self.main_grid)

        # Photoshop
        self.main_grid.addWidget(QtWidgets.QLabel("Photoshop"), 0, 0)
        self.main_grid.addWidget(QtWidgets.QLabel(
            self.version_dict["Photoshop"]), 0, 1)
        self.main_grid.addWidget(
            QtWidgets.QPushButton("Launch"), 0, 2)
        # Substance Painter
        self.main_grid.addWidget(
            QtWidgets.QLabel("Substance Painter"), 1, 0)
        self.main_grid.addWidget(QtWidgets.QLabel(
            self.version_dict["Substance 3D Painter"]), 1, 1)
        self.main_grid.addWidget(
            QtWidgets.QPushButton("Launch"), 1, 2)
        # Substance Designer
        self.main_grid.addWidget(
            QtWidgets.QLabel("Substance Designer"), 2, 0)
        self.main_grid.addWidget(QtWidgets.QLabel(
            self.version_dict["Substance 3D Designer"]), 2, 1)
        self.main_grid.addWidget(
            QtWidgets.QPushButton("Launch"), 2, 2)

if __name__ == "__main__":
    app = QtWidgets.QApplication()
    win = AppLauncher()
    win.show()
    app.exec()
    sys.exit()

実行すると、こんな感じです。▼

Adobeアプリのバージョンが取得できました!

まとめ

UninstallキーにはPCにインストールされているあらゆるアプリケーションの情報が格納されているので、この技術を応用すれば色々なことができそうですね。