[Tutor] Registering callbacks and .DLL

Wilson, Pete Pete.Wilson at atmel.com
Tue Oct 21 02:16:21 CEST 2014


This worked. Yeah! Thank you so much. Pete

> -----Original Message-----
> From: eryksun [mailto:eryksun at gmail.com]
> Sent: Friday, October 17, 2014 8:53 PM
> To: Wilson, Pete
> Cc: tutor at python.org
> Subject: Re: [Tutor] Registering callbacks and .DLL
> 
> On Thu, Oct 16, 2014 at 6:35 PM, Wilson, Pete <Pete.Wilson at atmel.com>
> wrote:
> >
> > The .DLL was written in C++ is working with C++ apps calling it.
> 
> ctypes doesn't support the platform C++ ABI (I don't think the VC++ ABI
> is even stable), classes, STL containers, or exceptions [*]. It isn't
> "cpptypes". To work with ctypes, a C++ library needs an extern "C"
> interface. The library you're using probably qualifies, but try a
> simple test program in C before jumping into ctypes.
> 
> [*] On Windows ctypes has limited support for Structured Exception
> Handling (SEH), a Microsoft extension of ANSI C. Inadvertently it also
> handles any exception raised by Win32 RaiseException, such as VC++
> exceptions. The code for VC++ exceptions is 0xE06D7363, i.e. "\xE0"
> "msc". ctypes isn't looking for this code and doesn't delve deeper to
> get the C++ exception type, so the OSError it raises is almost useless.
> Pretend this doesn't exist. SEH support is only implemented for the few
> cases ctypes handles explicitly such as access violations.
> 
> > I tried the methods in section 15.17.1.17 with the qsort() and
> > CFUNCTYPE, but it is not working.  My code and the .dll are attached.
> >
> > from ctypes import *
> >
> > pt_dll = cdll.LoadLibrary("c:/py_stuff/ProductionTest.dll")
> 
> You can use CDLL instead. It's fewer keystrokes.
> 
>     from ctypes import *
> 
>     pt_dll = CDLL("c:/py_stuff/ProductionTest.dll")
> 
> If the functions use the stdcall convention, substitute WinDLL for
> CDLL. If there's a mix of calling conventions you can simply load the
> library twice, once as CDLL and again as WinDLL. They'll each have the
> same _handle attribute.
> 
> You can also define prototypes manually via CFUNCTYPE and WINFUNCTYPE.
> Then instantiate them with a 2-tuple (name_or_ordinal, library), e.g.
> 
>     libc = CDLL('msvcr100')
> 
>     atoi_t = CFUNCTYPE(c_int, c_char_p)
>     atoi = atoi_t(('atoi', libc))
> 
>     >>> atoi(b'42')
>     42
> 
> FYI, 64-bit Windows has a single calling convention, so if you switch
> to 64-bit Python you don't have to worry about cdecl vs stdcall.
> 
> http://en.wikipedia.org/wiki/X86_calling_conventions
> 
> > reg_send_serial_data = pt_dll.RegSendSerialData
> >
> > class SendSerialData_t(Structure):
> >     _fields_ = [("tx_data", c_void_p),
> >        ("size", c_uint8)]
> >
> > send_serial_data = SendSerialData_t()
> 
> SendSerialData_t is a function pointer type, not a data structure.
> Here are the C prototypes from the attached PDF:
> 
>     typedef void (*SendSerialData_t) (uint8_t *tx_data, uint8_t size);
> 
>     void RegSendSerialData(SendSerialData_t SendSerialData);
> 
> A SenedSerialData_t function takes two parameters (uint8_t *, uint8_t)
> and returns nothing (void).
> 
> ctypes declarations:
> 
>     SendSerialData_t = CFUNCTYPE(None, POINTER(c_uint8), c_uint8)
> 
>     reg_send_serial_data = pt_dll.RegSendSerialData
>     reg_send_serial_data.argtypes = [SendSerialData_t]
>     reg_send_serial_data.restype = None
> 
> The first argument to CFUNCTYPE is the return type. Use None for void.
> 
> Next define the Python callback.
> 
>     def send_serial_data(tx_data, size):
>         # testing
>         print tx_data, size
>         print tx_data[:size]
> 
>     cb_send_serial_data = SendSerialData_t(send_serial_data)
> 
> Finally, register the callback with the library:
> 
>     reg_send_serial_data(cb_send_serial_data)
> 
> It's vital that you keep a reference to cb_send_serial_data (as a
> global, an instance attribute, in a container, etc). This prevents the
> callback from being deallocated while it's possible the library can
> call it. Otherwise at best you'll get an  access violation (or segfault
> on POSIX systems), but probably a less obvious error.
> 
> Next your test code sets up ProdBatVolRequest, which is prototyped as
> follows:
> 
>     typedef void (*BatVolReadRequest_cb)(uint16_t bat_vol, uint8_t
> status);
> 
>     typedef struct {
>         BatVolReadRequest_cb BatVolReadConf;
>     } BatVolReadRequest_t;
> 
>     void ProdBatVolReadRequest(BatVolReadRequest_t BatVolReadParam);
> 
> ProdBatVolReadRequest is passed a struct by value that consists of a
> single function pointer. You can skip defining this struct and just
> pass the function pointer. It's the same ABI for x86 and x64.
> 
>     BatVolReadRequest_t = CFUNCTYPE(None, c_uint16, c_uint8)
> 
>     prod_bat_vol_read_request = pt_dll.ProdBatVolReadRequest
>     prod_bat_vol_read_request.argtypes = [BatVolReadRequest_t]
>     prod_bat_vol_read_request.restype = None
> 
>     def bat_vol_read(bat_vol, status):
>         # testing
>         print bat_vol, status
> 
>     cb_bat_vol_read = BatVolReadRequest_t(bat_vol_read)
> 
>     prod_bat_vol_read_request(cb_bat_vol_read)
> 
> Remember to keep a reference to cb_bat_vol_read.
> 
> HTH


More information about the Tutor mailing list