python - 从 Python 调用时 C DLL 破解
问题描述
我有一个使用 C/Python API 嵌入 Python 解释器的 DLL。如果调用一次 DLL 可以正常工作,但如果调用 DLL 两次,代码就会破解并且我的程序会捕获内存错误。调用 DLL 的 C 代码很简单,对 DLL 函数(调用 Python 解释器)的调用是一次完成,如果第二次调用(在代码中)没有注释,代码就会破裂,只有在调用“Numpy”时才会发生这种情况Python 代码。
#include <stdio.h>
#include <conio.h>
#include <math.h>
#include <dll_simples.h>
int main() {
double in[] = { 4,2,5,4,2 };
double out[5] = {};
double a = 0;
double b = 0;
simuser(a,b,in,out);
//simuser(a, b, in, out); IF NOT COMMENTED -> ERROR
return 0;
}
我已经按照此处的建议在 DLL 中评论了 Py_Finalize(),并且此处的这篇文章从 Py_Finalize() 文档中确认“如果多次调用它们的初始化例程,某些扩展可能无法正常工作;如果应用程序调用 Py_Initialize( ) 和 Py_Finalize() 不止一次。” 所以我想知道为什么会发生这种情况,以及是否可以做任何其他事情,除了在最后一次调用 DLL 时只调用一次“Py_Finalize()”。
解决方案
清单[Python 3.Docs]:ctypes - Python 的外部函数库。
dll00.h:
#pragma once
#if defined(_WIN32)
# if defined DLL0_EXPORTS
# define DLL00_EXPORT_API __declspec(dllexport)
# else
# define DLL00_EXPORT_API __declspec(dllimport)
# endif
#else
# define DLL00_EXPORT_API
#endif
#if defined(__cplusplus)
extern "C" {
#endif
DLL00_EXPORT_API int dll00Func00(double t, double delta, unsigned int size, const double *pIn, double *pOut);
#if defined(__cplusplus)
}
#endif
dll00.c:
#include <stdio.h>
#include "Python.h"
#define DLL0_EXPORTS
#include "dll00.h"
#define C_TAG "From C .dll"
int dll00Func00(double t, double delta, unsigned int size, const double *pIn, double *pOut) {
int res = 0;
printf("%s: in function\n", C_TAG);
const int isInit = Py_IsInitialized();
// Modify array calling Python functions
if (!isInit) {
printf("%s: initializing Python interpreter\n", C_TAG);
Py_Initialize();
}
res = PyRun_SimpleString("print(\"From Python (within C .dll): test\")");
for (unsigned int i = 0; i < size; i++) {
pOut[i] = pIn[i] * t + delta;
}
if (!isInit) {
printf("%s: uninitializing Python interpreter\n", C_TAG);
Py_Finalize();
}
return 0;
}
main00.c:
#include <stdio.h>
#include "dll00.h"
#define SIZE 4
int main() {
int res = 0;
double in[SIZE] = { 10.0, 11.0, 12.0, 13.0 };
double out[SIZE] = { 0 };
res = dll00Func00(2, 0.5, SIZE, in, out);
printf("Output array:\n");
for (unsigned int i = 0; i < SIZE; i++) {
printf("%.03f ", out[i]);
}
printf("\n");
return 0;
}
代码00.py:
#!/usr/bin/env python
import sys
import ctypes as ct
DLL_NAME = "./dll00.dll"
def main(*argv):
DblPtr = ct.POINTER(ct.c_double)
size = 5
DblArr = ct.c_double * size
dll00 = ct.PyDLL(DLL_NAME)
dll00Func00 = dll00.dll00Func00
dll00Func00.argtypes = (ct.c_double, ct.c_double, ct.c_uint, DblPtr, DblPtr)
dll00Func00.restype = ct.c_int
in_arr = DblArr(*range(size))
out_arr = DblArr()
print("Output array:")
for i in range(size):
print("{:.3f}".format(out_arr[i]), end=" ")
print("\n")
res = dll00Func00(2, 2.5, size, in_arr, out_arr)
print("Output array:")
for i in range(size):
print("{:.3f}".format(out_arr[i]), end=" ")
print()
if __name__ == "__main__":
print("Python {0:s} {1:d}bit on {2:s}\n".format(" ".join(item.strip() for item in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
main(*sys.argv[1:])
print("\nDone.")
备注:
- 在Python中,使用ctypes.PyDLL(间接)调用Python API函数
- 在.dll中,使用[Python 3.Docs]: Initialization, Finalization, and Threads - int Py_IsInitialized ()
- 附带说明一下,在Py_Initialize的情况下不需要if测试,因为Py_Initialize只是在解释器已经初始化的情况下返回(所以我把它留在那里只是为了保持一致性),但Py_Finalize需要它,因为人们不想这样做在Python中取消初始化解释器。所以Py_Initialize / Py_Finalize对在“引用计数”上不起作用(每个Py_Initialize调用都需要一个Py_Finalize)
- 附带说明一下,在Py_Initialize的情况下不需要if测试,因为Py_Initialize只是在解释器已经初始化的情况下返回(所以我把它留在那里只是为了保持一致性),但Py_Finalize需要它,因为人们不想这样做在Python中取消初始化解释器。所以Py_Initialize / Py_Finalize对在“引用计数”上不起作用(每个Py_Initialize调用都需要一个Py_Finalize)
在函数中调用Py_Initialize / Py_Finalize似乎有点矫枉过正(如果函数被多次调用)。我会在.dll中执行 2 个包装函数并调用:
- 一开始
- 最后的另一个
( C ) 程序的
输出:
e:\Work\Dev\StackOverflow\q059937552>sopr.bat *** Set shorter prompt to better fit when pasted in StackOverflow (or other) pages *** [prompt]> "c:\Install\pc032\Microsoft\VisualStudioCommunity\2017\VC\Auxiliary\Build\vcvarsall.bat" x64 ********************************************************************** ** Visual Studio 2017 Developer Command Prompt v15.9.19 ** Copyright (c) 2017 Microsoft Corporation ********************************************************************** [vcvarsall.bat] Environment initialized for: 'x64' [prompt]> dir /b code00.py dll00.c dll00.h main00.c [prompt]> cl /nologo /MD /DDLL /I"c:\Install\pc064\Python\Python\03.07.06\include" dll00.c /link /NOLOGO /DLL /OUT:dll00.dll /LIBPATH:"c:\Install\pc064\Python\Python\03.07.06\libs" dll00.c Creating library dll00.lib and object dll00.exp [prompt]> cl /nologo /MD /W0 main00.c /link /NOLOGO /OUT:main00_064.exe dll00.lib main00.c [prompt]> dir /b code00.py dll00.c dll00.dll dll00.exp dll00.h dll00.lib dll00.obj main00.c main00.obj main00_064.exe [prompt]> "e:\Work\Dev\VEnvs\py_pc064_03.07.06_test0\Scripts\python.exe" code00.py Python 3.7.6 (tags/v3.7.6:43364a7ae0, Dec 19 2019, 00:42:30) [MSC v.1916 64 bit (AMD64)] 64bit on win32 Output array: 0.000 0.000 0.000 0.000 0.000 From C .dll: in function From Python (within C .dll): test Output array: 2.500 4.500 6.500 8.500 10.500 Done. [prompt]> set PATH=%PATH%;c:\Install\pc064\Python\Python\03.07.06 [prompt]> main00_064.exe From C .dll: in function From C .dll: initializing Python interpreter From Python (within C .dll): test From C .dll: uninitializing Python interpreter Output array: 20.500 22.500 24.500 26.500
推荐阅读
- angular - Angular 7第一个对象没有被正确推入数组
- akka - 是否可以从演员系统外部使用告诉-不要-询问模式?
- ruby-on-rails-3 - 如何自定义 Comfy CMS 页面生成的视图?
- javascript - Javascript需要在按钮单击后启用多个文本框
- java - java - 如何将Excel中的行放入java中的ArrayList?
- javascript - 在 Javascript 中检查结果时如何修复“未定义”对象?
- flutter - (Flutter) 在 UI 中以不同组显示 List 的数据
- java - Lombok 没有在 .class 文件中生成样板文件并且找不到任何解决方案
- java - android WebView中的上传按钮不起作用
- woocommerce - 在 WooCommerce 的“我的帐户”页面上添加一个按钮以订购详细信息