Page 1 of 1

Get previously activated window (using #WM_ACTIVATEAPP)

Posted: Wed Jun 08, 2011 12:45 pm
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

Re: Get previously activated window (using #WM_ACTIVATEAPP)

Posted: Wed Jun 08, 2011 10:02 pm
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)...

Re: Get previously activated window (using #WM_ACTIVATEAPP)

Posted: Wed Jun 08, 2011 10:56 pm
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


Re: Get previously activated window (using #WM_ACTIVATEAPP)

Posted: Thu Jun 09, 2011 9:13 am
by c4s
Thanks Rashad. Does the code work for you?

Re: Get previously activated window (using #WM_ACTIVATEAPP)

Posted: Thu Jun 09, 2011 9:47 am
by RASHAD
Yes
PB x86/PB x64 - Win 7 x64

Re: Get previously activated window (using #WM_ACTIVATEAPP)

Posted: Thu Jun 09, 2011 11:51 am
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:

Re: Get previously activated window (using #WM_ACTIVATEAPP)

Posted: Thu Jun 09, 2011 12:08 pm
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.