"Georg" <nob...@nowhere.org> wrote in message news:7padi2fsm...@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


--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to