3

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.

2 Answers 2

2

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

Sign up to request clarification or add additional context in comments.

3 Comments

Your answer seems like the correct one to me, but the other answer contradicts you. Normally I can tell who is right by running code, but not this time it seems, for the reasons you mention (no crash til way later). Is there a way to prove which answer is right, or should I go with my gut here?
@Andrew, the unused int is unnecessary. What matters is the address in the pointer, which is probably for a C++ object. Setting the result to a 4-byte struct is wrong on a 64-bit system; that truncates the pointer address. You can instead use c_void_p, but for better type safety stick with a pointer to a struct. You don't need to define _fields_. You can use HGRABBER = POINTER(type('HGRABBER', (Structure,), {})).
@eryksun: Good suggestion about using a pointer to an empty struct to represent an "opaque handle" of the kind frequently used in Win32 APIs.
1

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.

9 Comments

I agree the code works, but I get access violations when I make trivial modifications to the code, and I think this might be related. Also I'm using 64 bit, and I think you're using 32.
Unrelated, but while you're here: what's up with the set_format method? It would crash if called, but doesn't the documentation say it's mandatory to set the sink format before acquiring an image?
Do you need to use the 64-bit DLL? Are you using 64-bit Python? They must match. I'm using 32-bit Python and DLL (on 64-bit Windows 7).
You found a typo bug in set_format(), I've fixed it now on GitHub.
C, being a weakly-typed language, requires that casting an object pointer to an appropriate-sized integer and then casting it back before using it is guaranteed safe. On your 32-bit Python, an int is an appropriately-sized integer for any object pointer. So, you get away with it.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.