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?
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.
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?
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.