简体   繁体   中英

Am I using ctypes correctly to pythonify this struct?

I'm trying to talk to this DLL using python's ctypes. Many of the functions take or return an HGRABBER type:

typedef struct HGRABBER_t__ { int unused; } HGRABBER_t;
#define HGRABBER HGRABBER_t* 

(the full header file can be viewed here ). Here's an example of a function prototype that returns an HGRABBER type:

HGRABBER __stdcall IC_CreateGrabber();

Here's my attempt at implementing this struct in python, and using it to call that function from the DLL:

import ctypes as C
class GrabberHandle(C.Structure):
    _fields_ = [('unused', C.c_int)]

dll = C.windll.LoadLibrary('tisgrabber_x64.dll')
dll.create_grabber = dll.IC_CreateGrabber
dll.create_grabber.argtypes = []
dll.create_grabber.restype = GrabberHandle
my_handle = dll.create_grabber()

This seems to work, but I'm worried that I'm doing this wrong. I'm not experienced with C, and I don't think I understand the typedef and #define statements which define the HGRABBER type. Am I calling IC_CreateGrabber correctly? Should I have defined GrabberHandle to be a pointer to a struct, instead of a struct?

Thanks for reading, please let me know if I can clarify my question somehow.

You're right that you actually want a POINTER to the Structure , not the Structure itself.

Translating the C into English, being very loose (in a way that would be dangerous if you were trying to learn C but is good enough for using ctypes ):

  • The struct defines a type named struct HGRABBER_t__ , as a structure with one int in it.
  • The typedef defines a type named HGRABBER_t , as a synonym for struct HGRABBER_t__ .
  • The #define defines a type named HGRABBER as a pointer to HGRABBER_t .

So, your GrabberHandle is the equivalent of HGRABBER_t ; the equivalent of HGRABBER is:

GrabberHandlePtr = C.POINTER(GrabberHandle)

So you want this:

dll.create_grabber.restype = GrabberHandlePtr

It may be hard to debug the difference. AC struct with nothing but an int in it looks identical to an int in memory. And on Win32, an int and a pointer are both 32-bit values. And an int named unused is likely to be filled with meaningless garbage, making it hard to distinguish it from a pointer you've accidentally treated as an int. So everything will look fine, until you segfault 30 lines later in your code and have no idea what's wrong. :)

This library does what you are trying to do: https://github.com/morefigs/py-ic-imaging-control :)

But to answer your question, the library uses the code:

from ctypes import *
import os

class GrabberHandle(Structure):
    pass
GrabberHandle._fields_ = [('unused', c_int)]

# set and check path
dll_path = os.path.join(os.path.expanduser('~'),
                        'Documents\\The Imaging Source Europe GmbH\\TIS Grabber DLL\\bin\\win32\\tisgrabber.dll')
with open(dll_path) as thefile:
    pass

# open DLL
_ic_grabber_dll = windll.LoadLibrary(dll_path)

# create grabber
create_grabber = _ic_grabber_dll.IC_CreateGrabber
create_grabber.restype = POINTER(GrabberHandle)
create_grabber.argtypes = None

# get handle
handle = create_grabber()

Edit: changed code to use a pointer to GrabberHandle as per abarnert's answer as this is correct. However, in this particular case I have found no practical difference (with the 32-bit DLL), probably because the GrabberHandle structure is so simple.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM