如何在Python中使用DLL文件?

225

如何在Python中最简单地使用 DLL 文件?

具体而言,如何在不编写任何额外的包装器 C++ 代码以将功能暴露给 Python 的情况下完成这一操作?

强烈建议使用原生Python功能,而不是使用第三方库。


@Remuze 你是不是标记了错误的问题或者用错误的答案标记了?它们都是Python,但我看不出你为什么会认为它们有任何相似之处(我检查了修订历史记录以确保它们在过去的某个时刻没有更接近)。 - Foon
1
@foon 嗯,对不起,我本来想把另一个问题标记为重复并放置这个问题 :) 这表明它们有多么相似! - Trev Davies
7个回答

187

为了使用方便,ctypes 是一个不错的选择。

以下是我实际编写过的 ctypes 代码示例(在 Python 2.5 中)。这是我发现的迄今为止最简单的方法,可以实现你所需求的功能。

import ctypes

# Load DLL into memory.

hllDll = ctypes.WinDLL ("c:\\PComm\\ehlapi32.dll")

# Set up prototype and parameters for the desired function call.
# HLLAPI

hllApiProto = ctypes.WINFUNCTYPE (
    ctypes.c_int,      # Return type.
    ctypes.c_void_p,   # Parameters 1 ...
    ctypes.c_void_p,
    ctypes.c_void_p,
    ctypes.c_void_p)   # ... thru 4.
hllApiParams = (1, "p1", 0), (1, "p2", 0), (1, "p3",0), (1, "p4",0),

# Actually map the call ("HLLAPI(...)") to a Python name.

hllApi = hllApiProto (("HLLAPI", hllDll), hllApiParams)

# This is how you can actually call the DLL function.
# Set up the variables and call the Python name with them.

p1 = ctypes.c_int (1)
p2 = ctypes.c_char_p (sessionVar)
p3 = ctypes.c_int (1)
p4 = ctypes.c_int (0)
hllApi (ctypes.byref (p1), p2, ctypes.byref (p3), ctypes.byref (p4))

ctypes库包含了所有C语言数据类型(int, char, short, void*等),可以按值或引用传递。它也可以返回特定的数据类型,虽然我的示例没有这样做(HLL API通过修改通过引用传递的变量来返回值)。


就上面显示的具体示例而言,IBM的EHLLAPI是一个相当一致的接口。

根据IBM的文档here,所有调用都通过四个void指针传递(EHLLAPI通过第四个参数,指向int的指针将返回代码发送回来,因此,虽然我指定了int作为返回类型,但我可以安全地忽略它)。换句话说,函数的C变体将是:

int hllApi (void *p1, void *p2, void *p3, void *p4)

这导致一个单一的、简单的ctypes函数可以处理EHLLAPI库中提供的任何功能,但很可能其他库需要针对每个库函数设置单独的ctypes函数。

WINFUNCTYPE的返回值是函数原型,但仍需设置更多的参数信息(除了类型之外)。hllApiParams中的每个元组都有一个参数“direction”(1 = 输入,2 = 输出等),一个参数名称和一个默认值 - 有关详细信息请参见ctypes文档。

一旦您拥有了原型和参数信息,就可以创建Python“可调用”hllApi来调用该函数。您只需创建所需的变量(在我的情况下是p1p4)并使用它们调用函数即可。


2
你好,可能已经有一段时间没有人在这里回复了,但我想知道如何让这个程序完全适用于我的程序。你能告诉我这里的“HLLAPI”是什么吗?另外,如果我想使用函数“void BoxProperties(double L, double H, double W,double &A, double &V);”来访问我的dll,你能建议我对上面的代码进行哪些更改以使其正常工作吗?谢谢。 - Neophile
5
hllApiParams的作用是什么?这些元组有什么意义?这个例子中的一些内容很难与文档相对应。 - Jonathon Reinhart
1
@JonathonReinhart,我已经用一些底部的文本详细说明了具体示例。希望这样能更好地解释。如果不行,请告诉我,我会再试一次 :-) - paxdiablo
1
非常好的答案,对我帮助很大,在6年后仍然有用 :) 如果可以的话,我会给它加上+100! - Colonel Beauvel
3
从ctypes新手的角度来看,这真的很令人困惑。即使有解释,也不太确定发生了什么。 - RattleyCooper
显示剩余9条评论

91

这个页面提供了一个使用DLL文件调用函数的非常简单的示例。

以下是详细信息的概括:

在Python中调用DLL函数非常容易。我有一个自制的DLL文件,其中包含两个函数:addsub,它们都需要两个参数。

add(a, b) 返回两个数字的加法结果
sub(a, b) 返回两个数字的减法结果

DLL文件的名称将是“demo.dll”

程序:

from ctypes import*
# give location of dll
mydll = cdll.LoadLibrary("C:\\demo.dll")
result1= mydll.add(10,1)
result2= mydll.sub(10,1)
print("Addition value:"+result1)
print("Substraction:"+result2)

输出:

Addition value:11
Substraction:9

2
这个答案中的链接已经失效了。 - Kaliber64
13
如果链接对你不起作用,不用担心,这只是一个直接的复制粘贴。你没有错过任何信息。 - marsh
我在Visual Studio中使用C++编写了一个动态链接库(.dll)。然而,当我尝试在Python中使用它时,由于我将其编译为x64模式,出现了“这不是有效的Win32应用程序”的错误。我重新以x86模式编译了dll,错误消息消失了。 - Meisam Rasouli
路径到.dll文件可以是相对路径名吗? - undefined

18

使用ctypes在Python下构建并链接DLL

本文提供一个完整的示例,介绍了如何通过ctypes构建一个共享库并在Python中使用它。文章以Windows为例,处理DLLs。需要两个步骤:

  1. 使用Visual Studio的编译器从命令行或IDE中构建DLL;
  2. 使用ctypes在Python中链接DLL。

共享库

我考虑的共享库如下,包含在testDLL.cpp文件中。唯一的函数testDLL接收一个int并打印它。

#include <stdio.h>extern "C" {
​
__declspec(dllexport)
​
void testDLL(const int i) {
    printf("%d\n", i);
}
​
} // extern "C"

通过命令行构建DLL

要从命令行使用Visual Studio构建DLL,请运行:

"C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\Tools\vsdevcmd"

设置包含路径并运行

cl.exe /D_USRDLL /D_WINDLL testDLL.cpp /MT /link /DLL /OUT:testDLL.dll

构建DLL:

使用IDE从Visual Studio构建DLL

或者可以使用Visual Studio按如下方式构建DLL

  1. 文件 -> 新建 -> 项目;
  2. 已安装 -> 模板 -> Visual C++ -> Windows -> Win32 -> Win32Project;
  3. 下一步;
  4. 应用程序类型 -> DLL;
  5. 其他选项 -> 空项目(选中);
  6. 其他选项 -> 预编译头(不选);
  7. 项目 -> 属性 -> 配置管理器 -> 活动解决方案平台: x64;
  8. 项目 -> 属性 -> 配置管理器 -> 活动解决方案配置: Release.

在Python下链接DLL

在Python下执行以下操作:

import os
import sys
from ctypes import *

lib = cdll.LoadLibrary('testDLL.dll')

lib.testDLL(3)

如果Python包中有DLL文件,我该如何访问它? - decadenza

6
也许可以使用Dispatch
from win32com.client import Dispatch

zk = Dispatch("zkemkeeper.ZKEM") 

zkemkeeper是系统中注册的DLL文件,之后您可以通过调用它们来访问函数:

zk.Connect_Net(IP_address, port)

我正在处理zkemkeeper.ZKEM,你知道我怎样在Python中获取实时事件吗? - Wafula Samuel

6

3

ctypes是使用最简单的方法,但(mis)使用它会导致Python崩溃。如果你想快速完成某些操作,并且非常小心,那么它很棒。

我建议你查看Boost Python。是的,它需要编写一些C++代码并拥有C++编译器,但实际上你不需要学习C++就能使用它,而且你可以从Microsoft获得免费(免费啤酒)的C++编译器


2
如果 DLL 是 COM 库类型,则可以使用 pythonnet。
pip install pythonnet

那么在你的Python代码中,请尝试以下操作。
import clr
clr.AddReference('path_to_your_dll')

# import the namespace and class

from Namespace import Class

# create an object of the class

obj = Class()

# access functions return type using object

value = obj.Function(<arguments>)


然后根据DLL中的类实例化一个对象,并访问其中的方法。


1
请注意:当前版本的pythonnet不支持.NET Core(https://github.com/pythonnet/pythonnet/issues/984) - gdyrrahitis
@gdyrrahitis,在我的情况下,我使用了.NET框架,感谢提供的信息。 - sattva_venu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接