Python ctypes: прототип с параметром LPCSTR [out]

В настоящее время я перехожу к модулю ctypes и пытаюсь вызвать функцию user32 GetWindowText с дескриптором HWND, который я уже получил с помощью FindWindow. На этот раз я хотел сделать еще один шаг и использовать прототип функции вместо вызова функции с помощью ctypes.windll.user32.GetWindowText. Хотя у меня проблемы с объявлением аргумента lpString в качестве выходного параметра.

Моя первая попытка выглядела так:

GetWindowText = cfunc("GetWindowTextA",windll.user32,c_int,
                  ("hWnd",HWND,1),
                  ("lpString",LPCSTR,2),
                  ("nMaxCount",c_int,1)
                  )

(cfunc — это небольшая оболочка, которую я нашел здесь< /а>)

Этот прототип выдает следующее исключение, как только он вызывается:

    chars,name = user32.GetWindowText(handle,255)
TypeError: c_char_p 'out' parameter must be passed as default value

Я думал, что любые выходные переменные должны быть типа POINTER(...), поэтому я изменил свое определение на:

GetWindowText = cfunc("GetWindowTextA",windll.user32,c_int,
                      ("hWnd",HWND,1),
                      ("lpString",POINTER(c_char),2),
                      ("nMaxCount",c_int,1)
                      )

Но это также дает исключение:

    chars,name = user32.GetWindowText(handle,255)
ctypes.ArgumentError: argument 2: <type 'exceptions.TypeError'>: wrong type

Надеюсь, кто-нибудь знает, как правильно вызвать функцию GetWindowText, используя прототипирование ctypes.

Изменить:

Благодаря дальнейшим исследованиям я смог заставить его работать, по крайней мере, каким-то образом. Первая проблема, которую я исправил, заключалась в использовании cfunc() с неправильными спецификаторами вызова. Я определил точную копию этой функции, назвал ее winfunc() и заменил return CFUNCTYPE(result, *atypes)((name, dll), tuple(aflags)) на return WINFUNCTYPE(result, *atypes)((name, dll), tuple(aflags)).

Затем я осмотрел прототипирование дальше. Кажется, что если вы передадите что-то вроде ("someParameter",POINTER(aType),2) в WINFUNCTYPE, он создаст объект aType при вызове и передаст указатель на этот объект в функцию. В возвращенном кортеже вы можете получить доступ к объекту aType. Это поднимает еще одну проблему. cstring — это массив символов; поэтому нужно указать ctypes создать файл c_char array. Это означает, что что-то вроде:

GetWindowText = winfunc("GetWindowTextA",windll.user32,c_int,
                  ("hWnd",HWND,1),
                  ("lpString",POINTER(c_char*255),2),
                  ("nMaxCount",c_int,1)
                  )

работает просто отлично. Но, к сожалению, ctypes теперь будет передавать указатель на cstring длиной ВСЕГДА 255 символов, игнорируя размер, указанный nMaxCount.

На мой взгляд, я думаю, что нет никакого способа заставить эту функцию работать с cstring динамического размера, определенной как выходной параметр. Единственная возможность, кажется, просто отказаться от функции выходного параметра и определить LPCSTR в качестве входного параметра. Затем вызываемая сторона должна создать собственный буфер с помощью ctypes.create_string_buffer() и передать его функции (так же, как в C).


person Sebastian Hoffmann    schedule 05.06.2012    source источник


Ответы (2)


Вы должны создать строковый буфер для выходных параметров. Вы можете обернуть функцию, чтобы сделать ее несколько прозрачной:

# python3
from ctypes import *

_GetWindowText = WinDLL('user32').GetWindowTextW
_GetWindowText.argtypes = [c_void_p,c_wchar_p,c_int]
_GetWindowText.restype = c_int

def GetWindowText(h):
    b = create_unicode_buffer(255)
    _GetWindowText(h,b,255)
    return b.value

FindWindow = WinDLL('user32').FindWindowW
FindWindow.argtypes = [c_wchar_p,c_wchar_p]
FindWindow.restype = c_void_p

h = FindWindow(None,'Untitled - Notepad')
print(GetWindowText(h))

Или в этом случае вы можете просто использовать pywin32:

import win32gui
h = win32gui.FindWindow(None,'Untitled - Notepad')
print(win32gui.GetWindowText(h))
person Mark Tolonen    schedule 05.06.2012
comment
Да, я уже видел pywin32, но я не хочу использовать, потому что мне нужны ctypes в сочетании с win32 в другом месте, и мне нужен постоянный код (возможно, мне даже нужно обмениваться дескрипторами и т. д.). Тем временем я столкнулся с другой проблемой с шаблоном прототипа (можно просто получить доступ к выходным параметрам, а не к обычному возвращаемому значению; не в общем виде), и я серьезно рассматриваю возможность использования решения-оболочки по мере того, как вы приближались, или просто переход полностью (также для функций, где это будет работать) без функции выходных параметров, которая кажется мне довольно непроверенной. - person Sebastian Hoffmann; 06.06.2012
comment
Я не уверен, что вы подразумеваете под постоянным кодом, но объект PyHANDLE pywin32 может присоединяться/отсоединяться от необработанных дескрипторов окна, если вам нужно обменять дескрипторы. См. docs.activestate.com/activepython/2.4/pywin32/PyHANDLE.html документация. Также обратите внимание, что тип Python c_char_p предназначен только для передачи входных строк. Используйте только create_string_buffer(255), например, для создания буферов, которые можно использовать для вывода. - person Mark Tolonen; 06.06.2012

Ага. Вы должны создать буфер для каждого вызова. Если вы позволите определению функции сделать это, как вы сможете получить доступ к буферу позже?

Вы также, кажется, должны сказать ему ожидать указатель c_char с POINTER(c_char), а не просто c_char_p или LPSTR. Хотя не уверен, почему это происходит.

Во всяком случае, это должно работать:

from ctypes import *
from ctypes.wintypes import *

# defs

FindWindowF = WINFUNCTYPE(HWND, LPSTR, LPSTR)
FindWindow = FindWindowF(windll.user32.FindWindowA)

GetWindowTextF = WINFUNCTYPE(c_int, HWND, POINTER(c_char), c_int)
GetWindowText = GetWindowTextF(windll.user32.GetWindowTextA)

# code

text = create_string_buffer(255)

hwnd = FindWindow(None, 'Untitled - Notepad')
GetWindowText(hwnd, text, sizeof(text))

print text.value
person kichik    schedule 05.06.2012
comment
Это действительно работает, как я писал в своем редактировании, и я думаю, что в настоящее время это единственное решение. Проблема, с которой пытаются столкнуться эти вопросы, заключается в использовании функции выходного параметра с cstrings переменного размера. Что касается функции GetWindowRect(), имеющей выходной параметр Rect* для структуры Rect в куче, можно определить прототип как prototype = WINFUNCTYPE(BOOL, HWND, POINTER(RECT)), а затем указать paramflags = (1, "hwnd"), (2, "lprect"). Наконец, получить функцию GetWindowRect = prototype(("GetWindowRect", windll.user32), paramflags). Теперь можно вызвать rect = GetWindowRect(handle) - person Sebastian Hoffmann; 06.06.2012
comment
Проблема с указателями касается следующего: насколько мне известно, c_char_p на самом деле является родным типом, определенным в _ctyped.pyd. Это означает, что c_char_p != POINTER(c_char). Как мне кажется, аргумент paramlist всегда заставляет вас использовать тип POINTER(x), если вы хотите использовать функцию выходного параметра. Таким образом, он создает объект x при вызове и передает POINTER(x) функции. Вы, наконец, возвращаете объект x. Если вы не используете эту функцию, вы, конечно, можете использовать c_char_p или, в данном случае, LPCSTR. - person Sebastian Hoffmann; 06.06.2012