Get previously activated window (using #WM_ACTIVATEAPP)

Windows specific forum
c4s
Addict
Addict
Posts: 1981
Joined: Thu Nov 01, 2007 5:37 pm
Location: Germany

Get previously activated window (using #WM_ACTIVATEAPP)

Post by c4s »

Currently I'm trying really hard to get the window previously activated to my own - Without global hooks, dll and whatever. After a lot of testing I came up with the following:
  1. React on #WM_ACTIVATEAPP when wParam is #True
  2. lParam now has the previous threadid so enumerate all windows using EnumThreadWindows_()
  3. Check if WINDOWINFO structure has dwWindowStatus set to #WS_ACTIVECAPTION
  4. Assume that the found hWnd is the previously activated window...
Well, it works most of the time but isn't good enough that I can use it in my actual project. The problem is that clicking on the desktop and then on my own window seems to interfere the detection of following clicks (e.g. Firefox, Explorer etc.). Same for the PureBasic debugger and other topmost windows.

Can anyone help?
Here is the code I'm using right now:

Code: Select all

EnableExplicit


Enumeration
	#Window
	#GadgetText
	#GadgetList
EndEnumeration

Global EnumThreadWndProcWindowID

#WS_ACTIVECAPTION = $1


Macro ListGadgetScrollEnd(GadgetNr)
	SendMessage_(GadgetID(GadgetNr), #WM_VSCROLL, #SB_BOTTOM, #Null)
EndMacro

Macro ListIconGadgetAutoTooltip(GadgetNr)
	SendMessage_(GadgetID(GadgetNr), #LVM_SETEXTENDEDLISTVIEWSTYLE, 0, SendMessage_(GadgetID(GadgetNr), #LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0) | #LVS_EX_LABELTIP)
EndMacro

Procedure.s WindowTitle(WindowID)
	Protected Length
	Protected Title.s = Space(#MAX_PATH)

	Length = GetWindowText_(WindowID, @Title, #MAX_PATH)
	Title = Left(Title, Length)

	ProcedureReturn Title
EndProcedure

Procedure GadgetListAdd(WindowID)
	Protected Text.s

	; Time:
	Text + FormatDate("%hh:%ii:%ss", Date()) + #LF$

	; ID:
	Text + RSet(Hex(WindowID, #PB_Long), 8, "0") + #LF$

	; Title:
	Text + WindowTitle(WindowID) + #LF$


	; Add new list item and show it:
	AddGadgetItem(#GadgetList, -1, Text)
	ListGadgetScrollEnd(#GadgetList)
EndProcedure

Procedure EnumThreadWndProc(hWnd, lParam)
	Protected wi.WINDOWINFO
	Protected Result = #True

Debug "EnumThreadWndProc: " + RSet(Hex(hWnd, #PB_Long), 8, "0") + " - " + WindowTitle(hWnd)
	wi\cbSize = SizeOf(WINDOWINFO)
	If GetWindowInfo_(hWnd, @wi)
		If wi\dwWindowStatus = #WS_ACTIVECAPTION  ; Is it active?
			EnumThreadWndProcWindowID = hWnd
			Result = #False  ; Abort enumeration
		EndIf
	EndIf

	ProcedureReturn Result
EndProcedure

Procedure WindowCallback(hWnd, Msg, wParam, lParam)
	Protected Result = #PB_ProcessPureBasicEvents

	Select Msg
		Case #WM_ACTIVATEAPP  ; http://msdn.microsoft.com/en-us/library/ms632614(v=vs.85).aspx
			If wParam = #True
Debug "EnumThreadWndProc: --------------------"
				EnumThreadWindows_(lParam, @EnumThreadWndProc(), #Null)  ; Get WindowID from ThreadID
				GadgetListAdd(EnumThreadWndProcWindowID)  ; Returned WindowID from EnumThreadWndProc()
			EndIf
			Result = #False
	EndSelect

	ProcedureReturn Result
EndProcedure



If OpenWindow(#Window, #PB_Ignore, #PB_Ignore, 300, 200, "My Own Window", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
	;StickyWindow(#Window, #True)
	SetWindowCallback(@WindowCallback(), #Window)

	TextGadget(#GadgetText, 10, 10, 280, 20, "Previously activated window:")
	ListIconGadget(#GadgetList, 10, 40, 280, 150, "Time", 55, #PB_ListIcon_GridLines | #PB_ListIcon_FullRowSelect | #PB_ListIcon_AlwaysShowSelection)
	 ListIconGadgetAutoTooltip(#GadgetList)
		AddGadgetColumn(#GadgetList, 1, "ID", 65)
		AddGadgetColumn(#GadgetList, 2, "Title", 135)


	Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
EndIf
If any of you native English speakers have any suggestions for the above text, please let me know (via PM). Thanks!
c4s
Addict
Addict
Posts: 1981
Joined: Thu Nov 01, 2007 5:37 pm
Location: Germany

Re: Get previously activated window (using #WM_ACTIVATEAPP)

Post by c4s »

Thanks to all the replies! I've followed the tip of c4s and tried using GetGUIThreadInfo_() instead of EnumThreadWindows_() :mrgreen:

Code: Select all

EnableExplicit


Enumeration
	#Window
	#GadgetText
	#GadgetList
EndEnumeration

Structure GUITHREADINFO
	cbSize.l
	flags.l
	hwndActive.l
	hwndFocus.l
	hwndCapture.l
	hwndMenuOwner.l
	hwndMoveSize.l
	hwndCaret.l
	rcCaret.RECT
EndStructure


Macro ListGadgetScrollEnd(GadgetNr)
	SendMessage_(GadgetID(GadgetNr), #WM_VSCROLL, #SB_BOTTOM, #Null)
EndMacro

Macro ListIconGadgetAutoTooltip(GadgetNr)
	SendMessage_(GadgetID(GadgetNr), #LVM_SETEXTENDEDLISTVIEWSTYLE, 0, SendMessage_(GadgetID(GadgetNr), #LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0) | #LVS_EX_LABELTIP)
EndMacro

Procedure.s WindowTitle(WindowID)
	Protected Length
	Protected Title.s = Space(#MAX_PATH)

	Length = GetWindowText_(WindowID, @Title, #MAX_PATH)
	Title = Left(Title, Length)

	ProcedureReturn Title
EndProcedure

Procedure GadgetListAdd(WindowID)
	Protected Text.s

	; Time:
	Text + FormatDate("%hh:%ii:%ss", Date()) + #LF$

	; ID:
	Text + RSet(Hex(WindowID, #PB_Long), 8, "0") + #LF$

	; Title:
	Text + WindowTitle(WindowID) + #LF$


	; Add new list item and show it:
	AddGadgetItem(#GadgetList, -1, Text)
	ListGadgetScrollEnd(#GadgetList)
EndProcedure

Procedure WindowCallback(hWnd, Msg, wParam, lParam)
	Protected gti.GUITHREADINFO
	Protected Result = #PB_ProcessPureBasicEvents

	Select Msg
		Case #WM_ACTIVATEAPP
			If wParam = #True
				gti\cbSize = SizeOf(GUITHREADINFO)
				GetGUIThreadInfo_(lParam, @gti)
				GadgetListAdd(gti\hwndActive)
			EndIf
			Result = #False
	EndSelect

	ProcedureReturn Result
EndProcedure



If OpenWindow(#Window, #PB_Ignore, #PB_Ignore, 300, 200, "My Own Window", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
	;StickyWindow(#Window, #True)
	SetWindowCallback(@WindowCallback(), #Window)

	TextGadget(#GadgetText, 10, 10, 280, 20, "Previously activated window:")
	ListIconGadget(#GadgetList, 10, 40, 280, 150, "Time", 55, #PB_ListIcon_GridLines | #PB_ListIcon_FullRowSelect | #PB_ListIcon_AlwaysShowSelection)
	 ListIconGadgetAutoTooltip(#GadgetList)
		AddGadgetColumn(#GadgetList, 1, "ID", 65)
		AddGadgetColumn(#GadgetList, 2, "Title", 135)


	Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
EndIf
By the way: Can someone please tell me if my version of the GUITHREADINFO structure (see http://msdn.microsoft.com/en-us/library ... 85%29.aspx) is correct? I mean does it work on x64 etc. (can't test it)...
If any of you native English speakers have any suggestions for the above text, please let me know (via PM). Thanks!
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4997
Joined: Sun Apr 12, 2009 6:27 am

Re: Get previously activated window (using #WM_ACTIVATEAPP)

Post by RASHAD »

Hi :mrgreen:

For x64

Code: Select all

Structure GUITHREADINFO
   cbSize.l
   flags.l
   hwndActive.l
   hwndFocus.l
   hwndCapture.l
   hwndMenuOwner.l
   PB_Alignment.b[24]
   hwndMoveSize.l
   hwndCaret.l
   rcCaret.RECT
EndStructure

Egypt my love
c4s
Addict
Addict
Posts: 1981
Joined: Thu Nov 01, 2007 5:37 pm
Location: Germany

Re: Get previously activated window (using #WM_ACTIVATEAPP)

Post by c4s »

Thanks Rashad. Does the code work for you?
If any of you native English speakers have any suggestions for the above text, please let me know (via PM). Thanks!
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4997
Joined: Sun Apr 12, 2009 6:27 am

Re: Get previously activated window (using #WM_ACTIVATEAPP)

Post by RASHAD »

Yes
PB x86/PB x64 - Win 7 x64
Egypt my love
c4s
Addict
Addict
Posts: 1981
Joined: Thu Nov 01, 2007 5:37 pm
Location: Germany

Re: Get previously activated window (using #WM_ACTIVATEAPP)

Post by c4s »

Thanks for testing. Anyway, I switched back to simply check every 0.5 secs which window is in the foreground using GetForegroundWindow_(). My two solutions ain't reliable enough... :cry:
If any of you native English speakers have any suggestions for the above text, please let me know (via PM). Thanks!
MachineCode
Addict
Addict
Posts: 1482
Joined: Tue Feb 22, 2011 1:16 pm

Re: Get previously activated window (using #WM_ACTIVATEAPP)

Post by MachineCode »

It's easy... just use GetForegroundWindow_() in a loop of some sort. If it's 0, the desktop handle, the systray handle, or your own app: ignore. If it isn't, that's the new window. Badah bing, badah boom. Been doing it like that for years in my app.
Microsoft Visual Basic only lasted 7 short years: 1991 to 1998.
PureBasic: Born in 1998 and still going strong to this very day!
Post Reply