GUI Recovery Tool

Share your advanced PureBasic knowledge/code with the community.
firace
Addict
Addict
Posts: 946
Joined: Wed Nov 09, 2011 8:58 am

GUI Recovery Tool

Post by firace »

A little program that can be handy when you need to recover the GUI layout of a running win32 EXE.
Note: this is an experimental version that works best on very simple GUIs - feel free to adapt to your needs

Code: Select all


;; note: windows only.

Global winleft
Global wintop
Global reconstructedGUIcode$
Debug "--- ---"

Procedure.i CountListIconColumns(ligHandle)
  
  header = SendMessage_(ligHandle, #LVM_GETHEADER, 0, 0)   :Debug "header = " + header  
  colCount = SendMessage_(header, #HDM_GETITEMCOUNT, 0, 0)       :Debug "cols = " + cols 
  
  ProcedureReturn colCount
EndProcedure


Procedure EnumChildWindowsProcedure(hwnd, lparam)
  gadgetnum = GetDlgCtrlID_(hwnd)
  
  Protected Classname$ = Space(#MAX_PATH)
  Protected parentclassname$ = Space(#MAX_PATH)
  Protected Textname$ = Space(#MAX_PATH)
  
  parentHandle = GetParent_(hwnd)
  SendMessage_(hwnd,#WM_GETTEXT,128,@Textname$) 
  
  GetClassName_(hwnd, @Classname$, #MAX_PATH-1)
  GetClassName_(parentHandle , @parentclassname$, #MAX_PATH-1)
  
  rc.rect
  GetWindowRect_(hwnd,rc)
  XXX = rc\left - winleft 
  YYY = rc\top - wintop - GetSystemMetrics_(#SM_CYCAPTION)
  
  If Classname$ = "Button" 
    If gadgetnum > 13000 And gadgetnum < 13100     
      CheckBoxGadget(#PB_Any, XXX, YYY, rc\right - rc\left, rc\bottom - rc\top , Left(Textname$,60))
    Else
      ButtonGadget(#PB_Any, XXX, YYY , rc\right - rc\left, rc\bottom - rc\top , Left(Textname$,60))
      
      reconstructedGUIcode$ + #CRLF$ + "ButtonGadget(#PB_Any, " + XXX + "," + YYY + "," + Str(rc\right - rc\left) + "," + Str(rc\bottom - rc\top) + "," + #DQUOTE$ + Left(Textname$,60) + #DQUOTE$ + ")"
      
    EndIf
  ElseIf Classname$ = "Static"
    TextGadget(#PB_Any, XXX, YYY, rc\right - rc\left, rc\bottom - rc\top , Left(Textname$,60))
    reconstructedGUIcode$ + #CRLF$ + "TextGadget(#PB_Any, " + XXX + "," + YYY + "," + Str(rc\right - rc\left) + "," + Str(rc\bottom - rc\top) + "," + #DQUOTE$ + "..." + #DQUOTE$ + ")"
    
  ElseIf Classname$ = "Scintilla"
    EG = EditorGadget(#PB_Any, XXX, YYY, rc\right - rc\left, rc\bottom - rc\top ) : SetGadgetText(EG, Classname$)
    reconstructedGUIcode$ + #CRLF$ + "ScintillaGadget(#PB_Any, " + XXX + "," + YYY + "," + Str(rc\right - rc\left) + "," + Str(rc\bottom - rc\top) + ")"
    
  ElseIf Classname$ = "ListBox"
    ListViewGadget(#PB_Any, XXX, YYY, rc\right - rc\left, rc\bottom - rc\top)
    reconstructedGUIcode$ + #CRLF$ + "ListViewGadget(#PB_Any, " + XXX + "," + YYY + "," + Str(rc\right - rc\left) + "," + Str(rc\bottom - rc\top) + "," + ")"
    
  ElseIf Classname$ = "SysListView32"
    LIG = ListIconGadget(#PB_Any, XXX, YYY, rc\right - rc\left, rc\bottom - rc\top,"COL 0", 0)
    reconstructedGUIcode$ + #CRLF$ + "LIG = ListIconGadget(#PB_Any, " + XXX + "," + YYY + "," + Str(rc\right - rc\left) + "," + Str(rc\bottom - rc\top) + ", COL0, 123)"
    
    
    count=CountListIconColumns(hwnd)
    For a = 0 To count
      
      C0W = SendMessage_(hwnd, #LVM_GETCOLUMNWIDTH, a,0)
      Debug C0W
      
      If a <> 0 : AddGadgetColumn(LIG, a, "COL " + a, C0W) : EndIf 
      SetGadgetItemAttribute(LIG, -1, #PB_ListIcon_ColumnWidth, C0W, a) 
      reconstructedGUIcode$ + #CRLF$ + ~"AddGadgetColumn(LIG, "+a+~", \"COL\" + a, " + C0W+")"
    Next 
    
  ElseIf Classname$ = "Edit" And parentclassname$ <> "ComboBox"
    StringGadget(#PB_Any, XXX, YYY, rc\right - rc\left, rc\bottom - rc\top , Left(Textname$,60))
  ElseIf Classname$ = "ComboBox"
    ComboBoxGadget(#PB_Any, XXX, YYY, rc\right - rc\left, rc\bottom - rc\top, #PB_ComboBox_Editable )
    
    reconstructedGUIcode$ + #CRLF$ + "ComboBoxGadget(#PB_Any, " + XXX + "," + YYY + "," + Str(rc\right - rc\left) + "," + Str(rc\bottom - rc\top) + ", #PB_ComboBox_Editable) "
    
  ElseIf Classname$ = "RichEdit20W"  
    EG = EditorGadget(#PB_Any, XXX, YYY, rc\right - rc\left, rc\bottom - rc\top ) : SetGadgetText(EG, Classname$)
    
    reconstructedGUIcode$ + #CRLF$ + "EditorGadget(#PB_Any, " + XXX + "," + YYY + "," + Str(rc\right - rc\left) + "," + Str(rc\bottom - rc\top) + ") "
    
  EndIf 
  
  ProcedureReturn #True
  
EndProcedure

Procedure Deconstruct(targetWindowHandle)
  
  Debug targetWindowHandle
  
  rc.rect
  
  Textname$ = Space(128)
  
  SendMessage_(targetWindowHandle,#WM_GETTEXT,128,@Textname$) 
  
  GetWindowRect_(targetWindowHandle,rc)
  
  winleft = rc\left
  wintop = rc\top
  
  WW =  rc\right - rc\left - GetSystemMetrics_(#SM_CYFIXEDFRAME) *2
  HH =  rc\bottom - rc\top - GetSystemMetrics_(#SM_CYCAPTION) - GetSystemMetrics_(#SM_CYFIXEDFRAME)* 2
  
  OpenWindow(123, 100, 100, WW, HH, "[ReconstructedGUI]", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_ScreenCentered | #PB_Window_MaximizeGadget | #PB_Window_Invisible)   : StickyWindow(123, 1)
  
  reconstructedGUIcode$ = "; Automatically generated code! " + #CRLF$
  reconstructedGUIcode$ + ~"OpenWindow(#PB_Any, 100, 100, " + WW + "," + HH + ~", \"\", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget)" + #CRLF$ + #CRLF$
  
  EnumChildWindows_(targetWindowHandle, @EnumChildWindowsProcedure(), 0)
  
  reconstructedGUIcode$ + #CRLF$ + #CRLF$ + "Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow "
  
  SetGadgetText(6, reconstructedGUIcode$)
EndProcedure   


Procedure.l enumWins(hwnd.l, lParam.l) 
  If hwnd 
    If IsWindowVisible_(hwnd)  And Not IsIconic_(hwnd)       ;... Only looking for visible windows  
      pText.s = Space(256)
      SendMessage_(hwnd, #WM_GETTEXT, 256, @pText) 
      If Len(pText) > 0              ;... Skip windows with empty title 
        
        AddGadgetItem(52, 0, Str(hwnd) + " - " + Left(pText,48)  )
      EndIf 
    EndIf 
    ProcedureReturn 1 
  Else 
    ProcedureReturn 0 
  EndIf 
EndProcedure 

OpenWindow(0,30,30,540,700,"GUI Recovery 1.1", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget)

TextGadget(50, 10, 15, 100, 22, "Analyze window: ") 
ComboBoxGadget(52, 120, 10, 345, 20) 

ButtonGadget(20,10,620,140,20,"Test code")      : DisableGadget(20, 1)

EditorGadget(6, 10, 100, 460, 500, #PB_Editor_ReadOnly) : LoadFont(10, "Consolas", 12) : SetGadgetFont(6, FontID(10))

Classname$ = Space(#MAX_PATH)
parentHandleClassname$ = Space(#MAX_PATH)
Textname$ = Space(#MAX_PATH)

EnumWindows_(@enumWins(), 0) 


Repeat
  Event = WaitWindowEvent()
  
  If event = #PB_Event_Gadget
    If EventGadget() = 52
      handle = Val(StringField(GetGadgetText(52), 1, " ")) 
      Deconstruct(handle) : DisableGadget(20, 0)
    EndIf
    
    If EventGadget() = 20
      HideWindow(123, 0)
    EndIf
    
  EndIf
Until Event = #PB_Event_CloseWindow

Last edited by firace on Thu Jul 06, 2023 11:13 am, edited 1 time in total.
dige
Addict
Addict
Posts: 1406
Joined: Wed Apr 30, 2003 8:15 am
Location: Germany
Contact:

Re: GUI Recovery

Post by dige »

A very good start! It's already very helpful. 👍🏼
"Daddy, I'll run faster, then it is not so far..."
User avatar
Caronte3D
Addict
Addict
Posts: 1361
Joined: Fri Jan 22, 2016 5:33 pm
Location: Some Universe

Re: GUI Recovery Tool

Post by Caronte3D »

Thanks! :D
User avatar
idle
Always Here
Always Here
Posts: 5901
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: GUI Recovery Tool

Post by idle »

Thanks good idea.
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5494
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: GUI Recovery Tool

Post by Kwai chang caine »

Yes very interesting tool and idea, mainly for retrieve the GUI of own software not saved by a PB VD
Thanks for sharing 8)
ImageThe happiness is a road...
Not a destination
AZJIO
Addict
Addict
Posts: 2191
Joined: Sun May 14, 2017 1:48 am

Re: GUI Recovery Tool

Post by AZJIO »

I asked for this functionality for IceDesign
Post Reply