2

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

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.
Community
  • 1
  • 1
Daniel Magnusson
  • 9,541
  • 2
  • 38
  • 43

1 Answers1

0

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),
    ]
blubberdiblub
  • 4,085
  • 1
  • 28
  • 30