Jump to content
Sign in to follow this  
Markos

ControlSend by using only windows funcs

Recommended Posts

Markos

Hello,

I am trying to translate ControlSend to C#.

I have been successful with sending to windows that actually have some controls (like notepad has Edit) but not to windows that dont have any controls (such as games).

I know it HAS to be possible, since Autoits' ControlSend function CAN send to window without controls.

I have tried to find answer in AutoIt SourceCode that Jon released a long, long time ago, but i wasn't succesful.

So what I want to do:

Be able to use ControlSend feature without actually calling ControlSend. If we can accomplis this in AutoIt, I can translate it to C#.

If the window has controls, I can simply get the control handle and succesfully send to it:

#include <sendmessage.au3>
#Include <String.au3>

$hwnd = WinGetHandle('[CLASS:Notepad]')

$child = _GetTopChild($hwnd)

Global $WM_CHAR = 0x102

_SendMessage($Child, $WM_CHAR, '0x' & _StringToHex('d'), 0)

Func _GetTopChild($aParent)
    Local $last_top
    Local $hwnd_top = $aParent
    
    Do
        $last_top = $hwnd_top
        
        ConsoleWrite('last top: ' & $last_top & @CRLF)
        
        $hwnd_top = _GetTopWindow($last_top)
    Until $hwnd_top = 0
    
    Return $last_top
EndFunc

Func _GetTopWindow($hwnd)
    Local $ret = DllCall('user32.dll', 'hwnd', 'GetTopWindow', 'hwnd', $hwnd)
    Return $ret[0]
EndFunc

This does not work with windows that dont have any controls (like games). I have tried to search in the AutoIt source code and I think that autoit just Attaches to window and if it does not find any controls it sends to main window. (but it is probably wrong, since it does not work too)

This is my translation from AutoIt source code to autoit code, which does not work and needs some improvement.

#include <winapi.au3>
#include <WindowsConstants.au3>
#Include <String.au3>

$hwnd = WinGetHandle('[CLASS:Notepad]')
$vk = 0x44;D
$scan = _MapVirtualKey($vk, 0)


_WinAttach($hwnd, True)

$lparam = BitOR( 0x00000001, BitShift($scan, -16)  )
$ret = _WinAPI_PostMessage($hwnd, $WM_KEYDOWN, $vk, $lparam)
ConsoleWrite($ret & @CRLF)

Sleep(100);sleep between keyDown and keyUp

$lparam = BitOR( 0xC0000001, BitShift($scan, -16) )
$ret = _WinAPI_PostMessage($hwnd, $WM_KEYUP, $vk, $lparam)
ConsoleWrite($ret & @CRLF)

Func _MapVirtualKey($uCode, $uMapType)
    $ret = DllCall('user32.dll', 'int', 'MapVirtualKey', 'int', $uCode, 'int', $uMapType)
    ConsoleWrite('scan: ' & $ret[0] & @CRLF)
    Return $ret[0]
EndFunc


Func _WinAttach($hWnd, $fAttach)
    Local $myThread  = _GetCurrentThreadId();
    Local $curthread, $newThread
    
    If $fAttach Then
        
        $curthread = _GetWindowThreadProcessId(_GetForegroundWindow() , 0)
        _AttachThreadInput($myThread, $curthread, 1)

        If $hWnd Then
            $newThread = _GetWindowThreadProcessId($hwnd, 0)
            _AttachThreadInput($curthread, $newThread, 1)
            _AttachThreadInput($myThread, $newThread, 1)
        EndIf

    Else
        
    EndIf
    
EndFunc


Func _GetCurrentThreadId()
    Local $ret = DllCall('kernel32.dll', 'int', 'GetCurrentThreadId')
    ConsoleWrite('currentThreadID: ' & $ret[0] & @CRLF)
    Return $ret[0]
EndFunc

Func _GetWindowThreadProcessId($hwnd, $processid)
    Local $ret = DllCall('user32.dll', 'int', 'GetWindowThreadProcessId', 'hwnd', $hwnd, 'int', $processid)
    ConsoleWrite('GetWindowThreadProcessId: ' & $ret[0] & @CRLF)
    Return $ret[0]
EndFunc

Func _GetForegroundWindow()
    Local $ret = DllCall('user32.dll', 'int', 'GetForegroundWindow')
    ConsoleWrite('GetForegroundWindow: ' & $ret[0] & @CRLF)
    Return $ret[0]
EndFunc

Func _AttachThreadInput($idAttach, $idAttachTo, $fAttach)
    Local $ret = DllCall('user32.dll', 'int', 'AttachThreadInput', 'int', $idAttach, 'int', $idAttachTo, 'int', $fAttach)
    ConsoleWrite('AttachThreadInput: ' & $ret[0] & @CRLF)
    Return $ret[0]
EndFunc

So if ANYBODY has ANY idea, I would be glad to see it,

Markos


Share this post


Link to post
Share on other sites
enaiman

If you don't have any "identifiable" controls then ControlSend will behave exactly like Send: you need the application to be active at the moment of sending.

Have a look at "SendKeepActive" - it might help you.


SNMP_UDF ... for SNMPv1 and v2c so far, GetBulk and a new example script

wannabe "Unbeatable" Tic-Tac-Toe

Paper-Scissor-Rock ... try to beat it anyway :)

Share this post


Link to post
Share on other sites
Markos

This one quite didnt help since i dont want to use controlsend as I wrote before.


Share this post


Link to post
Share on other sites
enaiman

Well - you have plenty of time to think about. Good luck.


SNMP_UDF ... for SNMPv1 and v2c so far, GetBulk and a new example script

wannabe "Unbeatable" Tic-Tac-Toe

Paper-Scissor-Rock ... try to beat it anyway :)

Share this post


Link to post
Share on other sites
Markos

Well, I am sorry to be rude. If you dont want to help, dont post anything please.

You obviously didnt read my post. ControlSend without control specified does not behave exactly like send. It just behaves exactly like controlsend witjout control specified. But thanx for your advices.


Share this post


Link to post
Share on other sites
Authenticity

What you've posted is correct but is not what you expect it to do because ControlSend will do the same (almost, apart from VK_* processing etc..). Take these two snippet for example:

$hwnd = WinGetHandle('[CLASS:Notepad]')
ControlSend($hwnd, '', '', '123')oÝ÷ Ù©Ýjëh×6$hEdit = ControlGetHandle('[CLASS:Notepad]', '', 'Edit1')
ControlSend($hEdit, '', '', '123')

The latter is working as expected (assuming on both, the window is minimized or hidden) because the window handle on PostMessage is the edit control's while the former is trying to send to the parent window handle which may not work on your example or ControlSend.

Sending to games without controls is implemented using keybd_event() or SendInput() if the game window is the foreground window and implemented using PostMessage() otherwise. In short, the code you posted and ControlSend will produce the input almost identically.

Share this post


Link to post
Share on other sites
Markos

Justo clarify what I wanted to say:

Some games dont have ANY controls. For example Guild Wars.

$hWnd = WinGetHandle('Guild Wars')
ControlSend($hwnd, '', '', 'My Text to send')

I am 100 % sure that this works with guild wars on non-active window (my friend told me that i works for WoW too, but I didnt try it). It does not have any controls, so I am not specifying any control as Authenticity said nor I am sending text to active window as enaiman said.

I tried to send using Sendmessage or Postmessage directly to the hwnd but it does not work. But it DOES work (100 %) with controlsend (as I wrote in 1st post and you dont believe me). So I believe autoit does "something" jsut before the postmessage, and I dont know what exactly. The autoit source code is huge so its not so easy to find.

Thats what I wanted to get help with.

So one more time: I am hundred percent sure that sending to GW using ControlSend withou using any control to non-active window works.


Share this post


Link to post
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
Sign in to follow this  

×