DoubleLi

qq: 517712484 wx: ldbgliet

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
  4737 随笔 :: 2 文章 :: 542 评论 :: 1615万 阅读
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

如何将Python文件发布成DLL供C/C++调用,试过两种思路:

一种是用Cython将Python文件转为.c文件,但是简简单单4行代码,由于调用了NumPy,生成.c文件有5000+行,而且完全找不到原python函数的入口,无奈放弃;

另一种思路是用CPython API给原Python函数写一个C/C++接口调用原函数,再打包成DLL,也是这篇文章的主要内容。

先讲一下我使用的环境:

Python Interpreter: Anaconda3 Python 3.8.5

Python Library: Numpy,目前只试了这一个库,其他应该差不多

IDE: 宇宙最强VS2019 on Windows10

1. 如何生成调用Python函数的DLL

用一个简单的Python函数作为例子,放在test_numpy.py中

 
 
import numpy as np
 
 
 
def func(my_list1, my_list2):
 
list_np1 = np.array(my_list1)
 
list_np2 = np.array(my_list2)
 
return list(list_np1 + list_np2)
 
 
 
# if __name__ == "__main__":
 
# print(func([1, 2, 3], [4, 5, 6]))
 
# 输出:>> 5 7 9
 
 

打开VS2019,新建DLL项目,VS会自己生成几个.h和.cpp文件,只需要用到pch.h和pch.cpp两个文件,其他的不用管

首先将本地调试器换成Release x64模式,使用Debug模式需要python38_d.lib文件,有需要可以自行搜索

配置项目属性:给项目添加Python.h的包含目录和库目录,需要将路径换成自己的Python安装路径,在链接器中添加python38.lib,这个取决于你的Python解释的版本,可以在PythonPath\libs里面找到这个文件

配置好环境以后编写函数,主要是用CPython API将C的数组转换成Python的list对象,然后调用原Python函数,得到结果后转换成C的数组,CPython API里面函数很多

附个参考手册Python/C API 参考手册 — Python 3.8.12 文档

pch.h

 
 
// pch.h: 这是预编译标头文件。
 
// 下方列出的文件仅编译一次,提高了将来生成的生成性能。
 
// 这还将影响 IntelliSense 性能,包括代码完成和许多代码浏览功能。
 
// 但是,如果此处列出的文件中的任何一个在生成之间有更新,它们全部都将被重新编译。
 
// 请勿在此处添加要频繁更新的文件,这将使得性能优势无效。
 
 
 
#ifndef PCH_H
 
#define PCH_H
 
 
 
// 添加要在此处预编译的标头
 
#include "framework.h"
 
 
 
extern "C" _declspec(dllexport) long* listAdd(int a[], int b[], int n); // 将两个数组相加,并返回新数组
 
 
 
#endif //PCH_H
 
 

pch.cpp

 
 
// pch.cpp: 与预编译标头对应的源文件
 
 
 
#include "pch.h"
 
#include <Python.h>
 
#include <iostream>
 
 
 
using namespace std;
 
 
 
// 当使用预编译的头时,需要使用此源文件,编译才能成功。
 
 
 
long* listAdd(int a[], int b[], int n)
 
{
 
Py_SetPythonHome(L"C:\\Users\\10262\\miniconda3\\"); //这里地址一定要写对啊!
 
// 这句语句是在添加python.exe所在路径,不添加虽然编译没有问题,但是会在运行时出现
 
// Fatal Python error: initfsencoding: unable to load the file system codec
 
// ModuleNotFoundError: No module named 'encodings'
 
// 这种很无厘头的错误
 
 
 
//***python调用***//
 
//初始化python模块
 
Py_Initialize();
 
// 检查初始化是否成功
 
if (!Py_IsInitialized())
 
{
 
cout << "初始化失败" << endl;
 
Py_Finalize();
 
}
 
PyObject* pModule;
 
PyObject* pFunc = NULL;
 
PyObject* pArg = NULL;
 
PyRun_SimpleString("import sys");
 
PyRun_SimpleString("sys.path.append('./')");//设置python模块,搜寻位置,文件放在.cpp文件一起
 
 
 
 
 
pModule = PyImport_ImportModule("test_dll");//Python文件名
 
if (!pModule) {
 
cout << "py文件导入失败" << endl;
 
Py_Finalize();
 
return NULL;
 
}
 
else {
 
pFunc = PyObject_GetAttrString(pModule, "func");//Python文件中的函数名
 
if (!pFunc) {
 
cout << "函数导入失败" << endl;
 
Py_Finalize();
 
return NULL;
 
}
 
 
 
PyObject *pyParamsA = PyList_New(n), *pyParamsB = PyList_New(n); //c++类型转python类型
 
 
 
for (int i = 0; i < n; i++) {
 
PyList_SetItem(pyParamsA, i, PyLong_FromLong(a[i]));
 
PyList_SetItem(pyParamsB, i, PyLong_FromLong(b[i]));
 
}
 
 
 
long *result = new long(n);
 
pArg = PyObject_CallFunction(pFunc, "OO", pyParamsA, pyParamsB); //调用函数
 
for (int i = 0; i < n; i++) { // python类型转C++数组
 
result[i] = PyLong_AsLong(PyList_GetItem(pArg, i));
 
cout << result[i] << " ";
 
}
 
cout << endl;
 
 
 
return result;
 
}
 
}
 
 

这里需要注意一下一定要记得Py_SetPythonHome设为Python的安装路径,否则会报错。

OK,以上都搞定了只需要生成DLL即可,生成的dll和lib在SolutionPath\x64\Release里面

2. 调用DLL

在调用DLL之前,最好先建立另一个空项目将pch.cpp里面的代码复制过去测试一下直接调用listAdd函数能不能成功,在这里我省略了这一步

新建空项目,将生成dll和lib文件以及原Python文件test_numpy.py复制到新项目的路径下,新建main.cpp,用于调用dll

main.cpp

 
 
#include<iostream>
 
#include <Windows.h>
 
using namespace std;
 
 
 
int main()
 
{
 
// 加载DLL文件
 
HINSTANCE hDllInst;
 
hDllInst = LoadLibrary(L"test_numpy.dll"); //调用DLL
 
if(hDllInst) {
 
typedef long* (*PLUSFUNC)(int* a, int* b, int n); //后边为参数,前面为返回值
 
PLUSFUNC plus_str = (PLUSFUNC)GetProcAddress(hDllInst, "listAdd"); //GetProcAddress为获取该函数的地址
 
int a[3] = { 1, 2, 3 }, b[3] = { 4, 5, 6 };
 
long* result = plus_str(a, b, 3);
 
cout << "调用完成" << endl;
 
}
 
else {
 
cout << "DLL加载失败" << endl;
 
}
 
 
 
return 0;
 
}
 
 

运行之前,需要配置一下项目属性,将之前生成的lib文件加入到链接器里面

再运行程序,输出5 7 9,完结撒花!

 
posted on   DoubleLi  阅读(4383)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
历史上的今天:
2022-01-11 图解正向代理、反向代理、透明代理
2022-01-11 如何利用MobaX同时处理多台虚拟机输入相同命令
2022-01-11 linux ssh执行命令_在Linux上通过SSH在多个节点上并行执行命令的三种方法
2022-01-11 Window、Linux查看本机外网ip
2021-01-11 mysql 5.7 安装 (压缩包方式 .tar.gz)
2019-01-11 C/C++程序CPU问题分析
2012-01-11 List<T>.Contains(T item)判断是否包含的根据是什么
点击右上角即可分享
微信分享提示