Я использую Python/ctypes, чтобы написать приложение, основанное на коммерческой DLL. Эта DLL считывает файл flatfile и возвращает данные через struct
s. Соответствующая struct
C выглядит следующим образом:
struct System{
unsigned short user;
unsigned short version;
};
struct Message {
unsigned short header;
unsigned short data;
unsigned int messageType;
};
API, поставляемый с DLL, предоставляет указатель на функцию со следующим прототипом. Эта функция проходит через плоский файл и вызывает следующий вызов Callback
:
typedef int (__stdcall *Process) (const char* filename, const char* base, unsigned int flags, int user, Callback stdcallback);
и прототип Callback
(пятый аргумент выше) определяется как:
typedef int (__stdcall *Callback) (const System* Sys, const Message* Msg);
В моем файле python:
from ctypes import *
# using WinDLL for stdcall
lib = WinDLL('CVO.dll')
class System(Structure):
_fields_ = [('user', c_ushort),
('version', c_ushort)]
class Message(Structure):
_fields_ = [('header', c_ushort),
('data', c_ushort),
('messageType', c_uint)]
PROTO = WINFUNCTYPE(c_int, POINTER(System), POINTER(Message))
def py_callbck(Sys, Msg):
print Msg._type_.messageType
# return 0 to read the next line in the flatfile
return 0
Callback = PROTO(py_callbck)
pProcess lib.Process
pProcess.argtypes = [c_char_p,c_char_p,c_uint,c_int,PROTO]
pProcess.restype = c_int
pProcess(c_char_p('flat.ccf'),c_char_p(None),c_uint(0),c_int(0),Callback)
Моя проблема заключается в доступе к значению, которое DLL передает в структуру Msg
. Код должен печатать c_int
(0, 1, 2), но вместо этого возвращает тип Field
типа:
<Field type=c_ulong, ofs=136, size=4>
Я знаю, что файл flatfile обрабатывается, потому что мой код Python выводит сотни операторов <Field...>
, которые он должен делать.
Мой вопрос в том, как я могу получить доступ к значению, которое DLL Msg
Structure
Msg
вместо того, чтобы возвращать операторы <Field...>
?
Заметьте, что я относительно новичок в ctypes и очень мало C, поэтому, если вы заметили проблемы с определением чего-либо, сообщите мне. Например, я знаю, что существует разница между ctypes.pointer
который возвращает объект и ctypes.POINTER
который возвращает новый тип. Когда я использую ctypes.pointer
ошибки в коде.
Для того, чтобы быть относительно новым для ctypes, вы здесь неплохо справились. _type_
указателя является ссылкой на указанный в типе данных. В этом случае это Message
. Атрибут class messageType
- это CField
данных CField
используемый экземпляром.
Вместо этого вы хотите разыменовать указатель, чтобы получить экземпляр Message
. Вы можете использовать индекс [0]
или атрибут contents
:
def py_callbck(Sys, Msg):
print Msg[0].messageType # Msg.contents.messageType
# return 0 to read the next line in the flatfile
return 0
Не забудьте сохранить ссылку на Callback
чтобы он не собирался собирать мусор. В настоящее время вы ссылаетесь на нее как на глобальную. Это здорово.
Что касается способа, которым вы вызываете pProcess
, обычно не нужно вручную создавать объекты ctypes для простых типов, когда вы определяете argtypes
. Вы можете просто использовать следующее:
pProcess('flat.ccf', None, 0, 0, Callback)
Но обратите внимание, нужна ли функции перезаписываемому буферу строки. В этом случае используйте create_string_buffer
. Здесь нет необходимости, поскольку параметры char *
являются const
.