web-dev-qa-db-ja.com

Python&Ctypes:データを取得するためのポインターとして構造体を関数に渡す

私は他の答えを調べましたが、これを機能させることができないようです。 SMBusデバイスと通信するためにDLL内の関数を呼び出そうとしています。この関数は、フィールドの1つとして配列を持つ構造体へのポインターを取ります。

Cの場合:

_typedef struct _SMB_REQUEST
{
    unsigned char Address;
    unsigned char Command;
    unsigned char BlockLength;
    unsigned char Data[SMB_MAX_DATA_SIZE];
} SMB_REQUEST;
_

DLLがデータ配列を埋める間、Address、Command、BlockLengthの値を設定する必要があると思います。この構造体を必要とする関数はそれをポインターとして受け取ります

_SMBUS_API int SmBusReadByte( SMBUS_HANDLE handle, SMB_REQUEST *request );
_

だから私はPythonのように構造体を設定しました:

_class SMB_REQUEST(ctypes.Structure):
    _fields_ = [("Address", c_char),
            ("Command", c_char),
            ("BlockLength", c_char),
            ("Data", type(create_string_buffer(SMB_MAX_DATA_SIZE))]
_

*注:データ型としてctypes.c_char * SMB_MAX_DATA_SIZEも試しました*

このタイプの構造体へのポインターを関数に渡すために、最初に次のように初期化しようとしました。

_data = create_string_buffer(SMB_MAX_DATA_SIZE)
smb_request = SMB_REQUEST('\x53', \x00', 1, data)
_

これは次のように応答します。

_TypeError: expected string or Unicode object, c_char_Array_32 found
_

次のように、データ配列を省略しようとすると、次のようになります。

_smb_request = SMB_REQUEST('\x53', \x00', 1)
_

いいえ、エラーです。ただし、これを関数に渡そうとすると、次のようになります。

_int_response =  smbus_read_byte(smbus_handle, smb_request))
_

私は得る:

_ArgumentError: argument 2: <type 'exceptions.TypeError'>: expected LP_SMB_REQUES
T instance instead of SMB_REQUEST
_

私はそれをポインタとして渡してみました:

_int_response =  smbus_read_byte(smbus_handle, ctypes.POINTER(smb_request))
_

そして私は得る:

_----> 1
      2
      3
      4
      5

TypeError: must be a ctypes type
_

アートタイプの設定方法は次のとおりです。

_smbus_read_byte.argtypes = (ctypes.c_void_p, ctypes.POINTER(SMB_REQUEST))
_

キャストしてみましたが、まだうまくいきません。誰かが私のためにこれにいくつかの光を当てることができますか?

更新:

私が最初にそのように構造体を初期化した場合:

_smb_request = SMB_REQUEST('\xA6', '\x00', chr(1), 'a test string')
_

次に、参照による低音:

_int_response =  smbus_receive_byte(smbus_handle, ctypes.byref(smb_request))
_

エラーは発生しません。ただし、関数は、成功の場合は「0」を返し、失敗の場合はゼロ以外を返す必要がある場合は-1を返します。 smb_request.Dataの値を確認すると、「テスト文字列」が返されるため、変更はありません。ここで何が起こっているのかについての提案をいただければ幸いです。

ありがとう

更新:

ハンドルが正しいかどうかという問い合わせがいくつかあったので、次のように使用しています。 DLLのヘッダーファイルは、次のことを宣言しています。

_typedef void *SMBUS_HANDLE;

//
// This function call initializes the SMBus, opens the driver and 
// allocates the resources associated with the SMBus.
// All SMBus API calls are valid 
// after making this call except to re-open the SMBus.
//
SMBUS_API SMBUS_HANDLE OpenSmbus(void);
_

だからこれが私がPythonでこれをやっている方法です:

_smbus_handle = c_void_p() # NOTE: I have also tried it without this line but same result

open_smbus = CDLL('smbus.dll').OpenSmbus
smbus_handle =  open_smbus()
print 'SMBUS_API SMBUS_HANDLE OpenSmbus(void): ' + str(smbus_handle)
_

Smbus_read_byte()を呼び出す前に、これを呼び出します。 open_smbus.restype = c_void_p()を設定しようとしましたが、エラーが発生します:_TypeError: restype must be a type, a callable, or None_

18
Jack

これが実際の例です。間違った型を関数に渡しているようです。

テストDLLコード(Windowsでは "cl/W4/LD x.c")

#include <stdio.h>

#define SMBUS_API __declspec(dllexport)
#define SMB_MAX_DATA_SIZE 5

typedef void* SMBUS_HANDLE;

typedef struct _SMB_REQUEST
{
    unsigned char Address;
    unsigned char Command;
    unsigned char BlockLength;
    unsigned char Data[SMB_MAX_DATA_SIZE];
} SMB_REQUEST;

SMBUS_API int SmBusReadByte(SMBUS_HANDLE handle,SMB_REQUEST *request)
{
    unsigned char i;
    for(i = 0; i < request->BlockLength; i++)
        request->Data[i] = i;
    return request->BlockLength;
}

SMBUS_API SMBUS_HANDLE OpenSmbus(void)
{
    return (void*)0x12345678;
}

Pythonコード

from ctypes import *
SMB_MAX_DATA_SIZE = 5
ARRAY5 = c_ubyte * SMB_MAX_DATA_SIZE

class SMB_REQUEST(Structure):
    _fields_ = [
        ("Address", c_ubyte),
        ("Command", c_ubyte),
        ("BlockLength", c_ubyte),
        ("Data", ARRAY5)]

smbus_read_byte = CDLL('x').SmBusReadByte
smbus_read_byte.argtypes = [c_void_p,POINTER(SMB_REQUEST)]
smbus_read_byte.restype = c_int
open_smbus = CDLL('x').OpenSmbus
open_smbus.argtypes = []
open_smbus.restype = c_void_p

handle = open_smbus()
print 'handle = %08Xh' % handle

smb_request = SMB_REQUEST(1,2,5)

print 'returned =',smbus_read_byte(handle,byref(smb_request))
print 'Address =',smb_request.Address
print 'Command =',smb_request.Command
print 'BlockLength =',smb_request.BlockLength
for i,b in enumerate(smb_request.Data):
    print 'Data[%d] = %02Xh' % (i,b)

出力

handle = 12345678h
returned = 5
Address = 1
Command = 2
BlockLength = 5
Data[0] = 00h
Data[1] = 01h
Data[2] = 02h
Data[3] = 03h
Data[4] = 04h
26
Mark Tolonen

もうすぐです。 Dataの定義のタイプとして_c_char * SMB_MAX_DATA_SIZE_を使用する必要があります。これはMacOSXで私のために働きます:

共有ライブラリ:

_$ cat test.c
#include <stdio.h>

#define SMB_MAX_DATA_SIZE 16

typedef struct _SMB_REQUEST
{
  unsigned char Address;
  unsigned char Command;
  unsigned char BlockLength;
  unsigned char Data[SMB_MAX_DATA_SIZE];
} SMB_REQUEST;

int SmBusReadByte(void *handle, SMB_REQUEST *request)
{
  printf("SmBusReadByte: handle=%p request=[%d %d %d %s]\n", handle, 
      request->Address, request->Command, request->BlockLength, request->Data);
  return 13;
}

$ gcc test.c -fPIC -shared -o libtest.dylib
_

Pythonドライバー:

_$ cat test.py
import ctypes

SMB_MAX_DATA_SIZE = 16

class SMB_REQUEST(ctypes.Structure):
    _fields_ = [("Address", ctypes.c_ubyte),
                ("Command", ctypes.c_ubyte),
                ("BlockLength", ctypes.c_ubyte),
                ("Data", ctypes.c_char * SMB_MAX_DATA_SIZE)]

libtest = ctypes.cdll.LoadLibrary('libtest.dylib')

req = SMB_REQUEST(1, 2, 3, 'test')

result = libtest.SmBusReadByte(ctypes.c_voidp(0x12345678), ctypes.byref(req))

print 'result: %d' % result

$ python test.py
SmBusReadByte: handle=0x12345678 request=[1 2 3 test]
result: 13
_

[〜#〜] update [〜#〜]

_open_smbus_の結果タイプを_void*_に設定する必要があるため、問題が発生しています。デフォルトでは、ctypesは関数がintsを返すことを前提としています。あなたはこれを言う必要があります:

_open_smbus.restype = ctypes.c_void_p
_

c_void_p()を使用していたため、エラーが発生していました(余分な括弧に注意してください)。 _c_void_p_とc_void_p()には重要な違いがあります。前者はtypeであり、後者はインスタンスです。タイプ。 _c_void_p_はCタイプ_void*_を表し、c_void_p()は実際のポインターインスタンス(デフォルト値は0)を表します。

7
Adam Rosenfield

変更してみてください

("Data", type(create_string_buffer(SMB_MAX_DATA_SIZE))

("Data", (c_char * SMB_MAX_DATA_SIZE)]
1
Gabi Purcaru