How can I debug SendKeysCtypes on (win7 64bit, py2.7) further?

南笙酒味 提交于 2019-12-10 11:23:17

问题


Im trying to get SendKeysCtypes working on py2.7 and win7 64bit. Here is src

Problem:

Run SendKeysCtypes.py and nothing happens. Tests should open notepad and write some text.

The problem code is this:

def GetInput(self):
    "Build the INPUT structure for the action"
    actions = 1
    # if both up and down
    if self.up and self.down:
        actions = 2

    inputs = (INPUT * actions)()

    vk, scan, flags = self._get_key_info()

    for inp in inputs:
        inp.type = INPUT_KEYBOARD

        inp._.ki.wVk = vk
        inp._.ki.wScan = scan
        inp._.ki.dwFlags |= flags

    # if we are releasing - then let it up
    if self.up:
        inputs[-1]._.ki.dwFlags |= KEYEVENTF_KEYUP

    return inputs

def Run(self):
    "Execute the action"
    inputs = self.GetInput()
    return SendInput(
        len(inputs),
        ctypes.byref(inputs),
        ctypes.sizeof(INPUT))

SendInput() in above code does nothing.

Other tests

  • I tried the code in this answer and it worked ok. But this code had some other issues.

  • According to the author it should work with py3.1

  • SendInput() returns '0' , which means "blocked by another thread"

  • A call to ctypes.GetLastError() gives us error code 87, which means "ERROR_INVALID_PARAMETER"

And here we are stuck because my windows programming is very limited, can anyone shed some light on this?

EDIT 1:

  • Following the "64bit is problem" trace, lead me to this SO question, will see if I can convert it.

回答1:


There are two problems with the Structure and Union classes you're using. I'll reproduce a snippet from the code you linked to with one of the classes as example here:

class KEYBDINPUT(ctypes.Structure):
    "A particular keyboard event"
    _pack_ = 2  # FIXME: don't do this
    _fields_ = [
        # C:/PROGRA~1/MICROS~4/VC98/Include/winuser.h 4292
        ('wVk', WORD),
        ('wScan', WORD),
        ('dwFlags', DWORD),
        ('time', DWORD),
        ('dwExtraInfo', DWORD),  # FIXME: use correct data type
    ]

(The FIXMEs were added by me to point out the problematic parts.)

The first is that you're using _pack_ = 2, which is incorrect. You should not use any _pack_ alignment specifier at all. The defaults work fine both on 32-bit and on 64-bit Windows.

It's purely by chance that it worked with _pack_ = 2 on 32-bit Windows: All 2-byte sized data types used in the code come in pairs and start on already aligned boundaries and therefore happen to produce the same structures as with 4-byte alignment.

However, under 64-bit Windows the fundamental alignment is 8 byte, so the structures would be misaligned and have wrong sizes if you used _pack_ = 2 or _pack_ = 4 there.

The second problem is that there's no ULONG_PTR in ctypes.wintypes, which would be the correct data type for dwExtraInfo. Under 32-bit Windows ULONG_PTR is an unsigned 32-bit integer whereas under 64-bit Windows it is an unsigned 64-bit integer.

The MSDN article about Windows Data Types shows how it's defined:

#if defined(_WIN64)
 typedef unsigned __int64 ULONG_PTR;
#else
 typedef unsigned long ULONG_PTR;
#endif

Using DWORD or ULONG for dwExtraInfo therefore only happens to work under 32-bit Windows and produces the wrong size otherwise.

While using some POINTER based data type instead would happen to produce a correctly aligned and sized structure, it would be wrong both in meaning as well as in usage, as ULONG_PTR is "an unsigned integer that can also store a (casted) pointer" rather than an actual pointer.

Looking through wintypes, you'll notice that WPARAM happens to be defined exactly like what ULONG_PTR is supposed to be. So a quick and dirty (but still reasonably robust) way to get ULONG_PTR would be:

from ctypes.wintypes import WPARAM as ULONG_PTR

Alternatively, you could use a piece of code inspired by theirs and define it yourself:

import ctypes

for ULONG_PTR in [ctypes.c_ulong, ctypes.c_ulonglong]:
    if ctypes.sizeof(ULONG_PTR) == ctypes.sizeof(ctypes.c_void_p):
        break
else:
    raise TypeError("cannot find a suitable type for ULONG_PTR")

Armed with those adaptions, you could define the structures like this:

class MOUSEINPUT(ctypes.Structure):
    _fields_ = [
        ('dw',          LONG),
        ('dy',          LONG),
        ('mouseData',   DWORD),
        ('dwFlags',     DWORD),
        ('time',        DWORD),
        ('dwExtraInfo', ULONG_PTR),
    ]


class KEYBDINPUT(ctypes.Structure):
    _fields_ = [
        ('wVk',         WORD),
        ('wScan',       WORD),
        ('dwFlags',     DWORD),
        ('time',        DWORD),
        ('dwExtraInfo', ULONG_PTR),
    ]


class HARDWAREINPUT(ctypes.Structure):
    _fields_ = [
        ('uMsg',    DWORD),
        ('wParamL', WORD),
        ('wParamH', WORD),
    ]


class _INPUT(ctypes.Union):
    _fields_ = [
        ('mi', MOUSEINPUT),
        ('ki', KEYBDINPUT),
        ('hi', HARDWAREINPUT),
    ]


class INPUT(ctypes.Structure):
    _anonymous_ = ['']
    _fields_ = [
        ('type', DWORD),
        ('', _INPUT),
    ]


来源:https://stackoverflow.com/questions/10503170/how-can-i-debug-sendkeysctypes-on-win7-64bit-py2-7-further

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!