C++ Dll导出及python调用C++ DLL

目录

    • 1、C++ Dll 编译导出
      • 1.1、VS工程属性更改
      • 1.2、代码更改
      • 1.3、导出dll
    • 2、python 调用Dll样例
      • 2.1 代码简单调用
      • 2.2 调用的时候传参方式
      • 2.3改装代码1:
      • 2.3改装代码2:
    • 3、异常解决

缘由:由于在使用gdal的时候作矢量求交输出的时候python没用对应api,所以用C++对应的函数编译成dll,用python进行调用。

使用环境:
1、Visual Studio 2017
2、Pycharm

参考链接:
1、Python 调用DLL动态链接库——ctypes使用
2、python 调用C++ DLL 传参技巧

1、C++ Dll 编译导出

1.1、VS工程属性更改

按照正常的exe工程进行更改,或者直新建动态库的工程,下面介绍的是正常exe工程进行更改步骤
1、因为我在调式,所以更改的是Debug x64的模式,如果正式发布dll的时候用Release x64模式进行设置,点击右键->选择属性
C++ Dll导出及python调用C++ DLL_第1张图片
2、常规->项目默认值->配置类型 选择动态库(.dll)
C++ Dll导出及python调用C++ DLL_第2张图片

3、点击确认按钮,对话框关闭

1.2、代码更改

把要导出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.3、导出dll

1、点击菜单 生成->生成解决方案
2、切换到工程根目录,进入x64/Debug下面查看导出的dll,如果要移植,只需要把该目录的所有dll复制到要使用的地方
C++ Dll导出及python调用C++ DLL_第3张图片

2、python 调用Dll样例

2.1 代码简单调用

使用了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的导出函数

使用调试模式输出
C++ Dll导出及python调用C++ DLL_第4张图片
报错啥原因?我也不知道,不用调试直接运行吧
直接运行输入:

C++ Dll导出及python调用C++ DLL_第5张图片
通过上面可以看到原来是调用的时候传入字符串的时候接收出了错误,解决方法在下一节

2.2 调用的时候传参方式

通过上面简单使用发现dll在接受参数的时候出了错误,仔细查找相关资料发现python调用dll传参的时候需要注意
C++ Dll导出及python调用C++ DLL_第6张图片

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

所以基于上面的方法改装代码如下:

2.3改装代码1:

定义一下传参类型,然后传参。

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)

输出:
C++ Dll导出及python调用C++ DLL_第7张图片
从输出结果上看dll正常调用

2.3改装代码2:

不定义传参类型,直接转换参数后直接传参使用

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)

输出:
C++ Dll导出及python调用C++ DLL_第8张图片
从结果上看可以正常传入参数正常执行。

3、异常解决

在不同目录下调用这个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 切换工作目录执行程序

你可能感兴趣的:(C++,Python基础操作,笔记心得,c++,python,开发语言)