Skip to content

Instantly share code, notes, and snippets.

@Aniruddha-Tapas
Created April 26, 2017 15:09
Show Gist options
  • Save Aniruddha-Tapas/1627257344780e5429b10bc92eb2f52a to your computer and use it in GitHub Desktop.
Save Aniruddha-Tapas/1627257344780e5429b10bc92eb2f52a to your computer and use it in GitHub Desktop.
Script to emulate key-presses on Windows via Python.
# directkeys.py
# http://stackoverflow.com/questions/13564851/generate-keyboard-events
# msdn.microsoft.com/en-us/library/dd375731
import ctypes
from ctypes import wintypes
import time
user32 = ctypes.WinDLL('user32', use_last_error=True)
INPUT_MOUSE = 0
INPUT_KEYBOARD = 1
INPUT_HARDWARE = 2
KEYEVENTF_EXTENDEDKEY = 0x0001
KEYEVENTF_KEYUP = 0x0002
KEYEVENTF_UNICODE = 0x0004
KEYEVENTF_SCANCODE = 0x0008
MAPVK_VK_TO_VSC = 0
# List of all codes for keys:
# # msdn.microsoft.com/en-us/library/dd375731
UP = 0x26
DOWN = 0x28
A = 0x41
# C struct definitions
wintypes.ULONG_PTR = wintypes.WPARAM
class MOUSEINPUT(ctypes.Structure):
_fields_ = (("dx", wintypes.LONG),
("dy", wintypes.LONG),
("mouseData", wintypes.DWORD),
("dwFlags", wintypes.DWORD),
("time", wintypes.DWORD),
("dwExtraInfo", wintypes.ULONG_PTR))
class KEYBDINPUT(ctypes.Structure):
_fields_ = (("wVk", wintypes.WORD),
("wScan", wintypes.WORD),
("dwFlags", wintypes.DWORD),
("time", wintypes.DWORD),
("dwExtraInfo", wintypes.ULONG_PTR))
def __init__(self, *args, **kwds):
super(KEYBDINPUT, self).__init__(*args, **kwds)
# some programs use the scan code even if KEYEVENTF_SCANCODE
# isn't set in dwFflags, so attempt to map the correct code.
if not self.dwFlags & KEYEVENTF_UNICODE:
self.wScan = user32.MapVirtualKeyExW(self.wVk,
MAPVK_VK_TO_VSC, 0)
class HARDWAREINPUT(ctypes.Structure):
_fields_ = (("uMsg", wintypes.DWORD),
("wParamL", wintypes.WORD),
("wParamH", wintypes.WORD))
class INPUT(ctypes.Structure):
class _INPUT(ctypes.Union):
_fields_ = (("ki", KEYBDINPUT),
("mi", MOUSEINPUT),
("hi", HARDWAREINPUT))
_anonymous_ = ("_input",)
_fields_ = (("type", wintypes.DWORD),
("_input", _INPUT))
LPINPUT = ctypes.POINTER(INPUT)
def _check_count(result, func, args):
if result == 0:
raise ctypes.WinError(ctypes.get_last_error())
return args
user32.SendInput.errcheck = _check_count
user32.SendInput.argtypes = (wintypes.UINT, # nInputs
LPINPUT, # pInputs
ctypes.c_int) # cbSize
# Functions
def PressKey(hexKeyCode):
x = INPUT(type=INPUT_KEYBOARD,
ki=KEYBDINPUT(wVk=hexKeyCode))
user32.SendInput(1, ctypes.byref(x), ctypes.sizeof(x))
def ReleaseKey(hexKeyCode):
x = INPUT(type=INPUT_KEYBOARD,
ki=KEYBDINPUT(wVk=hexKeyCode,
dwFlags=KEYEVENTF_KEYUP))
user32.SendInput(1, ctypes.byref(x), ctypes.sizeof(x))
if __name__ == "__main__":
PressKey(A)
time.sleep(0.5)
ReleaseKey(A)
print("Pressed")
@PegasusClassSupplyShip
Copy link

sir, this code is just amazing.
Did you code this? because this needs more recognition.
I would love to see some detailed explanation of how the code works since I find it quite confusing since you are dealing with low level problem which is a rare case in python.

@chihiro888
Copy link

holy shit...this is god code

@PiepsC
Copy link

PiepsC commented Jun 12, 2019

Some elaboration would be useful here

@FimoX
Copy link

FimoX commented Mar 29, 2020

Thank you for your script that I'm using in a game.
But I have one big issue, I have a latency/delay of 4 frames, around 50ms.
Is there a way to fix it ?

@Rikka-404
Copy link

This is awesome. That in combination with win32gui = perfection

@freecodecampster
Copy link

@drdoof2019
Copy link

hey, my pc language is turkish and we got "ı" word in our language. My problem is even though I send ascii codes of "i" or "I" for example 0x49
it types "ı". how can i solve that?

@kluader1
Copy link

kluader1 commented Dec 17, 2022

Hi, I edited your code a bit for my needs.

I want create a simple keyboard macro with ctypes. When I press "spacebar", I need the keys "b" and "spacebar" be pressed (with a small delay between them). The thing is that when I run the code, it just presses continuously "f" and "space". I only need "b" and "space" to be pressed and only when I hit the spacebar.

import ctypes
from ctypes import wintypes
import time
import win32con

user32 = ctypes.WinDLL('user32', use_last_error=True)

INPUT_MOUSE = 0
INPUT_KEYBOARD = 1
INPUT_HARDWARE = 2

KEYEVENTF_EXTENDEDKEY = 0x0001
KEYEVENTF_KEYUP = 0x0002
KEYEVENTF_UNICODE = 0x0004
KEYEVENTF_SCANCODE = 0x0008

MAPVK_VK_TO_VSC = 0

List of all codes for keys:

# msdn.microsoft.com/en-us/library/dd375731

B = 0x46
SPACE = 0x20

C struct definitions

wintypes.ULONG_PTR = wintypes.WPARAM

class MOUSEINPUT(ctypes.Structure):
fields = (("dx", wintypes.LONG),
("dy", wintypes.LONG),
("mouseData", wintypes.DWORD),
("dwFlags", wintypes.DWORD),
("time", wintypes.DWORD),
("dwExtraInfo", wintypes.ULONG_PTR))

class KEYBDINPUT(ctypes.Structure):
fields = (("wVk", wintypes.WORD),
("wScan", wintypes.WORD),
("dwFlags", wintypes.DWORD),
("time", wintypes.DWORD),
("dwExtraInfo", wintypes.ULONG_PTR))

def __init__(self, *args, **kwds):
    super(KEYBDINPUT, self).__init__(*args, **kwds)

    if not self.dwFlags & KEYEVENTF_UNICODE:
        self.wScan = user32.MapVirtualKeyExW(self.wVk,
                                             MAPVK_VK_TO_VSC, 0)

class HARDWAREINPUT(ctypes.Structure):
fields = (("uMsg", wintypes.DWORD),
("wParamL", wintypes.WORD),
("wParamH", wintypes.WORD))

class INPUT(ctypes.Structure):
class _INPUT(ctypes.Union):
fields = (("ki", KEYBDINPUT),
("mi", MOUSEINPUT),
("hi", HARDWAREINPUT))
anonymous = ("_input",)
fields = (("type", wintypes.DWORD),
("_input", _INPUT))

LPINPUT = ctypes.POINTER(INPUT)

def _check_count(result, func, args):
if result == 0:
raise ctypes.WinError(ctypes.get_last_error())
return args

user32.SendInput.errcheck = _check_count
user32.SendInput.argtypes = (wintypes.UINT, # nInputs
LPINPUT, # pInputs
ctypes.c_int) # cbSize

Functions

def PressKey(hexKeyCode):
x = INPUT(type=INPUT_KEYBOARD,
ki=KEYBDINPUT(wVk=hexKeyCode))
user32.SendInput(1, ctypes.byref(x), ctypes.sizeof(x))
ctypes.windll.user32.SendInput(1, ctypes.pointer(x), ctypes.sizeof(x))

def ReleaseKey(hexKeyCode):
x = INPUT(type=INPUT_KEYBOARD,
ki=KEYBDINPUT(wVk=hexKeyCode,
dwFlags=KEYEVENTF_KEYUP))
user32.SendInput(1, ctypes.byref(x), ctypes.sizeof(x))
ctypes.windll.user32.SendInput(1, ctypes.pointer(x), ctypes.sizeof(x))

Register hotkeys

ctypes.windll.user32.RegisterHotKey(None, 1, 0, win32con.VK_SPACE)

Macro when the hotkey is pressed

try:
msg = ctypes.wintypes.MSG()
while ctypes.windll.user32.GetMessageA(ctypes.byref(msg), None, 0, 0) != 0:
if msg.message == win32con.WM_HOTKEY:
PressKey(0x46)
time.sleep(1)
ReleaseKey(0x46)
PressKey(0x20)
time.sleep(1)
ReleaseKey(0x20)

Cleanup

finally:
ctypes.windll.user32.UnregisterHotKey(None, 1)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment