使用环境:
1、Visual Studio 2017
2、Pycharm
参考链接:
1、Python 调用DLL动态链接库——ctypes使用
2、python 调用C++ DLL 传参技巧
按照正常的exe工程进行更改,或者直新建动态库的工程,下面介绍的是正常exe工程进行更改步骤
1、因为我在调式,所以更改的是Debug x64的模式,如果正式发布dll的时候用Release x64模式进行设置,点击右键->选择属性
2、常规->项目默认值->配置类型 选择动态库(.dll)
3、点击确认按钮,对话框关闭
把要导出dll的函数文件进行下面更改:
1、#define DLLEXPORT extern “C” __declspec(dllexport)
2、在函数前面增加定义的宏 DLLEXPORT
样例代码如下
#define DLLEXPORT extern "C" __declspec(dllexport)
DLLEXPORT void differce(const char* srcShpPath, const char* dstShpPath, const char* outDir)
{
//输出变化点的库
//const char* srcShpPath = myParasVec[1];
//const char* dstShpPath = myParasVec[2];
//const char* outDir = myParasVec[3];
printf("srcShpPath:%s\n", srcShpPath);
printf("dstShpPath:%s\n", dstShpPath);
printf("outDir:%s\n", outDir);
//以下代码是主要实现的功能,省略...
}
1、点击菜单 生成->生成解决方案
2、切换到工程根目录,进入x64/Debug下面查看导出的dll,如果要移植,只需要把该目录的所有dll复制到要使用的地方
使用了argparse进行传参,目前测试使用默认值进行传参
from ctypes import *
pDll = CDLL(r"E:\02_Project\C++\SuperXTestDemo\x64\Debug\SuperXTestDemo.dll")#dll路径在实际使用的时候可以放在同级目录,使用相对路
import argparse#传参包
parser = argparse.ArgumentParser(description='程序功能:输出变化点图和面积统计txt。V1.0 2021.11.12 zph ')
parser.add_argument("--SrcShpFile",help="srcShp路径;",default=r"D:/datas/WKK_ZPH/Result/GF2_PMS2_E111.5_N27.8_20171030_L1A0002721668-MSS2_rectify_Fusion_Cut_8bit_mask.shp")
parser.add_argument("--DstShpFile",help="dstShp路径",default=r"D:/datas/WKK_ZPH/Result/GF2_PMS1_E111.6_N27.8_20180416_L1A0003126832-MSS1_rectify_Fusion_Cut_8bit_mask.shp")
parser.add_argument("--InPutImg",help="待绘图影像",default="D:/datas/WKK_ZPH/Result/Cut/GF2_PMS2_E111.5_N27.8_20171030_L1A0002721668-MSS2_rectify_Fusion_Cut.tiff")
parser.add_argument('--outPutDir', help='输出影像文件夹',default=r"D:/datas/WKK_ZPH/Result/Dif/2017_2018")
args=parser.parse_args()
pDll.differce(args.SrcShpFile,args.DstShpFile,args.outPutDir)#调用dll的导出函数
使用调试模式输出
报错啥原因?我也不知道,不用调试直接运行吧
直接运行输入:
通过上面可以看到原来是调用的时候传入字符串的时候接收出了错误,解决方法在下一节
通过上面简单使用发现dll在接受参数的时候出了错误,仔细查找相关资料发现python调用dll传参的时候需要注意
1、如果不加任何修饰,默认传入参数为int,传出参数也为int
2、对于其他类型(float)需要声明python函数的传入参数类型,传出参数类型
#fun是dll里面编译的函数,导出函数头写法应该是这样的
#float fun(float para1,float para2);
fun.argtypes=[c_float,c_float] #定义传参类型
fun.restype=c_float #定义返回值类型
a=fun(c_float(1.4),c_float(1.2))
print(type(a))
print(a)
3、对于char*传参,需要声明为字符指针,然后分配一块char数组,最后把这个数组强制转换为字符指针
hello=dll.HelloWorld
hello.argtypes=[POINTER(c_char)] #传入参数为字符指针
STR=(c_char * 100)(*bytes("相信你还在这里",'utf-8')) #把一组100个的字符定义为STR
cast(STR, POINTER(c_char))
hello(STR)
4、对于其他数据类型的数组,(例如int*),操作相似:
Ints=dll.Ints
Ints.argtypes=[POINTER(c_int),c_int]
INT=(c_int * 100)(*[1,2,3]) #把列表传入变长参数args*中
cast(INT, POINTER(c_int))
Ints(INT,c_int(3))
所以基于上面的方法改装代码如下:
定义一下传参类型,然后传参。
from ctypes import *
pDll = CDLL(r"E:\02_Project\C++\SuperXTestDemo\x64\Debug\SuperXTestDemo.dll")
import argparse
parser = argparse.ArgumentParser(description='程序功能:输出变化点图和面积统计txt。V1.0 2021.11.12 zph ')
parser.add_argument("--SrcShpFile",help="srcShp路径;",default=r"D:/datas/WKK_ZPH/Result/GF2_PMS2_E111.5_N27.8_20171030_L1A0002721668-MSS2_rectify_Fusion_Cut_8bit_mask.shp")
parser.add_argument("--DstShpFile",help="dstShp路径",default=r"D:/datas/WKK_ZPH/Result/GF2_PMS1_E111.6_N27.8_20180416_L1A0003126832-MSS1_rectify_Fusion_Cut_8bit_mask.shp")
parser.add_argument("--InPutImg",help="待绘图影像",default="D:/datas/WKK_ZPH/Result/Cut/GF2_PMS2_E111.5_N27.8_20171030_L1A0002721668-MSS2_rectify_Fusion_Cut.tiff")
parser.add_argument('--outPutDir', help='输出影像文件夹',default=r"D:/datas/WKK_ZPH/Result/Dif/2017_2018")
args=parser.parse_args()
# pDll.differce(args.SrcShpFile,args.DstShpFile,args.outPutDir)
print("python_Src:",args.SrcShpFile)
print("python_Dst:",args.DstShpFile)
print("python_outDir:",args.outPutDir)
Dif = pDll.differce#定义即将使用的函数
Dif.argtypes = [POINTER(c_char),POINTER(c_char),POINTER(c_char)]#定义传参类型
SrcShpFile_STR=(c_char * 256)(*bytes(args.SrcShpFile,'utf-8')) #把一组100个的字符定义为STR
DstShpFile_STR=(c_char * 256)(*bytes(args.DstShpFile,'utf-8')) #把一组100个的字符定义为STR
OutDirShpFile_STR=(c_char * 256)(*bytes(args.outPutDir,'utf-8')) #把一组100个的字符定义为STR
cast(SrcShpFile_STR, POINTER(c_char))
cast(DstShpFile_STR, POINTER(c_char))
cast(OutDirShpFile_STR, POINTER(c_char))
Dif(SrcShpFile_STR,DstShpFile_STR,OutDirShpFile_STR)
不定义传参类型,直接转换参数后直接传参使用
from ctypes import *
pDll = CDLL(r"E:\02_Project\C++\SuperXTestDemo\x64\Debug\SuperXTestDemo.dll")
import argparse
parser = argparse.ArgumentParser(description='程序功能:输出变化点图和面积统计txt。V1.0 2021.11.12 zph ')
parser.add_argument("--SrcShpFile",help="srcShp路径;",default=r"D:/datas/WKK_ZPH/Result/GF2_PMS2_E111.5_N27.8_20171030_L1A0002721668-MSS2_rectify_Fusion_Cut_8bit_mask.shp")
parser.add_argument("--DstShpFile",help="dstShp路径",default=r"D:/datas/WKK_ZPH/Result/GF2_PMS1_E111.6_N27.8_20180416_L1A0003126832-MSS1_rectify_Fusion_Cut_8bit_mask.shp")
parser.add_argument("--InPutImg",help="待绘图影像",default="D:/datas/WKK_ZPH/Result/Cut/GF2_PMS2_E111.5_N27.8_20171030_L1A0002721668-MSS2_rectify_Fusion_Cut.tiff")
parser.add_argument('--outPutDir', help='输出影像文件夹',default=r"D:/datas/WKK_ZPH/Result/Dif/2017_2018")
args=parser.parse_args()
# pDll.differce(args.SrcShpFile,args.DstShpFile,args.outPutDir)
print("python_Src:",args.SrcShpFile)
print("python_Dst:",args.DstShpFile)
print("python_outDir:",args.outPutDir)
SrcShpFile_STR=(c_char * 256)(*bytes(args.SrcShpFile,'utf-8')) #把一组100个的字符定义为STR
DstShpFile_STR=(c_char * 256)(*bytes(args.DstShpFile,'utf-8')) #把一组100个的字符定义为STR
OutDirShpFile_STR=(c_char * 256)(*bytes(args.outPutDir,'utf-8')) #把一组100个的字符定义为STR
cast(SrcShpFile_STR, POINTER(c_char))
cast(DstShpFile_STR, POINTER(c_char))
cast(OutDirShpFile_STR, POINTER(c_char))
# hello(STR)
pDll.differce(SrcShpFile_STR,DstShpFile_STR,OutDirShpFile_STR)
在不同目录下调用这个dll的时候可能出现异常,异常代码如下
Traceback (most recent call last):
File "E:\03_study\WanMen\pytorch_study\My_Code\GetImageBoundary\WKK_zph\ChangeSeg_V1.0.py", line 51, in <module>
pDll = CDLL(dllPath)#剪切shp程序动态库
File "C:\Anaconda3\lib\ctypes\__init__.py", line 356, in __init__
self._handle = _dlopen(self._name, mode)
OSError: [WinError 126] 找不到指定的模块
解决方案,只需要在调用dll前把执行目录切换过来即可,具体可参考我另一篇博客:
python 切换工作目录执行程序