使用 ctypes 和 wchar_t 时如何从 C++ 获取字符串到 python?
Posted
技术标签:
【中文标题】使用 ctypes 和 wchar_t 时如何从 C++ 获取字符串到 python?【英文标题】:How to get a string from C++ to python when using ctypes and wchar_t? 【发布时间】:2018-05-17 15:41:10 【问题描述】:我可以:
-
从 C++ 中获取一个整数并在 python 中使用它
将 python 字符串(作为 wchar_t)发送到 C++ 并用它做一些逻辑
我不能 反方向的第 2 步。
这是我的 C++ 代码(使用 clion 和 cygwin 编译为使用 C++14 的共享库)。
#include <iostream>
wchar_t aa[2];
extern "C"
int DoA()
return 10;
int DoB(wchar_t * in)
if (in[1] == 'a')
return 25;
return 30;
wchar_t * DoC()
aa[0] = 'a';
aa[1] = 'b';
return aa;
这是我的 python 3.6.1 代码,它显示了我能做什么和不能做什么。那么我应该如何获取我的字符串并在 python 中使用它呢?我希望将地址与 wstring_at 一起使用来获取值,但它不起作用。
from ctypes import *
import os.path
print('Hello')
itExist = os.path.exists('C:/Users/Daan/CLionProjects/stringproblem/cmake-build-release/cygstringproblem.dll')
print(itExist)
lib = cdll.LoadLibrary('C:/Users/Daan/CLionProjects/stringproblem/cmake-build-release/cygstringproblem.dll')
print('dll loaded')
A = lib.DoA()
print(A)
Bx = lib.DoB(c_wchar_p('aaa'))
print(Bx)
By = lib.DoB(c_wchar_p('bbb'))
print(By)
Ca = lib.DoC()
print(Ca)
print('Issue is coming')
Cb = wstring_at(Ca,2)
print(Cb)
这是有错误的输出。
Hello
True
dll loaded
10
25
30
-1659080704
Issue is coming
Traceback (most recent call last):
File "ShowProblem.py", line 19, in <module>
Cb = wstring_at(Ca,2)
File "C:\Users\Daan\AppData\Local\Programs\Python\Python36\lib\ctypes\__init__.py", line 504, in wstring_at
return _wstring_at(ptr, size)
OSError: exception: access violation reading 0xFFFFFFFF9D1C7000
【问题讨论】:
【参考方案1】:我在 Linux 上重现了您的问题,并通过定义您的 DoC
函数的返回类型来纠正它:
from ctypes import *
print('Hello')
lib = cdll.LoadLibrary(PATH_TO_TOUR_LIB)
print('dll loaded')
# this line solved the issue for me
lib.DoC.restype = c_wchar_p
A = lib.DoA()
print(A)
Bx = lib.DoB(c_wchar_p('aaa'))
print(Bx)
By = lib.DoB(c_wchar_p('bbb'))
print(By)
Ca = lib.DoC()
print(Ca)
print('Issue is coming')
Cb = wstring_at(Ca,2)
print(Cb)
我还动态分配了内存(一些 Python 专家可能会对此发表评论,我猜这会导致内存泄漏):
extern "C"
int DoA()
return 10;
int DoB(wchar_t * in)
if (in[1] == 'a')
return 25;
return 30;
wchar_t * DoC()
wchar_t* aa = new wchar_t[2];
aa[0] = 'a';
aa[1] = 'b';
return aa;
让我知道它是否适用于 Windows。
【讨论】:
动态分配内存会泄漏。 OP 的方式不会是线程安全的。理想情况下,内存应该在 Python 端分配,并传入要填充的函数中。【参考方案2】:如果您设置了包装函数的.argtypes
和.restype
,您可以更自然地调用它们。要处理输出字符串,如果您在 Python 中分配缓冲区而不是使用全局变量,或者只返回一个宽字符串常量,这将是线程安全的。这是为 Microsoft 编译器编写的示例:
test.c
#include <wchar.h>
#include <string.h>
__declspec(dllexport) int DoA(void)
return 10;
__declspec(dllexport) int DoB(const wchar_t* in)
if(wcslen(in) > 1 && in[1] == 'a') // Make sure not indexing past the end.
return 25;
return 30;
// This version good if variable data is returned.
// Need to pass a buffer of sufficient length.
__declspec(dllexport) int DoC(wchar_t* aa, size_t length)
if(length < 3)
return 0;
aa[0] = 'a';
aa[1] = 'b';
aa[2] = '\0';
return 1;
// Safe to return a constant. No memory leak.
__declspec(dllexport) wchar_t* DoD(void)
return L"abcdefg";
test.py
from ctypes import *
# Set up the arguments and return type
lib = CDLL('test')
lib.DoA.argtypes = None
lib.DoA.restype = c_int # default, but just to be thorough.
lib.DoB.argtypes = [c_wchar_p]
lib.DoB.restype = c_int
lib.DoC.argtypes = [c_wchar_p,c_size_t]
lib.DoC.restype = c_int
lib.DoD.argtypes = None
lib.DoD.restype = c_wchar_p
# Map to local namespace functions
DoA = lib.DoA
DoB = lib.DoB
DoD = lib.DoD
# Do some pre- and post-processing to hide the memory details.
def DoC():
tmp = create_unicode_buffer(3) # Writable array of wchar_t.
lib.DoC(tmp,sizeof(tmp))
return tmp.value # return a Python string instead of the ctypes array.
print(DoA())
print(DoB('aaa'))
print(DoB('bbb'))
print(DoC())
print(DoD())
输出:
10
25
30
ab
abcdefg
【讨论】:
以上是关于使用 ctypes 和 wchar_t 时如何从 C++ 获取字符串到 python?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 ctypes 停止和重新启动从 python 运行的 C++ 代码
如何更改 wchar.h 以使 wchar_t 与 wint_t 类型相同?
C ++:从wchar_t *转换为unsigned short *是否安全?