├── .gitignore ├── Readme.md ├── __init__.py ├── ctypes_key.py ├── ctypes_key_creator.py ├── example.py ├── key_strategy.py └── new_down_up.py /.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bode135/VirtualKey_with_Ctypes/81ef6ea303130a1c0c11010fb69f7ac308729850/.gitignore -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # 驱动级按键模拟 2 | ## 基于ctypes实现 3 | > 更新后新增了两个scancode_down_up和keybd_event两种驱动级模拟方法, 4 | > 使用教程见[`VirTualKey使用说明`](https://zhuanlan.zhihu.com/p/355885881) 5 | > 后续将更新实际项目案例. 6 | ---------------------------------------------------------- 7 | 2021.1.9更新:
8 | 推荐使用keyboard和mouse模块.
9 | git上搜keyboard就行.
10 | ======================================
11 | 10.21日更新: 12 | 13 | # 安装: 14 | ``` 15 | pip install VirtualKey 16 | ``` 17 | > ctypes方案对比[`pydamo`](https://github.com/bode135/pydamo "jump to the pydamo project")方案(我不知道怎么用ctypes做后台),不支持后台模拟,但支持64位python。 -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | name = "VirtualKeyWithCtypes" 2 | 3 | from .ctypes_key import PressKey, ReleaseKey 4 | from .example import down_up, vk 5 | from .new_down_up import keybd_event, scancodes, scancode_down_up -------------------------------------------------------------------------------- /ctypes_key.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import time 3 | 4 | SendInput = ctypes.windll.user32.SendInput 5 | 6 | PUL = ctypes.POINTER(ctypes.c_ulong) 7 | class KeyBdInput(ctypes.Structure): 8 | _fields_ = [("wVk", ctypes.c_ushort), 9 | ("wScan", ctypes.c_ushort), 10 | ("dwFlags", ctypes.c_ulong), 11 | ("time", ctypes.c_ulong), 12 | ("dwExtraInfo", PUL)] 13 | 14 | class HardwareInput(ctypes.Structure): 15 | _fields_ = [("uMsg", ctypes.c_ulong), 16 | ("wParamL", ctypes.c_short), 17 | ("wParamH", ctypes.c_ushort)] 18 | 19 | class MouseInput(ctypes.Structure): 20 | _fields_ = [("dx", ctypes.c_long), 21 | ("dy", ctypes.c_long), 22 | ("mouseData", ctypes.c_ulong), 23 | ("dwFlags", ctypes.c_ulong), 24 | ("time",ctypes.c_ulong), 25 | ("dwExtraInfo", PUL)] 26 | 27 | class Input_I(ctypes.Union): 28 | _fields_ = [("ki", KeyBdInput), 29 | ("mi", MouseInput), 30 | ("hi", HardwareInput)] 31 | 32 | class Input(ctypes.Structure): 33 | _fields_ = [("type", ctypes.c_ulong), 34 | ("ii", Input_I)] 35 | 36 | def PressKey(hexKeyCode): 37 | 38 | extra = ctypes.c_ulong(0) 39 | ii_ = Input_I() 40 | ii_.ki = KeyBdInput( hexKeyCode, 0x48, 0, 0, ctypes.pointer(extra) ) 41 | x = Input( ctypes.c_ulong(1), ii_ ) 42 | ctypes.windll.user32.SendInput(1, ctypes.pointer(x), ctypes.sizeof(x)) 43 | 44 | def ReleaseKey(hexKeyCode): 45 | 46 | extra = ctypes.c_ulong(0) 47 | ii_ = Input_I() 48 | ii_.ki = KeyBdInput( hexKeyCode, 0x48, 0x0002, 0, ctypes.pointer(extra) ) 49 | x = Input( ctypes.c_ulong(1), ii_ ) 50 | ctypes.windll.user32.SendInput(1, ctypes.pointer(x), ctypes.sizeof(x)) 51 | 52 | 53 | def PressAltTab(): 54 | 55 | PressKey(0x012) #Alt 56 | PressKey(0x09) #Tab 57 | 58 | time.sleep(2) #optional : if you want to see the atl-tab overlay 59 | 60 | ReleaseKey(0x09) #~Tab 61 | ReleaseKey(0x012) #~Alt 62 | 63 | 64 | if __name__ =="__main__": 65 | 66 | PressAltTab() -------------------------------------------------------------------------------- /ctypes_key_creator.py: -------------------------------------------------------------------------------- 1 | from ctypes import POINTER, c_ulong, Structure, c_ushort, c_short, c_long, byref, windll, pointer, sizeof, Union 2 | from bdtime import tt, vk 3 | 4 | PUL = POINTER(c_ulong) 5 | 6 | 7 | class KeyBdInput(Structure): 8 | _fields_ = [("wVk", c_ushort), 9 | ("wScan", c_ushort), 10 | ("dwFlags", c_ulong), 11 | ("time", c_ulong), 12 | ("dwExtraInfo", PUL)] 13 | 14 | 15 | class HardwareInput(Structure): 16 | _fields_ = [("uMsg", c_ulong), 17 | ("wParamL", c_short), 18 | ("wParamH", c_ushort)] 19 | 20 | 21 | class MouseInput(Structure): 22 | _fields_ = [("dx", c_long), 23 | ("dy", c_long), 24 | ("mouseData", c_ulong), 25 | ("dwFlags", c_ulong), 26 | ("time", c_ulong), 27 | ("dwExtraInfo", PUL)] 28 | 29 | 30 | class Input_I(Union): 31 | _fields_ = [("ki", KeyBdInput), 32 | ("mi", MouseInput), 33 | ("hi", HardwareInput)] 34 | 35 | 36 | class Input(Structure): 37 | _fields_ = [("type", c_ulong), 38 | ("ii", Input_I)] 39 | 40 | 41 | class POINT(Structure): 42 | _fields_ = [("x", c_ulong), 43 | ("y", c_ulong)] 44 | 45 | 46 | def get_mpos(): 47 | orig = POINT() 48 | windll.user32.GetCursorPos(byref(orig)) 49 | return int(orig.x), int(orig.y) 50 | 51 | 52 | def set_mpos(pos): 53 | x, y = pos 54 | windll.user32.SetCursorPos(x, y) 55 | 56 | 57 | MOUSEEVENTF_LEFTDOWN = 0x0002 58 | MOUSEEVENTF_LEFTUP = 0x0004 59 | 60 | MOUSEEVENTF_RIGHTDOWN = 0x00008 61 | MOUSEEVENTF_RIGHTUP = 0x0010 62 | 63 | 64 | def move_click(pos, move_back=False): 65 | origx, origy = get_mpos() 66 | set_mpos(pos) 67 | FInputs = Input * 2 68 | extra = c_ulong(0) 69 | ii_ = Input_I() 70 | ii_.mi = MouseInput(0, 0, 0, 2, 0, pointer(extra)) 71 | 72 | tt.sleep(0.1) 73 | 74 | ii2_ = Input_I() 75 | ii2_.mi = MouseInput(0, 0, 0, 4, 0, pointer(extra)) 76 | x = FInputs((0, ii_), (0, ii2_)) 77 | windll.user32.SendInput(2, pointer(x), sizeof(x[0])) 78 | if move_back: 79 | set_mpos((origx, origy)) 80 | return origx, origy 81 | 82 | def move_right_click(pos, move_back=False): 83 | origx, origy = get_mpos() 84 | set_mpos(pos) 85 | FInputs = Input * 2 86 | extra = c_ulong(0) 87 | ii_ = Input_I() 88 | ii_.mi = MouseInput(0, 0, 0, MOUSEEVENTF_RIGHTDOWN, 0, pointer(extra)) 89 | 90 | tt.sleep(0.1) 91 | 92 | ii2_ = Input_I() 93 | ii2_.mi = MouseInput(0, 0, 0, MOUSEEVENTF_RIGHTUP, 0, pointer(extra)) 94 | x = FInputs((0, ii_), (0, ii2_)) 95 | windll.user32.SendInput(2, pointer(x), sizeof(x[0])) 96 | if move_back: 97 | set_mpos((origx, origy)) 98 | return origx, origy 99 | 100 | def sendkey(scancode, pressed): 101 | FInputs = Input * 1 102 | extra = c_ulong(0) 103 | ii_ = Input_I() 104 | flag = 0x8 105 | ii_.ki = KeyBdInput(0, 0, flag, 0, pointer(extra)) 106 | InputBox = FInputs((1, ii_)) 107 | if scancode is None: 108 | return 109 | InputBox[0].ii.ki.wScan = scancode 110 | InputBox[0].ii.ki.dwFlags = 0x8 111 | 112 | if not (pressed): 113 | InputBox[0].ii.ki.dwFlags |= 0x2 114 | 115 | windll.user32.SendInput(1, pointer(InputBox), sizeof(InputBox[0])) 116 | 117 | if __name__ == '__main__': 118 | 119 | tt.sleep(1) 120 | move_click(get_mpos()) 121 | tt.sleep(1) 122 | move_right_click(get_mpos()) 123 | 124 | def hex_to_dec(int_hex): 125 | if(isinstance(int_hex, (int, float))): 126 | int_hex = str(int_hex) 127 | int_dec = int(int_hex, 16) 128 | return int_dec 129 | 130 | class ScanCode: 131 | q = hex_to_dec(10) 132 | w = hex_to_dec(11) 133 | e = hex_to_dec(12) 134 | r = hex_to_dec(13) 135 | pass 136 | 137 | from keyboard import * 138 | 139 | sc = ScanCode() 140 | 141 | tt.sleep(1) 142 | sendkey(scancode=sc.e, pressed=1) 143 | 144 | # Using Keyboard module in Python 145 | import keyboard 146 | 147 | # It writes the content to output 148 | keyboard.write("GEEKS FOR GEEKS\n") 149 | 150 | # It writes the keys r, k and endofline 151 | keyboard.press_and_release('shift + r, shift + k, \n') 152 | keyboard.press_and_release('R, K') 153 | 154 | # it blocks until ctrl is pressed 155 | keyboard.wait('Ctrl') -------------------------------------------------------------------------------- /example.py: -------------------------------------------------------------------------------- 1 | from .ctypes_key import PressKey, ReleaseKey 2 | from bdtime import vk, tt 3 | 4 | # press any ch 5 | def down_up(ch, t = 0.5): 6 | ch = vk.conv_ord(ch) 7 | PressKey(ch) 8 | tt.sleep(t) 9 | ReleaseKey(ch) 10 | return 1 11 | 12 | def main(): 13 | # KeyDown and KeyUp 14 | ch = 'a' 15 | tt.sleep(1) 16 | down_up(ch) 17 | 18 | # select all 19 | tt.sleep(1) 20 | PressKey(vk.ctrl) 21 | PressKey(vk.a) 22 | tt.sleep(0.5) 23 | ReleaseKey(vk.a) 24 | ReleaseKey(vk.ctrl) 25 | 26 | return 1 27 | 28 | 29 | if __name__ == '__main__': 30 | main() -------------------------------------------------------------------------------- /key_strategy.py: -------------------------------------------------------------------------------- 1 | from .ctypes_key import PressKey, ReleaseKey 2 | from bdtime import vk, tt 3 | 4 | # press any ch 5 | def down_up(ch, t = 0.5): 6 | ch = vk.conv_ord(ch) 7 | PressKey(ch) 8 | tt.sleep(t) 9 | ReleaseKey(ch) 10 | 11 | 12 | if __name__ == '__main__': 13 | main() -------------------------------------------------------------------------------- /new_down_up.py: -------------------------------------------------------------------------------- 1 | from ctypes import POINTER, c_ulong, Structure, c_ushort, c_short, c_long, byref, windll, pointer, sizeof, Union 2 | from bdtime import tt, vk 3 | import keyboard 4 | import win32con 5 | 6 | 7 | PUL = POINTER(c_ulong) 8 | 9 | 10 | class KeyBdInput(Structure): 11 | _fields_ = [("wVk", c_ushort), 12 | ("wScan", c_ushort), 13 | ("dwFlags", c_ulong), 14 | ("time", c_ulong), 15 | ("dwExtraInfo", PUL)] 16 | 17 | 18 | class HardwareInput(Structure): 19 | _fields_ = [("uMsg", c_ulong), 20 | ("wParamL", c_short), 21 | ("wParamH", c_ushort)] 22 | 23 | 24 | class MouseInput(Structure): 25 | _fields_ = [("dx", c_long), 26 | ("dy", c_long), 27 | ("mouseData", c_ulong), 28 | ("dwFlags", c_ulong), 29 | ("time", c_ulong), 30 | ("dwExtraInfo", PUL)] 31 | 32 | 33 | class Input_I(Union): 34 | _fields_ = [("ki", KeyBdInput), 35 | ("mi", MouseInput), 36 | ("hi", HardwareInput)] 37 | 38 | 39 | class Input(Structure): 40 | _fields_ = [("type", c_ulong), 41 | ("ii", Input_I)] 42 | 43 | 44 | class POINT(Structure): 45 | _fields_ = [("x", c_ulong), 46 | ("y", c_ulong)] 47 | 48 | 49 | def get_mpos(): 50 | orig = POINT() 51 | windll.user32.GetCursorPos(byref(orig)) 52 | return int(orig.x), int(orig.y) 53 | 54 | 55 | def set_mpos(pos): 56 | x, y = pos 57 | windll.user32.SetCursorPos(x, y) 58 | 59 | 60 | MOUSEEVENTF_LEFTDOWN = 0x0002 61 | MOUSEEVENTF_LEFTUP = 0x0004 62 | 63 | MOUSEEVENTF_RIGHTDOWN = 0x00008 64 | MOUSEEVENTF_RIGHTUP = 0x0010 65 | 66 | 67 | def move_click(pos, move_back=False): 68 | origx, origy = get_mpos() 69 | set_mpos(pos) 70 | FInputs = Input * 2 71 | extra = c_ulong(0) 72 | ii_ = Input_I() 73 | ii_.mi = MouseInput(0, 0, 0, 2, 0, pointer(extra)) 74 | 75 | tt.sleep(0.1) 76 | 77 | ii2_ = Input_I() 78 | ii2_.mi = MouseInput(0, 0, 0, 4, 0, pointer(extra)) 79 | x = FInputs((0, ii_), (0, ii2_)) 80 | windll.user32.SendInput(2, pointer(x), sizeof(x[0])) 81 | if move_back: 82 | set_mpos((origx, origy)) 83 | return origx, origy 84 | 85 | def move_right_click(pos, move_back=False): 86 | origx, origy = get_mpos() 87 | set_mpos(pos) 88 | FInputs = Input * 2 89 | extra = c_ulong(0) 90 | ii_ = Input_I() 91 | ii_.mi = MouseInput(0, 0, 0, MOUSEEVENTF_RIGHTDOWN, 0, pointer(extra)) 92 | 93 | tt.sleep(0.1) 94 | 95 | ii2_ = Input_I() 96 | ii2_.mi = MouseInput(0, 0, 0, MOUSEEVENTF_RIGHTUP, 0, pointer(extra)) 97 | x = FInputs((0, ii_), (0, ii2_)) 98 | windll.user32.SendInput(2, pointer(x), sizeof(x[0])) 99 | if move_back: 100 | set_mpos((origx, origy)) 101 | return origx, origy 102 | 103 | def sendkey(scancode, pressed=1): 104 | """ 105 | Reference: 106 | [MOUSEINPUT structure (winuser.h)] 107 | (https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-mouseinput) 108 | [scancodes](https://www.win.tue.nl/~aeb/linux/kbd/scancodes-1.html) 109 | 110 | :param scancode: 扫描码, 若为tuple, 则取第一个. 111 | :param pressed: 112 | :return: 113 | """ 114 | if isinstance(scancode, tuple): 115 | scancode = scancode[0] # 若按键有两个扫描码, 类型: tuple; 则取第一个扫描码 . 116 | 117 | FInputs = Input * 1 118 | extra = c_ulong(0) 119 | ii_ = Input_I() 120 | flag = 0x8 121 | ii_.ki = KeyBdInput(0, 0, flag, 0, pointer(extra)) 122 | InputBox = FInputs((1, ii_)) 123 | if scancode is None: 124 | return 125 | InputBox[0].ii.ki.wScan = scancode 126 | InputBox[0].ii.ki.dwFlags = 0x8 127 | 128 | if not (pressed): 129 | InputBox[0].ii.ki.dwFlags |= 0x2 130 | # windll.user32.GetAsyncKeyState(scancodes.get('alt')[0]) 131 | # scancodes.get('q') 132 | # windll.user32.GetAsyncKeyState(vk.q) 133 | # 134 | # for i in range(10): 135 | # tt.sleep(1) 136 | # if tt.stop(): 137 | # break 138 | # print(windll.user32.GetAsyncKeyState(vk.q)) 139 | 140 | windll.user32.SendInput(1, pointer(InputBox), sizeof(InputBox[0])) 141 | return 1 142 | 143 | 144 | class scancodes: 145 | @classmethod 146 | def get(self, key): 147 | # 一个字符一般对应两个扫描码 148 | return keyboard.key_to_scan_codes(key) 149 | 150 | @classmethod 151 | def ret_one_scancode(self, key): 152 | # 只返回一个 153 | return keyboard.key_to_scan_codes(key)[0] 154 | 155 | def scancode_down_up(key, t=0.5): 156 | # 基于扫描码: ScanCodes, scancodes类 157 | return sendkey(scancode=scancodes.get(key)) 158 | 159 | 160 | def keybd_event(ch, t=0.1): 161 | # 基于虚拟按键码: VirtualKeyCodes, vk类 162 | ret = windll.user32.keybd_event( 163 | vk.conv_ord(ch), 164 | scancodes.get(ch)[0], 165 | win32con.KEYEVENTF_EXTENDEDKEY | 0, 166 | 0, 167 | ) 168 | return ret 169 | 170 | 171 | 172 | if __name__ == '__main__': 173 | # tt.sleep(1) 174 | # move_click(get_mpos()) 175 | # tt.sleep(1) 176 | # move_right_click(get_mpos()) 177 | # 178 | # def hex_to_dec(int_hex): 179 | # if (isinstance(int_hex, (int, float))): 180 | # int_hex = str(int_hex) 181 | # int_dec = int(int_hex, 16) 182 | # return int_dec 183 | tt.sleep(1) 184 | scancode_down_up('a') 185 | 186 | tt.sleep(0.1) 187 | keybd_event('a') 188 | 189 | 190 | # @tt.run_f_with_during(5, 1) 191 | # def f(): 192 | # sendkey(scancode=scancodes.get('e')) 193 | # tt.sleep(0.01) 194 | # sendkey(scancode=scancodes.get('w')) 195 | # tt.sleep(0.01) 196 | # sendkey(scancode=scancodes.get('q')) 197 | # f() 198 | # 199 | # tt.sleep(1) 200 | # windll.user32.keybd_event(vk.q, scancodes.get('q')[0]) 201 | # tt.sleep(1) 202 | # windll.user32.keybd_event(vk.q, scancodes.get('w')[0]) 203 | # tt.sleep(1) 204 | # windll.user32.keybd_event(vk.q, scancodes.get('e')[0]) 205 | # 206 | # tt.sleep(1) 207 | # windll.user32.keybd_event(2, scancodes.get('')) 208 | 209 | --------------------------------------------------------------------------------