ctype char char ** - PullRequest
       23

ctype char char **

1 голос
/ 28 августа 2009

Я пытаюсь выяснить, почему это работает после множества и много возни с

obo.librar_version является функцией c, которая требует char ** в качестве входных данных и выполняет strcpy перешел в чар.

from ctypes import *
_OBO_C_DLL = 'obo.dll'
STRING = c_char_p

OBO_VERSION = _stdcall_libraries[_OBO_C_DLL].OBO_VERSION
OBO_VERSION.restype = c_int
OBO_VERSION.argtypes = [POINTER(STRING)]

def library_version():
    s = create_string_buffer('\000' * 32)
    t = cast(s, c_char_p)
    res = obo.library_version(byref(t))
    if res != 0:
        raise Error("OBO error %r" % res)
    return t.value, s.raw, s.value

library_version()

Код выше возвращает

('OBO Version 1.0.1', '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', '')

Что я не понимаю, так это почему значение s не имеет значения? У кого-нибудь есть идеи? Thx

Ответы [ 2 ]

1 голос
/ 28 августа 2009

Когда вы приводите s к c_char_p, вы сохраняете новый объект в t, а не ссылку. Поэтому, когда вы передаете t своей функции по ссылке, s не обновляется.

UPDATE:

Вы действительно правы:

приведение принимает два параметра, a ctypes объект, который является или может быть преобразован в какой-то указатель и тип тип указателя Возвращает экземпляр второго аргумента, который ссылается на тот же блок памяти, что и первый аргумент.

Чтобы получить ссылку на ваш строковый буфер, вам нужно использовать следующее для вашего приведения:

t = cast(s, POINTER(c_char*33))

Понятия не имею, почему c_char_p не создает ссылку, где это происходит, но вы идете.

0 голосов
/ 28 августа 2009

Поскольку для library_version требуется символ **, они не хотят, чтобы вы выделяли символы (как вы делаете с create_string_buffer. Вместо этого они просто хотят, чтобы вы передали ссылку на указатель, чтобы они могли вернуть адрес где найти строку версии.

Итак, все, что вам нужно сделать, это выделить указатель, а затем передать ссылку на этот указатель.

Следующий код должен работать, хотя у меня нет obo.dll (или не знаю подходящей замены) для его проверки.

from ctypes import *
_OBO_C_DLL = 'obo.dll'
STRING = c_char_p

_stdcall_libraries = dict()
_stdcall_libraries[_OBO_C_DLL] = WinDLL(_OBO_C_DLL)
OBO_VERSION = _stdcall_libraries[_OBO_C_DLL].OBO_VERSION
OBO_VERSION.restype = c_int
OBO_VERSION.argtypes = [POINTER(STRING)]

def library_version():
    s_res = c_char_p()
    res = OBO_VERSION(byref(s_res))
    if res != 0:
        raise Error("OBO error %r" % res)
    return s_res.value

library_version()

[Изменить]

Я пошел дальше и написал свою собственную DLL, которая реализует возможную реализацию OBO_VERSION, которая не требует выделенного символьного буфера и не подвержена утечкам памяти.

int OBO_VERSION(char **pp_version)
{
    static char result[] = "Version 2.0";

    *pp_version = result;
    return 0; // success
}

Как видите, OBO_VERSION просто устанавливает значение * pp_version для указателя на символьный массив с нулевым символом в конце. Вероятно, так работает настоящий OBO_VERSION. Я проверил это с моей первоначально предложенной техникой выше, и она работает как предписано.

...