配列を受け取るDLLをPythonから呼び出す際に、躓いたのでメモを残しておきます。
環境
まずは環境を。
OS: Windows7 64bit
Python: 3.4.1 64bit
DLLについて
APIは以下のようなものです。 ちなみに呼び出し規約は__stdcallです。
int DLLAPI set_data(void* handle, int ndata, float *data);
引数の内容はこんな感じです。
- handle: ハンドル(今回は無関係)
- ndata: データの個数(=渡す配列の長さ)
- data: 配列本体
Cから呼ぶならなんてことない関数です。
PythonでDLLをロードする
PythonからDLLを扱うにはctypesを使用します。Windows環境でなら以下のようにしてDLLをロードできます。
また、そのDLL内関数がどのような引数と返り値を持つのかなどを指定します。
from ctypes import *
dllhandle = windll.LoadLibrary('<ファイルパス>')
# DLLの'set_data'関数をPythonから使用できるようにする。
set_data = dllhandle.set_data
set_data.argtypes = [c_void_p, c_int, c_void_p]
set_data.restype = c_int
ポイントは、配列を受け取る部分をc_void_pにするところです。
これで関数をPythonから呼び出す準備が出来ました。
Pythonから呼び出す
では、Pythonから呼び出してみましょう。 例としてitemsリストを渡してみます。 itemsの内容は c_float型として受け入れ可能なものである必要があります。
# リストの長さを取得
nlen = len(items)
# 配列のデータ型を作成する
# ここでは「長さnlenのc_float型の配列」のデータ型を作ります。
FARRAY = c_float * nlen
# 配列インスタンスの作成とデータの流し込み。
farray = FARRAY(*items)
# DLLに渡す。その際にポインタを取得するbyrefを使用する。
# byrefの代わりにpointerでも動作します。
set_data(handle, nlen, byref(farray))
これで動作しました。
もちろん、DLL側で配列の内容を変更するとPython側にも反映されます。 でも、もとのitemsではなく、 farrayが変更されます。
[おわり]