Search code examples
pythonpython-3.xdllctypesdllimport

How to read output of a dll with custom structure using ctypes?


I am working with a DLL called CL3_IF.dll that is described in its documentation as:

Format: CL3IF_VERSION_INFO CL3IF_GetVersion(void)

Parameters: —

Return value: Returns the version of this DLL.

typedef struct {
 INT majorNumber;
 INT minorNumber;
 INT revisionNumber;
 INT buildNumber;
} CL3IF_VERSION_INFO;

Explanation: This function gets the DLL version.

I have used the following python code:

import ctypes
CLP=ctypes.WinDLL('CL3_IF.dll')
class CLIF_VERSION_INFO(ctypes.Structure):
    _fields_ = [("majorNumber", ctypes.c_int),
                ("minorNumber", ctypes.c_int),
                ("revisionNumber", ctypes.c_int),
                ("buildNumber", ctypes.c_int)]
CLP.CL3IF_GetVersion(ctypes.c_void_p, CLIF_VERSION_INFO)

When I run the above code I get the following error:

ArgumentError: argument 1: <class 'TypeError'>: Don't know how to convert parameter 1

I want to be able to get the version info in a usable form, and many other functions of the dll give their outputs in complex structures.

The most complex function I will be using will be this

If anybody knows how I would go about reading the output of this, I would greatly appreciate it.


Solution

  • The returned structure is a return value, not an argument, and void means no parameters, whereas c_void_p means a void* parameter. Make sure to set .argtypes and .restype correctly.

    Also note that WinDLL is for 32-bit __stdcall APIs. Use CDLL for __cdecl (C calling convention) APIs. On 64-bit Python both will work, because there is only one calling convention, but for portable code use the correct one.

    Full example below:

    test.cpp

    #ifdef _WIN32
    #   define API __declspec(dllexport)
    #else
    #   define API
    #endif
    
    typedef int INT;
    
    typedef struct {
        INT majorNumber;
        INT minorNumber;
        INT revisionNumber;
        INT buildNumber;
    } CL3IF_VERSION_INFO;
    
    extern "C"
    {
    
    API CL3IF_VERSION_INFO CL3IF_GetVersion(void) {
        CL3IF_VERSION_INFO info = {1,2,3,4};
        return info;
    }
    
    }
    

    test.py

    import ctypes
    
    class CLIF_VERSION_INFO(ctypes.Structure):
    
        _fields_ = [("majorNumber", ctypes.c_int),
                    ("minorNumber", ctypes.c_int),
                    ("revisionNumber", ctypes.c_int),
                    ("buildNumber", ctypes.c_int)]
    
        def __repr__(self):
            return f'CLIF_VERSION_INFO(major={self.majorNumber},minor={self.minorNumber},rev={self.revisionNumber},build={self.buildNumber})'
    
    dll = ctypes.CDLL('./test')
    dll.CL3IF_GetVersion.argtypes = ()
    dll.CL3IF_GetVersion.restype = CLIF_VERSION_INFO
    
    print(dll.CL3IF_GetVersion())
    

    Output:

    CLIF_VERSION_INFO(major=1,minor=2,rev=3,build=4)