python ctypes 如何将字符串写入给定缓冲区
python ctypes how to write string to given buffer
test.cpp
#include <iostream>
#include <strsafe.h>
using namespace std;
typedef void(__stdcall* Mycallback2)(wchar_t* buff, size_t buffsize);
extern "C" __declspec(dllexport)
void TestMethod5(Mycallback2 callback)
{
wchar_t* buff = new wchar_t[MAX_PATH]; // MAX_PATH 260
StringCchPrintf(buff, MAX_PATH, L"%s", L"Test String");
if (callback) callback(buff, MAX_PATH);
wcout << buff << endl;
delete[] buff;
}
test.py
from ctypes import *
dll = CDLL(path)
MYCALLBACKTYPE = CFUNCTYPE(None, c_wchar_p, c_size_t)
dll.TestMethod5.restype = None
dll.TestMethod5.argtypes = [MYCALLBACKTYPE]
def callback(pt: c_wchar_p, ptsize: c_size_t) -> None:
pt.value = 'python string'
mycallback = MYCALLBACKTYPE(callback)
dll.TestMethod5(mycallback)
python 输出
Exception ignored on calling ctypes callback function: <function callback at 0x0000021D50DE7B80>
Traceback (most recent call last):
File "d:\MyProjects\GitRepo\CodePython\App\dlltest\main.py", line 59, in callback
pt.value = 'python string'
AttributeError: 'str' object has no attribute 'value'
我不知道如何写入给定的缓冲区。我将 pt
的类型指定为 c_wchar_p
,但 pt
的类型已更改为 str
。
我试图将字符串分配给 pt
,当然没有用。
编辑 1:
我找到了一个可行的方法,但不是很好。
MYCALLBACKTYPE = CFUNCTYPE(None, POINTER(c_wchar), c_size_t)
dll.TestMethod5.restype = None
dll.TestMethod5.argtypes = [MYCALLBACKTYPE]
def callback(pt: POINTER(c_wchar), ptsize: c_size_t) -> None:
s = 'python string'
if len(s) < ptsize:
for i in range(0,len(s)):
pt[i] = s[i]
还有什么好的方法吗..?
这里有一个稍微好一点的方法。将指向(单个)wchar 的指针转换为指向 wchar 数组的指针。然后你可以取消引用并将值直接写入数组:
def callback(pt: POINTER(c_wchar), ptsize: c_size_t) -> None:
pa = cast(pt,POINTER(c_wchar * ptsize))
pa.contents.value = 'python string'
这还有一个好处,如果你向数组写入多于ptsize
个字符,Python会抛出异常。例如:
def callback(pt: POINTER(c_wchar), ptsize: c_size_t) -> None:
s = 'a' * (ptsize + 1) # one too big
pa = cast(pt,POINTER(c_wchar * ptsize))
pa.contents.value = s
这导致:
Exception ignored on calling ctypes callback function: <function callback at 0x0000020B1C7F6310>
Traceback (most recent call last):
File "C:\test.py", line 13, in callback
pa.contents.value = s
ValueError: string too long
不过要小心空终止。 Python 只有在数组中有空间时才通过 .value
写入它。这是一个演示:
test.cpp
#include <iostream>
#include <stdio.h>
using namespace std;
typedef void(__stdcall* Mycallback2)(wchar_t* buff, size_t buffsize);
extern "C" __declspec(dllexport)
void TestMethod5(Mycallback2 callback)
{
wchar_t* buff = new wchar_t[7]; // length 7 buffer
swprintf_s(buff, 7, L"%s", L"123456"); // init buffer
if (callback) callback(buff, 5); // callback alters only 5
wcout << L'|' << buff << L'|' << endl;
delete[] buff;
}
test.py
from ctypes import *
dll = CDLL('./test')
MYCALLBACKTYPE = CFUNCTYPE(None, POINTER(c_wchar), c_size_t)
dll.TestMethod5.restype = None
dll.TestMethod5.argtypes = [MYCALLBACKTYPE]
def callback(pt: POINTER(c_wchar), ptsize: c_size_t) -> None:
s = 'a' * (ptsize if demo else ptsize-1) # 4 or 5 'a' string, no explicit null
pa = cast(pt,POINTER(c_wchar * ptsize))
print('pt =',pa.contents.value)
pa.contents.value = s
mycallback = MYCALLBACKTYPE(callback)
demo = 0
dll.TestMethod5(mycallback)
demo = 1
dll.TestMethod5(mycallback)
输出:
pt = 12345 # only "sees" the size reported
|aaaa| # null was written since there was room
pt = 12345
|aaaaa6| # null wasn't written when length 5 value written,
# So wcout "saw" the ending 6.
test.cpp
#include <iostream>
#include <strsafe.h>
using namespace std;
typedef void(__stdcall* Mycallback2)(wchar_t* buff, size_t buffsize);
extern "C" __declspec(dllexport)
void TestMethod5(Mycallback2 callback)
{
wchar_t* buff = new wchar_t[MAX_PATH]; // MAX_PATH 260
StringCchPrintf(buff, MAX_PATH, L"%s", L"Test String");
if (callback) callback(buff, MAX_PATH);
wcout << buff << endl;
delete[] buff;
}
test.py
from ctypes import *
dll = CDLL(path)
MYCALLBACKTYPE = CFUNCTYPE(None, c_wchar_p, c_size_t)
dll.TestMethod5.restype = None
dll.TestMethod5.argtypes = [MYCALLBACKTYPE]
def callback(pt: c_wchar_p, ptsize: c_size_t) -> None:
pt.value = 'python string'
mycallback = MYCALLBACKTYPE(callback)
dll.TestMethod5(mycallback)
python 输出
Exception ignored on calling ctypes callback function: <function callback at 0x0000021D50DE7B80>
Traceback (most recent call last):
File "d:\MyProjects\GitRepo\CodePython\App\dlltest\main.py", line 59, in callback
pt.value = 'python string'
AttributeError: 'str' object has no attribute 'value'
我不知道如何写入给定的缓冲区。我将 pt
的类型指定为 c_wchar_p
,但 pt
的类型已更改为 str
。
我试图将字符串分配给 pt
,当然没有用。
编辑 1:
我找到了一个可行的方法,但不是很好。
MYCALLBACKTYPE = CFUNCTYPE(None, POINTER(c_wchar), c_size_t)
dll.TestMethod5.restype = None
dll.TestMethod5.argtypes = [MYCALLBACKTYPE]
def callback(pt: POINTER(c_wchar), ptsize: c_size_t) -> None:
s = 'python string'
if len(s) < ptsize:
for i in range(0,len(s)):
pt[i] = s[i]
还有什么好的方法吗..?
这里有一个稍微好一点的方法。将指向(单个)wchar 的指针转换为指向 wchar 数组的指针。然后你可以取消引用并将值直接写入数组:
def callback(pt: POINTER(c_wchar), ptsize: c_size_t) -> None:
pa = cast(pt,POINTER(c_wchar * ptsize))
pa.contents.value = 'python string'
这还有一个好处,如果你向数组写入多于ptsize
个字符,Python会抛出异常。例如:
def callback(pt: POINTER(c_wchar), ptsize: c_size_t) -> None:
s = 'a' * (ptsize + 1) # one too big
pa = cast(pt,POINTER(c_wchar * ptsize))
pa.contents.value = s
这导致:
Exception ignored on calling ctypes callback function: <function callback at 0x0000020B1C7F6310>
Traceback (most recent call last):
File "C:\test.py", line 13, in callback
pa.contents.value = s
ValueError: string too long
不过要小心空终止。 Python 只有在数组中有空间时才通过 .value
写入它。这是一个演示:
test.cpp
#include <iostream>
#include <stdio.h>
using namespace std;
typedef void(__stdcall* Mycallback2)(wchar_t* buff, size_t buffsize);
extern "C" __declspec(dllexport)
void TestMethod5(Mycallback2 callback)
{
wchar_t* buff = new wchar_t[7]; // length 7 buffer
swprintf_s(buff, 7, L"%s", L"123456"); // init buffer
if (callback) callback(buff, 5); // callback alters only 5
wcout << L'|' << buff << L'|' << endl;
delete[] buff;
}
test.py
from ctypes import *
dll = CDLL('./test')
MYCALLBACKTYPE = CFUNCTYPE(None, POINTER(c_wchar), c_size_t)
dll.TestMethod5.restype = None
dll.TestMethod5.argtypes = [MYCALLBACKTYPE]
def callback(pt: POINTER(c_wchar), ptsize: c_size_t) -> None:
s = 'a' * (ptsize if demo else ptsize-1) # 4 or 5 'a' string, no explicit null
pa = cast(pt,POINTER(c_wchar * ptsize))
print('pt =',pa.contents.value)
pa.contents.value = s
mycallback = MYCALLBACKTYPE(callback)
demo = 0
dll.TestMethod5(mycallback)
demo = 1
dll.TestMethod5(mycallback)
输出:
pt = 12345 # only "sees" the size reported
|aaaa| # null was written since there was room
pt = 12345
|aaaaa6| # null wasn't written when length 5 value written,
# So wcout "saw" the ending 6.