Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

How to retrieve LAST active window?


  • Please log in to reply
26 replies to this topic
r0lZ
  • Members
  • 192 posts
  • Last active: Feb 11 2012 11:19 PM
  • Joined: 21 Apr 2007
My script needs to determine the active window.
Since the function is launched via the tray menu, the taskbar is activated before I have a chance to retrieve the active window title or ID.
Therefore, I need to retrieve the ID of the window that was active just before the taskbar was clicked.

When there are no Always On Top windows, it's easy.
I can retrieve the topmost window ID with something like that:
LastActiveId()
{
	WinGet, idlist, list,,, Program Manager
	Loop, %idlist%
	{
		this_id := idlist%A_Index%
		WinGetClass, this_class, ahk_id %this_id%
		if this_class != Shell_TrayWnd
			return %this_id%
	}
	return 0
}
... or I can send !{ESC} to deactivate the toolbar and retrieve the active window ID with WinGet id, ID, A.

But when there are Always On Top windows, neither of these methods work.
LastActiveId() returns always the Always On Top window, even if that window was not active.
The !{ESC} key reactivates always the last window that was activated except any Always On Top window. Therefore, if the Always On Top window was active, another window is activated, and a wrong ID is retrieved.

I have tried to toggle the Always On Top flag of all windows before sending the !{ESC} key (and restore it after):
LastActiveID()
{
	WinGet, idlist, list,,, Program Manager
	numtops = 0
	Loop, %idlist%
	{
	    this_id := idlist%A_Index%
	    WinGetClass, this_class, ahk_id %this_id%
		if this_class != Shell_TrayWnd
		{
			WinGet, exstyle, ExStyle, ahk_id %this_id%
			if (ExStyle & 8)  ; WS_EX_TOPMOST: Window is always on top
			{
				numtops += 1
				tops%numtops% := this_id
				WinSet, AlwaysOnTop, off, ahk_id %this_id%
			}
		}
	}
	send !{ESC}
	WinGet, id, ID, A
	loop %numtops%
	{
		this_id := tops%a_index%
		WinSet, AlwaysOnTop, on, ahk_id %this_id%
	}
	return id
}
No luck! The !{ESC} key still skips the windows that were Always On Top at the time the function is launched, and activates again a wrong window.

Another method that should work (although I haven't tested it) is to use a timer to constantly record the active window ID in a global variable, except when the taskbar is active, and use the recorded ID, but I would like to avoid using a timer for such a (normally) trivial operation.

Hence my question. How can I retrieve the ID of the window that was really active before the tray icon was clicked, regardless if its Always On Top state?
r0lZ

Grumpy
  • Guests
  • Last active:
  • Joined: --
I am sorry for the stupid question, but why don't you use a hotkey instead?

Otherwise, the timer solution is what came to mind, but I see you already thought of this.

Possible solution:

List: Retrieves the unique ID numbers of all existing windows [...] Windows are retrieved in order from topmost to bottommost (according to how they are stacked on the desktop).



r0lZ
  • Members
  • 192 posts
  • Last active: Feb 11 2012 11:19 PM
  • Joined: 21 Apr 2007
I don't want to be forced to use an hotkey for each action available in the tray menu of my script, because there is a big risk of conflicts, and I prefer to use some functions with the mouse. Some actions have a configurable hotkey, and, in this case, of course, there is no problem.

Although it's not easy to do because there are a lot of special cases to avoid, I've just finished programming my app with the timer trick. I don't like that too much, but it works, and the timer method has the advantage that the tray menu can be configured dynamically. I can now disable the useless menu entries when there is no active window.

Thanks for the WinGet's List hint, but, of course, I've tried that also. Unfortunately, that doesn't work neither, as the Always On Top windows are also on top of this list, even when they are not active. (BTW, the Windoze taskbar is always at the very top of the list, but of course, it is easy to skip it.)
It seems that "WinGet, id, ID, A" retrieves always the first window in that list that has NOT the Always On Top property. It's useful in many cases, but unfortunately not for me! :(

Thanks anyway!
r0lZ

Grumpy
  • Guests
  • Last active:
  • Joined: --
A stupid workaround would be to have a hotkey to capture the currently active window ID: the user would have to hit the hotkey, then to go to the tray menu.
OK, that's stupid... :o)

r0lZ
  • Members
  • 192 posts
  • Last active: Feb 11 2012 11:19 PM
  • Joined: 21 Apr 2007
Hum, yes, that's not very smart! :p

Currently, my method works fine, although the counter consumes some CPU power and memory (as I need several global variables) but it's not really a problem. Even it it's not an elegant way to do it, I'm happy with this method.
r0lZ

majkinetor
  • Moderators
  • 4512 posts
  • Last active: May 20 2019 07:41 AM
  • Joined: 24 May 2006
I have the very same problem.

My app racts on hotkey and on tray click. As it operates depending on the active window in time when hotkey is pressed, I lose focus to id when tray is accessed. I use currently !ESC but it has side efects that right click doesn't work on the menu I show. This doesn't happen when I remove !ESC.

So, I seek the way to get the last active window without changing focus.

I don't have any problems with !ESC though and always on top wins.
Posted Image

SHiLLySiT
  • Members
  • 12 posts
  • Last active: May 19 2007 04:06 AM
  • Joined: 17 May 2007
You could always set up the menu that you have in the tray as a right click pop up menu. I have this set up in one of my programs.

DetectHiddenWindows, on ;just a precaution

+RButton:: ;hold shift and right click.
WinGetActiveTitle, win ;get current title
MouseGetPos, mousex, mousey ;get current mouse position
Menu, desk, Add, MenuItem, Handler
Menu, desk, Show, %mousex%, %mousey%
return

Handler:
msgbox title %win%
return


majkinetor
  • Moderators
  • 4512 posts
  • Last active: May 20 2019 07:41 AM
  • Joined: 24 May 2006
That will not be possible in this case.
Posted Image

majkinetor
  • Moderators
  • 4512 posts
  • Last active: May 20 2019 07:41 AM
  • Joined: 24 May 2006
After PM communication with Sean, we discovered one way that in first test worked for me :

OnMessage(WM_ACTIVATEAPP := 0x1C, "OnActivate")

OnActivate() {
    msgbox % DllCall("GetForegroundWindow")
}

BTW, it seems that this problem is well known in scripting environments. I got AU3 solution on SysInternals forum. AutoIt users had the very same problem.

They used variation of r0lZ WinGet, , list but I didn't try to understand the code much as it looks like non-reliable to me (probably suffers from same "always on top" syndroms). Check it out here
Posted Image

r0lZ
  • Members
  • 192 posts
  • Last active: Feb 11 2012 11:19 PM
  • Joined: 21 Apr 2007
Thanks for your investigations, majkinetor!
I'll try the WM_ACTIVATEAPP method soon.
r0lZ

majkinetor
  • Moderators
  • 4512 posts
  • Last active: May 20 2019 07:41 AM
  • Joined: 24 May 2006
Well, bad news. I did more througly tests of WM_ACTIVATEPP.

It really works, sometimes, but again, not always.

The problem seems to be this: shellWnd is container for tray. When you click the tray, first shell takes focus, then your app. Sometimes, here, even DesktopX which keeps my tray is involved... Then, I get on my machine 5 times right window, and 6th time DesktopX or ShellWnd...

well... it was nice try.
Posted Image

majkinetor
  • Moderators
  • 4512 posts
  • Last active: May 20 2019 07:41 AM
  • Joined: 24 May 2006
Well, bad news. I did more througly tests of WM_ACTIVATEPP.

It really works, sometimes, but again, not always.

The problem seems to be this: shellWnd is container for tray. When you click the tray, first shell takes focus, then your app. Sometimes, here, even DesktopX which keeps my tray is involved... Then, I get on my machine 5 times right window, and 6th time DesktopX or ShellWnd...

well... it was nice try.

!ESC works the best so far. Acctually, I don't have any problem with it, it never made a mistake, ecept side effect is that my menu loses focus...
Posted Image

Sean
  • Members
  • 2462 posts
  • Last active: Feb 07 2012 04:00 AM
  • Joined: 12 Feb 2007

The problem seems to be this: shellWnd is container for tray. When you click the tray, first shell takes focus, then your app. Sometimes, here, even DesktopX which keeps my tray is involved... Then, I get on my machine 5 times right window, and 6th time DesktopX or ShellWnd...

If that's the issue, you may try to give WS_EX_NOACTIVATE ExStyle to the taskbar.
But, I'm not sure if it won't do more harm than good.

WinSet, ExStyle, +0x08000000, ahk_class Shell_TrayWnd	; WS_EX_NOACTIVATE


majkinetor
  • Moderators
  • 4512 posts
  • Last active: May 20 2019 07:41 AM
  • Joined: 24 May 2006
Acctually, you can't detect how many intermidate applications (shell, make-up, whatever) is going to be activated when the user presses tray icon... They will all be hidden though.. perhaps that can help... if ForeGroundWindow is not visible, don't update global var ... worth trying....

WS_EX_NOACTIVATE

Interesting idea.

I just got another side effect of tray. This problem appers to me in Favmenu which sets the path in common dialogs. So, using WM_ACtivateAPP it works sometimes, but then interesting problem appears. Favmenu does change the path in Open/Save dialog (OS) but path is changed only AFTER i click on the OS window. So, I can use tray icon, select folder I want to switch and it does that, but I can click in, lets say 10 seconds OS and it will change it then. Something is blocking.. don't know why.. I tried to eliminate tray activation from the story so i did this:

TrayHandler:
   Send !{Esc}
   SetTimer ActivateHotKey, 100
return

ActivateHotKey:
    SetTimer, ActivatehotKey, off
    Favmenu_Show()  ; show the menu
return

I was hopping that this will fake windows user is using a hotkey which doesn't have a problem, but it didn't... The problem here is that MMenu loses focus on subsequent call after tray is clicked ?!. So I did only this:

FavMenu_TrayDefault(){
	Send, !{ESC}
}

This was returning the focus to OS. Then I called a hotkey. Menu didnt' have a focus. If I reactivate the focus clicking something, subsequent menus had focus and I just can't activate it.

WinActivate, ahk_class #32768 always hide the menu.
Posted Image

r0lZ
  • Members
  • 192 posts
  • Last active: Feb 11 2012 11:19 PM
  • Joined: 21 Apr 2007

!ESC works the best so far. Acctually, I don't have any problem with it, it never made a mistake, ecept side effect is that my menu loses focus...

Hum, seems you're right. If I use a simple script to test the !{ESC} trick, it works. Here it is:
#NoEnv
SendMode Input
#persistent
#singleinstance force

menu tray, add
menu tray, add, Test !ESC, test_esc
return

test_esc:
	send !{ESC}
	winGet id, ID, A
	winGetTitle title, ahk_id %id%
	MsgBox %id%`n%title%
return
But when I include that in my application, it doesn't work! I wonder why. I will check again.
r0lZ