Доступ к указанной C строке структуры из python ctypes - PullRequest
1 голос
/ 06 мая 2020

Я не могу получить доступ из python ctypes (фиксированной длины) строки из ссылочной структуры, выделенной кодом C (библиотека dll). Но к типу int я могу получить доступ и изменить.

Это упрощенный код. На самом деле я использую проприетарную dll, которую не могу изменить, поэтому, пожалуйста, примите содержимое этой dll как исправленное. Я могу без проблем получить доступ к строке структуры dll из кода C - именно так я сконструировал этот пример dll, поэтому его интерфейс такой же, как интерфейс проприетарной dll.

Мое предположение было неправильным - это либо python объявление "текста" в DATA_STRUCT (перечислены различные проверенные возможности), либо способ, которым я хочу получить доступ к строке - комментируются, поскольку они либо возвращают только windows ошибка (нарушение прав доступа).

Просто обратите внимание, я использую компилятор, поставляемый с Dev-C ++ TDM-G CC 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;
} ;

script.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

1 Ответ

0 голосов
/ 06 мая 2020

Листинг [Python 3.Docs]: ctypes - Библиотека сторонних функций для Python.

Есть некоторые проблемы:

  1. DATA_STRUCT.text определение (как вы заметили). Это должно быть: ("text", ctypes.c_char * 41)

  2. argtypes и restype определений для сторонних функций. Topi c подробно описано в [SO]: C функция, вызываемая из Python через ctypes, возвращает неверное значение (ответ @ CristiFati)

  3. Доступ к члену : ctypes.cast(reference.dataP, ctypes.POINTER(DATA_STRUCT)).contents.text (можно сохранить указатель приведения в дополнительной переменной, чтобы избежать повторения (записи) его несколько раз)

...