2011年1月28日金曜日

pythonでctypesを使ったときにちょっと困った不可解なエラー

経験のないUSB機器をお手軽に試用するため、
久しぶりにpython+ctypesを試してみることにした。
長いこと使っていないと一度調べたことを再び
一から確認していかねばならなくなるので、
煩わしくて、せっかく適応可能な機会に恵まれても
使う気力は失せてしまう。
それでも、全く使わないですむかというと、
使ったときの快適感だけは鮮明に記憶されているので、
引っ張りだして使ってみたくなるのがpytho+ctypesだと
僕は思うのだ。
以前に書いたスクリプトとドキュメントを参考にしながら、
今回お世話になるDLLのヘッダファイルを見て、
ctypesラッパーを書いたのだけど、すぐにつまずいた。
正しく関数の引き数の指定しているにも関わらず、
引き数の数が合いませんとかいうエラーが出るのである。
エラーメッセージを頼りにしてGoogle先生に聞いてみたら、
DLLに組み込まれている関数のプロトタイプ宣言に
関係があるらしい。いわゆるstdlibとCDECLの違いだ。
簡潔に言えばstdlibは、Win32APIみたいなOSの
サービスコール的な関数をDLLに埋め込むときの規約で
CDECLは、C/C++でつくった関数を単純にDLL化したときに
適応される約束みたいなものだ。
話を元に戻すと自分が今回でくわしたエラーは、
stdcallで宣言された関数を呼び出すときに、
ctypesに対してそれはCDECL宣言の関数ですよと
謝った情報でDLLをオープンしていたかららしい。
DWORD testFunc(DWORD a, DWORD *b);
みたいなDLL関数をインポートするとき、次でエラーが出るような場合だ。
from ctypes import *
_lib_hoge=clib.LoadLibrary("hoge.dll")
testFunc=_lib_hoge.testFunc
testFunc=testFunc.args=[c_ulong, c_void_p]
bb=c_ulong()
ret=testFunc(10, byref(bb))
testFuncがstdcallな関数だった場合に、このスクリプトを
実行したときのエラーメッセージはこんな感じ。
"ValueError:Procedure called with not enough
argments (8 bytes missing) or wrong calling
convention"
一見,引数の数か型指定が間違っているだけとしか思えない.
でも正しい指定をしているから期待通りに動かない怒りは,
結果としてctypesに向けられることになるのだ.
エラー対策方法は,スクリプトの冒頭にあるDLL読み込みのところを
次のように改めればよい。
from ctypes import *
_lib_hoge=clib.LoadLibrary("hoge.dll")
testFunc=_lib_hoge.testFunc
testFunc=testFunc.args=[c_ulong, c_void_p]
bb=c_ulong()
ret=testFunc(10, byref(bb))
知っていれば些細な問題なんだけど、知らないとこれだけで
python+ctypesをあきらめてしまう要因になると思うのだ。

湯船につかりながらポメラでこの記事を書いているのだけど、
昨日やったばかりの作業やエラーメッセージを思い出せない。
日頃どれだけコピペに頼る生活をしているのか、
実感させられるのである。
2011/01/27 07:23