Jump to content

[SOLVED] SendInput (Like send() function) in new thread


 Share

Recommended Posts

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
Link to comment
Share on other sites

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
Link to comment
Share on other sites

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
Link to comment
Share on other sites

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
Link to comment
Share on other sites

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
 Share

  • Recently Browsing   0 members

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