Python调用DLL与回调

Python调用DLL与回调,第1张

Python调用DLL与回调

DLL(Dynamic link Library)动态链接库,是windows上常用的程序包类型。很多业务场景中需要制作和引用DLL。 这里举例说明Python调用DLL的方法,以及DLL中代码回调Python函数的方式。

1. CDLL和WinDLL

在Python中,常使用ctypes库进行与C/C++之间的数据类型的转换,以及DLL的调用。

在制作dll时,我们知道需要约定一种调用规则,调用规则通常是对于函数参数的入栈和清空方式的约定,而调用规则比较常用的有两种, __cdecl和__stdcall,简单来说,前者是C/C++的默认规则,后者是Windows上常用的规则。(关于两者的不同请参考如下链接,这里不展开说明了:

        https://blog.csdn.net/colinchan/article/details/4478721)

在使用VS制作dll时,默认的规则是__cdecl,如果要采用__stdcall则需要显式调用。如:

#include 

#include 

#include 



#define API_DLL __declspec(dllexport)

#define CALL_TYPE __stdcall

//#define CALL_TYPE __cdecl



extern "C" {

    API_DLL int CALL_TYPE dllDebug(int a, int * iParam, char * oParam);

    API_DLL void CALL_TYPE dllRegisterPythonFunc(void * pyFuncPtr);

    API_DLL int CALL_TYPE dllCallbackPythonFunc(void);

}

在Python中,使用ctypes库,对应__cdecl的DLL使用ctypes.CDLL方法引用;对应__stdcall的DLL则使用ctypes.WinDLL方法引用DLL:

def python_get_dll(winDll=False):
    dllDir = "D:/PythonDll/Release/PythonDll.dll"
    if winDll:
        myDll = ctypes.WinDLL(dllDir)
    else:
        myDll = ctypes.CDLL(dllDir)
    return myDll

2. Python调用DLL中的函数

在DLL中实现在1中声明的函数dllDebug,如下:

int CALL_TYPE dllDebug(int a, int * iParam, char * oParam) {

    sprintf(oParam,"Input:[%d %d %d]",iParam[0], iParam[1], iParam[2]);

    return a + iParam[0];

}

此函数中,三个参数,分别是一个int,一个int指针(数组),一个char指针(数组);函数的功能是把int数组中的内容添加到一个字符串中并保存在char指针指向的内存中;函数的返回值是int参数和int数组第一个元素的和。

在Python中调用此函数的方法如下:

def python_call_dll(myDll):

    dllFunc = myDll.dllDebug

    dllFunc.argtypes = [ctypes.c_int,ctypes.POINTER(ctypes.c_int),ctypes.c_char_p]

    dllFunc.restype = ctypes.c_int

    outStrBuf=" "*200

    intList=[1,2,3]

    inIntBuf=(ctypes.c_int * len(intList))(*intList)

    ret = dllFunc(100,inIntBuf,outStrBuf)

    print outStrBuf

    print "python_call_dll:",ret

其中,myDll是此前的ctypes.CDLL的实例。myDll中可以调用DLL中的接口函数,如调用dllDebug函数,使用dllFunc=myDll.dllDebug,此后 *** 作dllFunc即可。

之后要对函数的参数类型(argtypes)和返回值类型(restype)进行匹配,参数类型是int,int *, char *,对应ctypes.c_int, ctypes.POINTER(ctypes.c_int), ctypes.c_char_p。

由于C代码中,指针对应了数组,因此要在Python代码中开辟指针指向的内存空间。对应char * 可以直接用outStrBuf=” ”*200这种形式开辟。而对于int *,需要先定义一个Python列表,再通过ctypes把列表转换成C中能够识别的int数组空间。

运行Python得到的结果:

C:Python27python.exe "D:/VTSystem/Source Code/PyDll/PySrc/DllTest.py"
Input:[1 2 3]                                                                                                                                                                                    
python_call_dll: 101

3. DLL回调Python函数——注册

Python是胶水语言,业务中我们有时也会遇到DLL回调Python函数的时候。调用的方式与C语言的回调方式也类似。

首先我们在DLL中定义回调函数对应的函数指针和注册函数:

typedef void(*_callback_python_func)(int iParam, int * oParam);

_callback_python_func python_function;



void CALL_TYPE dllRegisterPythonFunc(void * pyFuncPtr) {

    python_function = (_callback_python_func)pyFuncPtr;

}

可以看到这个函数指针对应的回调函数的参数是一个int和一个int数组。返回值是一个bool型。

注册函数“dllRegisterPythonFunc”在1中被声明,并能够被Python调用和传入用于回调的函数。

这里DLL的接口用了void *类型。

回到Python中,我们定义一个Python函数用于被DLL回调:

​
def dll_callback_func(intParam,intArray):     
    intArray[0]=ctypes.c_int(intParam)     
    return True

​

这个函数的参数和返回值类型要与DLL中的函数指针匹配。这个函数的功能是把int参数赋值在int数组参数的首个元素中,并返回true。

下一步进行注册:

gCallbackFuncList=[]


def python_register_callback_func(myDll):

    callbackFunc= ctypes.CFUNCTYPE(ctypes.c_bool,ctypes.c_int,ctypes.POINTER(ctypes.c_int))(dll_callback_func)

    gCallbackFuncList.append(callbackFunc)

    myDll.dllRegisterPythonFunc(callbackFunc)

    注册需要使用ctypes.CFUNCTYPE进行函数匹配,CFUNCTYPE的第一个参数是函数的返回值类型,之后才是函数的参数类型,这点要注意。

    另外,将转换后的函数命名为“callbackFunc”,此时将其保存在了一个全局变量数组中,其原因是避免Python的回收机制将定义的回调函数释放。

    最后,调用DLL的注册函数把Python中的函数dll_callback_func赋值给DLL中的函数指针python_function。

4. DLL回调Python函数——调用

为了验证回调功能,我们在DLL中制作了一个函数,函数声明在1中:

int CALL_TYPE dllCallbackPythonFunc() {

    int outBuf[100] = {0};

    python_function(1,outBuf);

    return outBuf[0];

}

    函数中定义了一个数组,并使用了函数指针最终调用Python中的dll_callback_func函数把outBuf的第一个元素赋值成1,因此最后此函数返回值应当为1。

    我们在Python中调用此DLL中的函数:

def python_test_callback(myDll):

    dllFunc = myDll.dllCallbackPythonFunc

    dllFunc.argtypes = []

    dllFunc.restype = ctypes.c_int

    ret = dllFunc()

    print "python_test_callback",ret

    运行结果如下:

C:Python27python.exe "D:/VTSystem/Source Code/PyDll/PySrc/DllTest.py"
python_test_callback 1

5. Demo代码

DLL代码:

#include 
#include 
#include 

#define API_DLL __declspec(dllexport)
#define CALL_TYPE __stdcall
//#define CALL_TYPE __cdecl

extern "C" {
	API_DLL int CALL_TYPE dllDebug(int a, int * iParam, char * oParam);
	API_DLL void CALL_TYPE dllRegisterPythonFunc(void * pyFuncPtr);
	API_DLL int CALL_TYPE dllCallbackPythonFunc(void);
}


int CALL_TYPE dllDebug(int a, int * iParam, char * oParam) {
	sprintf(oParam,"Input:[%d %d %d]",iParam[0], iParam[1], iParam[2]);
	return a + iParam[0];
}

typedef void(*_callback_python_func)(int iParam, int * oParam);
_callback_python_func python_function;

void CALL_TYPE dllRegisterPythonFunc(void * pyFuncPtr) {
	python_function = (_callback_python_func)pyFuncPtr;
}

int CALL_TYPE dllCallbackPythonFunc() {
	int outBuf[100] = {0};
	python_function(1,outBuf);
	return outBuf[0];
}

Python代码

import ctypes

def python_get_dll(winDll=False):
    dllDir = "D:/DllSrc/PythonDll/Release/PythonDll.dll"
    if winDll:
        myDll = ctypes.WinDLL(dllDir)
    else:
        myDll = ctypes.CDLL(dllDir)
    return myDll

def python_call_dll(myDll):
    dllFunc = myDll.dllDebug
    dllFunc.argtypes = [ctypes.c_int,ctypes.POINTER(ctypes.c_int),ctypes.c_char_p]
    dllFunc.restype = ctypes.c_int
    outStrBuf=" "*200
    intList=[1,2,3]
    inIntBuf=(ctypes.c_int * len(intList))(*intList)
    ret = dllFunc(100,inIntBuf,outStrBuf)
    print outStrBuf
    print "python_call_dll:",ret

def dll_callback_func(intParam,intArray):
    intArray[0]=ctypes.c_int(intParam)
    return True

gCallbackFuncList=[]
def python_register_callback_func(myDll):
    callbackFunc= ctypes.CFUNCTYPE(ctypes.c_bool,ctypes.c_int,ctypes.POINTER(ctypes.c_int))
        (dll_callback_func)
    gCallbackFuncList.append(callbackFunc)
    myDll.dllRegisterPythonFunc(callbackFunc)

def python_test_callback(myDll):
    dllFunc = myDll.dllCallbackPythonFunc
    dllFunc.argtypes = []
    dllFunc.restype = ctypes.c_int
    ret = dllFunc()
    print "python_test_callback",ret


if __name__ == '__main__':
    myDll=python_get_dll(True)
    python_call_dll(myDll)
    python_register_callback_func(myDll)
    python_test_callback(myDll)

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5158408.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-11-18
下一篇 2022-11-18

发表评论

登录后才能评论

评论列表(0条)

保存