🐷
Python から DLL を利用する
Python から DLL を利用する
まとめ
- C DLL なら ctypes、C# DLL なら Python.NET(pythonnet) パッケージを利用する。
- Python と DLL のアーキテクチャを合わせる。Python が x64 版なら DLL も x64、Python が x86 なら DLL も x86 でないと呼び出せません。
- pythonnet は手動で追加が必要です。(
pip install pythonnet
) - これを書いている時点(2022/02)で、pythonnet は Python 3.8 までしかサポートしていない。実際に Python 3.9 では動かなかった。(
Python.NET is currently compatible and tested with Python releases 2.7 and 3.5-3.8.
) - DLL は Python スクリプトと同じ場所に置くか、パスを通った場所に置く。
C/C++ DLL with ctypes
- C++ クラス DLL は直接呼び出せないので、C++ クラス DLL を呼び出したければ ラッパー用の C DLL を用意する。
- C DLL は呼び出し規約に応じて、
__cdecl
なら cdll、__stdcall
なら windll を用いる。
サンプルコード with ctypes
下記の例では DLL 側が__cdecl
呼び出しのメソッド定義をしているため、cdll を経由してライブラリを読み込んでいます。2つの数を掛け合わせる multiple というメソッドを呼び出しています。
sample_ctypes.py
import ctypes
mydll = ctypes.cdll.LoadLibrary("my.dll")
mydll.multiple(3, 4)
Python からコールバックを設定する with ctypes
callback-functions にある通り。
C# DLL with pythonnet
サンプルコード with pythonnet
C# の DLL 側(だいぶ省略してます)
- CsPyinterop.dll として作成。
- HogeHoge クラス の startTask が Python から呼び出すメソッド。
- startTask では非同期処理を開始して、すぐに制御を返します。非同期処理が終わったら、コールバックを呼び出します。
CsPyInterop.cs
// 呼び出す DLL 側の例
namespace CsPyInterop
{
public class HogeHoge
{
private Action<string> _callback; // 文字列引数を1つとるコールバック
private Task _task;
private string _name;
public HogeHoge()
{
this._callback = null;
this._task = null;
}
// 呼び出すメソッド(文字列引数、コールバック)
// このメソッドは内部的に非同期タスクを開始して、すぐに制御を戻します。
// 非同期タスクが終わったら、指定されたコールバックを呼び出します。
public bool startTask(string jobName, Action<string> f)
{
bool retValue = false;
if (!string.IsNullOrEmpty(jobName) && null != f)
{
this._name = jobName;
this._callback = f;
this._task = Task.Run(() => {
// {このあたりで時間のかかる処理をする}
// 処理が終わったら、コールバックを呼び出す。
string answer = this._name + " を終了しました。";
this._callback(answer);
});
retValue = true;
}
return retValue;
}
}
}
DLL を呼び出す Python 側
- pythonnet(clr) を import。
- HogeHoge クラスのインスタンス hogehoge を作成。
- startTask にコールバック渡して呼び出す。
- C# 側の非同期処理が終わったタイミングで、Python 側で指定したコールバック(py_func)が呼ばれます。
- 当然ですが、コールバックが呼ばれるまで Python 側で hogehoge が解放されないように注意しましょう。この例では Python が終わってしまわないように、input で待機しています。
sample_pythonnet.py
import clr
clr.AddReference("System")
clr.AddReference("CsPyInterop")
import CsPyInterop
import System
hogehoge = CsPyInterop.HogeHoge()
def py_func(param):
print("python callback: "+ param)
ret = hogehoge.startTask("お仕事", System.Action[str](py_func))
print("startTask: "+ str(ret))
print("\n")
input("waiting...\n\n")
おまけ
この記事を書いた後に、以下に気づきました。
C++ クラスをラップする C DLL を作るぐらいなら、pybind11 使うほうがいいですね。
Discussion