如果需要用脚本来扩展一个程序的功能,或是定制差异化的客制功能,或是用脚本自动运行一系列需要手动执行的操作, 可以考虑用python脚本来实现。当然也可以用lua脚本来实现,不过python库更丰富,可扩展空间更大。
要达成上述功能,就需要主程序与python能够实现双向交互,即主程序可以调用python脚本并取得结果,python脚本也可以调用主程序的功能。
已知主程序是可以单向调用python解释器来执行脚本的,也已知python可以通过ctypes库来调用Dll中的函数。当然主程序可以调用Dll更是众所周知。如果三者在运行时同处一个进程空间,那就完全可以通过Dll这个中间媒介来达到主程序和python的双向交互。经验证,该方法完全可行。下面说说具体的实现步骤:
生成动态链接库
1. 本文所用VS版本为VS 2015 ,进入:文件- >新建->项目,新建一个MFC Dll工程,这里只是演示,因此所有设定都采用默认设定。
2.添加供外部调用的函数,添加MyDll.h和MyDll.cpp到工程中,代码如下:
MyDll.h文件代码:
#pragma once
#ifdef MYDLL_EXPORTS
#define DLL_API __declspec(dllexport)
#else
#define DLL_API __declspec(dllimport)
#endif
typedef void(__stdcall * _tx_function)();
#ifdef __cplusplus
extern "C" {
#endif
DLL_API void __stdcall set_tx_function(_tx_function tx_func);
DLL_API void __stdcall perform_tx();
DLL_API void __stdcall set_value(int v);
DLL_API int __stdcall get_value();
}
MyDll.cpp文件代码:
#include "StdAfx.h"
#include "MyDll.h"
int tmp = 0;
_tx_function tx_fun = NULL;
DLL_API void __stdcall set_tx_function(_tx_function tx_func)
{
tx_fun = tx_func;
}
DLL_API void __stdcall perform_tx()
{
if (tx_fun != NULL)
tx_fun();
}
DLL_API void __stdcall set_value(int v)
{
tmp = v;
}
DLL_API int __stdcall get_value()
{
return tmp;
}
至此,提供给python和主程序调用的Dll编写完毕。
代码说明:
Dll中定义了4个函数,
- 函数set_tx_function是提供给主程序调用的,主程序将一个函数地址传给Dll,Dll用tx_fun保存起来。
- 函数perform_tx是提供给python调用的,用于执行保存在tx_fun里的函数,这样通过间接执行主程序中的函数,就达到了python控制主程序的目的。
- 函数set_value和get_value,主程序和python都可以调用,是用来验证在主程序或python中设定一个值之后,另一方取出的值是否有变化。实际验证,当一方设定该值时,另一方取该值时是会有变化的。这样也说明可以用python来修改主程序中的状态。
生成脚本
python调用Dll的代码如下:
from ctypes import *
api = WinDLL("MFCLibrary1.dll")
print(api.get_value())
api.set_value(200)
api.perform_tx()
生成主程序
进入VS,通过文件- >新建->项目,新建一个MFC应用程序
1. 加入对Dll库和Python库的引用,其中C:\Python27是Python的安装目录,如下图:
2.编写一个函数,用于设定到Dll中供python间接调用,代码如下:
void __stdcall tx_funtion( )
{
TRACE(_T("tx_funtion\n"));
AfxMessageBox(_T("tx_funtion\n"));
//..编写要控制主程序的代码
}
3.在主程序中启动执行python脚本,代码如下:
Py_Initialize();
set_tx_function(tx_funtion);
PyObject * pModule = PyImport_ImportModule("python_call_dll");//python_call_dll为python脚本文件名
int v = get_value();
TRACE(_T("v=%d\n"),v);
Py_Finalize();
从代码执行结果来看,会弹出消息框,说明pyton成功调用主程序中的函数,在python脚本中设定的值,在主程序中通过get_value取出来也是与脚本的设定值一致的,说明主程序与python之间的交互成功。
同理,C#,QT程序与Python的交互也可以如法炮制。
注意:如果你用的Python2.7, 在Debug下编译主程序时,如果出现“无法打开文件“python27_d.lib”的提示,需要将文件c:\Python27\include\pyconfig.h中的pragma comment(lib,"python27_d.lib"),改为pragma comment(lib,"python27.lib")
例程代码下载