🐷

Python から DLL を利用する

2022/02/06に公開

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 使うほうがいいですね。

C++で書いたコードをPythonで動かすには【pybind11】

Discussion