功能不够用?使用C++编写通达信插件及接入Python(二)
参考:https://zhuanlan.zhihu.com/p/613157262
一、准备工作(参考上一篇)
安装 VS2019
安装pycharm
下载 http://help.tdx.com.cn/book.asp 《通达信DLL函数编程规范.rar》
二、下载python3.x的32位版本,http://www.python.org,随便找个32位版就行了。
我准备下载Windows embeddable package (32-bit),然而下载速度感人,于是参考https://blog.csdn.net/m0_64336780/article/details/123534308
从 https://mirrors.huaweicloud.com/python/ 下载了 python-3.11.3.exe 安装
由于我已经安装了python 64 位的,所以安装后,要调整环境变量,将32位的环境变量调到上面,就切换成32位了。需要63位时,再将64位的环境变量调整到上面即可。
三、从 通达信DLL函数编程规范 里的项目开始
1.修改VS2019,勾选 windows10 SDK
2. 用Visual studio 打开 “TestPluginTCale.dsw”文件
出现“评审项目和解决方案更改” ,直接 确定
出现迁移报告,存在错误
3.解决BUG - 修改项目属性
在解决方案资源管理器中,项目的 TestPluginTCale上右键 属性
在弹出框的 配置属性 c/c++ 常规 中,将 “调试信息格式”的选项更改为“程序数据库(/Zi)”
在 配置属性 c/c++ 代码生成 中 “启用最小重新生成”设为“否(/Gm-)”
在 配置属性 c/c++ 代码生成 中 “启用函数级链接”设为“是(/Gy)”
在 配置属性 链接器 常规 附加库目录
输入 D:\Python\python31132\libs (根据你的32位python安装位置确定)之后,值变为:D:\Python\python31132\libs;%(AdditionalLibraryDirectories)
如果添加后,vs报错,说什么python3x_d找不到,那就在安装目录下的libs(不是lib)里找到python3x.lib,复制一份改个名,改成vs要求的文件名就行了。总之,vs说差什么文件,改成vs要求的文件名就可以了。
四、改写TCalcFuyncSets.cpp如下:
#include "stdafx.h" #include "TCalcFuncSets.h" #include "Python.h" //严重注意,一定要include这个。 //生成的dll请拷贝到通达信安装目录的T0002/dlls/内,再在公式管理器进行绑定 //有人老是无法正常绑定,这里给出两种解决方案,任选其一,本人喜欢第二种 //1、不运行快捷方式,直接点开通达信目录里的执行文件 //2、运行快捷方式,但需要将快捷方式里的起始位置清空 void CallFunction(int DataLen, float* pfOUT, float* pfINa, float* pfINb, float* pfINc, char* funcName) { //填上自己的32位python安装目录,当然,你的python也可以直接装在这个目录下。 Py_SetPythonHome(L"C:\\Python31132"); //用这个版本的python来执行python文件 Py_Initialize(); //初始化python接口 //初始化使用的变量 PyObject* pModule = NULL; PyObject* pFunc = NULL; PyObject* pName = NULL; //获得绝对路径,保证可以访问到 .py文件 //这里需要注意的时候,运行通达信时,找到的路径是通达信的安装路径,所以需要把子路径补充进去 PyRun_SimpleString("import os,sys"); PyRun_SimpleString("sys.path.append(os.getcwd()+ '/T0002/dlls/')"); //当前的测试python文件名是TDXPYDLL.py,只需要写文件的名称就可以了。也可以换个名字,但需要把名字改了。 pModule = PyImport_ImportModule("TDXPYDLL"); //获取调用的函数 pFunc = PyObject_GetAttrString(pModule, funcName); //定义一个与数组等长的PyList对象数组 PyObject* HList = PyList_New(DataLen); PyObject* LList = PyList_New(DataLen); PyObject* CList = PyList_New(DataLen); //定义一个元组,有四个变量的参数元组 PyObject* paramTuple = PyTuple_New(4); //PyList对象的每个元素赋值PyList_Size(HList),以便传入价格序列 for (int i = 0; i < DataLen; i++) { PyList_SetItem(HList, i, Py_BuildValue("f", pfINa[i])); PyList_SetItem(LList, i, Py_BuildValue("f", pfINb[i])); PyList_SetItem(CList, i, Py_BuildValue("f", pfINc[i])); } //向python传参数 PyTuple_SetItem(paramTuple, 0, Py_BuildValue("i", DataLen));//第一个参数 PyTuple_SetItem(paramTuple, 1, HList); //1:表示序号。第二个参数。 PyTuple_SetItem(paramTuple, 2, LList); //2:表示序号。第三个参数。 PyTuple_SetItem(paramTuple, 3, CList); //3:表示序号。第四个参数。 //使用C++的python接口调用该函数 PyObject* pReturn = PyObject_CallObject(pFunc, paramTuple); if (pReturn) //类型检查 { for (int i = 0; i < DataLen; i++) { //PyObject *Item = PyList_GetItem(pReturn, i);//获取List对象中的每一个元素 float result; PyArg_Parse(PyList_GetItem(pReturn, i), "f", &result);//f表示转换成浮点变量 pfOUT[i] = result; } } ////用完接口得放掉,呼应前面的Py_Initialize(); Py_Finalize(); } void TestPlugin1(int DataLen, float* pfOUT, float* pfINa, float* pfINb, float* pfINc) { CallFunction(DataLen, pfOUT, pfINa, pfINb, pfINc, "TestPlugin1"); } void TestPlugin2(int DataLen, float* pfOUT, float* pfINa, float* pfINb, float* pfINc) { CallFunction(DataLen, pfOUT, pfINa, pfINb, pfINc, "TestPlugin2"); } void TestPlugin3(int DataLen, float* pfOUT, float* pfINa, float* pfINb, float* pfINc) { CallFunction(DataLen, pfOUT, pfINa, pfINb, pfINc, "TestPlugin3"); } void TestPlugin4(int DataLen, float* pfOUT, float* pfINa, float* pfINb, float* pfINc) { CallFunction(DataLen, pfOUT, pfINa, pfINb, pfINc, "TestPlugin4"); } void TestPlugin5(int DataLen, float* pfOUT, float* pfINa, float* pfINb, float* pfINc) { CallFunction(DataLen, pfOUT, pfINa, pfINb, pfINc, "TestPlugin5"); } void TestPlugin6(int DataLen, float* pfOUT, float* pfINa, float* pfINb, float* pfINc) { CallFunction(DataLen, pfOUT, pfINa, pfINb, pfINc, "TestPlugin6"); } //加载的函数,这里的序号和上面函数对应,这只是绑了六个,想多绑的自己加 PluginTCalcFuncInfo g_CalcFuncSets[] = { {1,(pPluginFUNC)&TestPlugin1}, {2,(pPluginFUNC)&TestPlugin2}, {3,(pPluginFUNC)&TestPlugin3}, {4,(pPluginFUNC)&TestPlugin4}, {5,(pPluginFUNC)&TestPlugin5}, {6,(pPluginFUNC)&TestPlugin6}, {0,NULL}, }; //导出给TCalc的注册函数 BOOL RegisterTdxFunc(PluginTCalcFuncInfo** pFun) { if (*pFun == NULL) { (*pFun) = g_CalcFuncSets; return TRUE; } return FALSE; }
结果提示没头文件,python.h
参考:https://blog.csdn.net/JLinkTwo/article/details/87917504 用 pip install python-dev 安装
再参考:https://blog.csdn.net/weixin_44575152/article/details/114186758 对 配置属性 VC++目录 外部包含目录 进行编辑, 加入了 python.h所在目录: D:\Python\python31132\include
生成解决方案
又提示: 无法打开文件“python311_d.lib
于是将D:\Python\python31132\libs下的 python311.lib 复制 为 python311_d.lib
终于成功了
到了这里, Dll写完了,编译成32位TestPluginTCale.dll放到T0002/dlls目录里。然后创建一个TDXPYDLL.py,该文件同样需要放在T0002/dlls内。
def TestPlugin1(N: int, H: list, L: list, C: list): for i in range(1, N): result[i] = i return result def TestPlugin2(N: int, H: list, L: list, C: list): for i in range(1, N): result[i] = i return result def TestPlugin3(N: int, H: list, L: list, C: list): for i in range(1, N): result[i] = i return result def TestPlugin4(N: int, H: list, L: list, C: list): for i in range(1, N): result[i] = i return result def TestPlugin5(N: int, H: list, L: list, C: list): for i in range(1, N): result[i] = i return result def TestPlugin6(N: int, H: list, L: list, C: list): for i in range(1, N): result[i] = i return result
按上一篇 绑定DLL,写demo公式,结果通达信死机
后来发现 dll 源文件 中有一句忘记改了:
Py_SetPythonHome(L"C:\\Python31132"); //用这个版本的python来执行python文件
改为Py_SetPythonHome(L"D:\\Python\\python31132");
重新生成dll,替换,不要从快捷方式而是从所在目录中启动TdxW.exe,看似成功了,python脚本似乎也运行了。然而得不到输出。
又参照网贴,将TestPlugin2改为如下代码:
def TestPlugin2(N: int, H: list, L: list, C: list): result=[] for i in range(1, N): temp = i * 6.0 result.append(temp) return result
终于成功了
上面的temp也可以:temp = (H[i]+L[i]+C[i])*1.0
说明:在python代码中,可以 import os
然后用os.mkdir('你的目录')在通达信运行目录中生成文件夹
或者用
log = open('r.txt', 'a') print(“相关数据”, file=log)
更明显地看到效果。