Reputation: 31
I'm programming an interface with 3M document scanners.
I am calling a function called MMMReader_GetData
MMMReaderErrorCode MMMReader_GetData(MMMReaderDataType aDataType,void* DataPtr,int* aDataLen);
Description:
After a data item has been read from a document it may be obtained via this API. The buffer supplied in the aDataPtr parameter will be written to with the data, and aDataLen updated to be the length of the data.
The problem is how can I create a void* DataPrt
and how can get it the data?
I have tried:
from ctypes import *
lib=cdll.LoadLibrary('MMMReaderHighLevelAPI.dll')
CD_CODELINE = 0
aDataLen = c_int()
aDataPtr = c_void_p()
index= c_int(0)
r = lib.MMMReader_GetData(CD_CODELINE,byref(aDataPtr),byref(aDataLen),index)
aDataLen
always returns a value but aDataPtr
returns None
Upvotes: 2
Views: 1353
Reputation: 2412
There are several issues with your code:
aDataPtr
.aDataLen
. According to [1], if the buffer isn't big enough, MMMReader_GetData
will reallocate it as needed.aDataPtr
directly, not byref
.index
argument) based on the method descriptor of MMMReader_GetData
you provided.Try the following:
import ctypes
lib = ctypes.cdll.LoadLibrary('MMMReaderHighLevelAPI.dll')
CD_CODELINE = 0
aDataLen = ctypes.c_int(1024)
aDataPtr = ctypes.create_string_buffer(aDataLen.value)
err = lib.MMMReader_GetData(CD_CODELINE, aDataPtr, ctype.byref(aDataLen))
Then you can read the content of the buffer as a regular character array. The actual length is returned back for you in aDataLen
.
[1] 3M Page Reader Programmers' Guide: https://wenku.baidu.com/view/1a16b6d97f1922791688e80b.html
Upvotes: 2
Reputation: 178334
With ctypes
, it is best to define the argument types and return value for better error checking, and declaring pointer types is especially important on 64-bit systems.
from ctypes import *
MMMReaderErrorCode = c_int # Set to an appropriate type
MMMReaderDataType = c_int # ditto...
lib = CDLL('MMMReaderHighLevelAPI')
lib.MMMReader_GetData.argtypes = MMMReaderDataType,c_void_p,POINTER(c_int)
lib.MMMReader_GetData.restype = MMMReaderErrorCode
CD_CODELINE = 0
# Make sure to pass in the original buffer size.
# Assumption: the API should update it on return with the actual size used (or needed)
# and will probably return an error code if the buffer is not large enough.
aDataLen = c_int(256)
# Allocate a writable buffer of the correct size.
aDataPtr = create_string_buffer(aDataLen.value)
# aDataPtr is already a pointer, so no need to pass it by reference,
# but aDataLen is a reference so the value can be updated.
r = lib.MMMReader_GetData(CD_CODELINE,aDataPtr,byref(aDataLen))
On return you can access just the returned portion of the buffer by string slicing, e.g.:
>>> from ctypes import *
>>> aDataLen = c_int(10)
>>> aDataPtr = create_string_buffer(aDataLen.value)
>>> aDataPtr.raw
'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
>>> aDataLen.value = 5 # Value gets updated
>>> aDataPtr[:aDataLen.value] # Get the valid portion of buffer
'\x00\x00\x00\x00\x00'
Upvotes: 2
Reputation: 2877
What you need to do is allocate a "buffer". The address of the buffer will be passed as the void* parameter, and the size of the buffer in bytes will be passed as the aDataLen
parameter. Then the function will put its data in the buffer you gave it, and then you can read the data back out of the buffer.
In C or C++ you would use malloc
or something similar to create a buffer. When using ctypes
, you can use ctypes.create_string_buffer to make a buffer of a certain length, and then pass the buffer and the length to the function. Then once the function fills it in, you can read the data out of the buffer you created, which works like a list of characters with []
and len()
.
Upvotes: 2