Dsmoeg999 Posted April 5, 2023 Share Posted April 5, 2023 Hi guys I share with you this code the purpose is to send the letter "a" to notepad interacting with WinAPI. I'm not getting it to work and I don't see the error. If someone can see the error I would appreciate it.Maybe something needs to be imported ? $hWnd = WinGetHandle("[CLASS:Notepad]") WinActivate($hWnd) Const $KEYEVENTF_UNICODE = 4 Const $INPUT_KEYBOARD = 1 Const $iInputSize = 28 Const $tagKEYBDINPUT = _ 'word wVk;' & _ 'word wScan;' & _ 'dword dwFlags;' & _ 'dword time;' & _ 'ulong_ptr dwExtraInfo' Const $tagINPUT = _ 'dword type;' & _ $tagKEYBDINPUT & _ ';dword pad;' $tINPUTs = DllStructCreate($tagINPUT) $pINPUTs = DllStructGetPtr($tINPUTs) $iINPUTs = 1 $Key = AscW('a') DllStructSetData($tINPUTs, 1, $INPUT_KEYBOARD) DllStructSetData($tINPUTs, 3, $Key) DllStructSetData($tINPUTs, 4, $KEYEVENTF_UNICODE) DllCall('user32.dll', 'uint', 'SendInput', 'uint', $iINPUTs, _ 'ptr', $pINPUTs, 'int', $iInputSize) Link to comment Share on other sites More sharing options...
Solution mistersquirrle Posted April 5, 2023 Solution Share Posted April 5, 2023 I also tried some things with SendInput, and in the end I decided that it wasn't worth it, as I'm pretty sure that AutoIt's Send uses it (though I haven't confirmed, so if someone with more knowledge wants to correct me...). The main problem, and likely that problem that you're having is that SendInput doesn't work (with what you have here) in x64 mode. Your code works for me if you make sure it's running as x86 by putting this at the top of your script: #AutoIt3Wrapper_UseX64=n I messed around with SendInput for a while trying to get it work with 64-bit but couldn't. I got it to the point where DllCall did not return any errors, so it seemed like it and SendInput were happy with everything, but nothing happened. Maybe someone knows how to get SendInput working with 64-bit, but it wasn't worth it to me. Also, keep in mind that with your SendInput call, you're not sending the key back UP, it's only being pressed down. You should send the key back up as well: Global Const $KEYEVENTF_KEYUP = 0x0002 And from the Microsoft page on SendInput: This function is subject to UIPI [User Interface Privilege Isolation]. Applications are permitted to inject input only into applications that are at an equal or lesser integrity level Meaning you may want/need to add #RequireAdmin to your script as well. Dsmoeg999 1 We ought not to misbehave, but we should look as though we could. Link to comment Share on other sites More sharing options...
Popular Post TheXman Posted April 5, 2023 Popular Post Share Posted April 5, 2023 (edited) The example below works in 32 bit and 64 bit environments. INPUT is a struct that contains a union. Since the original post only asked about keyboard input, I did not include what the MOUSEINPUT and HARDWAREINPUT unions would look like. There are alignment issues that have to be taken into account when running 32 bit or 64 bit. The example shows one way of handling it. expandcollapse popup#AutoIt3Wrapper_AU3Check_Parameters=-w 3 -w 4 -w 5 -w 6 -d ;~ #AutoIt3Wrapper_UseX64=N #include <Constants.au3> #include <Array.au3> #include <WinAPIDiag.au3> Enum Step *2 _ $KEYEVENTF_EXTENDEDKEY = 1, _ $KEYEVENTF_KEYUP, _ $KEYEVENTF_UNICODE, _ $KEYEVENTF_SCANCODE Const $INPUT_KEYBOARD = 1 Const $VK_SHIFT = 0x10 Const $VK_A = 0x41 Const $tagKeyInput = _ "dword type;" & _ (@AutoItX64 ? "byte alignment_pad[4];" : "") & _ "struct;" & _ " word wVk;" & _ " word wScan;" & _ " dword dwFlags;" & _ " dword time;" & _ " ulong_ptr dwExtraInfo;" & _ " byte reserved[8];" & _ "endstruct;" example() Func example() #cs ========================================================= Using the SendInput win32 API, this example sends "AbC" to the last active notepad instance (if it exists). Note: "A" is sent by using virtual key code and "C" is sent using a scan code. It was done just to show the 2 different ways of sending keyboard input. #ce ========================================================= Local $tKeyInput, _ $tInputArray Local $aResult Local $iNumberOfInputs = 8 ;Create a struct to get size $tKeyInput = DllStructCreate($tagKeyInput) ;Create an inputs array buffer $tInputArray = DllStructCreate(StringFormat("byte bytes[%i];", DllStructGetSize($tKeyInput) * $iNumberOfInputs)) ;Load input array[0] $tKeyInput = DllStructCreate($tagKeyInput, DllStructGetPtr($tInputArray) + (0 * DllStructGetSize($tKeyInput))) $tKeyInput.type = $INPUT_KEYBOARD $tKeyInput.wVk = $VK_SHIFT ;Load input array[1] $tKeyInput = DllStructCreate($tagKeyInput, DllStructGetPtr($tInputArray) + (1 * DllStructGetSize($tKeyInput))) $tKeyInput.type = $INPUT_KEYBOARD $tKeyInput.wVk = $VK_A ;Load input array[2] $tKeyInput = DllStructCreate($tagKeyInput, DllStructGetPtr($tInputArray) + (2 * DllStructGetSize($tKeyInput))) $tKeyInput.type = $INPUT_KEYBOARD $tKeyInput.wvk = $VK_A $tKeyInput.dwFlags = $KEYEVENTF_KEYUP ;Load input array[3] $tKeyInput = DllStructCreate($tagKeyInput, DllStructGetPtr($tInputArray) + (3 * DllStructGetSize($tKeyInput))) $tKeyInput.type = $INPUT_KEYBOARD $tKeyInput.wVk = $VK_SHIFT $tKeyInput.dwFlags = $KEYEVENTF_KEYUP ;Load input array[4] $tKeyInput = DllStructCreate($tagKeyInput, DllStructGetPtr($tInputArray) + (4 * DllStructGetSize($tKeyInput))) $tKeyInput.type = $INPUT_KEYBOARD $tKeyInput.wScan = AscW("b") $tKeyInput.dwFlags = $KEYEVENTF_UNICODE ;Load input array[5] $tKeyInput = DllStructCreate($tagKeyInput, DllStructGetPtr($tInputArray) + (5 * DllStructGetSize($tKeyInput))) $tKeyInput.type = $INPUT_KEYBOARD $tKeyInput.wScan = AscW("b") $tKeyInput.dwFlags = $KEYEVENTF_UNICODE + $KEYEVENTF_KEYUP ;Load input array[6] $tKeyInput = DllStructCreate($tagKeyInput, DllStructGetPtr($tInputArray) + (6 * DllStructGetSize($tKeyInput))) $tKeyInput.type = $INPUT_KEYBOARD $tKeyInput.wScan = AscW("C") $tKeyInput.dwFlags = $KEYEVENTF_UNICODE ;Load input array[7] $tKeyInput = DllStructCreate($tagKeyInput, DllStructGetPtr($tInputArray) + (7 * DllStructGetSize($tKeyInput))) $tKeyInput.type = $INPUT_KEYBOARD $tKeyInput.wScan = AscW("C") $tKeyInput.dwFlags = $KEYEVENTF_UNICODE + $KEYEVENTF_KEYUP ;Activate notepad window If Not WinActivate("[CLASS:Notepad]") Then Exit MsgBox($MB_ICONERROR, "WinActivate Error", "Notepad window not found.") ;Send input $aResult = DllCall('user32.dll', 'uint', 'SendInput', _ 'uint' , $iNumberOfInputs, _ 'struct*', $tInputArray, _ 'int' , DllStructGetSize($tKeyInput)) If $aResult[0] = 0 Then MsgBox(0, "GetLastError", _WinAPI_GetLastErrorMessage()) EndFunc Edited April 7, 2023 by TheXman Added input of virtual keys to the example. KaFu, pixelsearch, Dsmoeg999 and 3 others 5 1 CryptoNG UDF: Cryptography API: Next Gen jq UDF: Powerful and Flexible JSON Processor | jqPlayground: An Interactive JSON Processor Xml2Json UDF: Transform XML to JSON | HttpApi UDF: HTTP Server API | Roku Remote: Example Script About Me How To Ask Good Questions On Technical And Scientific Forums (Detailed) | How to Ask Good Technical Questions (Brief) "Any fool can know. The point is to understand." -Albert Einstein "If you think you're a big fish, it's probably because you only swim in small ponds." ~TheXman Link to comment Share on other sites More sharing options...
Dsmoeg999 Posted April 5, 2023 Author Share Posted April 5, 2023 Hi guys thanks for your contribution both examples work. @mistersquirrle indeed you are right Send does make a call to SendInput. I also get it to work by setting #AutoIt3Wrapper_UseX64=n I don't quite understand what the wrapper is for. Thank you both for the examples and solutions I will try to understand them better. TheXman 1 Link to comment Share on other sites More sharing options...
mistersquirrle Posted April 5, 2023 Share Posted April 5, 2023 (edited) 2 hours ago, TheXman said: The example below works in 32 bit and 64 bit environments.... There are alignment issues that have to be taken into account when running 32 bit or 64 bit. The example shows one way of handling it. Awesome, I had tried messing around with padding and alignment of structs, but I'm not very good with DllStructs and that type of thing. So I never got it working, but I had tried some similar methods. Thank you for that example, for sure! 19 minutes ago, Dsmoeg999 said: #AutoIt3Wrapper_UseX64=n I don't quite understand what the wrapper is for. This tells AutoIt if it should run your script under x86 (32-bit) or x64 (64-bit). By default when you install AutoIt it uses x86, though while installing you can change the default runtype to x64. So it sounds like that you've installed AutoIt to use x64 by default, and to get the script running as x86/32-bit, that "wrapper" line is needed. It just tells AutoIt to run as x86 (n) or x64 (y). Edited April 5, 2023 by mistersquirrle TheXman and Dsmoeg999 2 We ought not to misbehave, but we should look as though we could. Link to comment Share on other sites More sharing options...
pixelsearch Posted May 20, 2023 Share Posted May 20, 2023 (edited) @TheXman great example in your script above, it explains a lot, thanks I would like to discuss the part where "you're sending 'C' using a scan code" with these lines : $tKeyInput.wScan = AscW("C") $tKeyInput.dwFlags = $KEYEVENTF_UNICODE We could also use the scan code for another purpose, as read on msdn Keyboard Input Overview (scan codes paragraph) which stipulates : The scan code is the value that the system generates when the user presses a key. It is a value that identifies the key pressed regardless of the active keyboard layout, as opposed to the character represented by the key. For example, if we replace array[6] and array[7] in your script with these ones... ;Load input array[6] $tKeyInput = DllStructCreate($tagKeyInput, DllStructGetPtr($tInputArray) + (6 * DllStructGetSize($tKeyInput))) $tKeyInput.type = $INPUT_KEYBOARD $tKeyInput.wScan = 0x10 ; key location #17 (q on Qwerty kbd, a on Azerty kbd etc...) got a Scan 1 Make = 0x10 $tKeyInput.dwFlags = $KEYEVENTF_SCANCODE ;Load input array[7] $tKeyInput = DllStructCreate($tagKeyInput, DllStructGetPtr($tInputArray) + (7 * DllStructGetSize($tKeyInput))) $tKeyInput.type = $INPUT_KEYBOARD $tKeyInput.wScan = 0x10 ; key location #17 (reuse Scan 1 Make, no need of deprecated Scan 1 Break) $tKeyInput.dwFlags = $KEYEVENTF_SCANCODE + $KEYEVENTF_KEYUP ...then a letter (not depending on the keyboard layout) will be displayed in NotePad. It may be a 'q' (if qwerty keyboard) or an 'a' (my azerty keyboard) or whatever. Key location #17 can be found on the keyboard picture at the msdn link above, with its corresponding "Scan 1 Make". We just need to indicate the correct flag $KEYEVENTF_SCANCODE in the structure. Maybe this could answer the question asked in this topic ? Edited May 22, 2023 by pixelsearch no need of deprecated Scan 1 Break in array[7], let's reuse Scan 1 Make TheXman 1 Link to comment Share on other sites More sharing options...
TheXman Posted May 20, 2023 Share Posted May 20, 2023 (edited) 41 minutes ago, pixelsearch said: Maybe this could answer the question asked in this topic ? @pixelsearch I saw the topic that you referenced above. I couldn't tell whether the OP wanted to be able to send the same character regardless of the locale or if he wanted to always send the character from the same physical key on the keyboard, regardless of the locale. I see conflicting information between the title of the post, the images of the keyboards, and some of the posts. I try not to answer questions based on my assumptions. I prefer to just ask OP's for clarification in order to not waste unnecessary time and effort. Without a definitive answer to what the OP is actually trying to achieve, I really can't answer your question. For the record, I understood it as wanting to send the same character regardless of the locale. If that's true, then Send() or the SendInput API could just send a "C". Edited May 20, 2023 by TheXman CryptoNG UDF: Cryptography API: Next Gen jq UDF: Powerful and Flexible JSON Processor | jqPlayground: An Interactive JSON Processor Xml2Json UDF: Transform XML to JSON | HttpApi UDF: HTTP Server API | Roku Remote: Example Script About Me How To Ask Good Questions On Technical And Scientific Forums (Detailed) | How to Ask Good Technical Questions (Brief) "Any fool can know. The point is to understand." -Albert Einstein "If you think you're a big fish, it's probably because you only swim in small ponds." ~TheXman Link to comment Share on other sites More sharing options...
KaFu Posted May 21, 2023 Share Posted May 21, 2023 @TheXman Great example, thanks a lot! Quote I did not include what the MOUSEINPUT and HARDWAREINPUT unions would look like. It would be great if you could post examples on these too! Maybe directly to "Examples"? TheXman 1 OS: Win10-22H2 - 64bit - German, AutoIt Version: 3.3.16.1, AutoIt Editor: SciTE, Website: https://funk.eu AMT - Auto-Movie-Thumbnailer (2024-Oct-13) BIC - Batch-Image-Cropper (2023-Apr-01) COP - Color Picker (2009-May-21) DCS - Dynamic Cursor Selector (2024-Oct-13) HMW - Hide my Windows (2024-Oct-19) HRC - HotKey Resolution Changer (2012-May-16) ICU - Icon Configuration Utility (2018-Sep-16) SMF - Search my Files (2024-Oct-20) - THE file info and duplicates search tool SSD - Set Sound Device (2017-Sep-16) Link to comment Share on other sites More sharing options...
TheXman Posted May 21, 2023 Share Posted May 21, 2023 (edited) Thanks @KaFu. I'll leave the creation of additional examples to others. If someone has questions or problems with the creation of those examples, then as always Semper volens auxilium (I'm always willing to help). Of course that assumes he or she is actively trying to learn, not just passively trying to get solutions or answers (and not on my list of members to ignore). 😉 Edited May 21, 2023 by TheXman CryptoNG UDF: Cryptography API: Next Gen jq UDF: Powerful and Flexible JSON Processor | jqPlayground: An Interactive JSON Processor Xml2Json UDF: Transform XML to JSON | HttpApi UDF: HTTP Server API | Roku Remote: Example Script About Me How To Ask Good Questions On Technical And Scientific Forums (Detailed) | How to Ask Good Technical Questions (Brief) "Any fool can know. The point is to understand." -Albert Einstein "If you think you're a big fish, it's probably because you only swim in small ponds." ~TheXman Link to comment Share on other sites More sharing options...
pixelsearch Posted May 22, 2023 Share Posted May 22, 2023 (edited) I modified my example above : when $KEYEVENTF_SCANCODE is applied in array[7] , there's no need to use "Scan 1 Break" any more as it's deprecated. Reusing twice the same "Scan 1 Make" for the same key physical location does the job, in array[6] and array[7] Could you imagine MS modified all the "Scan codes" paragraph on their web page Keyboard Input Overview ? I downloaded their web page on October 9, 2022 and all "Scan 1 Break" codes were indicated in a separate column. Now this column has disappeared in 2023. The rest of the web page (before and after the "Scan codes" paragraph) is exactly the same, as checked with Beyond Compare, not a single byte has changed ! Here is a part of the 2022 web page, referring to older keyboards : The following table lists the full set of Scan Codes as presently recognized by Windows. The US Key assignments are for reference to a type 101/102 Enhanced keyboard. The "Scan 1 make" code is delivered in WM_KEYDOWN/WM_SYSKEYDOWN messages, while the "Scan 1 break" code is delivered in WM_KEYUP/WM_SYSKEYUP messages. ...and here is a part of the 2023 actual web page : Modern keyboards are using Human Interface Devices (HID) specification to communicate with a computer. Keyboard driver converts reported HID Usage values sent from the keyboard to scan codes and passes them on to applications. The Scan 1 Make code is delivered in WM_KEYDOWN/WM_KEYUP/WM_SYSKEYDOWN/WM_SYSKEYUP and WM_INPUT messages Edited May 22, 2023 by pixelsearch msdn indications that "Scan 1 Make" should be used everywhere, bye bye "Scan 1 break" Link to comment Share on other sites More sharing options...
pixelsearch Posted May 22, 2023 Share Posted May 22, 2023 Still a keyboard example, based on @TheXman script above. It shows clearly that if you try to press keys while the 5.000 "a" are being displayed in Notepad, all your key presses will be buffered, until the compact block of 5.000 "a" is fully displayed. Only then your buffered keys will be displayed. It's a big difference with the Send() function where everything would be intermingled even if you Send() a unique string of 5.000 "a" at once (tested). This confirms what we can read on msdn : The SendInput function works by injecting a series of simulated input events into a device's input stream. The effect is similar to calling the keybd_event or mouse_event function repeatedly, except that the system ensures that no other input events intermingle with the simulated events. When the call completes, the return value indicates the number of input events successfully played. If this value is zero, then input was blocked. Before running the script, a blank NotePad should be opened, with its Wrap option ticked (not mandatory but it will be more 'enjoyable' to display a block of "a" appearing on different 'lines') . Anyway, wrapped or not, no big deal expandcollapse popup #AutoIt3Wrapper_AU3Check_Parameters=-w 3 -w 4 -w 5 -w 6 -d ;~ #AutoIt3Wrapper_UseX64=N #include <Constants.au3> #include <Array.au3> #include <WinAPIDiag.au3> Const $INPUT_KEYBOARD = 1 Const $VK_A = 0x41 Const $tagKeyInput = _ "dword type;" & _ (@AutoItX64 ? "byte alignment_pad[4];" : "") & _ "struct;" & _ " word wVk;" & _ " word wScan;" & _ " dword dwFlags;" & _ " dword time;" & _ " ulong_ptr dwExtraInfo;" & _ " byte reserved[8];" & _ "endstruct;" example() Func example() #cs ========================================================== Using the SendInput win32 API, this example sends a string of "a" to the last active notepad instance (if it exists). Note: If user press other keys while the string of "a" is sent, they are buffered and displayed AFTER the string of "a" This is a huge difference with the Send() function where the display would be intermingled when you send for ex. a string of 5.000 "a" with a single Send() instruction. #ce ========================================================== ; Line moved inside the function, or the script won't run with AutoIt 3.3.14.5 ("includes chain" changed from 3.3.14.5 to 3.3.16.1) Local Enum Step *2 _ $KEYEVENTF_EXTENDEDKEY = 1, _ $KEYEVENTF_KEYUP, _ $KEYEVENTF_UNICODE, _ $KEYEVENTF_SCANCODE Local $tKeyInput, _ $tInputArray Local $aResult Local $iNumberOfInputs = 5000 * 2 ; => 10000 inputs (press/release) => 5000 chars displayed If $iNumberOfInputs < 2 Or (($iNumberOfInputs / 2) <> Int($iNumberOfInputs / 2)) Then _ Exit MsgBox($MB_ICONERROR, "$iNumberOfInputs", "It has to be an even number, greater or equal to 2") ;Create a struct to get size $tKeyInput = DllStructCreate($tagKeyInput) ;Create an inputs array buffer $tInputArray = DllStructCreate(StringFormat("byte bytes[%i];", DllStructGetSize($tKeyInput) * $iNumberOfInputs)) ; Local $hTimer = TimerInit() For $i = 0 To $iNumberOfInputs - 1 Step 2 ;Load input array[$i] $tKeyInput = DllStructCreate($tagKeyInput, DllStructGetPtr($tInputArray) + ($i * DllStructGetSize($tKeyInput))) $tKeyInput.type = $INPUT_KEYBOARD $tKeyInput.wVk = $VK_A ;Load input array[$i + 1] $tKeyInput = DllStructCreate($tagKeyInput, DllStructGetPtr($tInputArray) + (($i + 1) * DllStructGetSize($tKeyInput))) $tKeyInput.type = $INPUT_KEYBOARD $tKeyInput.wvk = $VK_A $tKeyInput.dwFlags = $KEYEVENTF_KEYUP Next ; ConsoleWrite($iNumberOfInputs & " Inputs array buffer prepared in " & Int(TimerDiff($htimer)) & " ms" & @crlf) ; ConsoleWrite(DllStructGetSize($tKeyInput) & @crlf) ; 28 ; ConsoleWrite(DllStructGetSize($tInputArray) & @crlf) ; 28 * $iNumberOfInputs (280.000 if $iNumberOfInputs = 10.000) ;Activate notepad window If Not WinActivate("[CLASS:Notepad]") Then Exit MsgBox($MB_ICONERROR, "WinActivate Error", "Notepad window not found.") ;Send input $aResult = DllCall('user32.dll', 'uint', 'SendInput', _ 'uint' , $iNumberOfInputs, _ 'struct*', $tInputArray, _ 'int' , DllStructGetSize($tKeyInput)) If $aResult[0] = 0 Then MsgBox(0, "GetLastError", _WinAPI_GetLastErrorMessage()) ElseIf $aResult[0] <> $iNumberOfInputs Then MsgBox(0, "Warning", $aResult[0] & " keyboard events returned (it should have been " & $iNumberOfInputs & ")") EndIf EndFunc Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now