Child gadget enumeration (Windows)

Share your advanced PureBasic knowledge/code with the community.
Justin
Addict
Addict
Posts: 948
Joined: Sat Apr 26, 2003 2:49 pm

Child gadget enumeration (Windows)

Post by Justin »

Functions to enumerate child gadgets, useful if you want to resize using your own layout now that the container gadgets support the resize event.

Code: Select all

EnableExplicit

Macro GadgetFromHwnd(hwnd)
	GetProp_(hwnd, "PB_ID")
EndMacro

Procedure GetHwnd(object.i)
	If IsWindow(object)
		ProcedureReturn WindowID(object)
		
	ElseIf IsGadget(object)
		ProcedureReturn GadgetID(object)
	EndIf
EndProcedure

;Gets the first child gadget of a window or gadget container.
;item : for panel gadgets the panel item, -1 to get the selected panel.
Procedure GetFirstChildGadget(parentGadget.i, item.i = -1)
	Define.i hwParent, hwnd, gadget, x
	
	hwParent = GetHwnd(parentGadget)
	If hwParent
		If IsWindow(parentGadget)
			hwnd = GetWindow_(hwParent, #GW_CHILD)
			gadget = GadgetFromHwnd(hwnd)
			If IsGadget(gadget)
				ProcedureReturn gadget
			EndIf 
			
		ElseIf IsGadget(parentGadget)
			If GadgetType(parentGadget) = #PB_GadgetType_Panel
				If item < -1 : item = -1 : EndIf
				
				If item = -1
					item = GetGadgetState(parentGadget)
				EndIf 
				
				;Go to the panel item
				x = 0
				hwnd = GetWindow_(hwParent, #GW_CHILD)
				While x < item
					hwnd = GetWindow_(hwnd, #GW_HWNDNEXT)
					x = x + 1
				Wend 
				
				;Get first child of this panel item
				gadget = GadgetFromHwnd(GetWindow_(hwnd, #GW_CHILD))
				If IsGadget(gadget)
					ProcedureReturn gadget
				EndIf 
				
			Else ;All other gadgets.
				gadget = GadgetFromHwnd(GetWindow_(hwParent, #GW_CHILD))
				If IsGadget(gadget)
					ProcedureReturn gadget
				EndIf 
			EndIf 
		EndIf 
	EndIf 
EndProcedure

Procedure GetNextSiblingGadget(gadget.i)
	Define.i hwnd
	
	hwnd = GetWindow_(GadgetID(gadget), #GW_HWNDNEXT)
	gadget = GadgetFromHwnd(hwnd)
	If IsGadget(gadget)
		ProcedureReturn gadget
	EndIf 
EndProcedure

;- TEST
Global.i be, mainWin

Procedure clickHandler()
	Define.i gadget, gadget2
	
	gadget = GetFirstChildGadget(mainWin)
	While gadget
		Debug GetGadgetText(gadget)
		
		If GadgetType(gadget) = #PB_GadgetType_Panel
			gadget2 = GetFirstChildGadget(gadget)
			While gadget2
				Debug "--- " + GetGadgetText(gadget2)
				gadget2 = GetNextSiblingGadget(gadget2)
			Wend 
		EndIf 
		
		gadget = GetNextSiblingGadget(gadget)
	Wend 
EndProcedure


mainWin = OpenWindow(#PB_Any, 0, 0, 322, 220, "Test", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
be = ButtonGadget(#PB_Any, 0, 0, 80, 24,"Enumerate")
BindGadgetEvent(be, @clickHandler(), #PB_EventType_LeftClick)
PanelGadget(1, 8, 30, 306, 180)
AddGadgetItem (1, -1, "Panel 1")
ButtonGadget(2, 10, 15, 80, 24,"Button 1")
AddGadgetItem (1, -1,"Panel 2")
ButtonGadget(3, 95, 15, 80, 24,"Button 2")
CloseGadgetList()
        
Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
Dude
Addict
Addict
Posts: 1907
Joined: Mon Feb 16, 2015 2:49 pm

Re: Child gadget enumeration (Windows)

Post by Dude »

Nice code, Justin. :)

Is there a way to do this with third-party windows? Like, to enumerate Calculator and make the debug output show "1", "2", "3", "+", "-", etc?
User avatar
Keya
Addict
Addict
Posts: 1890
Joined: Thu Jun 04, 2015 7:10 am

Re: Child gadget enumeration (Windows)

Post by Keya »

Dude wrote:Is there a way to do this with third-party windows? Like, to enumerate Calculator and make the debug output show "1", "2", "3", "+", "-", etc?
Windows only

Code: Select all

Procedure EnumChildren(hWnd.i)
  Protected nexthwnd.i, szClass.s{1024}, szText.s{1024}
  nexthwnd = GetWindow_(hwnd, #GW_CHILD | #GW_HWNDFIRST)
  While nexthwnd <> 0
    GetClassName_(nexthwnd, @szClass, SizeOf(szClass))
    ;GetWindowText_(nexthwnd, @szText, SizeOf(szText))
    SendMessage_(nexthwnd, #WM_GETTEXT, SizeOf(szText), @szText)
    PrintN(" CHILD: Class=" + szClass + " Text=" + szText)
    EnumChildren(nexthwnd)
    nexthwnd = GetWindow_(nexthwnd, #GW_HWNDNEXT)
  Wend
EndProcedure


Procedure cbEnumParents(hWnd.i, lNotUsed.i)
Protected szClass.s{1024}, szText.s{1024}
GetClassName_(hWnd, @szClass, SizeOf(szClass))
GetWindowText_(hWnd, @szText, SizeOf(szText))
PrintN("PARENT=" + szText)
  EnumChildren(hWnd)
ProcedureReturn 1
EndProcedure


OpenConsole()

;// DEMO 1 - ENUM PARENT WINDOWS
;hWnd = #HWND_DESKTOP
;EnumWindows_(@cbEnumParents(), hWnd)

;// DEMO 2 - ENUM CHILDREN OF A PARENT WINDOW
RunProgram("calc.exe")
Delay(500) ;wait for window to load, there are better ways but this will do for demo
hWnd = FindWindow_(#Null, "Calculator")
EnumChildren(hWnd)

Input()
Last edited by Keya on Mon Feb 06, 2017 8:15 am, edited 3 times in total.
User avatar
Bisonte
Addict
Addict
Posts: 1305
Joined: Tue Oct 09, 2007 2:15 am

Re: Child gadget enumeration (Windows)

Post by Bisonte »

@Keya:
hWnd is an os-handle. To avoid strange errors ... always youse INTEGER for it... not long.
PureBasic 6.21 (Windows x64) | Windows 11 Pro | AsRock B850 Steel Legend Wifi | R7 9800x3D | 64GB RAM | RTX 5080 | ThermaltakeView 270 TG ARGB | build by vannicom​​
English is not my native language... (I often use DeepL.)
User avatar
Keya
Addict
Addict
Posts: 1890
Joined: Thu Jun 04, 2015 7:10 am

Re: Child gadget enumeration (Windows)

Post by Keya »

good catch thankyou
Dude
Addict
Addict
Posts: 1907
Joined: Mon Feb 16, 2015 2:49 pm

Re: Child gadget enumeration (Windows)

Post by Dude »

Doesn't work, Keya. Only the "0" button of Calculator shows, with nothing for everything else on Calculator's window. :(

Also doesn't show anything when I try it with Word's "About" dialog box. :(
User avatar
Keya
Addict
Addict
Posts: 1890
Joined: Thu Jun 04, 2015 7:10 am

Re: Child gadget enumeration (Windows)

Post by Keya »

"doesn't work" might be an exaggeration. It's better than your version anyway :) I don't claim it's completely bug free though lol. btw i updated it to use WM_GETTEXT instead of GetWindowText, it seems to be able to get some text that GetWindowText cant (like the main textbox where numbers are calculated/displayed). For whatever reason.

This is what i get for the main window (XP):

Code: Select all

CHILD: Class=Edit Text=0.
 CHILD: Class=Button Text=Hyp
 CHILD: Class=Button Text=Inv
 CHILD: Class=Button Text=
 CHILD: Class=Button Text=Sta
 CHILD: Class=Button Text=Hex
 CHILD: Class=Button Text=Dec
 CHILD: Class=Button Text=Oct
 CHILD: Class=Button Text=Bin
 CHILD: Class=Button Text=
 CHILD: Class=Button Text=
 CHILD: Class=Button Text=Degrees
 CHILD: Class=Button Text=Radians
 CHILD: Class=Button Text=Grads
 CHILD: Class=Button Text=Qword
 CHILD: Class=Button Text=Dword
 CHILD: Class=Button Text=Word
 CHILD: Class=Button Text=Byte
 CHILD: Class=Button Text=Ave
 CHILD: Class=Button Text=Sum
 CHILD: Class=Button Text=s
 CHILD: Class=Button Text=Dat
 CHILD: Class=Button Text=F-E
 CHILD: Class=Button Text=dms
 CHILD: Class=Button Text=sin
 CHILD: Class=Button Text=cos
 CHILD: Class=Button Text=tan
 CHILD: Class=Button Text=(
 CHILD: Class=Button Text=Exp
 CHILD: Class=Button Text=x^y
 CHILD: Class=Button Text=x^3
 CHILD: Class=Button Text=x^2
 CHILD: Class=Button Text=)
 CHILD: Class=Button Text=ln
 CHILD: Class=Button Text=log
 CHILD: Class=Button Text=n!
 CHILD: Class=Button Text=1/x
 CHILD: Class=Button Text=MC
 CHILD: Class=Button Text=MR
 CHILD: Class=Button Text=MS
 CHILD: Class=Button Text=M+
 CHILD: Class=Button Text=pi
 CHILD: Class=Button Text=7
 CHILD: Class=Button Text=4
 CHILD: Class=Button Text=1
 CHILD: Class=Button Text=0
 CHILD: Class=Button Text=A
 CHILD: Class=Button Text=8
 CHILD: Class=Button Text=5
 CHILD: Class=Button Text=2
 CHILD: Class=Button Text=+/-
 CHILD: Class=Button Text=B
 CHILD: Class=Button Text=9
 CHILD: Class=Button Text=6
 CHILD: Class=Button Text=3
 CHILD: Class=Button Text=.
 CHILD: Class=Button Text=C
 CHILD: Class=Button Text=/
 CHILD: Class=Button Text=*
 CHILD: Class=Button Text=-
 CHILD: Class=Button Text=+
 CHILD: Class=Button Text=D
 CHILD: Class=Button Text=Mod
 CHILD: Class=Button Text=OR
 CHILD: Class=Button Text=Lsh
 CHILD: Class=Button Text==
 CHILD: Class=Button Text=E
 CHILD: Class=Button Text=And
 CHILD: Class=Button Text=Xor
 CHILD: Class=Button Text=Not
 CHILD: Class=Button Text=Int
 CHILD: Class=Button Text=F
 CHILD: Class=Button Text=Backspace
 CHILD: Class=Button Text=CE
 CHILD: Class=Button Text=C
 CHILD: Class=Static Text=
 CHILD: Class=Static Text=
 CHILD: Class=Static Text=
"About Calculator" window:

Code: Select all

 CHILD: Class=Static Text=
 CHILD: Class=Static Text=Microsoft r Calculator
 CHILD: Class=Static Text=-snipped-
 CHILD: Class=Static Text=Copyright c 2007 Microsoft Corporation
 CHILD: Class=Static Text=
 CHILD: Class=SysLink Text=This product is licensed under the terms of the <A>End-User License Agreement</A> to:
 CHILD: Class=Static Text=-snipped-
 CHILD: Class=Static Text=
 CHILD: Class=Static Text=
 CHILD: Class=Static Text=Physical memory available to Windows:
 CHILD: Class=Static Text=-snipped-KB
 CHILD: Class=Button Text=OK
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Re: Child gadget enumeration (Windows)

Post by netmaestro »

I'm not sure why but the keya code isn't producing any text here at all except the 0 from the static control. Windows 7 Pro, PB 5.60b2. It's obviously working for you keya. That's a head scratcher.
BERESHEIT
User avatar
Keya
Addict
Addict
Posts: 1890
Joined: Thu Jun 04, 2015 7:10 am

Re: Child gadget enumeration (Windows)

Post by Keya »

is very weird. Surely it wouldnt be an admin thing either, pretty sure any process can look at others windows ... or has that changed for security reasons? It would be annoying and not very practical to have to inject a DLL into each process to do the enum. i'll see if i can take a look at it on my Win7 VM a bit later and follow along in a debugger

btw there's also the EnumChildWindows api which i wasnt using, msdn for GetWindow says its better to use that than the GetWindow(#GW_HWNDNEXT) loop i was using, but i think that's just for efficiency as it allows for a callback, whereas im guessing if you use #GW_HWNDNEXT it has to enumerate the entire list (until specified hwnd) each time. But if it's just an efficiency thing it might not be the solution we're after.

It's supposed to be something along the lines of this, but not working yet for me:

Code: Select all

Procedure.i cbEnumChildren (hWnd.i, lParam.i) 
  Protected szClass.s{1024}, szText.s{1024}
  GetClassName_(hWnd, szClass, SizeOf(szClass))
  GetWindowText_(hWnd, szText, SizeOf(szText))
  Debug " CHILD: Class=" + szClass + " " + "Text=" + szText
  ProcedureReturn EnumChildWindows_(hWnd, @cbEnumChildren(), lParam) 
EndProcedure


RunProgram("calc.exe")
Delay(500) ;wait for window to load
hWnd = FindWindow_(0, "Calculator")
Debug "hWnd="+ Hex(hWnd)
EnumChildWindows_(hWnd, @cbEnumChildren(), 0) 
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Re: Child gadget enumeration (Windows)

Post by netmaestro »

MSDN says that when working with non-owned windows, you shouldn't use GetWindowText, which composes and sends WM_GETTEXT for you because it won't work - you need to send WM_GETTEXT explicitly yourself. Yet this doesn't work for me either.
BERESHEIT
User avatar
Keya
Addict
Addict
Posts: 1890
Joined: Thu Jun 04, 2015 7:10 am

Re: Child gadget enumeration (Windows)

Post by Keya »

I just tried Inspect.exe on Windows 7, which confirms that yes it's still possible (seemingly no security/permission issues). So at least we're not on a wild goose chase!
Image
165kb exe though but i'll try and break on EnumChildWindows etc and see what its up to
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Re: Child gadget enumeration (Windows)

Post by netmaestro »

I did EnumChildWindows with no joy. Some kind of permission thing I'm sure.
BERESHEIT
User avatar
Keya
Addict
Addict
Posts: 1890
Joined: Thu Jun 04, 2015 7:10 am

Re: Child gadget enumeration (Windows)

Post by Keya »

im still thinking injecting dll may be required ... Microsoft's Spy++ comes with spyhk55.dll "Spy++ Hook". But i think that's just for intercepting WM_ messages etc, not window enum. Trying to test that theory now
User avatar
Keya
Addict
Addict
Posts: 1890
Joined: Thu Jun 04, 2015 7:10 am

Re: Child gadget enumeration (Windows)

Post by Keya »

actually my previous demo is working fine enumerating Calculator on my Win7-64, logged in as admin. NOT working when logged in as Guest (only seeing like one of the gadgets return a "0" and pretty much nothing for the others, echoing what Dude said/saw)
Last edited by Keya on Mon Feb 06, 2017 9:46 am, edited 1 time in total.
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Re: Child gadget enumeration (Windows)

Post by netmaestro »

I'm always logged in as admin and in compiler options I tried request admin mode - nothing.
BERESHEIT
Post Reply