質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
DLL

DLL(Dynamic Link Library)とは、他のモジュールからも使用する事が出来る、関数とデータが格納されているモジュールのことです。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Q&A

解決済

2回答

10659閲覧

PythonからDLLを呼ぶ際にエラーが発生する

masa0715

総合スコア11

DLL

DLL(Dynamic Link Library)とは、他のモジュールからも使用する事が出来る、関数とデータが格納されているモジュールのことです。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

0グッド

0クリップ

投稿2017/06/01 07:31

###前提・実現したいこと

あるハードウェアを制御する為のDLLをPythonから制御しようとしています。
環境はPython3.6.0b1(Win32),Windows10(32ビット)です。

DLL内には、そのパソコンに接続されている機器の一覧を取得する関数と、
機器をオープンする関数、実際に機器に対して何かしらの制御を行う関数があります。

DLL内の関数は以下の様に定義されています。
EXPORT BOOL __stdcall Get_Device_Handle( DWORD *num, UINT64 *dFoundHandle );
EXPORT BOOL __stdcall Open_Handle( UINT64 dFoundHandle, UINT64 *dOpenHandle );

###発生している問題・エラーメッセージ

ValueError: Procedure probably called with not enough arguments (4 bytes missing)

###該当のソースコード

num = c_ulong(); FoundHandle = ( c_ulonglong * 16 )(); dOpenHandle = c_ulonglong(); if dll.Get_Device_Handle( byref( num ), byref( FoundHandle )) == False: print("Device not found!!"); sys.exit(1); else: print('Device %d found!!' % num.value ); for i in range( num.value ): このfor文の中で見つかった機器を順番にオープン→制御→クローズする if dll.Open_Handle( FoundHandle[i], byref( dOpenHandle )) == False: ←ここでエラーが発生する print("OPEN Failed!!"); sys.exit(1); dOpenHandleを使って何かしらの制御 ここでオープンしたデバイスをクローズしてfor文の先頭に戻る

###試したこと

違う環境や違う開発環境で試してみた。

Open_Hanlde()でエラーがでていますが、
実際にはGet_Device_Handle()でFoundHandleに正しい値を受け取れていないのではないかと考えています。
Get_Device_Handle()は、パソコンに接続されている機器の台数(最大16台)とそのハンドル(FoundHandle)が帰って来て、
そのFoundHandleを使いOpen_Handle()で機器をオープンします。

###補足情報(言語/FW/ツール等のバージョンなど)

1,同じ環境(Win10/32ビット)で当該DLLをVC2015から制御すると機器をオープンして制御する事ができました。
2,違う環境(Win7/64ビット+Python3.6.0b1(Win64)+64ビット版DLL)でVC2015から制御すると機器をオープンして制御する事ができました。
3,違う環境(Win7/64ビット+Python3.6.0b1(Win64)+64ビット版DLL)でPythonから制御すると機器をオープンして制御する事ができました。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

すでにほぼ答えが出てしまっていますが。。。

print(type(FoundHandle[0])) <class 'int'>

上記のようにFoundHandleの要素にアクセスした時に一旦Pythonのint型にキャストされて、それがOpen_Handleを呼び出す時にちゃんとUINT64にキャストされないことがおそらく原因です。

以下、ご参考までに。関数のargtypesを明示してあげるとちゃんとUINT64にキャストされるようになると思います。

from ctypes import byref from ctypes import c_uint64 as UINT64 from ctypes import POINTER from ctypes import windll from ctypes import wintypes num = wintypes.DWORD() FoundHandle = (UINT64 * 16)() dOpenHandle = UINT64() dll = windll.LoadLibrary("ORGUSB.dll") dll.Get_Device_Handle.restype = wintypes.BOOL dll.Get_Device_Handle.argtypes = (POINTER(wintypes.DWORD), POINTER(UINT64)) dll.Open_Handle.restype = wintypes.BOOL dll.Open_Handle.argtypes = (UINT64, POINTER(UINT64)) ...

投稿2017/06/01 11:43

編集2017/06/01 11:48
YouheiSakurai

総合スコア6142

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

pashango2

2017/06/01 13:13

原因はそれだと思います、4バイトスタックサイズが足りないのも説明つきますし。 > 違う環境(Win7/64ビット+Python3.6.0b1(Win64)+64ビット版DLL)でPythonから制御すると機器をオープンして制御する事ができました。 しかし、これについて少し引っかかります。 ctypesはPythonの数値型はintと解釈しますよね、たぶん4バイトより大きい数値でも4バイトに丸めると思うんです。 だとすると64ビット環境でもエラーが出るべきだと思うんですが。 64ビット環境だとスタックのアライメント処理でたまたまスタックサイズが一致したんですかね?
pashango2

2017/06/01 13:38

うーん、このコードにいくのはintとlongが同一サイズの環境のときですね。 まぁ、ここらへんは環境に依存するところでパティングとかアライメントの話になるので難しいですね。 ありがとうございます。
guest

0

ベストアンサー

変数dllctypes.WinDLLでしょうか?
stdcallWinDLLでロードする必要があります。

https://docs.python.jp/3/library/ctypes.html

実際にはGet_Device_Handle()でFoundHandleに正しい値を受け取れていないのではないかと考えています。

その推測はおそらく合っています、上のページで全く同じエラーコードが出ています。
出ているエラーを見ますと、スタックが4バイト少ないと出ています。

あと考えられる可能性はc_ulongなどの環境依存の型指定です。
ctypesにはctypes.c_int16などのサイズ固定の指定があるので、そちらを使ってはどうでしょうか?

追記

YouheiSakuraiさんの回答と同意見です。

argtypeを指定してあげるか、明示的に変換すればOKです。

python

1if dll.Open_Handle( ctypes.u_int64(FoundHandle[i]), byref( dOpenHandle )) == False:

個人的にはYouheiSakuraiさんのargtyperestypeを定義する方法がきれいですね。

ただし引っかかるのは64ビット版で動作したということですね。
推測ですが偶然動作しているだけで、いつ問題が出てもおかしくないコードだと思います。

投稿2017/06/01 08:25

編集2017/06/01 23:41
pashango2

総合スコア930

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

masa0715

2017/06/01 09:24

アドバイスありがとうございます。 DLLロード部分のコードが抜けていました。 失礼しました。 dll = windll.LoadLibrary("ORGUSB.dll"); さっそくc_ulonglongを宣言をc_uint64に変更してみましたが同じエラーが発生しました。 アドバイス頂いた内容は以下の理解で宜しいでしょうか? num = c_uint64(); FoundHandle = ( c_uint64 * 16 )(); dOpenHandle = c_uint64();
pashango2

2017/06/01 09:40

なるほど、なら答えは絞られてきました。 if dll.Open_Handle( FoundHandle[i], byref( dOpenHandle )) == False の部分ですが x = c_uint64(FoundHandle[i]) if dll.Open_Handle( x, byref( dOpenHandle )) == False としてみてください。
masa0715

2017/06/02 02:17

明示的にキャストする方法で解決する事ができました。 pashango2様、YouheiSakurai様、丁寧なアドバイスありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問