論旨
Python
でCOMオブジェクトを操作するcomtypes
へコントリビュートを行い、私が追加した機能を含むものがバージョン1.1.13
で最近リリースされたのでその紹介です。
追加機能概要
client.GetModule(("{ライブラリのGUID}",))
でモジュールが生成・取得できるようになります。
GUIDとは
Globally Unique Identifier
「グローバル一意識別子」のこと。
{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
(xは16進数)で表現されます。
COMオブジェクトやCOM型ライブラリは、(レジストリに登録されていれば)この一意のIDから呼び出すことが可能です。
https://ja.wikipedia.org/wiki/GUID
Examples
from comtypes.client import GetModule
GetModule(("{00020813-0000-0000-C000-000000000046}",)) # Excel
GetModule(("{00020905-0000-0000-C000-000000000046}",)) # Word
従来機能との違い
これまでもGetModule
に様々な引数を渡すことでCOM型ライブラリからPythonのモジュールを生成/呼び出すことは可能でした。
# `(GUID, メジャーバージョン, マイナーバージョン)`のタプル
GetModule(("{00020813-0000-0000-C000-000000000046}", 1, 9)) # Excel
# 型ライブラリ情報があるファイルのフルパス
GetModule("C:\\Program Files\\Microsoft Office\\Root\\Office16\\EXCEL.EXE") # Excel
# 環境変数にアサインされたDLL(TLB)名
GetModule("UIAutomationCore.dll") # UIAutomationClient
GetModule("stdole2.tlb") # stdole
# GetModule("EXCEL.EXE") # これは(デフォルトで)環境変数にアサインされていないため不可
ですが、環境によってライブラリのメジャーバージョンやマイナーバージョン、型ライブラリファイルのフルパスは異なります。
client.CreateObject("Excel.Application")
のようにオブジェクトの生成は環境が違ってもクラス名文字列(VersionIndependentProgID
)のみでバージョン情報が不要だったのに対し、client.GetModule
によるモジュールの生成はバージョン情報の指定が必要でした。
今回の機能追加によって、ライブラリGUIDのみのタプルが渡された場合にはclient.GetModule
関数内部でレジストリを調べて規定値のバージョンを取得してモジュールを生成できるようになります。
新機能がどう役立つか
私が参画しているプロジェクトでは、comtypes
でExcelを操作する際にcomtypes.gen.Excel
モジュールが環境内で確実に存在する必要がある実装をしているスクリプトがあります。
そのためにスクリプト実行前にclient.CreateObject("Excel.Application")
でアプリケーションオブジェクトを生成して、その副作用によってcomtypes.gen.Excel
モジュールが作成されるようにしていました。
しかし、オブジェクトを操作したいわけでもないのにオブジェクトを生成していることは不健全です。
- オブジェクトの生成に時間がかかります(手元環境で31.79秒)
- ユーザーがスクリプトの本処理が始まらないことに焦れて途中で処理を終了させると、
comtypes.gen.Excel
モジュールの作成に失敗して、一度gen
フォルダを消さない限り何度再実行してもスクリプトが異常終了し続けることがあります
代わりに、client.GetModule(("{00020813-0000-0000-C000-000000000046}",))
を使うことで、オブジェクトを生成することなくモジュールが生成できるため
- 時間がかからない(手元環境で3.33秒)
- ユーザーが焦れる要因が少なくなる
- 後続処理を早く進める
ことが可能となります。
今後
現在、私はcomtypes
へのコントリビュートを続けています。
機能追加の計画やバグフィックスなど最新の情報はリポジトリをご覧ください。
みなさんのコントリビュートもお待ちしております。