├── .gitattributes ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── appveyor.yml ├── appveyor_build.bat ├── build.sh ├── command.h ├── commandlinetoargva.h ├── crc32.h ├── main.h ├── mouse.h ├── sendinput.c ├── sendinput.h ├── sendinput_io.c ├── sendkey.h └── winmain.h /.gitattributes: -------------------------------------------------------------------------------- 1 | *.h linguist-language=C -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | 54 | # builds 55 | build/* 56 | build_*/* 57 | cmake-build-*/* 58 | 59 | # codeblocks 60 | bin/* 61 | obj/* 62 | *.cbp 63 | *.depend 64 | *.layout 65 | 66 | # JetBrains 67 | .idea/* -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | project(sendinput C) 3 | if (NOT CMAKE_BUILD_TYPE) 4 | set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: Debug Release." FORCE) 5 | endif (NOT CMAKE_BUILD_TYPE) 6 | set(CMAKE_C_STANDARD 11) 7 | set(HEADERS crc32.h mouse.h sendkey.h sendinput.h) 8 | add_executable(sendinput_io sendinput_io.c main.h commandlinetoargva.h ${HEADERS}) 9 | add_executable(sendinput sendinput.c winmain.h ${HEADERS}) 10 | 11 | if ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU") 12 | set(CMAKE_C_FLAGS "-pipe -Wall -Wextra -fmerge-all-constants -Wl,--gc-sections,--build-id=none") 13 | set(CMAKE_C_FLAGS_DEBUG "-g -Og") 14 | set(CMAKE_C_FLAGS_RELEASE "-s -Os") 15 | if (WIN32) 16 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -nostartfiles -DNO_START_FILES") 17 | set_target_properties(sendinput PROPERTIES 18 | COMPILE_FLAGS "${CMAKE_C_FLAGS} -mwindows" 19 | LINK_FLAGS "${CMAKE_C_FLAGS} -mwindows") 20 | target_link_libraries(sendinput shlwapi) 21 | endif () 22 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_C_FLAGS}") 23 | endif () -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sendinput 2 | keyboard and mouse input simulator for windows 3 | 4 | ## Release 5 | [![Latest Release](https://img.shields.io/github/release/myfreeer/sendinput.svg)](https://github.com/myfreeer/sendinput/releases/latest) 6 | [![Download](https://img.shields.io/github/downloads/myfreeer/sendinput/total.svg)](https://github.com/myfreeer/sendinput/releases) 7 | 8 | ## Usage 9 | See [Wiki/Usage](https://github.com/myfreeer/sendinput/wiki/Usage) 10 | 11 | ## Building 12 | ### Prerequisites 13 | * msys2 mingw64 14 | * git 15 | * mingw-w64-x86_64-gcc 16 | * mingw-w64-x86_64-cmake 17 | * mingw-w64-x86_64-ninja 18 | 19 | ### Build script example 20 | ```bash 21 | # Getting source 22 | git clone https://github.com/myfreeer/sendinput 23 | cd sendinput 24 | 25 | # Creating folder for build 26 | mkdir -p build 27 | cd build 28 | 29 | # Running cmake 30 | cmake -GNinja .. 31 | 32 | # Building with ninja 33 | ninja 34 | ``` 35 | 36 | ## Credits 37 | * -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 1.0-{build} 2 | build_script: 3 | - cmd: >- 4 | appveyor_build.bat 5 | on_failure: 6 | - 7z a -mx9 -r fail.7z *.exe *.sln *.txt *.log 7 | - appveyor.exe PushArtifact fail.7z 8 | shallow_clone: true 9 | test: off 10 | artifacts: 11 | - path: sendinput_x64.exe 12 | - path: sendinput_x86.exe 13 | - path: sendinput_io_x64.exe 14 | - path: sendinput_io_x86.exe 15 | - path: sendinput.7z 16 | cache: 17 | - tests 18 | skip_commits: 19 | files: 20 | - 'LICENSE' 21 | - '*.md' 22 | - '.gitingore' 23 | - '.gitattributes' 24 | deploy: 25 | - provider: Environment 26 | name: github 27 | on: 28 | appveyor_repo_tag: true 29 | -------------------------------------------------------------------------------- /appveyor_build.bat: -------------------------------------------------------------------------------- 1 | echo Syncing msys2 packages... 2 | C:\msys64\usr\bin\pacman -Sq --noconfirm --needed --noprogressbar --ask=20 mingw-w64-x86_64-ninja mingw-w64-i686-ninja 3 | rem C:\msys64\usr\bin\pacman -Scq --noconfirm 4 | 5 | echo Building and testing 64-bit version... 6 | set MSYSTEM=MINGW64 7 | call C:\msys64\usr\bin\bash -lc "cd \"$APPVEYOR_BUILD_FOLDER\" && exec ./build.sh" 8 | move /Y .\build_x86_64-w64-mingw32\sendinput.exe .\sendinput_x64.exe 9 | move /Y .\build_x86_64-w64-mingw32\sendinput_io.exe .\sendinput_io_x64.exe 10 | 11 | echo Building and testing 32-bit version... 12 | set MSYSTEM=MINGW32 13 | call C:\msys64\usr\bin\bash -lc "cd \"$APPVEYOR_BUILD_FOLDER\" && exec ./build.sh" 14 | move /Y .\build_i686-w64-mingw32\sendinput.exe .\sendinput_x86.exe 15 | move /Y .\build_i686-w64-mingw32\sendinput_io.exe .\sendinput_io_x86.exe 16 | 17 | echo Packaging... 18 | 7z a -mx9 sendinput.7z .\sendinput_x64.exe .\sendinput_x86.exe .\sendinput_io_x64.exe .\sendinput_io_x86.exe 19 | echo Done. -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | MACHINE="$(gcc -dumpmachine)" 3 | echo Building... 4 | mkdir -p "build_${MACHINE}" 5 | cd "build_${MACHINE}" 6 | cmake -GNinja .. 7 | ninja 8 | cd .. 9 | -------------------------------------------------------------------------------- /command.h: -------------------------------------------------------------------------------- 1 | #ifndef WIN32_LEAN_AND_MEAN 2 | #define WIN32_LEAN_AND_MEAN 3 | #endif 4 | #include 5 | #include 6 | #include "mouse.h" 7 | #include "sendkey.h" 8 | 9 | #define COMMAND_MAX_LENGTH 16 10 | static WORD ParseCommand(const uint32_t hash) { 11 | switch (hash) { 12 | case 4213349504U: // BACKSPACE 13 | case 1911832374U: // BS 14 | case 2523624168U: // BKSP 15 | return VK_BACK; 16 | case 3727033226U: // SPACE 17 | return VK_SPACE; 18 | case 3846439302U: // TAB 19 | return VK_TAB; 20 | case 317010870U: // CLEAR 21 | return VK_CLEAR; 22 | case 1310982878U: // ENTER 23 | return VK_RETURN; 24 | case 1382021621U: // SHIFT 25 | return VK_SHIFT; 26 | case 884448462U: // CTRL 27 | return VK_CONTROL; 28 | case 3201542913U: // ALT 29 | return VK_MENU; 30 | case 550391901U: // PAUSE 31 | return VK_PAUSE; 32 | case 2439287330U: // CAPS 33 | case 3274666208U: // CAPSLOCK 34 | return VK_CAPITAL; 35 | case 4149446788U: // ESC 36 | return VK_ESCAPE; 37 | case 82448359U: // PAGEUP 38 | case 859550494U: // PGUP 39 | return VK_PRIOR; 40 | case 1528306972U: // PAGEDOWN 41 | case 2599267693U: // PGDN 42 | return VK_NEXT; 43 | case 2522575163U: // END 44 | return VK_END; 45 | case 1195630948U: // HOME 46 | return VK_HOME; 47 | case 3864525061U: // LEFTARROW 48 | case 1290930908U: // LEFT 49 | return VK_LEFT; 50 | case 156158474U: // UPARROW 51 | case 3984517658U: // UP 52 | return VK_UP; 53 | case 112782023U: // RIGHTARROW 54 | case 1134475172U: // RIGHT 55 | return VK_RIGHT; 56 | case 2947281234U: // DOWNARROW 57 | case 711622031U: // DOWN 58 | return VK_DOWN; 59 | case 3138006342U: // SELECT 60 | return VK_SELECT; 61 | case 3800219350U: // PRINT 62 | return VK_PRINT; 63 | case 3223776964U: // EXECUTE 64 | return VK_EXECUTE; 65 | case 2836567439U: // PRINTSCREEN 66 | case 2328061742U: // PRTSC 67 | return VK_SNAPSHOT; 68 | case 479333784U: // INS 69 | case 892623219U: // INSERT 70 | return VK_INSERT; 71 | case 2058767093U: // DEL 72 | case 3404380929U: // DELETE 73 | return VK_DELETE; 74 | case 1041412376U: // HELP 75 | return VK_HELP; 76 | case 1256517092U: // NUM 77 | case 3844504896U: // NUMLOCK 78 | return VK_NUMLOCK; 79 | case 2251377965U: // SCROLLLOCK 80 | return VK_SCROLL; 81 | case 4067218798U: // LEFTWINDOWS 82 | case 699046007U: // LWIN 83 | return VK_LWIN; 84 | case 2401157523U: // RIGHTWINDOWS 85 | case 2574039259U: // RWIN 86 | return VK_RWIN; 87 | case 1660628960U: // APPLICATIONS 88 | return VK_APPS; 89 | case 1771420896U: // COMPUTERSLEEP 90 | return VK_SLEEP; 91 | case 3615866119U: // MULTIPLY 92 | return VK_MULTIPLY; 93 | case 1807561069U: // ADD 94 | return VK_ADD; 95 | case 3543192423U: // SEPARATOR 96 | return VK_SEPARATOR; 97 | case 3882583749U: // SUBTRACT 98 | return VK_SUBTRACT; 99 | case 1706577289U: // DECIMAL 100 | return VK_DECIMAL; 101 | case 1936067278U: // DIVIDE 102 | return VK_DIVIDE; 103 | case 1408638381U: // NUM0 104 | case 2111931934U: // NUMPAD0 105 | return VK_NUMPAD0; 106 | case 619785531U: // NUM1 107 | case 182892168U: // NUMPAD1 108 | return VK_NUMPAD1; 109 | case 3187178625U: // NUM2 110 | case 2481973042U: // NUMPAD2 111 | return VK_NUMPAD2; 112 | case 3405728791U: // NUM3 113 | case 3840464804U: // NUMPAD3 114 | return VK_NUMPAD3; 115 | case 1419499956U: // NUM4 116 | case 2056012295U: // NUMPAD4 117 | return VK_NUMPAD4; 118 | case 597485858U: // NUM5 119 | case 227242641U: // NUMPAD5 120 | return VK_NUMPAD5; 121 | case 3130374296U: // NUM6 122 | case 2491556651U: // NUMPAD6 123 | return VK_NUMPAD6; 124 | case 3448932366U: // NUM7 125 | case 3817149373U: // NUMPAD7 126 | return VK_NUMPAD7; 127 | case 1563268511U: // NUM8 128 | case 1933184556U: // NUMPAD8 129 | return VK_NUMPAD8; 130 | case 707437833U: // NUM9 131 | case 71122618U: // NUMPAD9 132 | return VK_NUMPAD9; 133 | case 3055876678U: // F1 134 | return VK_F1; 135 | case 791522300U: // F2 136 | return VK_F2; 137 | case 1479187306U: // F3 138 | return VK_F3; 139 | case 3327004361U: // F4 140 | return VK_F4; 141 | case 2974367327U: // F5 142 | return VK_F5; 143 | case 675311589U: // F6 144 | return VK_F6; 145 | case 1598513011U: // F7 146 | return VK_F7; 147 | case 3489153762U: // F8 148 | return VK_F8; 149 | case 3103748724U: // F9 150 | return VK_F9; 151 | case 1808932734U: // F10 152 | return VK_F10; 153 | case 483733480U: // F11 154 | return VK_F11; 155 | case 2245819986U: // F12 156 | return VK_F12; 157 | case 4074458820U: // F13 158 | return VK_F13; 159 | case 1824512871U: // F14 160 | return VK_F14; 161 | case 465103857U: // F15 162 | return VK_F15; 163 | case 2192685643U: // F16 164 | return VK_F16; 165 | case 4122381021U: // F17 166 | return VK_F17; 167 | case 1695127372U: // F18 168 | return VK_F18; 169 | case 302950362U: // F19 170 | return VK_F19; 171 | case 1090473149U: // F20 172 | return VK_F20; 173 | case 939031595U: // F21 174 | return VK_F21; 175 | case 2935041425U: // F22 176 | return VK_F22; 177 | case 3656785159U: // F23 178 | return VK_F23; 179 | case 1200785572U: // F24 180 | return VK_F24; 181 | case 2607492222U: // LEFTSHIFT 182 | case 1562731868U: // LSHIFT 183 | return VK_LSHIFT; 184 | case 2072264636U: // RIGHTSHIFT 185 | case 1694041783U: // RSHIFT 186 | return VK_RSHIFT; 187 | case 4233133246U: // LEFTCONTROL 188 | case 1322385799U: // LCTRL 189 | return VK_LCONTROL; 190 | case 2166515779U: // RIGHTCONTROL 191 | case 2432837732U: // RCTRL 192 | return VK_RCONTROL; 193 | case 1936054868U: // LEFTMENU 194 | case 838158958U: // LMENU 195 | case 2970897034U: // LALT 196 | return VK_LMENU; 197 | case 1235158622U: // RIGHTMENU 198 | case 3995427725U: // RMENU 199 | case 30566950U: // RALT 200 | return VK_RMENU; 201 | case 3954267447U: // IMEPROCESS 202 | return VK_PROCESSKEY; 203 | case 1710951019U: // LBUTTON 204 | return VK_LBUTTON; 205 | case 1377496568U: // RBUTTON 206 | return VK_RBUTTON; 207 | case 2800616180U: // CANCEL 208 | return VK_CANCEL; 209 | case 3280732639U: // MBUTTON 210 | return VK_MBUTTON; 211 | case 2703791036U: // XBUTTON1 212 | return VK_XBUTTON1; 213 | case 941736454U: // XBUTTON2 214 | return VK_XBUTTON2; 215 | case 1945007743U: // BROWSERBACK 216 | return VK_BROWSER_BACK; 217 | case 2673191389U: // BROWSERFORWARD 218 | return VK_BROWSER_FORWARD; 219 | case 407185056U: // BROWSERREFRESH 220 | return VK_BROWSER_REFRESH; 221 | case 2809569790U: // BROWSERSTOP 222 | return VK_BROWSER_STOP; 223 | case 2009014991U: // BROWSERSEARCH 224 | return VK_BROWSER_SEARCH; 225 | case 387432145U: // BROWSERFAVORITES 226 | return VK_BROWSER_FAVORITES; 227 | case 1878440856U: // BROWSERHOME 228 | return VK_BROWSER_HOME; 229 | case 2714356834U: // VOLUMEMUTE 230 | case 4197228264U: // MUTE 231 | return VK_VOLUME_MUTE; 232 | case 1905209093U: // VOLUMEDOWN 233 | case 2053074917U: // VDOWN 234 | return VK_VOLUME_DOWN; 235 | case 2143919418U: // VOLUMEUP 236 | case 1003975669U: // VUP 237 | return VK_VOLUME_UP; 238 | case 2145333128U: // MEDIANEXTTRACK 239 | return VK_MEDIA_NEXT_TRACK; 240 | case 644010588U: // MEDIAPREVTRACK 241 | return VK_MEDIA_PREV_TRACK; 242 | case 4222509918U: // MEDIASTOP 243 | case 3368439803U: // MSTOP 244 | return VK_MEDIA_STOP; 245 | case 1261637064U: // MEDIAPLAYPAUSE 246 | case 790202359U: // MPLAY 247 | return VK_MEDIA_PLAY_PAUSE; 248 | case 11509296U: // LAUNCHMAIL 249 | return VK_LAUNCH_MAIL; 250 | case 3101998649U: // LAUNCHMEDIASELECT 251 | return VK_LAUNCH_MEDIA_SELECT; 252 | default:break; 253 | } 254 | return FALSE; 255 | } 256 | 257 | static BOOL ParseCommandWithParams(const uint32_t hash, const int params[], 258 | const unsigned char paramCount) { 259 | if (paramCount == 1) { 260 | switch (hash) { 261 | case 4167499804U: // SLEEP 262 | case 1266380369U: // WAIT 263 | Sleep((DWORD) params[0]); 264 | return TRUE; 265 | case 2439287330U: // CAPS 266 | case 3274666208U: // CAPSLOCK 267 | SetKeyState((BOOL) params[0], VK_CAPITAL); 268 | return TRUE; 269 | case 1256517092U: // NUM 270 | case 3844504896U: // NUMLOCK 271 | SetKeyState((BOOL) params[0], VK_NUMLOCK); 272 | return TRUE; 273 | case 2251377965U: // SCROLLLOCK 274 | SetKeyState((BOOL) params[0], VK_SCROLL); 275 | return TRUE; 276 | default:break; 277 | } 278 | } else if (paramCount == 2) { 279 | switch (hash) { 280 | case 1302462608U: // CLICK 281 | case 1121492025U: // LCLICK 282 | case 2224477467U: // LEFTCLICK 283 | MouseClick((const unsigned int) params[0], (const unsigned int) params[1], 0); 284 | return TRUE; 285 | case 2063925202U: // RCLICK 286 | case 1685702361U: // RIGHTCLICK 287 | MouseClick((const unsigned int) params[0], (const unsigned int) params[1], 1); 288 | return TRUE; 289 | case 2307149724U: // MCLICK 290 | case 1843602477U: // MIDDLECLICK 291 | MouseClick((const unsigned int) params[0], (const unsigned int) params[1], 2); 292 | return TRUE; 293 | case 2858569247U: // MOUSEMOVE 294 | SetCursorPos(params[0], params[1]); 295 | return TRUE; 296 | default:break; 297 | } 298 | } 299 | return FALSE; 300 | } 301 | 302 | 303 | BOOL ParseKeyCombination(const char param[][COMMAND_MAX_LENGTH], 304 | const WORD paramLength[], 305 | const unsigned char paramCount) { 306 | WORD keyCode[paramCount]; 307 | uint32_t hash[paramCount]; 308 | for (unsigned char i = 0; i < paramCount; i++) { 309 | hash[i] = crc32(param[i], paramLength[i]); 310 | keyCode[i] = ParseCommand(hash[i]); 311 | if (!keyCode[i] && paramLength[i] == 1) { 312 | SHORT vKeyCode = VkKeyScan(param[i][0]); 313 | if (vKeyCode >> 8 == 0 || vKeyCode >> 8 == 1) 314 | keyCode[i] = (WORD) (vKeyCode & 0xFF); 315 | } 316 | if (!keyCode[i]) 317 | return FALSE; 318 | } 319 | SendMultipleKey(keyCode, paramCount); 320 | return TRUE; 321 | } 322 | -------------------------------------------------------------------------------- /commandlinetoargva.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef COMMANDLINETOARGVA_H 3 | #define COMMANDLINETOARGVA_H 4 | 5 | #include 6 | #include 7 | // https://github.com/wine-mirror/wine/blob/5946973021285dd6ecb8df224956fea4817f8fed/dlls/shell32/shell32_main.c 8 | // https://github.com/futurist/CommandLineToArgvA/blob/09765ffef215f0a066d6b0ea5c1edda88b6739af/CommandLineToArgvA.c 9 | /************************************************************************* 10 | * CommandLineToArgvA [SHELL32.@] 11 | * 12 | * We must interpret the quotes in the command line to rebuild the argv 13 | * array correctly: 14 | * - arguments are separated by spaces or tabs 15 | * - quotes serve as optional argument delimiters 16 | * '"a b"' -> 'a b' 17 | * - escaped quotes must be converted back to '"' 18 | * '\"' -> '"' 19 | * - consecutive backslashes preceding a quote see their number halved with 20 | * the remainder escaping the quote: 21 | * 2n backslashes + quote -> n backslashes + quote as an argument delimiter 22 | * 2n+1 backslashes + quote -> n backslashes + literal quote 23 | * - backslashes that are not followed by a quote are copied literally: 24 | * 'a\b' -> 'a\b' 25 | * 'a\\b' -> 'a\\b' 26 | * - in quoted strings, consecutive quotes see their number divided by three 27 | * with the remainder modulo 3 deciding whether to close the string or not. 28 | * Note that the opening quote must be counted in the consecutive quotes, 29 | * that's the (1+) below: 30 | * (1+) 3n quotes -> n quotes 31 | * (1+) 3n+1 quotes -> n quotes plus closes the quoted string 32 | * (1+) 3n+2 quotes -> n+1 quotes plus closes the quoted string 33 | * - in unquoted strings, the first quote opens the quoted string and the 34 | * remaining consecutive quotes follow the above rule. 35 | */ 36 | LPSTR WINAPI *CommandLineToArgvA(LPCSTR lpCmdline, int *numargs) { 37 | DWORD argc; 38 | LPSTR *argv; 39 | LPCSTR s; 40 | LPSTR d; 41 | LPSTR cmdline; 42 | int qcount, bcount; 43 | 44 | if (!numargs) { 45 | SetLastError(ERROR_INVALID_PARAMETER); 46 | return NULL; 47 | } 48 | 49 | if (lpCmdline == NULL || *lpCmdline == 0) { 50 | /* Return the path to the executable */ 51 | DWORD len, deslen = MAX_PATH, size; 52 | 53 | size = sizeof(LPSTR) * 2 + deslen * sizeof(char); 54 | for (;;) { 55 | if (!(argv = LocalAlloc(LMEM_FIXED, size))) 56 | return NULL; 57 | len = GetModuleFileNameA(0, (LPSTR)(argv + 2), deslen); 58 | if (!len) { 59 | LocalFree(argv); 60 | return NULL; 61 | } 62 | if (len < deslen) 63 | break; 64 | deslen *= 2; 65 | size = sizeof(LPSTR) * 2 + deslen * sizeof(char); 66 | LocalFree(argv); 67 | } 68 | argv[0] = (LPSTR)(argv + 2); 69 | argv[1] = NULL; 70 | *numargs = 1; 71 | 72 | return argv; 73 | } 74 | 75 | /* --- First count the arguments */ 76 | argc = 1; 77 | s = lpCmdline; 78 | /* The first argument, the executable path, follows special rules */ 79 | if (*s == '"') { 80 | /* The executable path ends at the next quote, no matter what */ 81 | s++; 82 | while (*s) 83 | if (*s++ == '"') 84 | break; 85 | } else { 86 | /* The executable path ends at the next space, no matter what */ 87 | while (*s && *s != ' ' && *s != '\t') 88 | s++; 89 | } 90 | /* skip to the first argument, if any */ 91 | while (*s == ' ' || *s == '\t') 92 | s++; 93 | if (*s) 94 | argc++; 95 | 96 | /* Analyze the remaining arguments */ 97 | qcount = bcount = 0; 98 | while (*s) { 99 | if ((*s == ' ' || *s == '\t') && qcount == 0) { 100 | /* skip to the next argument and count it if any */ 101 | while (*s == ' ' || *s == '\t') 102 | s++; 103 | if (*s) 104 | argc++; 105 | bcount = 0; 106 | } else if (*s == '\\') { 107 | /* '\', count them */ 108 | bcount++; 109 | s++; 110 | } else if (*s == '"') { 111 | /* '"' */ 112 | if ((bcount & 1) == 0) 113 | qcount++; /* unescaped '"' */ 114 | s++; 115 | bcount = 0; 116 | /* consecutive quotes, see comment in copying code below */ 117 | while (*s == '"') { 118 | qcount++; 119 | s++; 120 | } 121 | qcount = qcount % 3; 122 | if (qcount == 2) 123 | qcount = 0; 124 | } else { 125 | /* a regular character */ 126 | bcount = 0; 127 | s++; 128 | } 129 | } 130 | 131 | /* Allocate in a single lump, the string array, and the strings that go 132 | * with it. This way the caller can make a single LocalFree() call to free 133 | * both, as per MSDN. 134 | */ 135 | argv = LocalAlloc(LMEM_FIXED, (argc + 1) * sizeof(LPSTR) + 136 | (strlen(lpCmdline) + 1) * sizeof(char)); 137 | if (!argv) 138 | return NULL; 139 | cmdline = (LPSTR)(argv + argc + 1); 140 | strcpy(cmdline, lpCmdline); 141 | 142 | /* --- Then split and copy the arguments */ 143 | argv[0] = d = cmdline; 144 | argc = 1; 145 | /* The first argument, the executable path, follows special rules */ 146 | if (*d == '"') { 147 | /* The executable path ends at the next quote, no matter what */ 148 | s = d + 1; 149 | while (*s) { 150 | if (*s == '"') { 151 | s++; 152 | break; 153 | } 154 | *d++ = *s++; 155 | } 156 | } else { 157 | /* The executable path ends at the next space, no matter what */ 158 | while (*d && *d != ' ' && *d != '\t') 159 | d++; 160 | s = d; 161 | if (*s) 162 | s++; 163 | } 164 | /* close the executable path */ 165 | *d++ = 0; 166 | /* skip to the first argument and initialize it if any */ 167 | while (*s == ' ' || *s == '\t') 168 | s++; 169 | if (!*s) { 170 | /* There are no parameters so we are all done */ 171 | argv[argc] = NULL; 172 | *numargs = argc; 173 | return argv; 174 | } 175 | 176 | /* Split and copy the remaining arguments */ 177 | argv[argc++] = d; 178 | qcount = bcount = 0; 179 | while (*s) { 180 | if ((*s == ' ' || *s == '\t') && qcount == 0) { 181 | /* close the argument */ 182 | *d++ = 0; 183 | bcount = 0; 184 | 185 | /* skip to the next one and initialize it if any */ 186 | do { 187 | s++; 188 | } while (*s == ' ' || *s == '\t'); 189 | if (*s) 190 | argv[argc++] = d; 191 | } else if (*s == '\\') { 192 | *d++ = *s++; 193 | bcount++; 194 | } else if (*s == '"') { 195 | if ((bcount & 1) == 0) { 196 | /* Preceded by an even number of '\', this is half that 197 | * number of '\', plus a quote which we erase. 198 | */ 199 | d -= bcount / 2; 200 | qcount++; 201 | } else { 202 | /* Preceded by an odd number of '\', this is half that 203 | * number of '\' followed by a '"' 204 | */ 205 | d = d - bcount / 2 - 1; 206 | *d++ = '"'; 207 | } 208 | s++; 209 | bcount = 0; 210 | /* Now count the number of consecutive quotes. Note that qcount 211 | * already takes into account the opening quote if any, as well as 212 | * the quote that lead us here. 213 | */ 214 | while (*s == '"') { 215 | if (++qcount == 3) { 216 | *d++ = '"'; 217 | qcount = 0; 218 | } 219 | s++; 220 | } 221 | if (qcount == 2) 222 | qcount = 0; 223 | } else { 224 | /* a regular character */ 225 | *d++ = *s++; 226 | bcount = 0; 227 | } 228 | } 229 | *d = '\0'; 230 | argv[argc] = NULL; 231 | *numargs = argc; 232 | 233 | return argv; 234 | } 235 | #endif 236 | -------------------------------------------------------------------------------- /crc32.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static const uint32_t crc32tab[256] = { 4 | 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 5 | 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 6 | 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 7 | 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 8 | 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 9 | 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 10 | 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 11 | 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 12 | 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 13 | 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 14 | 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 15 | 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 16 | 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 17 | 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 18 | 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 19 | 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 20 | 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 21 | 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 22 | 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 23 | 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 24 | 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 25 | 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 26 | 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 27 | 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 28 | 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 29 | 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 30 | 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 31 | 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 32 | 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 33 | 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 34 | 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 35 | 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 36 | 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 37 | 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 38 | 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 39 | 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 40 | 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 41 | 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 42 | 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 43 | 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 44 | 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 45 | 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 46 | 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 47 | 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 48 | 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 49 | 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 50 | 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 51 | 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 52 | 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 53 | 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 54 | 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 55 | 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 56 | 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 57 | 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 58 | 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 59 | 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 60 | 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 61 | 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 62 | 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 63 | 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 64 | 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 65 | 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 66 | 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 67 | 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d 68 | }; 69 | 70 | uint32_t crc32(const char * buf, size_t size) { 71 | uint32_t crc = 0xFFFFFFFF; 72 | for (size_t i = 0; i < size; i++) 73 | crc = crc32tab[(crc ^ buf[i]) & 0xff] ^ (crc >> 8); 74 | return crc ^ 0xFFFFFFFF; 75 | } -------------------------------------------------------------------------------- /main.h: -------------------------------------------------------------------------------- 1 | // Small alternative startfile for main() 2 | // Build with gcc perms: 3 | // -nostartfiles 4 | #pragma once 5 | #ifndef STARTFILE_MAIN_H 6 | #define STARTFILE_MAIN_H 7 | #include 8 | 9 | #ifdef UNICODE 10 | #define wmain(argc, argv) _wmain(argc, argv) 11 | int wmain(int argc, wchar_t *argv[]); 12 | #else 13 | #include "commandlinetoargva.h" 14 | // workaround undefined reference to `_pei386_runtime_relocator' 15 | #define main(argc, argv) _main(argc, argv) 16 | int main(int argc, char *argv[]); 17 | #endif 18 | int __cdecl mainCRTStartup() { 19 | int __arg_c; 20 | #ifdef UNICODE 21 | wchar_t **__arg_v = CommandLineToArgvW(GetCommandLineW(), &__arg_c); 22 | const int exitCode = wmain(__arg_c, __arg_v); 23 | #else 24 | char **__arg_v = CommandLineToArgvA(GetCommandLineA(), &__arg_c); 25 | const int exitCode = main(__arg_c, __arg_v); 26 | #endif 27 | LocalFree(__arg_v); 28 | ExitProcess(exitCode); 29 | return exitCode; 30 | } 31 | #endif 32 | -------------------------------------------------------------------------------- /mouse.h: -------------------------------------------------------------------------------- 1 | #ifndef WIN32_LEAN_AND_MEAN 2 | #define WIN32_LEAN_AND_MEAN 3 | #endif 4 | #include 5 | 6 | static void MouseClick(const unsigned int x, const unsigned int y, 7 | const unsigned char type) { 8 | SetCursorPos(x, y); 9 | INPUT input; 10 | ZeroMemory(&input, sizeof(input)); 11 | input.type = INPUT_MOUSE; 12 | switch (type) { 13 | case 1: 14 | input.mi.dwFlags = MOUSEEVENTF_RIGHTDOWN; 15 | SendInput(1, &input, sizeof(input)); 16 | input.mi.dwFlags = MOUSEEVENTF_RIGHTUP; 17 | break; 18 | case 2: 19 | input.mi.dwFlags = MOUSEEVENTF_MIDDLEDOWN; 20 | SendInput(1, &input, sizeof(input)); 21 | input.mi.dwFlags = MOUSEEVENTF_MIDDLEUP; 22 | break; 23 | default: 24 | input.mi.dwFlags = MOUSEEVENTF_LEFTDOWN; 25 | SendInput(1, &input, sizeof(input)); 26 | input.mi.dwFlags = MOUSEEVENTF_LEFTUP; 27 | } 28 | SendInput(1, &input, sizeof(input)); 29 | } -------------------------------------------------------------------------------- /sendinput.c: -------------------------------------------------------------------------------- 1 | #include "sendinput.h" 2 | #ifdef NO_START_FILES 3 | #include "winmain.h" 4 | #endif 5 | #ifndef __GNUC__ 6 | #define UNUSED 7 | #else // __GNUC__ 8 | #define UNUSED __attribute__((unused)) 9 | #endif // __GNUC__ 10 | 11 | void EmitError(const unsigned short errorCode) { 12 | MessageBox(NULL, ErrorInfo[errorCode], NULL, 13 | MB_OK | MB_ICONEXCLAMATION | MB_SETFOREGROUND); 14 | } 15 | 16 | // main entry 17 | int WINAPI WinMain(HINSTANCE UNUSED hInstance, HINSTANCE UNUSED hPrevInstance, 18 | LPSTR lpCmdLine, int UNUSED nShowCmd) { 19 | unsigned int cmdLineLength = (unsigned int) lstrlen(lpCmdLine); 20 | if (cmdLineLength == 0) { 21 | return 1; 22 | } 23 | ParseSendKeys(lpCmdLine, cmdLineLength); 24 | return 0; 25 | } -------------------------------------------------------------------------------- /sendinput.h: -------------------------------------------------------------------------------- 1 | #ifndef SENDINPUT_H 2 | #define SENDINPUT_H 3 | 4 | #ifndef WIN32_LEAN_AND_MEAN 5 | #define WIN32_LEAN_AND_MEAN 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "crc32.h" 14 | #include "command.h" 15 | 16 | extern void EmitError(unsigned short); 17 | const LPSTR ErrorInfo[] = { 18 | "no error", 19 | "keyCodeCombine == -1, VkKeyScan fails", 20 | "unknown high-order byte of VkKeyScan's return value", 21 | "cannot get high-order byte from VkKeyScan" 22 | }; 23 | static const unsigned char vkeyCodeCombine[3] = { 24 | VK_SHIFT, // shift 25 | VK_CONTROL, // ctrl 26 | VK_MENU // alt 27 | }; 28 | 29 | void ParseSendKeys(const char *keyString, size_t length) { 30 | if (length < 1) 31 | return; 32 | for (size_t index = 0; index < length;) { 33 | if (index & 0x200) 34 | Sleep(1); // workaround: preventing mistype by waiting for a while 35 | if (keyString[index] == '\r' && keyString[index + 1] == '\n') { 36 | // skip cr-lf line endings 37 | ++index; 38 | } 39 | if (length - index > 4 && keyString[index] == '$' && keyString[index + 1] == '{') { 40 | char param[3][COMMAND_MAX_LENGTH] = {{0}}; 41 | if (sscanf(keyString + index, "${%15[A-Z0-9]}", param[0]) == 1) { 42 | const size_t offset = 3 + strnlen(param[0], COMMAND_MAX_LENGTH); 43 | if (keyString[index + offset - 1] == '}') { 44 | const uint32_t hash = crc32(param[0], offset - 3); 45 | if (hash == 1005452284) { // DOLLAR 46 | ParseSendKeys("$", 1); 47 | index += offset; 48 | continue; 49 | } else { 50 | const WORD keyCode = ParseCommand(hash); 51 | if (keyCode) { 52 | SendSingleKey(keyCode); 53 | index += offset; 54 | continue; 55 | } 56 | } 57 | } 58 | } 59 | if (sscanf(keyString + index, "${%15[A-Z] %9[0-9]}", param[0], param[1]) == 2) { 60 | const size_t offset = 4 + strnlen(param[0], COMMAND_MAX_LENGTH) + 61 | strnlen(param[1], COMMAND_MAX_LENGTH); 62 | if (keyString[index + offset - 1] == '}') { 63 | const int paramInt = atoi(param[1]); 64 | if (ParseCommandWithParams( 65 | crc32(param[0], strnlen(param[0], COMMAND_MAX_LENGTH)), 66 | ¶mInt, 1)) { 67 | 68 | index += offset; 69 | continue; 70 | } 71 | } 72 | } 73 | if (sscanf(keyString + index, "${%15[A-Z]+%15[0-9A-Za-z]}", param[0], param[1]) == 2) { 74 | WORD paramLength[2]; 75 | for (unsigned char i = 0; i < 2; i++) 76 | paramLength[i] = (WORD) strnlen(param[i], COMMAND_MAX_LENGTH); 77 | const size_t offset = 4 + paramLength[0] + paramLength[1]; 78 | if (keyString[index + offset - 1] == '}') { 79 | if (ParseKeyCombination(param, paramLength, 2)) { 80 | 81 | index += offset; 82 | continue; 83 | } 84 | } 85 | } 86 | if (sscanf(keyString + index, "${%15[A-Z] %9[0-9] %9[0-9]}", param[0], param[1], 87 | param[2]) == 3) { 88 | const size_t offset = 5 + strnlen(param[0], COMMAND_MAX_LENGTH) + 89 | strnlen(param[1], COMMAND_MAX_LENGTH) + 90 | strnlen(param[2], COMMAND_MAX_LENGTH); 91 | if (keyString[offset + index - 1] == '}') { 92 | int paramInt[2]; 93 | paramInt[0] = atoi(param[1]); 94 | paramInt[1] = atoi(param[2]); 95 | if (ParseCommandWithParams( 96 | crc32(param[0], strnlen(param[0], COMMAND_MAX_LENGTH)), 97 | paramInt, 2)) { 98 | 99 | index += offset; 100 | continue; 101 | } 102 | } 103 | } 104 | if (sscanf(keyString + index, "${%15[A-Z]+%15[0-9A-Za-z]+%15[0-9A-Za-z]}", 105 | param[0], param[1], param[2]) == 3) { 106 | WORD paramLength[3]; 107 | for (unsigned char i = 0; i < 3; i++) { 108 | paramLength[i] = (WORD) strnlen(param[i], COMMAND_MAX_LENGTH); 109 | }; 110 | const size_t offset = 111 | 5 + paramLength[0] + paramLength[1] + paramLength[2]; 112 | if (keyString[offset + index - 1] == '}') { 113 | if (ParseKeyCombination(param, paramLength, 3)) { 114 | 115 | index += offset; 116 | continue; 117 | } 118 | } 119 | } 120 | } 121 | 122 | // const SHORT vKeyCode = VkKeyScanEx(keyString[0],GetKeyboardLayout(0)); 123 | const SHORT vKeyCode = VkKeyScan(keyString[index]); 124 | 125 | WORD keyCode[3] = {0}; 126 | keyCode[0] = (WORD) (vKeyCode & 0xFF); 127 | const WORD keyCodeCombine = (const WORD) (vKeyCode >> 8); 128 | if (vKeyCode == -1) { 129 | EmitError(1); // VkKeyScan fails 130 | ++index; 131 | continue; 132 | } 133 | if (keyCodeCombine == 0) { 134 | SendSingleKey(keyCode[0]); 135 | ++index; 136 | continue; 137 | } 138 | unsigned char keyCount = 0; 139 | for (unsigned char i = 0; i < 3; i++) { 140 | if (((keyCodeCombine >> i) & 0x1) == 1) { 141 | keyCode[keyCount + 1] = keyCode[keyCount]; 142 | keyCode[keyCount] = vkeyCodeCombine[i]; 143 | keyCount++; 144 | } 145 | } 146 | if (keyCount > 0) 147 | SendMultipleKey(keyCode, (const unsigned short) (keyCount + 1)); 148 | else { 149 | if (keyCodeCombine > 4) 150 | EmitError(2); // unknown high-order byte of VkKeyScan's return value 151 | else 152 | EmitError(3); // cannot get high-order byte from VkKeyScan 153 | SendSingleKey(keyCode[0]); 154 | } 155 | ++index; 156 | } 157 | } 158 | 159 | #endif //SENDINPUT_H 160 | -------------------------------------------------------------------------------- /sendinput_io.c: -------------------------------------------------------------------------------- 1 | #include "sendinput.h" 2 | #ifdef NO_START_FILES 3 | #include "main.h" 4 | #endif 5 | #define BLOCK_SIZE 4096 6 | 7 | char *readStdIn(size_t *size) { 8 | int c = 0; 9 | char *buf = malloc(sizeof(char) * BLOCK_SIZE); 10 | size_t bufSize = BLOCK_SIZE, currSize = 0; 11 | while ((c = getchar()) != EOF) { 12 | if (currSize + 1 > bufSize) { 13 | if (!(buf = realloc(buf, bufSize + BLOCK_SIZE))) { 14 | return NULL; 15 | } 16 | bufSize += BLOCK_SIZE; 17 | } 18 | buf[currSize++] = (char) c; 19 | } 20 | buf[currSize] = '\0'; 21 | *size = currSize; 22 | return buf; 23 | } 24 | 25 | char *readFile(const char *fileName, size_t *size) { 26 | /* declare a file pointer */ 27 | FILE *filePtr = fopen(fileName, "rb"); 28 | /* quit if the file does not exist */ 29 | if (filePtr == NULL) 30 | return NULL; 31 | 32 | /* Get the number of bytes */ 33 | fseek(filePtr, 0, SEEK_END); 34 | size_t fileSize = (size_t) ftell(filePtr); 35 | 36 | /* reset the file position indicator to 37 | the beginning of the file */ 38 | fseek(filePtr, 0, SEEK_SET); 39 | 40 | /* grab sufficient memory for the 41 | buffer to hold the text */ 42 | char *buffer = calloc(fileSize + 1, sizeof(char)); 43 | 44 | /* memory error */ 45 | if (buffer == NULL) 46 | return NULL; 47 | 48 | /* copy all the text into the buffer */ 49 | *size = fread(buffer, sizeof(char), fileSize, filePtr); 50 | fclose(filePtr); 51 | buffer[fileSize + 1] = '\0'; 52 | return buffer; 53 | } 54 | 55 | void EmitError(const unsigned short errorCode) { 56 | fputs(ErrorInfo[errorCode], stderr); 57 | } 58 | 59 | #define HELP_TEXT "Sendinput utility\n" \ 60 | "Usage: sendinput_io [-|]\n" \ 61 | "Parses sendinput command from stdin or file and exec it." 62 | 63 | int main(int argc, char *argv[]) { 64 | size_t size = 0; 65 | char *command = NULL; 66 | if (argc > 1) { 67 | char *filePath = argv[1]; 68 | if (filePath[0] == '-' && filePath[1] == '\0') { 69 | command = readStdIn(&size); 70 | } else { 71 | command = readFile(filePath, &size); 72 | } 73 | if (command) { 74 | ParseSendKeys(command, size); 75 | free(command); 76 | return 0; 77 | } 78 | } 79 | puts(HELP_TEXT); 80 | return 1; 81 | } -------------------------------------------------------------------------------- /sendkey.h: -------------------------------------------------------------------------------- 1 | #ifndef WIN32_LEAN_AND_MEAN 2 | #define WIN32_LEAN_AND_MEAN 3 | #endif 4 | #include 5 | 6 | static void SetKeyState(const BOOL bState, const WORD keyCode) { 7 | BYTE keyState[256]; 8 | INPUT input; 9 | ZeroMemory(&input, sizeof(input)); 10 | 11 | GetKeyboardState((LPBYTE)&keyState); 12 | if ((bState && !(keyState[keyCode] & 1)) || 13 | (!bState && (keyState[keyCode] & 1))) { 14 | input.type = INPUT_KEYBOARD; 15 | input.ki.wVk = keyCode; 16 | SendInput(1, &input, sizeof(input)); 17 | 18 | input.ki.dwFlags = KEYEVENTF_KEYUP; 19 | SendInput(1, &input, sizeof(input)); 20 | } 21 | } 22 | 23 | static void SendSingleKey(const WORD keyCode) { 24 | INPUT input; 25 | ZeroMemory(&input, sizeof(input)); 26 | input.type = INPUT_KEYBOARD; 27 | input.ki.wVk = keyCode; 28 | input.ki.dwFlags = 0; 29 | SendInput(1, &input, sizeof(input)); 30 | 31 | input.ki.dwFlags = KEYEVENTF_KEYUP; 32 | SendInput(1, &input, sizeof(input)); 33 | } 34 | 35 | static void SendMultipleKey(const WORD keyCode[], const unsigned short count) { 36 | INPUT input; 37 | ZeroMemory(&input, sizeof(input)); 38 | input.type = INPUT_KEYBOARD; 39 | input.ki.dwFlags = 0; 40 | for (unsigned short i = 0; i < count; i++) { 41 | input.ki.wVk = keyCode[i]; 42 | SendInput(1, &input, sizeof(input)); 43 | } 44 | input.ki.dwFlags = KEYEVENTF_KEYUP; 45 | for (unsigned short i = count; i-- > 0;) { 46 | input.ki.wVk = keyCode[i]; 47 | SendInput(1, &input, sizeof(input)); 48 | } 49 | } -------------------------------------------------------------------------------- /winmain.h: -------------------------------------------------------------------------------- 1 | // Small alternative startfile for WinMain() 2 | // Build with gcc perms: 3 | // -nostartfiles -mwindows -lshlwapi 4 | #pragma once 5 | #ifndef STARTFILE_WINMAIN_H 6 | #define STARTFILE_WINMAIN_H 7 | #include 8 | #include 9 | 10 | #ifdef UNICODE 11 | int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 12 | PWSTR lpCmdLine, int nCmdShow); 13 | #else 14 | int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 15 | LPSTR lpCmdLine, int nCmdShow); 16 | #endif 17 | int __cdecl WinMainCRTStartup() { 18 | STARTUPINFO startupInfo; 19 | ZeroMemory(&startupInfo, sizeof(STARTUPINFO)); 20 | GetStartupInfo(&startupInfo); 21 | DWORD __nShowCmd = SW_SHOWDEFAULT; 22 | __nShowCmd = startupInfo.dwFlags & STARTF_USESHOWWINDOW 23 | ? startupInfo.wShowWindow 24 | : SW_SHOWDEFAULT; 25 | const int exitCode = 26 | #ifdef UNICODE 27 | wWinMain(GetModuleHandleW(NULL), NULL, PathGetArgsW(GetCommandLineW()), __nShowCmd); 28 | #else 29 | WinMain(GetModuleHandleA(NULL), NULL, PathGetArgsA(GetCommandLineA()), __nShowCmd); 30 | #endif 31 | ExitProcess(exitCode); 32 | return exitCode; 33 | } 34 | #endif 35 | --------------------------------------------------------------------------------