Andrew
Andrew

Reputation: 2892

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.

Upvotes: 3

Views: 306

Answers (2)

101
101

Reputation: 8969

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.

Upvotes: 1

abarnert
abarnert

Reputation: 365577

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. A C 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. :)

Upvotes: 2

Related Questions