Pythonから配列をDLLに渡す

配列を受け取る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が変更されます。

[おわり]