Sign in to follow this  
Followers 0
RandomGuest

Killing the hidden AutoIt Window

10 posts in this topic

#1 ·  Posted (edited)

Is the "AutoIt v3" window necessary?

It's created and hidden every time my scripts start, comprising of a text area, presumably for debug output.

This'll reveal it:

WinSetState("AutoIt v3", "", @SW_SHOW)

Question: Is it possible to prevent the hidden window from being created, or to destroy it without exiting my script?

I stumbled across this when I was writing an app, spawned by a service, that moves between desktops.

It appears on Winsta0\Winlogon if no one's logged in, or Winsta0\Default if someone is.

It can get the handle to the Station Winsta0, and if run as localsystem, it can even get the handle to Winlogon, (the welcome screen).

But it can't bind to the new desktop if it has a window open (GetLastError returns 170: in use).

The C source here works perfectly when spawned with localsystem credentials (service, schedule, psexec, etc...)

http://didierstevens.wordpress.com/2006/08...ith-utilmanexe/

For the curious, here's my AutoIt translation.

;Disable the tray icon
AutoItSetOption("TrayIconHide", 1)
AutoItSetOption("TrayMenuMode", 1)

Dim $station_opened = -1
Dim $desktop_opened = -1
Dim $result = ""
Dim $flags = ""

open_station()

If ($station_opened <> -1) Then
  $result = DllCall('user32.dll', 'int', 'SetProcessWindowStation', 'hwnd', $station_opened)
  If (@error = 0 AND $result[0] <> 0) Then MsgBox(4096, "Test", "Opened station.")
EndIf

If ($station_opened <> -1) Then
  open_desktop()

  If ($desktop_opened <> -1) Then
;This jumps to the chosen desktop, so long as there aren't any windows on this one
    $result = DllCall('user32.dll', 'int', 'SetThreadDesktop', 'hwnd', $desktop_opened)

    If (@error = 0 AND $result[0] <> 0) Then
      MsgBox(4096, "Test", "Opened desktop.")
    Else
      $result = DllCall('Kernel32.dll', 'int', 'GetLastError')
      MsgBox(4096, "Test", "Failed to switch desktops: " & $result[0])
    EndIf
  EndIf
EndIf

close_desktop()
close_station()


;----------------

Func open_station()
;This comes out to 895
  $flags = BitOR(4,32,8,1,256,64,2,512,16)
;WINSTA_ACCESSCLIPBOARD  4
;WINSTA_ACCESSGLOBALATOMS  32
;WINSTA_CREATEDESKTOP      8
;WINSTA_ENUMDESKTOPS        1
;WINSTA_ENUMERATE        256
;WINSTA_EXITWINDOWS     64
;WINSTA_READATTRIBUTES    2
;WINSTA_READSCREEN      512
;WINSTA_WRITEATTRIBUTES 16

;OpenWindowStation gets the handle to the primary display
  $result = DllCall('user32.dll', 'hwnd', 'OpenWindowStation', 'str', 'WinSta0', 'int', 1, 'int', $flags)
  If (@error = 0 AND $result[0] <> 0) Then $station_opened = $result[0]
EndFunc


Func open_desktop()
;This comes out to 511
  $flags = BitOR(4,2,64,8,32,16,1,256,128)
;DESKTOP_CREATEMENU      4
;DESKTOP_CREATEWINDOW      2
;DESKTOP_ENUMERATE       64
;DESKTOP_HOOKCONTROL        8
;DESKTOP_JOURNALPLAYBACK   32
;DESKTOP_JOURNALRECORD   16
;DESKTOP_READOBJECTS        1
;DESKTOP_SWITCHDESKTOP  256
;DESKTOP_WRITEOBJECTS    128

;Comment/Uncomment one of these dllcalls...
;OpenDesktop gets a specific virtual desktop: Default (where explorer lives) or Winlogon (Welcome screen)
;$result = DllCall('user32.dll', 'hwnd', 'OpenDesktop', 'str', 'Default', 'int', 0, 'int', 0, 'int', $flags)

;OpenInputDesktop gets whatever desktop is in use
  $result = DllCall('user32.dll', 'hwnd', 'OpenInputDesktop', 'int', 0, 'int', 0, 'int', $flags)

  If (@error = 0 AND $result[0] <> 0) Then $desktop_opened = $result[0]
EndFunc


Func close_desktop()
  If ($desktop_opened <> -1) Then
    MsgBox(4096, "Test", "Closing desktop handle (" & $desktop_opened & ")...")
    DllCall('user32.dll', 'none', 'CloseWindowStation', 'hwnd', $desktop_opened)
  EndIf
EndFunc


Func close_station()
  If ($station_opened <> -1) Then
    MsgBox(4096, "Test", "Closing station handle (" & $station_opened & ")...")
    DllCall('user32.dll', 'none', 'CloseWindowStation', 'hwnd', $station_opened)
  EndIf
EndFunc

Yeah I can, and probably will, put the desktop code in the launcher service.

Having a window I didn't create myself irks me. And with callbacks, it's nearly possible to make an AutoIt service...

This is my favorite example of a C service; just add a payload. :)

http://www.muukka.net/programming/service.html

BTW I haven't tried, but it's entirely possible that once an AutoIt script is running on the Winlogon desktop, it could get the controls of the user/pass textfields and automate logins!

Edited by RandomGuest

Share this post


Link to post
Share on other sites



#3 ·  Posted (edited)

Maybe you can try the winkill() or winclose() functions?

Nah. That'd be too easy. If the window gets a WM_CLOSE message, its internally coded to destroy and exit.

Come to think of it, the tray icon's not really gone either, it's only hidden... :)

Edited by RandomGuest

Share this post


Link to post
Share on other sites

Hmm your right. maybe report it as a bug or put in a request to remove it.

It's no bug, and likely to never get removed.

Common sense plays a role in the basics of understanding AutoIt... If you're lacking in that, do us all a favor, and step away from the computer.

Share this post


Link to post
Share on other sites

Known window... per help

$a = AutoItWinGetTitle()

one use

; check if process already exists
$g_szVersion = "My XP Cleaner"
If WinExists($g_szVersion) Then
    MouseMove(500,5)
    MsgBox(262208, "* NOTE * ", "*XPClean Menu* was already running  ", 5)
    SoundPlay ($Sound_lnk,1)
    Exit ; It's already running
EndIf
AutoItWinSetTitle($g_szVersion)

8)


NEWHeader1.png

Share this post


Link to post
Share on other sites

Known window... per help

It's no bug, and likely to never get removed.

I understand a number of scripts would break if that function were removed, but couldn't something like this be added the same way as the vista "#requireadmin" directive?

#noAutoItWindow

#noTray

Share this post


Link to post
Share on other sites

I understand a number of scripts would break if that function were removed, but couldn't something like this be added the same way as the vista "#requireadmin" directive?

#noAutoItWindow

#noTray

No, I don't know exactly why it's added to be honest... if I had to guess... It's the interpreter's window... so I can't see it going away...

It has a classname why not just port all class AutoIt v3 GUI along with what you need?


Common sense plays a role in the basics of understanding AutoIt... If you're lacking in that, do us all a favor, and step away from the computer.

Share this post


Link to post
Share on other sites

#9 ·  Posted (edited)

It has a classname why not just port all class AutoIt v3 GUI along with what you need?

???

If you mean move that window to the new desktop, GUI's can't move, and once a process creates its first GUI, the process is tied to that desktop.

It's a MS limitation.

I think if the GUI were destroyed, the process would be mobile again.

If you mean write my app in another language, my app reads ini files, talks to the network, regex tests strings, spawns msg&input dialogs, and edits the registry.

AutoIt does all those things very easily.

And the only things stopping it from being able to jump to the Welcome screen, are the automatically generated window and tray.

I've seen several requests for login automation. Switching desktops is the key to doing so (You can only find windows/controls within your current desktop).

Removing the requirement that one compile a custom C service to spawn the script, would be a significant boon to AutoIt.

...and there are numerous requests to shed GUI altogether from the command-line guys.

If you mean, fork AutoIt's source code to not include the GUI myself. That's a bit over my head. :)

If you mean subclassing or something to override the window's destroy code with a func of my own... or a hook that keeps the script alive mid-closing?

New territory. I'd need an example.

I'm still hoping modifying the language won't be necessary and that there's a magic DllCall to destroy a window without closing it. :)

...

Grr

The "#NoTrayIcon" directive does exist, but it only hides the tray? :P

TraySetState(2) claims to 'Destroy/Hide' in the doc. Those are two very different actions. Typo?

Edited by RandomGuest

Share this post


Link to post
Share on other sites

#10 ·  Posted (edited)

A double post, but a separate thought...

In all the n00b C window examples I've seen in the past, there's been an indefinite loop that listens for and repeatedly runs a function that interprets window events with a switch statement.

switch (message) {
  //Various events like WM_COMMAND for clicks...

  case (WM_CLOSE):
    //Something politely asked that this close
    //Offer to save work...

    //Tell user32.dll to deallocate this GUI
    DestroyWindow( this_windows_hwnd );
    return 0;
  }

  case(WM_DESTROY):
  {
    //The user32.dll finished DestroyWindow() and sent a WM_DESTROY message
    //Or something rudely said die
    //Quit the application
    PostQuitMessage(0);
  }
}

For example, ctrl-alt-del to end a process first sends WM_CLOSE, waits a bit for the app and user32.dll to clean up and die on their own; then it sends WM_DESTROY if it still isn't dead.

In either case, polite or rude, PostQuitMessage(0) executes and the app exits.

I'd need to somehow call DestroyWindow() then intercept the WM_DESTROY that comes as a result.

Intercepting all WM_DESTROYs would be bad practice. :P

Or find a function that does the exact same thing as DestroyWindow() without sending the message at all. Like the dispose method in Java.

If one were to edit the AutoIt source code, wrapping the PostQuitMessage() in an if statement checking a global var (maybe wrapping some of WM_CLOSE's case too, leaving the user32 call), and adding AutoItWinExitOnClose(boolean) would do the trick.

;Hypothetical way to destroy the window without it exiting altogether
AutoItWinExitOnClose(False)

WinClose(AutoItWinGetTitle())

;Reset the variable in case the window is miraculuosly recreated
AutoItWinExitOnClose(True)

Darn it. How can finding that method, recompiling the interpreter, be easier than finding than a non-invasive way? :)

Of course, this approach assumes the window's initial creation can't be turned off with a directive, and that its absence later won't wreck anything.

Edit, to avoid triple-posting:

It's absence would indeed wreck things.

Looking through the AutoIt source, I can see why they exist now.

The tray icon's not a problem after all. "Hiding" the icon essentially deletes it, and "#NoTrayIcon" does, in fact, prevent it from being created.

In other contexts, hide means setVisible(false), and destroy means erase from existence.

Here it means remove the icon from the tray, and keep the struct that holds its state in case it gets put back later.

The hidden window is the parent of everything so it can receive messages to/from the children and destroy them all.

It'd take significant rewrite to remove that: making any new windows parentless and adding their handles to a global array with a means to remove themselves from the array when they're destroyed.

I suggested a less extreme workaround patch in the bugs feature request forum. Feedback about it might appear there.

http://www.autoitscript.com/forum/index.ph...345#entry388493

Conclusion: Use a separate GUI-less launcher (scheduled utility or service), running as localsystem, that switches itself to an available desktop, then spawn your AutoIt app from it.

Edited by RandomGuest

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  
Followers 0