Page 1 of 2

Child gadget enumeration (Windows)

Posted: Sun Feb 05, 2017 11:37 am
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

Re: Child gadget enumeration (Windows)

Posted: Sun Feb 05, 2017 1:06 pm
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?

Re: Child gadget enumeration (Windows)

Posted: Sun Feb 05, 2017 2:33 pm
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()

Re: Child gadget enumeration (Windows)

Posted: Sun Feb 05, 2017 2:38 pm
by Bisonte
@Keya:
hWnd is an os-handle. To avoid strange errors ... always youse INTEGER for it... not long.

Re: Child gadget enumeration (Windows)

Posted: Sun Feb 05, 2017 3:02 pm
by Keya
good catch thankyou

Re: Child gadget enumeration (Windows)

Posted: Mon Feb 06, 2017 6:40 am
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. :(

Re: Child gadget enumeration (Windows)

Posted: Mon Feb 06, 2017 8:13 am
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

Re: Child gadget enumeration (Windows)

Posted: Mon Feb 06, 2017 8:44 am
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.

Re: Child gadget enumeration (Windows)

Posted: Mon Feb 06, 2017 8:54 am
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) 

Re: Child gadget enumeration (Windows)

Posted: Mon Feb 06, 2017 9:11 am
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.

Re: Child gadget enumeration (Windows)

Posted: Mon Feb 06, 2017 9:30 am
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

Re: Child gadget enumeration (Windows)

Posted: Mon Feb 06, 2017 9:37 am
by netmaestro
I did EnumChildWindows with no joy. Some kind of permission thing I'm sure.

Re: Child gadget enumeration (Windows)

Posted: Mon Feb 06, 2017 9:39 am
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

Re: Child gadget enumeration (Windows)

Posted: Mon Feb 06, 2017 9:43 am
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)

Re: Child gadget enumeration (Windows)

Posted: Mon Feb 06, 2017 9:46 am
by netmaestro
I'm always logged in as admin and in compiler options I tried request admin mode - nothing.