オートメーションサーバ
pywin32は、win32com.serverパッケージに
オートメーションサーバとしての機能を実装しています。
また、CLSIDなどの128ビットGUIDを生成する機能はpythoncomパッケージにあります。
pythonのオートメーションサーバは次の図のように、
dll、exe両対応のIDispatchインターフェースを公開します。
COMサーバは次の手順で実装します。
CLSIDの生成
pythonクラスの実装
サーバ登録部分の実装
CLSIDの生成
サーバの実装にはCLSIDが必要です。
オートメーションではインターフェースとしてIDispatchを使いますので、
新しいIIDを生成する必要はありません。
次のコマンドを実行すると、pythonでCLSIDを生成できます。
C:\> python
>>> import pythoncom
>>> pythoncom.CreateGuid()
pythonクラスの実装
COMとして公開したいクラスは、通常のpythonクラスとして実装します。
実装の際にCOMの情報を追加しておくことで、
次の簡易サーバ登録関数が使えるようになります。
例えば、文字列を引数にとり、それをログファイルに書き出すコードは次のようになります。
class PyIchigo1:
def __init__(self):
self.filename = 'c:/tmp/pyichigo1.txt'
def Write(self, logstr):
with open(self.filename,'a') as f:
f.write(logstr)
win32com.server.register.RegisterClasses()は、
COM情報をクラス変数の形で与えると、その情報を登録します。
クラス変数として次の情報を与えることができます。
最低でもCLSID、ProgID、公開メソッドは与える必要があるでしょう。
変数 | 内容 |
_public_methods_ | 公開するメソッド名をpythonのシーケンスとして与えます。 |
_reg_clsid_ | 自分のクラスのために生成したCLSIDを指定します。必須です。 |
_reg_progid_ | ProgIDとして使う文字列を与えます。必須です。 |
_reg_verprogid_ | ProgIDをCOMサーバのバージョンごとに分けたい場合には、こちらも与えます。 |
_reg_desc_ | desc デフォルトでは_reg_progid_と同じ |
_reg_icon_ | 関連するアイコンのフルパスを文字列として与えます。 |
_reg_threading_ | ThreadingModelを変更したい場合、その文字列です。なければbothになります。 |
_reg_catids_ | COMサーバのカテゴリIDを与えます。 |
_reg_clsctx_ | サーバフラグ、デフォルトではpythoncom.CLSCTX_INPROC_SERVER | pythoncom.CLSCTX_LOCAL_SERVERです。 |
_reg_typelib_filename_ | 予め生成したタイプライブラリをこのCOMサーバと同時に登録したい場合、そのファイル名を指定します。 |
例えば、PyIchigo1.Logwriterという名前でWrite()を公開したい場合は、
まずGUIDを生成して_reg_clsid_に与えたうえで、次の変数を追加します。
_reg_clsid_ = '{7F0AAD75-E363-469A-9310-5B78E1BB3D9E}'
_reg_progid_ = 'PyIchigo1.Logwriter'
_public_methods_ = ['Write']
サーバ登録部分の実装
COMサーバとしてシステムに登録する処理は、次の1行でできます。
pythonで実装したCOMサーバは、
INPROCとLOCALの2つの形で登録され、どちらの形でも利用できます。
win32com.server.register.RegisterClasses(PyIchigo1)
これらをまとめたpython版COMオートメーションサーバのサンプルコードは、
次のようになります。
import win32com.server.register
class PyIchigo1:
# > import pythoncom
# > pythoncom.CreateGuid()
_reg_clsid_ = '{7F0AAD75-E363-469A-9310-5B78E1BB3D9E}'
_reg_progid_ = 'PyIchigo1.Logwriter'
_public_methods_ = ['Write']
def __init__(self):
self.filename = 'c:/tmp/pyichigo1.txt'
def Write(self, logstr):
with open(self.filename,'a') as f:
f.write(logstr)
if __name__ == '__main__':
# --unregister: unregister
win32com.server.register.RegisterClasses(PyIchigo1)
C++と違いIClassFactoryやIDispatchを自分で実装する必要がないため、簡単に実装できます。
別のpythonスクリプトから呼び出す場合はimportを使うと楽ですが、
pythonのオートメーションクライアント機能を使って呼び出したい場合は、
次のように呼び出せます。
import win32com.client
obj = win32com.client.Dispatch("PyIchigo1.Logwriter")
obj.Write("ichigopack python COM sample 1\n");
pythonから呼び出してもあまりありがたみはありませんが、
COMサーバとして実装すれば、IDispatchを介して、
C#やJavaScriptなど、他の言語からも呼び出せるようになります。
また、手間はかかりますが、次の例のようにC++コードから呼び出すことも可能です。
//
// Copyright (c) 2018 The Ichigopack Project (http://ichigopack.net/).
//
#include <windows.h>
#include <ole2.h>
#include <iostream>
#include <iomanip>
extern "C" const GUID CLSID_PyIchigo1 =
{0x7F0AAD75, 0xE363, 0x469A, {0x93,0x10,0x5B,0x78,0xE1,0xBB,0x3D,0x9E}};
int main(int argc, char** argv)
{
HRESULT hr;
hr = CoInitialize(NULL);
if ( FAILED(hr) ) {
std::cout << "CoInitialize() error: " << hr << std::endl;
return 1;
}
IDispatch* pObj = NULL;
hr = CoCreateInstance(
CLSID_PyIchigo1, NULL,
CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER |
CLSCTX_LOCAL_SERVER,
IID_IDispatch, (LPVOID*)&pObj);
if ( SUCCEEDED(hr) ) {
LCID lcid = GetSystemDefaultLCID();
LPOLESTR szFuncNames[1] = {L"Write"};
DISPID dispFunc[1] = {0};
VARIANT v, vret;
VariantInit(&v);
VariantInit(&vret);
hr = pObj->GetIDsOfNames(IID_NULL, szFuncNames, 1, lcid, dispFunc);
if (SUCCEEDED(hr)) {
v.bstrVal = SysAllocString(
L"ichigopack python sample 1 from c++\n");
if (v.bstrVal != NULL) {
v.vt = VT_BSTR;
} else {
hr = E_OUTOFMEMORY;
}
}
if (SUCCEEDED(hr)) {
DISPPARAMS dp;
EXCEPINFO ei;
UINT uArgErr = 0;
dp.rgvarg = &v;
dp.rgdispidNamedArgs = NULL;
dp.cArgs = 1;
dp.cNamedArgs = 0;
hr = pObj->Invoke(dispFunc[0], IID_NULL, lcid, DISPATCH_METHOD,
&dp, &vret, &ei, &uArgErr);
}
VariantClear(&v);
VariantClear(&vret);
if (FAILED(hr)) {
std::cout << "PyIchigo1 call error: "
<< std::hex << std::setfill('0')
<< std::setw(8) << hr << std::endl;
}
pObj->Release();
} else {
std::cout << "CoCreateInstance() error: "
<< std::hex << std::setfill('0')
<< std::setw(8) << hr << std::endl;
}
CoUninitialize();
return 0;
}
解説一覧
COM/ActiveXの解説ページ 目次
セットアップ
オートメーションクライアント
オートメーションサーバ