Jump to content

Recommended Posts

Posted (edited)

Hello, I'm trying to send keys in a program but I want to do this in new thread.

While my application is sending keys, it freezes until finish to send all keys.

I need to do the send() from a new thread then while the new created thread is sending keys, my application do other functions.

I found some sources:

This is the send() in SendInput API

Global Const $KEYEVENTF_KEYUP = 2
Global Const $KEYEVENTF_UNICODE = 4
Global Const $INPUT_KEYBOARD = 1
Global Const $iInputSize = 28

Global Const $tagKEYBDINPUT = _
    'ushort wVk;' & _
    'ushort wScan;' & _
    'dword dwFlags;' & _
    'dword time;' & _
    'ulong_ptr dwExtraInfo'
    
Global Const $tagINPUT = _
    'dword type;' & _
    $tagKEYBDINPUT & _
    ';dword pad;' & _
    'dword pad;'

Global $hDll = DllOpen('user32.dll')
Global $sString = "abcde abcde abcde abcde abcde "

HotKeySet("{HOME}", "__send")

While 1
    
WEnd

func __send()
    _SendEx($sString)
EndFunc

DllClose($hDll)
Exit

Func _SendInputKB($iInputs, $pInputs, $iSize, $hDll = 'user32.dll')
    Local $aRet = DllCall($hDll, 'uint', 'SendInput', 'uint', $iInputs, 'ptr', $pInputs, 'int', $iSize)
    If @error Or Not $aRet[0] Then Return SetError(1, 0, False)
    Return SetError(0, 0, True)
EndFunc

Func _SendEx($sString)
    Local $tINPUTs, $pINPUTs, $iINPUTs
    Local $sStruct
    Local $iFlags, $iStrLen
    
    $iFlags = BitOR($KEYEVENTF_UNICODE, $KEYEVENTF_KEYUP)
    $iStrLen = StringLen($sString)
    
    $sStruct = ''
    
    For $i = 1 To $iStrLen * 2
        $sStruct &= $tagINPUT
    Next
    
    $tINPUTs = DllStructCreate($sStruct)
    $pINPUTs = DllStructGetPtr($tINPUTs)
    $iINPUTs = $iStrLen * 2
    
    For $i = 0 To $iStrLen-1
        Local $Temp = AscW(StringMid($sString, $i+1, 1))
        Local $iOffsetDown = $i * 8
        Local $iOffsetUp   = $i * 16
        
        DllStructSetData($tINPUTs, $iOffsetDown+1, $INPUT_KEYBOARD)
        DllStructSetData($tINPUTs, $iOffsetDown+3, $Temp)
        DllStructSetData($tINPUTs, $iOffsetDown+4, $KEYEVENTF_UNICODE)
        
        DllStructSetData($tINPUTs, $iOffsetUp+9, $INPUT_KEYBOARD)
        DllStructSetData($tINPUTs, $iOffsetUp+11, $Temp)
        DllStructSetData($tINPUTs, $iOffsetUp+12, $iFlags)
    Next
        
    _SendInputKB($iINPUTs, $pINPUTs, $iInputSize, $hDll)
EndFunc

And this is the script to use APIs in new thread:

Global Const $STATUS_PENDING = 0x103
Global Const $STILL_ACTIVE = $STATUS_PENDING

; ThreadID is @extended
;===============================================================================
;
; Function Name:   _Thread_Create
; Description::    Creates a thread
; Parameter(s):    see MSDN (lpThreadId is removed)
; Requirement(s):  minimum Win2000
; Return Value(s): see MSDN
;                  @extended will be ThreadID
;                  On error, @error will be set to 1
; Author(s):       Prog@ndy
;
;===============================================================================
;
Func _Thread_Create($lpThreadAttributes, $dwStackSize, $lpStartAddress, $lpParameter, $dwCreationFlags)
    Local $result = DllCall("kernel32.dll","ptr","CreateThread", "ptr", $lpThreadAttributes, "dword", $dwStackSize, "ptr", $lpStartAddress, "ptr", $lpParameter, "dword", $dwCreationFlags, "dword*", 0)
    Return SetError($result[0]=0,$result[6],$result[0])
EndFunc
;===============================================================================
;
; Function Name:   _Thread_Terminate
; Description::    Terminates a thread
; Parameter(s):    see MSDN
; Requirement(s):  minimum Win2000
; Return Value(s): see MSDN
;                  On error, @error will be set to 1
; Author(s):       Prog@ndy
;
;===============================================================================
;
Func _Thread_Terminate($hThread,$dwExitCode)
    Local $result = DllCall("Kernel32.dll","int","TerminateThread","ptr",$hThread,"dword",$dwExitCode)
    Return SetError($result[0]=0,0,$result[0])
EndFunc
;===============================================================================
;
; Function Name:   _Thread_Exits
; Description::    Exits the current thread
; Parameter(s):    see MSDN
; Requirement(s):  minimum Win2000
; Return Value(s): none
; Author(s):       Prog@ndy
;
;===============================================================================
;
Func _Thread_Exit($dwExitCode)
    DllCall("Kernel32.dll","none","ExitThread","dword",$dwExitCode)
EndFunc
;===============================================================================
;
; Function Name:   _Thread_GetExitCode
; Description::    retrieves ExitCode of a thread
; Parameter(s):    see MSDN
; Requirement(s):  minimum Win2000
; Return Value(s): see MSDN
;                  On error, @error will be set to 1
; Author(s):       Prog@ndy
;
;===============================================================================
;
Func _Thread_GetExitCode($hThread)
    Local $result = DllCall("Kernel32.dll","int","GetExitCodeThread","ptr",$hThread,"dword*",0)
    Return SetError($result[0]=0,0,$result[2])
EndFunc
;===============================================================================
;
; Function Name:   _Thread_GetID
; Description::    retrieves ThreadID of a thread
; Parameter(s):    see MSDN
; Requirement(s):  minimum Win2000
; Return Value(s): see MSDN
;                  On error, @error will be set to 1
; Author(s):       Prog@ndy
;
;===============================================================================
;
Func _Thread_GetID($hThread)
    Local $result = DllCall("Kernel32.dll","dword","GetThreadId","ptr",$hThread)
    Return SetError($result[0]=0,0,$result[0])
EndFunc
;===============================================================================
;
; Function Name:   _Thread_GetPriority
; Description::    retrieves priority of a thread
; Parameter(s):    see MSDN
; Requirement(s):  minimum Win2000
; Return Value(s): see MSDN
;                  On error, @error will be set to 1
; Author(s):       Prog@ndy
;
;===============================================================================
;
Func _Thread_GetPriority($hThread)
    Local $result = DllCall("Kernel32.dll","int","GetThreadPriority","ptr",$hThread)
    Return SetError($result[0]=0,0,$result[0])
EndFunc
;===============================================================================
;
; Function Name:   _Thread_SetPriority
; Description::    sets priority of a thread
; Parameter(s):    see MSDN
; Requirement(s):  minimum Win2000
; Return Value(s): see MSDN
;                  On error, @error will be set to 1
; Author(s):       Prog@ndy
;
;===============================================================================
;
Func _Thread_SetPriority($hThread,$nPriority)
    Local $result = DllCall("Kernel32.dll","int","SetThreadPriority","ptr",$hThread,"int",$nPriority)
    Return SetError($result[0]=0,0,$result[0])
EndFunc
;===============================================================================
;
; Function Name:   _Thread_Suspend
; Description::    suspends a thread
; Parameter(s):    see MSDN
; Requirement(s):  minimum Win2000
; Return Value(s): see MSDN
;                  On error, @error will be set to 1
; Author(s):       Prog@ndy
;
;===============================================================================
;
Func _Thread_Suspend($hThread)
    Local $result = DllCall("Kernel32.dll","int","SuspendThread","ptr",$hThread)
    Return SetError($result[0]=-1,0,$result[0])
EndFunc
;===============================================================================
;
; Function Name:   _Thread_Resume
; Description::    resumes a thread
; Parameter(s):    see MSDN
; Requirement(s):  minimum Win2000
; Return Value(s): see MSDN
;                  On error, @error will be set to 1
; Author(s):       Prog@ndy
;
;===============================================================================
;
Func _Thread_Resume($hThread)
    Local $result = DllCall("Kernel32.dll","int","ResumeThread","ptr",$hThread)
    Return SetError($result[0]=-1,0,$result[0])
EndFunc
;===============================================================================
;
; Function Name:   _Thread_Wait
; Description::    Waits for a thread to terminate
; Parameter(s):    $hThread  - Handle of thread
;                  $nTimeOut - [optional] Timeout (default: 0xFFFFFFFF => INFINTE)
; Requirement(s):  minimum Win2000
; Return Value(s): Success: true
;                  on TimeOut, @eeor will be set to -1
;                  On error, @error will be set to 1
; Author(s):       Prog@ndy
;
;===============================================================================
;
Func _Thread_Wait($hThread,$nTimeout=0xFFFFFFFF)
    Local $result = DllCall("Kernel32.dll", "dword", "WaitForSingleObject", "ptr", $hThread, "int", $nTimeout)
    If @error Then Return SetError(2,0,0)
    Switch $result[0]
        Case -1, 0xFFFFFFFF
            Return SetError(1,0,0)
        Case 0x00000102
            Return SetError(-1,0,1)
        Case Else
            Return 1
    EndSwitch
EndFunc


#include <WinAPI.au3>
$tagMSGBOXPARAMS = _
        "UINT cbSize;" & _
        "HWND hwndOwner;" & _
        "ptr hInstance;" & _
        "ptr lpszText;" & _
        "ptr lpszCaption;" & _
        "DWORD dwStyle;" & _
        "ptr lpszIcon;" & _
        "UINT_PTR dwContextHelpId;" & _
        "ptr lpfnMsgBoxCallback;" & _
        "DWORD dwLanguageId;"



; creates a struct for an Unicode-String
Func _UnicodeStruct($text)
    ; Prog@ndy
    Local $s = DllStructCreate("wchar[" & StringLen($text)+1 & "]")
    DllStructSetData($s,1,$text)
    Return $s
EndFunc

; retrieves Address of a function
Func _Thread_GetProcAddress($hModule,$sProcname)
    ; Prog@ndy
    Local $result = DllCall("kernel32.dll","ptr","GetProcAddress","hwnd",$hModule,"str",$sProcname)
    Return $result[0]
EndFunc

; Struct to send to the Thread
; in this case the MsgBox-Params
$MSGBOXPARAMS = DllStructCreate($tagMSGBOXPARAMS)
DllStructSetData($MSGBOXPARAMS,"cbSize",DllStructGetSize($MSGBOXPARAMS))
$stText = _UnicodeStruct("The messageBox in a separate thead!")
DllStructSetData($MSGBOXPARAMS,"lpszText",DllStructGetPtr($stText))
$stCaption = _UnicodeStruct("Caption")
DllStructSetData($MSGBOXPARAMS,"lpszCaption",DllStructGetPtr($stCaption))
DllStructSetData($MSGBOXPARAMS,"dwStyle",17) ; msgBox-style

; Use MessageBoxIndirect Unicode as ThreadProc
; normal MessageBox doesn't work, since CreateThread just has one possible parameter.
Local $hThreadProc = _Thread_GetProcAddress(_WinAPI_GetModuleHandle("user32.dll"),"MessageBoxIndirectW")

$hThread = _Thread_Create(0 ,0, $hThreadProc, DllStructGetPtr($MSGBOXPARAMS), 0)
$ThreadID = @extended

While MsgBox(69, "main", "Main script is not blocked" )=4
WEnd
MsgBox(0, "Thread-Info", "Handle: " & $hThread & @CRLF & "Thread-ID: " & $ThreadID)

_Thread_Wait($hThread)
$code = _Thread_GetExitCode($hThread)
MsgBox(0, 'Thread ended', "Threaded MsgBox returned: " & $code)

Is it possible to send() from a new thread?

I've tryied to write the first script (SendInput) in the second but don't work.

Thank-you

Edited by dijir
Posted (edited)

AutoIt does not support multithreading.

Have you tested the second script?

Edited by dijir
Posted (edited)

Have you tested the second script?

Richard Robertson is correct about multithreading and AutoIt. I have no idea why he felt a need to say that, but he's correct.

That doesn't mean you can't run some code in new thread.

For example I could write something like this from your script:

#include <WinAPI.au3>
#include <Memory.au3>

Func _SendInputKB_NewThread($iINPUTs, $pINPUTs, $iSize, $fFreeRes = False)

    If @AutoItX64 Then Return SetError(1, 0, 0) ; your code wouldn't work for x64
    Local $aCall
    Local Static $pSpace, $pSendInput

    If $pSpace Then
        If $fFreeRes Then
            _MemVirtualFree($pSpace, 0, $MEM_RELEASE)
            $pSpace = 0
            $pSendInput = 0
        EndIf
    Else
        $aCall = DllCall("kernel32.dll", "ptr", "GetProcAddress", _
                "handle", _WinAPI_GetModuleHandle("user32.dll"), _
                "str", "SendInput")
        If @error Or Not $aCall[0] Then Return SetError(1, 0, 0)
        $pSendInput = $aCall[0]
        $pSpace = _MemVirtualAlloc(0, 23, $MEM_COMMIT, $PAGE_EXECUTE_READWRITE)
    EndIf
    Local $tCodeBuffer = DllStructCreate("byte[23]", $pSpace)

    DllStructSetData($tCodeBuffer, 1, _
            "0x" & _
            "68" & Hex(BinaryMid($iSize, 1, 4)) & _              ; push cbSize
            "68" & Hex(BinaryMid($pINPUTs, 1, 4)) & _            ; push pInputs
            "68" & Hex(BinaryMid($iINPUTs, 1, 4)) & _            ; push nInputs
            "B8" & Hex(BinaryMid($pSendInput, 1, 4)) & _         ; mov eax, [SendInput]
            "FFD0" & _                                           ; call eax
            "C3" _
            )

    $aCall = DllCall("kernel32.dll", "handle", "CreateThread", _
            "ptr", 0, _
            "dword", 0, _
            "ptr", $pSpace, _
            "ptr", 0, _
            "dword", 0, _
            "dword*", 0)
    If @error Or Not $aCall[0] Then Return SetError(8, 0, "")

    Return $aCall[0] ; Thread handle (to be able to close it at some point)

EndFunc
Edited by trancexx
Posted (edited)

Thank-you so much trancexx, that's exactly what I need.

Another question, is it possible to use send() parameters with SendInput?

I've checked with api monitor and found this script SendInput uses:

Out:

SendInput(nInputs: 0x4E,pInputs:0x00FF3008,cbSize: 0x1C)
0x0000004E
0x00410D4F
autoit3.exe + 0x00010D4F
0x00000FA0
0x000003DC
0x00000000
EAX=0x00000004, EBX=0x008cf1f8, ECX=0x008cee08, EDX=0x7e37f140, ESI=0x7e360000, EDI=0x004a7f20, EFL=0x00000246, ESP=0x008cedfc, EBP=0x008cee3c  EAX=0x0000004e, EBX=0x008cf1f8, ECX=0x008cedf4, EDX=0x7c90e514, ESI=0x7e360000, EDI=0x004a7f20, EFL=0x00000246, ESP=0x008cee08, EBP=0x008cee3c  -1.#IND 14:25:47:435:291,4
301 - user32.dll - SendInput

and function autoit send() uses:

Out:

First SendInput
----------------------------------------------------------------
SendInput(nInputs: 0x1,pInputs:0x008CF6E0,cbSize: 0x1C)
0x00000001
0x00437851
autoit3.exe + 0x00037851
0x00000F0C
0x000008F0
0x00000000
EAX=0x00000000, EBX=0x00000051, ECX=0x7e370010, EDX=0x008cf6e0, ESI=0x00000000, EDI=0x008cf874, EFL=0x00000246, ESP=0x008cf6d4, EBP=0x004a7fa8  EAX=0x00000001, EBX=0x00000051, ECX=0x008cf6cc, EDX=0x7c90e514, ESI=0x00000000, EDI=0x008cf874, EFL=0x00000246, ESP=0x008cf6e0, EBP=0x004a7fa8  -1.#IND 18:35:26:121:891,0
11 - user32.dll - SendInput

Second SendInput:
----------------------------------------------------------------
SendInput(nInputs: 0x1,pInputs:0x008CF6E4,cbSize: 0x1C)
0x00000001
0x00437851
autoit3.exe + 0x00037851
0x00000F0C
0x000008F0
0x00000000
EAX=0x00000000, EBX=0x00000051, ECX=0x00000010, EDX=0x008cf6e4, ESI=0x00000071, EDI=0x008cf874, EFL=0x00000246, ESP=0x008cf6d8, EBP=0x004a7fa8  EAX=0x00000001, EBX=0x00000051, ECX=0x008cf6d0, EDX=0x7c90e514, ESI=0x00000071, EDI=0x008cf874, EFL=0x00000246, ESP=0x008cf6e4, EBP=0x004a7fa8  -1.#IND 18:35:26:120:242,2
17 - user32.dll - SendInput

How can I use parameters from SendInput send() to the script?

Source:

#include <WinAPI.au3>
#include <Memory.au3>

Global Const $KEYEVENTF_KEYUP = 2
Global Const $KEYEVENTF_UNICODE = 4
Global Const $INPUT_KEYBOARD = 1
Global Const $iInputSize = 28

Global Const $MEM_COMMIT=0x00001000
Global Const $PAGE_EXECUTE_READWRITE = 0x40

Global Const $tagKEYBDINPUT = _
    'ushort wVk;' & _
    'ushort wScan;' & _
    'dword dwFlags;' & _
    'dword time;' & _
    'ulong_ptr dwExtraInfo'
    
Global Const $tagINPUT = _
    'dword type;' & _
    $tagKEYBDINPUT & _
    ';dword pad;' & _
    'dword pad;'

Global $hDll = DllOpen('user32.dll')
Global $sString = "abcde abcde abcde abcde abcde "

HotKeySet("{HOME}", "__send")
HotKeySet("{END}", "exiter")

Run("notepad.exe")
WinWait("Untitled - Notepad")
WinActivate("Untitled - Notepad")
$timer2=''

While 1
    $timer=TimerInit()
    if $timer2='' Then
        $timer2=TimerInit()
    EndIf
    
    if TimerDiff($timer2)>1000 Then
        _SendEx("00")
        $timer2=''
    EndIf
WEnd

DllClose($hDll)
Exit

func __send()
    _SendEx("AA")
EndFunc

func exiter()
    Exit
EndFunc

Func _SendInputKB_NewThread($iINPUTs, $pINPUTs, $iSize, $fFreeRes = False)

    If @AutoItX64 Then Return SetError(1, 0, 0) ; your code wouldn't work for x64
    Local $aCall
    Local Static $pSpace, $pSendInput

    If $pSpace Then
        If $fFreeRes Then
            _MemVirtualFree($pSpace, 0, $MEM_RELEASE)
            $pSpace = 0
            $pSendInput = 0
        EndIf
    Else
        $aCall = DllCall("kernel32.dll", "ptr", "GetProcAddress", _
                "handle", _WinAPI_GetModuleHandle("user32.dll"), _
                "str", "SendInput")
        If @error Or Not $aCall[0] Then Return SetError(1, 0, 0)
        $pSendInput = $aCall[0]
        $pSpace = _MemVirtualAlloc(0, 23, $MEM_COMMIT, $PAGE_EXECUTE_READWRITE)
    EndIf
    Local $tCodeBuffer = DllStructCreate("byte[23]", $pSpace)

    DllStructSetData($tCodeBuffer, 1, _
            "0x" & _
            "68" & Hex(BinaryMid($iSize, 1, 4)) & _              ; push cbSize
            "68" & Hex(BinaryMid($pINPUTs, 1, 4)) & _            ; push pInputs
            "68" & Hex(BinaryMid($iINPUTs, 1, 4)) & _            ; push nInputs
            "B8" & Hex(BinaryMid($pSendInput, 1, 4)) & _         ; mov eax, [SendInput]
            "FFD0" & _                                           ; call eax
            "C3" _
            )

    $aCall = DllCall("kernel32.dll", "handle", "CreateThread", _
            "ptr", 0, _
            "dword", 0, _
            "ptr", $pSpace, _
            "ptr", 0, _
            "dword", 0, _
            "dword*", 0)
    If @error Or Not $aCall[0] Then Return SetError(8, 0, "")

    Return $aCall[0] ; Thread handle (to be able to close it at some point)

EndFunc

Func _SendInputKB($iInputs, $pInputs, $iSize, $hDll = 'user32.dll')
    Local $aRet = DllCall($hDll, 'uint', 'SendInput', 'uint', $iInputs, 'ptr', $pInputs, 'int', $iSize)
    If @error Or Not $aRet[0] Then Return SetError(1, 0, False)
    Return SetError(0, 0, True)
EndFunc

Func _SendEx($sString)
    Local $tINPUTs, $pINPUTs, $iINPUTs
    Local $sStruct
    Local $iFlags, $iStrLen
    
    $iFlags = BitOR($KEYEVENTF_UNICODE, $KEYEVENTF_KEYUP)
    $iStrLen = StringLen($sString)
    
    $sStruct = ''
    
    For $i = 1 To $iStrLen * 2
        $sStruct &= $tagINPUT
    Next
    
    $tINPUTs = DllStructCreate($sStruct)
    $pINPUTs = DllStructGetPtr($tINPUTs)
    $iINPUTs = $iStrLen * 2
    
    For $i = 0 To $iStrLen-1
        Local $Temp = AscW(StringMid($sString, $i+1, 1))
        Local $iOffsetDown = $i * 8
        Local $iOffsetUp   = $i * 16
        
        DllStructSetData($tINPUTs, $iOffsetDown+1, $INPUT_KEYBOARD)
        DllStructSetData($tINPUTs, $iOffsetDown+3, $Temp)
        DllStructSetData($tINPUTs, $iOffsetDown+4, $KEYEVENTF_UNICODE)
        
        DllStructSetData($tINPUTs, $iOffsetUp+9, $INPUT_KEYBOARD)
        DllStructSetData($tINPUTs, $iOffsetUp+11, $Temp)
        DllStructSetData($tINPUTs, $iOffsetUp+12, $iFlags)
    Next
    _SendInputKB_NewThread($iINPUTs, $pINPUTs, $iInputSize, False)
   ;_SendInputKB($iINPUTs, $pINPUTs, $iInputSize, $hDll)
EndFunc

Thanks

Edited by dijir
Posted (edited)

I found API that send async keys and solved my problem without need create new thread.

Source:

global const $KEYEVENTF_EXTENDEDKEY = 0x0001
global const $KEYEVENTF_KEYUP = 0x0002

HotKeySet("{HOME}", "press")
HotKeySet("^{HOME}", "press2")
HotKeySet("{END}", "exiter")

$key_to_send='a'

func press()
    $timer=TimerInit()
    DllCall('user32.dll', 'Int', 'keybd_event', 'byte', StringToBinary(StringUpper($key_to_send)), 'dword', '0x45' , 'dword', 0x0, 'int', 0)
    DllCall('user32.dll', 'Int', 'keybd_event', 'byte', StringToBinary(StringUpper($key_to_send)), 'dword', '0x45' , 'dword', $KEYEVENTF_KEYUP, 'int', 0)
    ConsoleWrite("Send "&$key_to_send&"("&StringToBinary(StringUpper($key_to_send))&") in "&Round(TimerDiff($timer), 2)&" ms"&@CRLF)
EndFunc

func press2()
    $timer=TimerInit()
    send($key_to_send)
    ConsoleWrite("Send "&$key_to_send&"("&StringToBinary(StringUpper($key_to_send))&") in "&Round(TimerDiff($timer), 2)&" ms"&@CRLF)
EndFunc

func exiter()
    exit
EndFunc

while 1
WEnd

Thank-you again trancexx, I like your code :)

Edited by dijir

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...