I want to use a DLL file with a function that has the following signature -
bool isValid = isImageValid((unsigned char *) buff, const __uint32& buffLen,
int imageW, int imageH, __uint32 requiredSize);
Now, buff has to result from a string I'm getting to the python wrapper that is equaivalent to
pyBuff = open(someimagefile, 'rb').read()
Since pyBuff is big (mostly), I don't want to allocate a new c uchar array and copy, but to make use of the original buffer. Basically, grab the char buffer of the pyBuff object and reference it as a ctypes (c_ubyte *).
The closest I came to it is:
buff = cast(pyBuff, POINTER(c_ubyte))
But I'm not sure this is the type I want. Anyway, the function gives me the following error
WindowsError: exception: access violation writing 0x00000000
Will be glad for any help..
Also - two short questions:
Is the following a right definition,given that in an existing C wrapper to this DLL (which I'm working with as a reference) the function that calls (also) this one has the signature: (const char * buff, const __uint32& buffLen)
byref(c_ulong(len(pyBuff))
What's the difference between CDLL and WinDLL, and which is the right one to use?
解决方案
If all you have is the Python string and you're sure isImageValid won't modify its contents, then you can just declare the buff argument as type c_char_p. The buffer contents won't be copied.
The _type_ code of c_char_p is 'z'. In the 2.5.4 source for the setter function, _ctypes/cfield.c : z_set, you see that for an str object it simply uses the underlying ob_sval pointer:
1309 if (PyString_Check(value)) {
1310 *(char **)ptr = PyString_AS_STRING(value);
1311 Py_INCREF(value);
1312 return value;
where the macro is defined as follows:
#define PyString_AS_STRING(op) (((PyStringObject *)(op))->ob_sval)
That said, if you can read the source file, you may prefer to read it into a mutable buffer. Here's how to read the contents into a ctypes c_char array (tested in 2.5.4):
Create a test file to be read:
>>> open('tmp.txt', 'w').write('''\
... This is a test.''')
Get the file size in a c_uint32, create the buffer, and read in the file contents:
>>> import os
>>> from ctypes import *
>>> buffLen = c_uint32()
>>> buffLen.value = os.path.getsize('tmp.txt')
>>> buff = (c_char * buffLen.value)()
>>> open('tmp.txt').readinto(buf)
15
>>> buff.value
'This is a test.'
Regarding your two other questions, I'd set up the function prototype like this:
lib = CDLL(path_to_dll)
isImageValid = lib.isImageValid
isImageValid.argtypes = c_char_p, POINTER(c_uint32), c_int, c_int, c_uint32
isImageValid.restype = c_bool
ctypes will automatically pass buffLen by reference given the above argtypes definition. You can also explicitly use byref(buffLen).
CDLL is for the cdecl (C declaration) calling convention, in which the caller cleans up the stack. This allows variadic functions such as printf. WinDLL is for the stdcall convention, in which the callee cleans up the stack. stdcall is mostly used by the 32-bit Win32 API, such as Kernel32 functions. See the Wikipedia article on x86 calling conventions.