Search code examples
pythonctypeshandle

How to use c_void_p as a handle


I have a .dll I'm trying to access using ctypes. The header for the .dll looks like this:

typedef void* CMS_HANDLE;
CMS_HANDLE  WINAPI cmsOpen( int nDeviceID , int nComType);
BOOL        WINAPI cmsClose(CMS_HANDLE hCms);

My python code looks like this:

from ctypes import WinDLL, c_void_p, c_double, byref
dll = WinDLL("CMS_APIX64.dll")
handle = c_void_p(dll.cmsOpen(15, 3))
print(handle)
dll.cmsClose(handle)

It does the dll.cmsOpen correctly and returns a value. However, it doesn't like the value I pass into dll.cmsClose():

c_void_p(18446744072525716064)
Traceback (most recent call last):
  File "C:\Users\mjwilson\dev\Cms100-tool\cms100-tool.py", line 40, in <module>
    dll.cmsClose(handle)
OSError: exception: access violation reading 0xFFFFFFFFB9701A60

I'm sure I'm just doing something dumb with the handle. Any ideas? (I have C-code that works with this .dll)


Solution

  • The problem is on 64-bit Python, handles are 64-bit, but ctypes assumes return types are c_int (32-bit) if not specifically defined, so the cmsOpen return value is already truncated when you put it into a c_void_p.

    What is always good practice, is to fully define both .argtypes and .restype for each function you use. This will convert parameters correctly, and provide error checking if passed incorrect or wrong number of arguments:

    from ctypes import *
    
    dll = WinDLL("CMS_APIX64.dll")
    dll.cmsOpen.argtypes = c_int,c_int
    dll.cmsOpen.restype = c_void_p
    dll.cmsClose.argtypes = c_void_p,
    dll.cmsClose.restype = c_int
    
    handle = dll.cmsOpen(15, 3)
    print(handle)
    dll.cmsClose(handle)