系统: Win7,64位
Python:3.5,64位
1、VS2008下,创建一个DLL。
头文件:CallC++.h
#ifdef CALLC_EXPORTS
#define CALLC_API __declspec(dllexport)
#else
#define CALLC_API __declspec(dllimport)
#endif
#define ARRAY_NUM 10
#define STR_LEN 30
struct StructInfo
{
int iNum;
int iArray[ARRAY_NUM];
char* pszName;
char szName[STR_LEN];
};
#ifdef __cplusplus
extern "C"{
#endif
CALLC_API int Sum(int, int);
CALLC_API void GetString(char*);
CALLC_API char* GetStructInfo(struct StructInfo* pStInfo);
#ifdef __cplusplus
}
#endif
源文件:CallC++.cpp
#include "stdafx.h"
#include "CallC++.h"
CALLC_API int Sum(int a, int b)
{
return a + b;
}
CALLC_API void GetString(char* pszName)
{
strcpy(pszName, "CallC++ DLL");
}
CALLC_API char* GetStructInfo(struct StructInfo* pStInfo)
{
pStInfo->iNum = 100;
for (int i = 0; i < ARRAY_NUM; i++)
{
pStInfo->iArray[i] = i;
}
pStInfo->pszName = "pszName:python call C++!";
strcpy(pStInfo->szName, "szName:python call C++!");
return "finish";
}
注意:
(1)弄清楚编译的时候函数的调用约定采用的__cdecl还是__stdcall,因为根据DLL中函数调用约定方式,Python将使用相应的函数加载DLL。
(2)如果采用C++的工程,那么导出的接口需要extern "C",这样Python中才能识别导出的函数。
编译生成的CallC++.dll,复制到Python安装目录下的DLLs目录下,例如C:\Program Files\Python35\DLLs
如果生成的是32位的DLL,使用64位的Python3.5加载时会出错。
Traceback (most recent call last):
File "C:\Program Files\Python35\Lib\site-packages\pythonwin\pywin\framework\scriptutils.py", line 326, in RunScript
exec(codeObject, __main__.__dict__)
File "C:\Users\Administrator\Desktop\CallC++.py", line 3, in <module>
dll = cdll.LoadLibrary('C:\\Program Files\\Python35\\DLLs\\CallC++.dll')
File "C:\Program Files\Python35\lib\ctypes\__init__.py", line 425, in LoadLibrary
return self._dlltype(name)
File "C:\Program Files\Python35\lib\ctypes\__init__.py", line 347, in __init__
self._handle = _dlopen(self._name, mode)
OSError: [WinError 193] %1 不是有效的 Win32 应用程序。
DLL要编译成64位的。
参见 Win7,64位,VS2008的64位编译环境的安装和配置
2、Python中,调用DLL
dll = cdll.LoadLibrary('CallC++.dll')
导入动态链接时,如果没有加入完全路径,会出错:
Traceback (most recent call last):
File "C:\Program Files\Python35\Lib\site-packages\pythonwin\pywin\framework\scriptutils.py", line 326, in RunScript
exec(codeObject, __main__.__dict__)
File "C:\Users\Administrator\Desktop\CallC++.py", line 2, in <module>
dll = cdll.LoadLibrary('CallC++.dll')
File "C:\Program Files\Python35\lib\ctypes\__init__.py", line 425, in LoadLibrary
return self._dlltype(name)
File "C:\Program Files\Python35\lib\ctypes\__init__.py", line 347, in __init__
self._handle = _dlopen(self._name, mode)
OSError: [WinError 126] 找不到指定的模块。
Python载入 dll 产生的 [WinError 126]的 错误原因
(1) 系统无法定位到你的DLL 动态库
(2)你的DLL 动态库依赖于其他其他DLL 动态库无法被系统找到。
dll = cdll.LoadLibrary('C:\\Program Files\\Python35\\DLLs\\CallC++.dll')
导入动态链接时,加入完全路径,就OK了。
GetStructInfo这个函数通过传递一个StructTest类型的指针,然后对对象中的属性进行赋值,最后返回"finish"。
Python调用代码如下:
首先在Python中继承Structure构造一个和DLL中一致的数据结构StructInfo,
其次设置函数GetStructInfo的参数类型和返回值类型,
然后创建一个StructInfo对象,并将其转化为指针作为参数,调用函数GetStrcutInfo,
最后通过输出数据结构的值来检查是否调用成功:
from ctypes import *
#dll = cdll.LoadLibrary('CallC++.dll')
dll = cdll.LoadLibrary('C:\\Program Files\\Python35\\DLLs\\CallC++.dll')
ret = dll.Sum(2, 3)
print (ret)
str = create_string_buffer(15)
dll.GetString(str)
#print (str.raw)
#print (str.value)
print (str.value.decode())
ARRAY_NUM = 10
STR_LEN = 30
#define type
INTARRAY10 = c_int * ARRAY_NUM;
CHARARRAY30 = c_char * STR_LEN;
#define struct
class StructInfo(Structure):
_fields_ = [
("iNum", c_int),
("iArray", INTARRAY10),
("pszName", c_char_p),
("szName", CHARARRAY30)
]
#load dll and get the function object
dll = cdll.LoadLibrary('C:\\Program Files\\Python35\\DLLs\\CallC++.dll');
GetStructInfo = dll.GetStructInfo;
#set the return type
GetStructInfo.restype = c_char_p;
#set the argtypes
GetStructInfo.argtypes = [POINTER(StructInfo)];
objectStruct = StructInfo();
#invoke api GetStructInfo
retStr = GetStructInfo(byref(objectStruct));
#check result
print ("iNum: ", objectStruct.iNum)
print ("pszName: ", objectStruct.pszName.decode())
print ("szName: ", objectStruct.szName.decode())
for i,val in enumerate(objectStruct.iArray):
print ('Array[i]: ', val)
print (retStr.decode())
3、运行输出结果:
>>> 5
CallC++ DLL
iNum: 100
pszName: pszName:python call C++!
szName: szName:python call C++!
Array[i]: 0
Array[i]: 1
Array[i]: 2
Array[i]: 3
Array[i]: 4
Array[i]: 5
Array[i]: 6
Array[i]: 7
Array[i]: 8
Array[i]: 9
finish
小结:
1)使用64位的Python加载32位的DLL,会出错。
2)Python导入动态链接时,如果没有加入完全路径,会出错。
3)Python3.5中,要将bytes转变为str,bytes对象必须要进行解码。使用decode()方法解码。