添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
extern "C" char* DLL_EXPORT func(const char* str1, const char* str2)
    return getSomeString(str1, str2);
// Goal is to call this function correctly from Python.    
extern "C" void DLL_EXPORT freeMem(void *mem)
        if(mem!=NULL)
            delete mem;

但我没有任何想法,我如何在Python中把收到的指针传回去进行删除?

python
pointers
dll
char
ctypes
VirtualVoid
VirtualVoid
发布于 2012-12-17
3 个回答
Mark Tolonen
Mark Tolonen
发布于 2021-02-18
已采纳
0 人赞同

通常情况下,你在 ctypes 中使用的每个函数都应该声明它的参数和返回类型,这样 Python 就可以检查参数的正确数量和类型,并将 Python 对象的参数转换成正确的 C 数据对象。 不幸的是,在这种情况下,func的正常返回值应该是c_char_p。但是 ctypes试图提供帮助,将 c_char_p 的返回值转换为 Python 字符串,失去了对原始 C 指针值的访问。 相反,你可以将返回类型声明为POINTER(c_char),并使用cast来检索字符串值,这使得返回值成为可以释放的LP_c_char对象。

这里有一个例子。 注意,声明正确的.restype对于64位Python特别重要,因为默认的返回类型是c_int(32位),64位指针可能被截断。 这段代码在32位和64位的构建中都进行了测试。

test.c

#include <string.h>
#include <stdlib.h>
#ifdef _WIN32
#   define API __declspec(dllexport)
#else
#   define API
#endif
API char* func(const char* str1, const char* str2) {
    size_t len = strlen(str1) + strlen(str2) + 1;
    char* tmp = malloc(len);
    strcpy_s(tmp, len, str1);
    strcat_s(tmp, len, str2);
    return tmp;
API void freeMem(void *mem) {
    free(mem);

test.py

import ctypes as ct
dll = ct.CDLL('./test')
dll.func.argtypes = ct.c_char_p,ct.c_char_p
dll.func.restype = ct.POINTER(ct.c_char)
dll.freeMem.argtypes = ct.c_void_p,
dll.freeMem.restype = None
# Helper function to extract the return value as a Python object
# and always free the pointer.
def freeMem(a,b):
    p = dll.func(b'abcdef', b'ghijkl')
    print(p)
    s = ct.cast(p, ct.c_char_p).value
    dll.freeMem(p)
    return s
print(freeMem(b'abcdef', b'ghijkl'))

Output:

<ctypes.LP_c_char object at 0x00000279D7959DC0>
b'abcdefghijkl'
    
Anonymous Coward
Anonymous Coward
发布于 2021-02-18
0 人赞同

你在正确的轨道上。

// TestDLL.cpp
#include <string.h> // strcpy
extern "C" __declspec(dllexport) char* stringdup(const char* str) {
    char* p = new char[strlen(str)+1];
    strcpy(p,str);
    return p;
// if you have no good reason to use void*, use the type
// you've allocated. while it usually works for built-in
// types, it wouldn't work for classes (it wouldn't call
// the destructor)
extern "C" __declspec(dllexport) void stringfree(char* ptr) {
    // you don't need to check for 0 before you delete it,
    // but if you allocate with new[], free with delete[] !
    delete [] ptr; 

而在python中。

# Test.py
import ctypes
lib = ctypes.cdll.TestDLL
# this creates a c-style char pointer, initialized with a string whose
# memory is managed by PYTHON! do not attempt to free it through the DLL!
cstr = ctypes.c_char_p("hello ctypes")
# call the dll function that returns a char pointer 
# whose memory is managed by the DLL.
p = lib.stringdup(cstr)
# p is just an integer containing the memory address of the 
# char array. therefore, this just prints the address:
print p
# this prints the actual string
print ctypes.c_char_p(p).value
# free the memory through the DLL
lib.stringfree(p)
    
stringdup.restype需要被设置为一个指针类型,比如POINTER(c_char),因为默认的结果类型是32位的int。要得到一个Python字符串,可以使用cast(p, c_char_p).valuec_char_p.from_buffer(p).value
ghchoi
ghchoi
发布于 2021-02-18
0 人赞同

For me

#cdll.execute.restype = ctypes.c_char_p               # failed to free
cdll.execute.restype = ctypes.POINTER(ctypes.c_char)  # worked
p = cdll.execute('...')
print(