C Structure rebuild with ctypes
Mark Tolonen
metolone+gmane at gmail.com
Mon Dec 21 20:50:22 EST 2009
"Georg" <nobody at nowhere.org> wrote in message
news:7padi2Fsm5U1 at mid.individual.net...
> Hi Mark,
>
> many thanks for your help. I tried your code in my program and it worked.
>
> I would like to understand what the code is doing and I have some
> questions to it.
>
>> Are you passing in these values, or are they being returned? To me the
>> depth of the pointer references implies numVars, varNames, and varTypes
>> are out parameters. I'll assume that for now. If they are in/out
>> parameters let me know.
>
> Your exactly right: the parameters numVars, varNames and VarTypes are out
> paramters.
>
>> I mocked up a DLL to test returning values of these types. I used VS2008
>> and compiled with "cl /LD func.c":
>>
>> --- func.c -------------------------------
>> #include <stdlib.h>
>> #define FUNCDLL
>> #include "func.h"
>>
>> static char* g_data[] = {"one","two","three"};
>> static int g_types[] = {1,2,3};
>>
>> FUNCAPI int func (int handle, int *numVars, char ***varNames, int
>> **varTypes)
>> {
>> *numVars = _countof(g_data);
>> *varNames = g_data;
>> *varTypes = g_types;
>> return handle + 1;
>> }
>
> What does the signature FUNCAPI do in this context?
In Microsoft's compiler, to export functions from a DLL, one way is to mark
functions with __declspec(dllexport). To import functions from a DLL, the
functions should be marked with __declspec(dllimport). FUNCAPI is declared
in func.h below to be one of these definitions. In the actual DLL source,
FUNCDLL is defined before including the header to declare all the
FUNCAPI-tagged functions in the header as exported.
In an executable file that uses the DLL, it can include func.h without
defining FUNCDLL, and all the FUNCAPI-tagged functions in the header will be
declared imported.
It was overkill for this example, but I had code skeletons for DLLs already.
The example could be simplified by replacing FUNCAPI in func.cpp with
__declspec(dllimport) and not using func.h at all.
>
>
>>
>>
>> --- func.h -------------------------------
>> #ifdef FUNCDLL
>> # define FUNCAPI __declspec(dllexport)
>> #else
>> # define FUNCAPI __declspec(dllimport)
>> #endif
>>
>> FUNCAPI int func (int handle, int *numVars, char ***varNames, int
>> **varTypes);
>>
>
> Do I need to wrap the compiled DLL file this way? I can load the DLL with
> the CDLL method, make calls to simple functions, e.g. no parameters,
> returning stirng and get the right values.
No, if the DLL is exporting functions correctly this method doesn't have to
be used. As I said above it was a code template I already had. IIRC, the
Microsoft code wizard to generate a DLL uses this method. I didn't have
your DLL to test with, so I had to make my own and I wanted you to see the
assumptions I made about its implementation, in case they were wrong :^)
>
>
> I added all the code from your func.py module to the code I already had.
> And -- it works fine and delivers the expected results.
>
>> --- func.py -------------------------------
> ... cut ...
>>
>> # int func (int handle, int *numVars, char ***varNames, int **varTypes)
>> func = c.CDLL('func').func
>> func.restype = INT
>> func.argtypes = [INT,PINT,PPPCHAR,PPINT]
>
> I added this part to my program.
>
>> # allocate storage for the out parameters
>> numVars = INT()
>> varNames = PPCHAR()
>> varTypes = PINT()
>
> I added this part also.
>
>> print func(5,c.byref(numVars),c.byref(varNames),c.byref(varTypes))
>
> I called the library routine.
>
>> # numVars contains size of returned arrays. Recast to access.
>> varNamesArray = c.cast(varNames,c.POINTER(PCHAR * numVars.value))
>> varTypesArray = c.cast(varTypes,c.POINTER(INT * numVars.value))
>
> What does this cast? How do I know how I have to cast the objects returned
> from the library function?
The library is returning a pointer to an array of some size you didn't know
in advance. So, for example, we passed varTypes as the address of a pointer
to int, and a pointer to int was returned. Since varTypes was declared as a
pointer to a single int, I recast it to a pointer to "numVars" ints. This
allowed me to use indexing syntax ([n]) to access the other elements of the
returned array.
> What kind of objects do I get? I learned that the values of objects
> created by the ctypes module are accessed using object.value?
numVars is the name of an INT() object, not a Python int. numVars.value
returns the Python int that the INT represents. An example might be
helpful:
>>> import ctypes
>>> x=ctypes.c_int(5)
>>> x # here x is a ctypes object.
c_long(5)
>>> x.value # here is the Python value it represents
5
>>> y=(ctypes.c_int * x)() # I want to create an array of c_ints
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can't multiply sequence by non-int of type 'c_long'
>>> y=(ctypes.c_int * x.value)() # It works with Python values.
>>> y
<__main__.c_long_Array_5 object at 0x00A04D50>
>>> y[0]
0
>>> y[1]
0
Hope that helps,
Mark
More information about the Python-list
mailing list