Я сделал простой пример, когда код C (фактически обернутый C ++) получает список строк и копирует их, затем функция get_list_of_strings
возвращает их с суффиксом позиции строки.
Что касается определенияфункции в python:
# wchar_t** get_list_of_strings()
get_list_of_strings.restype = ctypes.POINTER(ctypes.c_wchar_p)
# void set_list_of_strings(wchar_t** list)
set_list_of_strings.argtypes = (ctypes.POINTER(ctypes.c_wchar_p), )
Способ получить строку из wchar_t**
довольно прост: вы можете индексировать сам возвращаемый указатель и извлекать из него строки. Поскольку возвращаемый массив заканчивается символом nullptr
(преобразованным в None
), вы можете просто остановить итерацию в этой точке:
result = get_list_of_strings()
index = 0
while True:
p = result[index]
if p is None:
break
index += 1
print(p)
Сложная часть инициализирует массив указателя wchar_t*
:
def list_to_wchar_t_pp(l):
if not l:
return None
buffers = [ctypes.create_unicode_buffer(s) for s in l]
addresses = list(map(ctypes.addressof, buffers))
addresses.append(0) # append nullptr at the end
pp = (ctypes.c_wchar_p * (len(l) + 1))(*addresses)
return pp
Мы создаем буферы Unicode из строк, получаем адреса этих буферов, добавляем нулевой указатель в конце. Затем мы инициализируем массив указателей с этими адресами.
Код Python:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
import string
import ctypes
def list_to_wchar_t_pp(l):
if not l:
return None
buffers = [ctypes.create_unicode_buffer(s) for s in l]
addresses = list(map(ctypes.addressof, buffers))
addresses.append(0) # append nullptr at the end
pp = (ctypes.c_wchar_p * (len(l) + 1))(*addresses)
return pp
def main():
dll = ctypes.WinDLL(r"TestDll.dll")
# wchar_t** get_list_of_strings()
get_list_of_strings = dll.get_list_of_strings
get_list_of_strings.restype = ctypes.POINTER(ctypes.c_wchar_p)
# void set_list_of_strings(wchar_t** list)
set_list_of_strings = dll.set_list_of_strings
set_list_of_strings.argtypes = (ctypes.POINTER(ctypes.c_wchar_p), )
strings = ["foo", "bar", "baz"]
pp_strings = list_to_wchar_t_pp(strings)
if not pp_strings:
return -1
set_list_of_strings(pp_strings)
result = get_list_of_strings()
index = 0
while True:
p = result[index]
if p is None:
break
index += 1
print(p)
return 0
if __name__ == "__main__":
sys.exit(main())
Вывод:
foo #1
bar #2
baz #3
C ++ код дляобщая библиотека (Windows DLL);обратите внимание, что освобождение глобального буфера и его указателей также должно быть реализовано.
#include <cstdlib>
#include <string>
#include <iostream>
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
#define EXTERN_DLL_EXPORT extern "C" __declspec(dllexport)
static wchar_t** g_list;
EXTERN_DLL_EXPORT void set_list_of_strings(wchar_t** list)
{
auto count = 0;
while(true)
{
const auto p = list[count];
if (p == nullptr)
break;
count++;
}
if (count == 0)
return;
g_list = new wchar_t* [count + 1];
for(int i = 0; i < count; i++)
{
const auto s = list[i];
const auto len = wcslen(s);
auto p = new wchar_t[len + 1];
wcscpy_s(p, len + 1, s);
g_list[i] = p;
}
g_list[count] = nullptr;
}
EXTERN_DLL_EXPORT wchar_t** get_list_of_strings()
{
if (g_list == nullptr)
return nullptr;
auto count = 0;
while(true) {
const auto p = g_list[count];
if (p == nullptr)
break;
count++;
}
if (count == 0)
return nullptr;
const auto new_list = new wchar_t* [count + 1];
for(int i = 0; i < count; i++)
{
std::wstring ws(g_list[i]);
ws += L" #" + std::to_wstring(i + 1);
const auto length = ws.length();
const auto buff_len = ws.length() + 1;
const auto buff = new wchar_t[buff_len];
wcscpy_s(buff, ws.length() + 1, ws.data());
new_list[i] = buff;
}
new_list[count] = nullptr;
return new_list;
}