How to create a Python wrapper for a DLL library

Question:

I am trying to take a provided DLL file from a software SDK and create a python wrapper so to integrate it with the rest of my code base. I have followed quite a few guides online, and still having no luck.

The current Python code I have is:

from ctypes import *
from ctypes.wintypes import HWND
import os

class OptistarDLL(object):
    dll_path = 'OSDS142MRT.dll'

    with open(dll_path) as thefile:
        pass

    _dll = WinDLL(dll_path)

    init_library = _dll['OSDS142M_Initialize']
    init_library.restype = c_int
    init_library.argtypes = (c_int, c_bool, HWND, c_bool, c_int)


class OpticstarControl(object):
    
    def __init__(self):
        err = OptistarDLL.init_library(c_int(0), c_bool(False), HWND(0), c_bool(False), c_int(0))
        if err != 0:
            raise Exception("Doom")

The SDK documentation I am using provides this as the header for the function:

DLLDIR int OSDS142M_Initialize(int iModel, bool bOutVid, HWND hwOutVid, bool bStarView, int iRt);

And the example PDF gives:

OSDS142M_Initialize(1, false, 0, true, 0);

Initialisation currently gets me only

ValueError: Procedure probably called with too many arguments (20 bytes in excess)

I have read, but not understood, about the WinDLL vs CDLL, and the loading of the DLL fails when I change to CDLL. I have also seen in all the guides that the headers in them have DLLEXPORT and mine has DLLDIR, and I don’t know if this is a concern.

Does anyone have any ideas?


ctypes documentation

WinDLL examples

Asked By: Samreay

||

Answers:

The most likely explanation, given the information in the question, is that the DLL uses cdecl rather than stdcall. Your use of WinDLL matches a stdcall DLL. Use CDLL instead to switch to cdecl calling convention.

The error message is consistent with this. The difference between the calling conventions is that stdcall has callee stack cleanup and cdecl has caller clean up. The parameters consume 20 bytes on the stack, being 5 parameters all of size 4. And ctypes pushed those parameters on and expected the callee to clean up the stack. Which it not do because it’s a cdecl function.

Your call to the function is needlessly complex. You can write:

err = OptistarDLL.init_library(0, False, 0, False, 0)

Note that the example call that you quote passes different parameters. To match that call you would write:

err = OptistarDLL.init_library(1, False, 0, True, 0)

You should certainly remove this code:

with open(dll_path) as thefile:
    pass

That serves no purpose other than to waste time. If the DLL does not exist, you will encounter failure soon enough.

Answered By: David Heffernan
Categories: questions Tags: , , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.