如何为DLL库创建Python封装器

3 投票
1 回答
7362 浏览
提问于 2025-04-28 02:48

我正在尝试使用一个软件开发工具包(SDK)提供的DLL文件,创建一个Python的包装器,以便将它和我的其他代码结合起来。我在网上查阅了很多指南,但还是没有成功。

我现在的Python代码是:

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")

我使用的SDK文档给出的函数头是:

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

而示例PDF中提供了:

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

目前初始化只让我得到

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

我读过一些关于WinDLLCDLL的内容,但没搞明白,切换到CDLL时加载DLL失败。我还注意到所有指南中的头文件都有DLLEXPORT,而我的却是DLLDIR,我不知道这是否会影响。

有没有人有什么想法?


ctypes文档
WinDLL示例

暂无标签

1 个回答

4

根据问题中的信息,最可能的解释是这个DLL使用的是cdecl调用方式,而不是stdcall。你用WinDLL是和stdcall DLL相匹配的。要切换到cdecl调用方式,可以使用CDLL

错误信息也和这个解释一致。调用方式之间的区别在于,stdcall是由被调用的函数来清理栈,而cdecl则是由调用的函数来清理栈。参数在栈上占用了20个字节,因为有5个参数,每个参数占4个字节。而ctypes把这些参数放上去后,期望被调用的函数来清理栈,但它并没有这样做,因为这是一个cdecl函数。

你调用这个函数的方式有点复杂。你可以这样写:

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

注意你引用的示例调用传递了不同的参数。要匹配那个调用,你可以这样写:

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

你肯定应该删除这段代码:

with open(dll_path) as thefile:
    pass

这段代码除了浪费时间没有其他作用。如果DLL不存在,你很快就会遇到错误。

撰写回答