首页 > 解决方案 > 从 python ctypes 访问引用的 C 结构字符串

问题描述

我无法从 python ctypes访问C 代码(dll 库)分配的引用结构中的(固定长度)字符串。但是我可以访问和更改int类型。

这是简化的代码。实际上,我正在访问无法更改的专有 dll,因此请将此 dll 的内容视为已修复。我可以毫无问题地从 C 代码访问 dll 结构字符串 - 这就是我构造这个示例 dll 的方式,因此它的接口与专有 dll 的接口相同。

我的假设是错误是DATA_STRUCT中“文本”的python 声明(列出了各种测试的可能性),或者我想要访问字符串的方式 - 被注释为它们要么只返回对象,要么在 Windows 错误中失败(访问冲突)。

请注意,我使用的是 Dev-C++ TDM-GCC 4.9.2 64 位(应该使用 MingW64)和 64 位 Python 3.8.2 提供的编译器。使用 64 位 python 的原因是专有 dll,即 64 位。一切都在 Windows (7) 下。

dll.h

typedef struct REFERENCE_STRUCT {
    int     type ;
    void * dataP ;
} REFERENCE_STRUCT ;

typedef struct DATA_STRUCT {
    int        number ;
    char       text [41] ; // the string is fixed length
} DATA_STRUCT ;

__declspec(dllexport) int test_alloc (REFERENCE_STRUCT *refP) ;
__declspec(dllexport) int test_print (REFERENCE_STRUCT *refP) ;
__declspec(dllexport) int test_free (REFERENCE_STRUCT *refP) ;

maindll.c

#include "dll.h"
#include <windows.h>
#include <string.h>
#include <stdio.h>

__declspec(dllexport) int test_alloc (REFERENCE_STRUCT *refP) {
    DATA_STRUCT         *dataP ;
    dataP = malloc (sizeof (DATA_STRUCT));
    dataP->number = 5 ;
    strcpy (dataP->text, "number 1");
    refP->type = 40 ;
    refP->dataP = ( void *) dataP ;
    printf ("DLL - alloc: reference type: %d;  data <%d>; <%s>\n", refP->type, dataP->number, dataP->text) ;
    return 0;
} ;

__declspec(dllexport) int test_print (REFERENCE_STRUCT *refP) {
    DATA_STRUCT         *dataP ;
    dataP = (DATA_STRUCT*) refP->dataP ;
    printf ("DLL - print: reference type: %d;  data <%d>; <%s>\n", refP->type, dataP->number, dataP->text) ;
    return 0;
} ;

__declspec(dllexport) int test_free (REFERENCE_STRUCT *refP){
    free(refP->dataP) ;
    printf ("DLL - free\n") ;
    return 0;
} ;

脚本.py

import sys,os, ctypes, ctypes.util, faulthandler
faulthandler.enable()

os.add_dll_directory("D:/path_to_lib/")
mylib_path = ctypes.util.find_library("mydll")
mylib = ctypes.CDLL(mylib_path)

class REFERENCE_STRUCT(ctypes.Structure):
    _fields_ = [("type", ctypes.c_int),
               ("dataP", ctypes.c_void_p)]

class DATA_STRUCT(ctypes.Structure):
    _fields_ = [("number", ctypes.c_int),
                ("text", ctypes.c_char * (41))]    # !!! THIS declaration is correct for C "char       text [41] ;" !!!
                ("text", ctypes.POINTER(ctypes.c_char * (41)))]    # WHICH declaration is correct for C "char       text [41] ;" ?
#                 ("text", ctypes.POINTER(ctypes.c_char))]           # WHICH declaration is correct for C "char       text [41] ;" ?
#                 ("text", ctypes.c_char_p)]                         # WHICH declaration is correct for C "char       text [41] ;" ?

reference = REFERENCE_STRUCT()

print("test_alloc: ", mylib.test_alloc (ctypes.byref(reference)))
print("reference.type: ", reference.type)

dataP = ctypes.cast(reference.dataP, ctypes.POINTER(DATA_STRUCT))

# accessing the number without problem:

print("dataP.contents.number:      ",dataP.contents.number)
print(ctypes.cast(reference.dataP,         
ctypes.POINTER(DATA_STRUCT)).contents.text)
print("test_print: ", mylib.test_print (ctypes.byref(reference)))
dataP.contents.number = 7
ctypes.cast(reference.dataP, ctypes.POINTER(DATA_STRUCT)).contents.text = b'num 6'
print("dataP.contents.number:      ",dataP.contents.number)
print(ctypes.cast(reference.dataP, ctypes.POINTER(DATA_STRUCT)).contents.text)
print("test_print: ", mylib.test_print (ctypes.byref(reference)))
print("\n")

print("test_free: ", mylib.test_free (ctypes.byref(reference))) # freeing the alocated memory

标签: cstringpointersstructctypes

解决方案


清单[Python.Docs]:ctypes - Python 的外部函数库

有一些问题:

  1. DATA_STRUCT.text定义(如您所见)。它应该是:("text", ctypes.c_char * 41)

  2. 外部函数的argtypesrestype定义。[SO]中详述的主题:通过 ctypes 从 Python 调用的 C 函数返回不正确的值(@CristiFati 的回答)

  3. 访问成员:(ctypes.cast(reference.dataP, ctypes.POINTER(DATA_STRUCT)).contents.text可以将强制转换指针存储在附加变量中以避免多次执行(写入)它)


推荐阅读