Jump to content

WinAPI_CreateDesktop (CommandSend to GUI)


Recommended Posts

Hello,

I'm trying to communicate with a GUI over a secondary desktop thread, but It doesn't seem to pick up any commands sent to the GUI even though it responds perfectly on the main desktop. Any ideas as to how I can go about this?

Link to comment
Share on other sites

Select,

Nice username :P

Right off the top of my head, I'd say that the simplest approach would be change desktop threads and set the X,Y to something offscreen until you've finished then reset everything back to normal. When you say communicate do you mean sending commands via ControlSend or what?

I'd rather read code then chit chat :thumbsup:

#include "WinAPIEx.au3" ; Get WinAPIEx from -> http://www.autoitscript.com/forum/topic/98712-winapiex-udf/
#include "APIConstants.au3" ; Included with WinAPIEx
#include <Array.au3> ; Included with AutoIt3

Opt("WinTitleMatchMode", 2) ; 2 = Match any substring in the windows title, 3 = Exact title match

Global Enum $nStation, $nDesktop, $nWIndow ; Used for array index identification
Global $aWindow = _GetWindowHwndEx("Firefox")

If IsArray($aWindow) Then
    ConsoleWrite(">Station: " & $aWindow[$nStation] & @CRLF) ; Low level station handle
    ConsoleWrite(">Desktop: " & $aWindow[$nDesktop] & @CRLF) ; This is not the handle to the desktop window.
    ConsoleWrite(">Window: " & $aWindow[$nWIndow] & @CRLF) ; The window handle as returned by WinGetHandle
    If _WinAPI_SwitchDesktop($aWindow[$nDesktop]) Then
        ConsoleWrite("-->Switched Desktop Successfully!" & @CRLF)
        Dim $aPos = WinGetPos($aWindow[$nWIndow])
        WinMove($aWindow[$nWIndow], "", -5000, -5000)
        Sleep(3000) ; Send Commands
        WinMove($aWindow[$nWIndow], "", $aPos[0], $aPos[1]) ; Reset to original position
    EndIf
Else
    ConsoleWrite("!>Failed to find the specified window!" & @CRLF)
EndIf

Func _GetWindowHwndEx($sTitle, $sText = "")
    ; Author: Decipher
    Local $aStations = _WinAPI_EnumWindowStations(), $aDesktops, $hStation, $hDesktop, $aWindows, $hWindow = WinGetHandle($sTitle, $sText)
    If Not $hWindow Then Return SetError(1, 0, "")
    For $n = 1 To $aStations[0] Step 1
        $hStation = _WinAPI_OpenWindowStation($aStations[$n],  $WINSTA_ENUMDESKTOPS)
        If $hStation Then
            $aDesktops = _WinAPI_EnumDesktops($hStation)
            For $i = 1 To $aDesktops[0] Step 1
                $hDesktop = _WinAPI_OpenDesktop($aDesktops[$i], BitOR($DESKTOP_SWITCHDESKTOP, $DESKTOP_READOBJECTS))
                If $hDesktop Then
                    $aWindows = _WinAPI_EnumDesktopWindows($hDesktop, True) ; Change the boolean to false to search through hidden windows
                    For $d = 1 To $aWindows[0][0] Step 1
                        If $aWindows[$d][0] = $hWindow Then
                            Local $aRet[3] = [$hStation, $hDesktop, $hWindow]
                            Return SetError(0, 0, $aRet)
                        EndIf
                    Next
                EndIf
            Next
        EndIf
    Next
    Return SetError(2, 0, "")
EndFunc

Edit - Added comment on EnumDesktopWindows line.

Anonymous

Edited by Decipher
Spoiler

censored.jpg

 

Link to comment
Share on other sites

Good morning,

Thanks for the reply. I'm using this example snippet from the WinAPI, here it is.
 

#Include <APIConstants.au3>
#Include <WinAPIEx.au3>

Opt('MustDeclareVars', 1)

Global $hDesktop, $hPrev, $pText, $tProcess, $tStartup

; Retrieve a handle to the current desktop and create a new desktop named "MyDesktop"
$hPrev = _WinAPI_GetThreadDesktop(_WinAPI_GetCurrentThreadID())
$hDesktop = _WinAPI_CreateDesktop('MyDesktop', BitOR($DESKTOP_CREATEWINDOW, $DESKTOP_SWITCHDESKTOP))
If Not $hDesktop Then
    MsgBox(16, 'Error', 'Unable to create desktop.')
    Exit
EndIf

; Switch to the newly created desktop
_WinAPI_SwitchDesktop($hDesktop)

; Run "calc.exe" on "MyDesktop" and wait until a process will not be closed by user
$pText = _WinAPI_CreateString('MyDesktop')
$tProcess = DllStructCreate($tagPROCESS_INFORMATION)
$tStartup = DllStructCreate($tagSTARTUPINFO)
DllStructSetData($tStartup, 'Size', DllStructGetSize($tStartup))
DllStructSetData($tStartup, 'Desktop', $pText)
If _WinAPI_CreateProcess('', @SystemDir & '\calc.exe', 0, 0, 0, $CREATE_NEW_PROCESS_GROUP, 0, 0, DllStructGetPtr($tStartup), DllStructGetPtr($tProcess)) Then
    ProcessWaitClose(DllStructGetData($tProcess, 'ProcessID'))
EndIf

; Switch to previous desktop and close "MyDesktop"
_WinAPI_SwitchDesktop($hPrev)
_WinAPI_CloseDesktop($hDesktop)

; Free memory allocated for a string
_WinAPI_FreeMemory($pText)

The desktop gets created successfully, I can switch over, etc. However the tricky part is that I'm automating an installation package, and so I want it to be as non-influencing to the users current desktop experience. I have multiple packages being simultaneously being installed. I realize i made a mistake with the topic title, it was ControlSend that I'm using to automate key presses to the GUI. I'm not sure if it's possible to do this without interrupting the users experience, unless there is another way of making the window hidden before WM_Create gets initiated, I'm guessing through SetWindowsHookEx, however anti-viruses tend to pick this up as malicious I find.

Forgive me, as I'm a beginner programmer. Cheers guys.

Link to comment
Share on other sites

@Decipher,

Very nice work with that code :)

@Select,

It seems that what Decipher posted should do what you need. Use his code to get the window handle. Use ControlGetHandle with the handle that you just got using Decipher's code, ControlSend or SendMessage (likely the latter, look in the helpfile under "my script does not work on a locked workstation - same concept here)

I don't have time to code it right now, but I'll do it later when I get home if there is no solution yet :)

Edited by 0xdefea7
Link to comment
Share on other sites

When testing my code earlier I noticed that the desktop handle returned by _GetWindowHwndEx was different than what is returned by this line of code:

$hPrev = _WinAPI_GetThreadDesktop(_WinAPI_GetCurrentThreadID())

I was going to compare the two and if they were different then switch back to the original desktop like your doing now. Although the desktops(Just one) are the same, the handles aren't matching on my system.... It appears as if the desktop thread handle and standard desktop handle are interchangable but I don't know how to compare the two!

Select,

You mentioned SetWindowsHookEx, how would you use it?

0xdefea7,

Any ideas on how to move an already existent process onto another desktop?

Edit - Perfectionist ;)

Anonymous

Edited by Decipher
Spoiler

censored.jpg

 

Link to comment
Share on other sites

Are you on the same desktop when you are testing this code? Or are your running firefox on another desktop, then running your function?

For the second question, I have no idea. The desktop name is passed in the startupinfo struct. I could imagine that if there was a way to change that dynamically, it might be possible. Thanks for giving me something to pass my free time today :) To Google!

Edited by 0xdefea7
Link to comment
Share on other sites

0xdefea7,

I'm testing this code with the default desktop(Winsta0) only but seeing how enumerate the stations and so forth it should work as advertised with multiple stations, desktop, etc.

Are you on the same desktop when you are testing this code? Or are your running firefox on another desktop, then running your function?

For the second question, I have no idea. The desktop name is passed in the startupinfo struct. I could imagine that if there was a way to change that dynamically, it might be possible. Thanks for giving me something to pass my free time today :) To Google!

http://msdn.microsoft.com/en-us/library/ms686232(VS.85).aspx - SetProcessWindowStation

Let's get something working. Ready? Go.

Your Welcome!

Anonymous

Spoiler

censored.jpg

 

Link to comment
Share on other sites

Thanks a lot for the help guys, when I get home I'll do some testing :)

@ Decipher - You mentioned how I would use SetWindowsHookEx. I've never tried this, specially to hook a window before it's created, but my guess would be to use WH_CBT and then HCBT_CREATEWND to then change the properties before it's created.

Cheers!

Link to comment
Share on other sites

Zooovel2,

If your concerned with window manipulation on another desktop besides the default desktop then I would recommend that you use the code above. I've only ever tried ControlSend or other methods in the context of the default desktop. I'd think that you'd have to switch to the desktop that of which the application is currently running on before sending commands to it's interface based on the problems the OP is experiencing. Use the UDF above to get the window, desktop, and station handles and then switch to that desktop using its handle before utilizing ControlSend or other methods. I don't know of a way around this. If you can't figure it out then show some code and I'd be glad to assit.

Note: Software like VirtualWin only virtualize multiple desktops, meaning they don't use Windows API to create their desktops. This topic is concerned with Microsoft's user interactive work stations(desktops created using native API) and their properties. If your using such software then this topic is irrevelant to your question(s).

Anonymous

Spoiler

censored.jpg

 

Link to comment
Share on other sites

I'm tempted to test the Desktop switch code to see what the difference is between desktops and secondary logons or sessions.. but once I'm switched over to a new desktop, how do I switch back..  what exactly changes? And is there something in windows I can use to manually switch? Thx

Link to comment
Share on other sites

Ascend4nt,

There is no way to switch desktops using the WIndows Interface that I know of because there isn't anything to switch, only one interactive desktop is created on the "Winsta0" station at login. I edited the example code above to hopefully elaborate how to create a desktop, switch desktops, and switch back to the previous desktop. Just run the code.

#Include <APIConstants.au3>
#Include <WinAPIEx.au3>

Opt('MustDeclareVars', 1)

; Retrieve a handle to the current desktop and create a new desktop
Global $hDefaultDesktop = _WinAPI_GetThreadDesktop(_WinAPI_GetCurrentThreadID())

Global $hNewDesktop = _CreateDesktop("SecondDesktop")
If Not $hNewDesktop Then
    MsgBox(16, 'Error', 'Unable to create desktop.')
    Exit
EndIf

; Switch to the newly created desktop
_WinAPI_SwitchDesktop($hNewDesktop)

Dim $nPID = _LaunchProcessOnDesktop("SecondDesktop", @SystemDir & '\calc.exe')

Sleep(2000)

ProcessClose($nPID)

; Switch to previous desktop
_WinAPI_SwitchDesktop($hDefaultDesktop)
_WinAPI_CloseDesktop($hNewDesktop) ; Close the other one


Func _CreateDesktop($sName)
    Return _WinAPI_CreateDesktop($sName, BitOR($DESKTOP_CREATEWINDOW, $DESKTOP_SWITCHDESKTOP))
EndFunc
Func _LaunchProcessOnDesktop($sDesktopName, $sAppName, $sCommandLine = "")
    Local $pText = _WinAPI_CreateString($sDesktopName)
    Local $tProcess = DllStructCreate($tagPROCESS_INFORMATION)
    Local $tStartup = DllStructCreate($tagSTARTUPINFO)
    DllStructSetData($tStartup, 'Size', DllStructGetSize($tStartup))
    DllStructSetData($tStartup, 'Desktop', $pText)
    If _WinAPI_CreateProcess($sAppName, $sCommandLine, 0, 0, 0, $CREATE_NEW_PROCESS_GROUP, 0, 0, DllStructGetPtr($tStartup), DllStructGetPtr($tProcess)) Then
        Return DllStructGetData($tProcess, 'ProcessID')
    EndIf
    _WinAPI_FreeMemory($pText)
EndFunc

I'd think of stations as objects with properties such as interactive or non-interactive. Desktops inherit their properties from stations. When you switch desktops its like changing an environment variable so when an applications attempts to create a window it expands that variable to mean the current desktop and inherits its privileges and etc from that desktop before creating any GUI. If the inheritted properties don't contain the interactive dwflag then the process dosen't have privileges to interact with the user furthermore if the current desktop thread doesn't belong to that application then its operations are hidden the same. Thats the basics of it anyway. ;)

One more thing which ya probably already know is that processes inherit their access rights and etc. from the parent process. They inherit the window station and desktop of their parent aswell. So if a service process created on a non-interactive station starts a child process than that process won't have access to the desktop either. You can distinguish between service processes and standard process I quess but they work very similar when it comes to stations and desktops prior to Vista. I think theres more restrictions imposed on services now but if you use WinAPI to create a process on a seperate station even services should be able to bypass this, dunno.

Anonymous

Edited by Decipher
Spoiler

censored.jpg

 

Link to comment
Share on other sites

decipher, that's a pretty cool effect.  I'm sorta confused with what exactly is happening.. I read what you said but still need time to digest it.  Dang there's always some feature I didn't know existed in Windows...

Link to comment
Share on other sites

  • 5 months later...

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...