Visibility of work areas, variables, viewing contents of objects
Visibility of work areas, variables, viewing contents of objects
Hi Thomas,
yes, they are the references of your application.
Personally, I would add a call to the debugging functions somewhere in your menu, so you can call then whenever you like - even when the application is running on another machine than yours.
But of course, if you add that library to the references, you have to deliver the relative DLL together with your application.
Wolfgang
yes, they are the references of your application.
Personally, I would add a call to the debugging functions somewhere in your menu, so you can call then whenever you like - even when the application is running on another machine than yours.
But of course, if you add that library to the references, you have to deliver the relative DLL together with your application.
Wolfgang
Wolfgang Riedmann
Meran, South Tyrol, Italy
wolfgang@riedmann.it
https://www.riedmann.it - https://docs.xsharp.it
Meran, South Tyrol, Italy
wolfgang@riedmann.it
https://www.riedmann.it - https://docs.xsharp.it
-
- Posts: 43
- Joined: Thu Jun 20, 2019 5:19 pm
Visibility of work areas, variables, viewing contents of objects
Hi, Chris.
I tried it and it works. DbgShowMemvars produces an RTE, but the only other downside is that DbgShowGlobals and I think I may have a solution for that.
I was thinking of building a dialog, using one or more global variables or objects as arguments, and constructing a class which uses Object2Array() along with IVarListGetInfo(), IVarList() and IVarListClass(). This class implemented in the dialog would traverse the array and add treeview items with data derived from these functions. It would check the type with IsString(), IsSymbol(), IsLogic(), et al to display the values, iterate the element if IsArray(), and in cases of IsObject(), an object within an object, call the method for accessing the object element recursively.
When I get it to work, would you like me to share the code with you? I don't know whether it would make emulating CAVO's global treeview for objects in XIDE any easier, but if it does, I'd be glad to help.
I tried it and it works. DbgShowMemvars produces an RTE, but the only other downside is that DbgShowGlobals and I think I may have a solution for that.
I was thinking of building a dialog, using one or more global variables or objects as arguments, and constructing a class which uses Object2Array() along with IVarListGetInfo(), IVarList() and IVarListClass(). This class implemented in the dialog would traverse the array and add treeview items with data derived from these functions. It would check the type with IsString(), IsSymbol(), IsLogic(), et al to display the values, iterate the element if IsArray(), and in cases of IsObject(), an object within an object, call the method for accessing the object element recursively.
When I get it to work, would you like me to share the code with you? I don't know whether it would make emulating CAVO's global treeview for objects in XIDE any easier, but if it does, I'd be glad to help.
Visibility of work areas, variables, viewing contents of objects
Hi Thomas,
Yes, please send us the code, if it's suitable it can be used in both XIDE and VS!
Yes, please send us the code, if it's suitable it can be used in both XIDE and VS!
Chris Pyrgas
XSharp Development Team
chris(at)xsharp.eu
XSharp Development Team
chris(at)xsharp.eu
Visibility of work areas, variables, viewing contents of objects
Thomas,
Robert
We are working on this at this moment, as well as an improved "Debugger Expression Evaluator" (both for the VS debugger, the XIDE debugger is another thing).ThomasWYale wrote:De heer van der Hulst, is de feature toegevoegd? Ik zag dat je de post hebt bekeken.
Robert
XSharp Development Team
The Netherlands
robert@xsharp.eu
The Netherlands
robert@xsharp.eu
Visibility of work areas, variables, viewing contents of objects
Thomas,
Robert
I am not sure what you mean with RTE, but if it is an exception, can you please share that with us, so we can take care of it.ThomasWYale wrote:Hi, Chris.
I tried it and it works. DbgShowMemvars produces an RTE, but the only other downside is that DbgShowGlobals and I think I may have a solution for that.
Robert
XSharp Development Team
The Netherlands
robert@xsharp.eu
The Netherlands
robert@xsharp.eu
Visibility of work areas, variables, viewing contents of objects
Robert,
It gives a runtime exception, this is why I did not include this function in my post above
Also DbgShowWorkAreas() gives some handled exceptions, looking into it and will open tickets if necessary.
It gives a runtime exception, this is why I did not include this function in my post above
Code: Select all
System.InvalidCastException
Unable to cast object of type 'System.Reflection.RtFieldInfo' to type 'System.Reflection.MethodInfo'.
Callstack :
XSharp.Debugger.MemVarsWindow.System.Void XSharp.Debugger.MemVarsWindow.LoadValues()()
XSharp.Debugger.MemVarsWindow.System.Void XSharp.Debugger.MemVarsWindow..ctor()()
static System.Void XSharp.RT.Debugger.Functions.DbgShowMemvars()()
Chris Pyrgas
XSharp Development Team
chris(at)xsharp.eu
XSharp Development Team
chris(at)xsharp.eu
Visibility of work areas, variables, viewing contents of objects
Chris,
I checked in a fix
Robert
I checked in a fix
Robert
XSharp Development Team
The Netherlands
robert@xsharp.eu
The Netherlands
robert@xsharp.eu
Visibility of work areas, variables, viewing contents of objects
Robert,
Yes, works fine now!
Yes, works fine now!
Chris Pyrgas
XSharp Development Team
chris(at)xsharp.eu
XSharp Development Team
chris(at)xsharp.eu
-
- Posts: 43
- Joined: Thu Jun 20, 2019 5:19 pm
Visibility of work areas, variables, viewing contents of objects
Here you are, Chris. I would've finished a lot sooner, but I have a day job, had to contend with walking a few miles to work every day in the wake of a recent snowstorm, among other things.
I tried to emulate as closely as possible to the way VO 2.8 does this. Like the functions in XSharp.RT.Debugger, it has to be either inserted within the code or called from a menu option. As long-time VO 2.8 users know, however, the feature seems to use a kind of hybrid control with the properties of both a listview and treeview. Since I didn't have that, I settled on a treeview instead. And since I can't include the actual form here, you need only construct one yourself with the two PROTECTs as listed in the class declaration for ViewObjects. One or more GLOBALs, or any combination of objects or arrays to be inspected, are included in an array designated as uExtra argument in ViewObjects.
Early on I figured that I'd face the problem of a stack overflow error if I constructed a complete tree, particularly if there were circular references, so I put in safeguards prevent that. As in VO, a user double-clicks a node to reveal its content, but the tree adds items only incrementally, revealing only what the user intends to see. That way, the tree indicates the presence of any array elements or object attributes one step in advance, with the [+] designation beside the node as unexpanded.
I tested it as extensively as I could, and hope that you find use with it.
I tried to emulate as closely as possible to the way VO 2.8 does this. Like the functions in XSharp.RT.Debugger, it has to be either inserted within the code or called from a menu option. As long-time VO 2.8 users know, however, the feature seems to use a kind of hybrid control with the properties of both a listview and treeview. Since I didn't have that, I settled on a treeview instead. And since I can't include the actual form here, you need only construct one yourself with the two PROTECTs as listed in the class declaration for ViewObjects. One or more GLOBALs, or any combination of objects or arrays to be inspected, are included in an array designated as uExtra argument in ViewObjects.
Code: Select all
LOCAL oViewObjects AS ViewObjects
...
oViewObjects:=ViewObjects{,,,{<arrayOrObject1,arrayOrObject2,...}}
oViewObjects:Show(SHOWCENTERED)
I tested it as extensively as I could, and hope that you find use with it.
Code: Select all
CLASS ViewObjects INHERIT DATADIALOG
PROTECT oDCtv AS TREEVIEW
PROTECT oCCbtnExit AS PUSHBUTTON
//{{%UC%}} USER CODE STARTS HERE (do NOT remove this line)
PROTECT aObjectsInTree AS ARRAY
PROTECT nStopAdding AS BYTE
METHOD Init(oWindow,iCtlID,oServer,uExtra) CLASS ViewObjects
SELF:PreInit(oWindow,iCtlID,oServer,uExtra)
SUPER:Init(oWindow,ResourceID{"ViewObjects",_GetInst()},iCtlID)
oDCtv := TreeView{SELF,ResourceID{VIEWOBJECTS_TV,_GetInst()}}
oDCtv:HyperLabel := HyperLabel{#tv,"View Objects",NULL_STRING,NULL_STRING}
oCCbtnExit := PushButton{SELF,ResourceID{VIEWOBJECTS_BTNEXIT,_GetInst()}}
oCCbtnExit:HyperLabel := HyperLabel{#btnExit,"Exit",NULL_STRING,NULL_STRING}
SELF:Caption := "View Objects"
SELF:HyperLabel := HyperLabel{#ViewObjects,"View Objects",NULL_STRING,NULL_STRING}
IF !IsNil(oServer)
SELF:Use(oServer)
ENDIF
SELF:PostInit(oWindow,iCtlID,oServer,uExtra)
RETURN SELF
METHOD PostInit(oWindow,iCtlID,oServer,uExtra) CLASS ViewObjects
LOCAL y AS BYTE
SELF:aObjectsInTree:={}
SELF:nStopAdding:=0
FOR y=1 UPTO ALen(uExtra)
IF IsObject(uExtra[y])
SELF:doObject(uExtra[y],#ROOT)
ELSE
SELF:doArray(uExtra[y],#ROOT)
ENDIF
NEXT
RETURN NIL
METHOD doArray(aArray,sParentName) CLASS ViewObjects
LOCAL sName AS SYMBOL
LOCAL y AS BYTE
LOCAL oTVI AS TreeViewItem
sName:=String2Symbol(PadL(SELF:oDCtv:ItemCount,3,"0"))
oTVI:=TreeViewItem{sName,"array",aArray}
SELF:oDCtv:AddItem(sParentName,oTVI)
IF SELF:nStopAdding<3
SELF:nStopAdding:=SELF:nStopAdding+1
FOR y=1 UPTO ALen(aArray)
SELF:doAttribute(sName,"["+NTrim(y)+"]",aArray[y],4)
NEXT
SELF:nStopAdding:=SELF:nStopAdding-1
ENDIF
RETURN NIL
METHOD doAttribute(sParentName,cName,uValue,dwlInfo) CLASS ViewObjects
LOCAL sName AS SYMBOL
LOCAL cExp AS STRING
LOCAL y AS BYTE
LOCAL oTVI AS TreeViewItem
sName:=String2Symbol(PadL(SELF:oDCtv:ItemCount,3,"0"))
IF IsNil(uValue)
IF IsArray(uValue)
oTVI:=TreeViewItem{sName,cName+" NULL_ARRAY ",uValue}
ELSEIF IsString(uValue)
oTVI:=TreeViewItem{sName,cName+" NULL_STRING ",uValue}
ELSEIF IsObject(uValue)
oTVI:=TreeViewItem{sName,cName+" NULL_OBJECT ",uValue}
ELSE
oTVI:=TreeViewItem{sName,cName+" nil ",uValue}
ENDIF
SELF:oDCtv:AddItem(sParentName,oTVI)
ELSEIF IsLogic(uValue)
oTVI:=TreeViewItem{sName,cName+" logic "+LTOC(uValue),uValue}
SELF:oDCtv:AddItem(sParentName,oTVI)
ELSEIF IsSymbol(uValue)
oTVI:=TreeViewItem{sName,cName+" symbol "+Symbol2String(uValue),uValue}
SELF:oDCtv:AddItem(sParentName,oTVI)
ELSEIF IsFloat(uValue)
oTVI:=TreeViewItem{sName,cName+" float "+NTrim(uValue),uValue}
SELF:oDCtv:AddItem(sParentName,oTVI)
ELSEIF IsLong(uValue)
oTVI:=TreeViewItem{sName,cName+" long "+NTrim(uValue),uValue}
SELF:oDCtv:AddItem(sParentName,oTVI)
ELSEIF IsNumeric(uValue)
oTVI:=TreeViewItem{sName,cName+" numeric "+NTrim(uValue),uValue}
SELF:oDCtv:AddItem(sParentName,oTVI)
ELSEIF IsDate(uValue)
oTVI:=TreeViewItem{sName,cName+" date "+DToC(uValue),uValue}
SELF:oDCtv:AddItem(sParentName,oTVI)
ELSEIF IsString(uValue)
oTVI:=TreeViewItem{sName,cName+" string "+CHR(34)+uValue+CHR(34),uValue}
SELF:oDCtv:AddItem(sParentName,oTVI)
ELSEIF IsArray(uValue)
oTVI:=TreeViewItem{sName,cName+" array",uValue}
SELF:oDCtv:AddItem(sParentName,oTVI)
IF SELF:nStopAdding<3
SELF:nStopAdding:=SELF:nStopAdding+1
FOR y=1 UPTO ALen(uValue)
SELF:doAttribute(sName,"["+NTrim(y)+"]",uValue[y],4)
NEXT
SELF:nStopAdding:=SELF:nStopAdding-1
ENDIF
ELSEIF IsObject(uValue)
cExp:=cName+" class "+Symbol2String(ClassName(uValue))
IF AScan(SELF:aObjectsInTree,uValue)>0
oTVI:=TreeViewItem{sName,cName+" class "+Symbol2String(ClassName(uValue))+" (circular reference)",uValue}
SELF:oDCtv:AddItem(sParentName,oTVI)
ELSE
SELF:doObject(uValue,sParentName)
ENDIF
ELSE // IF IsPtr(uValue)
oTVI:=TreeViewItem{sName,cName+" Other, assuming Pointer",uValue}
SELF:oDCtv:AddItem(sParentName,oTVI)
ENDIF
RETURN NIL
METHOD doObject(oObject,sParentName) CLASS ViewObjects
LOCAL sName,sAttrib AS SYMBOL
LOCAL y AS BYTE
LOCAL cExp AS STRING
LOCAL oTVI AS TreeViewItem
LOCAL aVarList:=IvarList(oObject)
cExp:="CLASS "+Symbol2String(ClassName(oObject))
sName:=String2Symbol(PadL(SELF:oDCtv:ItemCount,3,"0"))
AAdd(SELF:aObjectsInTree,oObject)
oTVI:=TreeViewItem{sName,cExp,oObject}
SELF:oDCtv:AddItem(sParentName,oTVI)
FOR y=1 UPTO ALen(aVarList)
sAttrib:=aVarList[y]
SELF:doAttribute(sName,Symbol2String(sAttrib),IVarGet(oObject,sAttrib),IVarGetInfo(oObject,sAttrib))
NEXT
RETURN NIL
METHOD TreeViewMouseButtonDoubleClick(oTreeViewMouseEvent) CLASS ViewObjects
LOCAL sName,sAttrib AS SYMBOL
LOCAL y AS BYTE
LOCAL uValue AS USUAL
LOCAL oTVI,oTVI2,oTVI3 AS TreeViewItem
LOCAL aVarList AS ARRAY
LOCAL oControl AS Control
oControl:=IF(oTreeViewMouseEvent==NULL_OBJECT,NULL_OBJECT,oTreeViewMouseEvent:Control)
SUPER:TreeViewMouseButtonDoubleClick(oTreeViewMouseEvent)
oTVI:=SELF:oDCtv:GetSelectedItem()
sName:=oTVI:NameSym
uValue:=oTVI:Value
IF(!IsArray(uValue).AND.!IsObject(uValue)).OR.IsObject(uValue).AND.AScan(SELF:aObjectsInTree,uValue)>0
RETURN NIL
ENDIF
oTVI2:=SELF:oDCtv:GetFirstChildItem(oTVI)
IF!oTVI2==NULL_OBJECT
WHILE!oTVI2==NULL_OBJECT
IF IsArray(oTVI2:VALUE).OR.IsObject(oTVI2:VALUE).AND.AScan(SELF:aObjectsInTree,oTVI2:VALUE)>0
oTVI3:=SELF:oDCtv:GetFirstChildItem(oTVI2)
WHILE!oTVI3==NULL_OBJECT
IF IsArray(oTVI3:VALUE).AND.SELF:oDCtv:GetFirstChildItem(oTVI3:NameSym)==NULL_OBJECT
FOR y=1 UPTO ALen(oTVI3:VALUE)
SELF:doAttribute(oTVI3:NameSym,"["+NTrim(y)+"]",oTVI3:VALUE[y],4)
NEXT
ELSEIF IsObject(oTVI3:VALUE)
aVarList:=IvarList(oTVI3:VALUE)
FOR y=1 UPTO ALen(aVarList)
sAttrib:=aVarList[y]
SELF:doAttribute(oTVI3:NameSym,Symbol2String(sAttrib),IVarGet(oTVI3:Value,sAttrib),IVarGetInfo(oTVI3:Value,sAttrib))
NEXT
ENDIF
oTVI3:=SELF:oDCtv:GetNextSiblingItem(oTVI3)
ENDDO
ENDIF
oTVI2:=SELF:oDCtv:GetNextSiblingItem(oTVI2)
ENDDO
ELSEIF IsArray(uValue).AND.ALen(uValue)>0
IF SELF:nStopAdding<3
SELF:nStopAdding:=SELF:nStopAdding+1
FOR y=1 UPTO ALen(uValue)
SELF:doAttribute(sName,"["+NTrim(y)+"]",uValue[y],4)
NEXT
SELF:nStopAdding:=SELF:nStopAdding-1
ENDIF
ENDIF
RETURN NIL
METHOD btnExit( ) CLASS ViewObjects
SELF:EndWindow()
RETURN NIL
-
- Posts: 43
- Joined: Thu Jun 20, 2019 5:19 pm
Visibility of work areas, variables, viewing contents of objects
Here you are, Chris. I would've finished a lot sooner, but I have a day job, had to contend with walking a few miles to work every day in the wake of a recent snowstorm, among other things.
I tried to emulate as closely as possible to the way VO 2.8 does this. Like the functions in XSharp.RT.Debugger, it has to be either inserted within the code or called from a menu option. As long-time VO 2.8 users know, however, the feature seems to use a kind of hybrid control with the properties of both a listview and treeview. Since I didn't have that, I settled on a treeview instead. And since I can't include the actual form here, you need only construct one yourself with the two PROTECTs as listed in the class declaration for ViewObjects. One or more GLOBALs, or any combination of objects or arrays to be inspected, are included in an array designated as uExtra argument in ViewObjects.
Early on I figured that I'd face the problem of a stack overflow error if I constructed a complete tree, particularly if there were circular references, so I put in safeguards prevent that. As in VO, a user double-clicks a node to reveal its content, but the tree adds items only incrementally, revealing only what the user intends to see. That way, the tree indicates the presence of any array elements or object attributes one step in advance, with the [+] designation beside the node as unexpanded.
I tested it as extensively as I could, and hope that you find use with it.
I tried to emulate as closely as possible to the way VO 2.8 does this. Like the functions in XSharp.RT.Debugger, it has to be either inserted within the code or called from a menu option. As long-time VO 2.8 users know, however, the feature seems to use a kind of hybrid control with the properties of both a listview and treeview. Since I didn't have that, I settled on a treeview instead. And since I can't include the actual form here, you need only construct one yourself with the two PROTECTs as listed in the class declaration for ViewObjects. One or more GLOBALs, or any combination of objects or arrays to be inspected, are included in an array designated as uExtra argument in ViewObjects.
Code: Select all
LOCAL oViewObjects AS ViewObjects
...
oViewObjects:=ViewObjects{,,,{<arrayOrObject1,arrayOrObject2,...}}
oViewObjects:Show(SHOWCENTERED)
I tested it as extensively as I could, and hope that you find use with it.
Code: Select all
CLASS ViewObjects INHERIT DATADIALOG
PROTECT oDCtv AS TREEVIEW
PROTECT oCCbtnExit AS PUSHBUTTON
//{{%UC%}} USER CODE STARTS HERE (do NOT remove this line)
PROTECT aObjectsInTree AS ARRAY
PROTECT nStopAdding AS BYTE
METHOD Init(oWindow,iCtlID,oServer,uExtra) CLASS ViewObjects
SELF:PreInit(oWindow,iCtlID,oServer,uExtra)
SUPER:Init(oWindow,ResourceID{"ViewObjects",_GetInst()},iCtlID)
oDCtv := TreeView{SELF,ResourceID{VIEWOBJECTS_TV,_GetInst()}}
oDCtv:HyperLabel := HyperLabel{#tv,"View Objects",NULL_STRING,NULL_STRING}
oCCbtnExit := PushButton{SELF,ResourceID{VIEWOBJECTS_BTNEXIT,_GetInst()}}
oCCbtnExit:HyperLabel := HyperLabel{#btnExit,"Exit",NULL_STRING,NULL_STRING}
SELF:Caption := "View Objects"
SELF:HyperLabel := HyperLabel{#ViewObjects,"View Objects",NULL_STRING,NULL_STRING}
IF !IsNil(oServer)
SELF:Use(oServer)
ENDIF
SELF:PostInit(oWindow,iCtlID,oServer,uExtra)
RETURN SELF
METHOD PostInit(oWindow,iCtlID,oServer,uExtra) CLASS ViewObjects
LOCAL y AS BYTE
SELF:aObjectsInTree:={}
SELF:nStopAdding:=0
FOR y=1 UPTO ALen(uExtra)
IF IsObject(uExtra[y])
SELF:doObject(uExtra[y],#ROOT)
ELSE
SELF:doArray(uExtra[y],#ROOT)
ENDIF
NEXT
RETURN NIL
METHOD doArray(aArray,sParentName) CLASS ViewObjects
LOCAL sName AS SYMBOL
LOCAL y AS BYTE
LOCAL oTVI AS TreeViewItem
sName:=String2Symbol(PadL(SELF:oDCtv:ItemCount,6,"0"))
oTVI:=TreeViewItem{sName,"array",aArray}
SELF:oDCtv:AddItem(sParentName,oTVI)
FOR y=1 UPTO ALen(aArray)
SELF:doAttribute(sName,"["+NTrim(y)+"]",aArray[y],4)
NEXT
RETURN NIL
METHOD doAttribute(sParentName,cName,uValue,dwlInfo) CLASS ViewObjects
LOCAL sName AS SYMBOL
LOCAL cExp AS STRING
LOCAL y AS BYTE
LOCAL oTVI AS TreeViewItem
sName:=String2Symbol(PadL(SELF:oDCtv:ItemCount,6,"0"))
IF IsNil(uValue)
IF IsArray(uValue)
oTVI:=TreeViewItem{sName,cName+" NULL_ARRAY ",uValue}
ELSEIF IsString(uValue)
oTVI:=TreeViewItem{sName,cName+" NULL_STRING ",uValue}
ELSEIF IsObject(uValue)
oTVI:=TreeViewItem{sName,cName+" NULL_OBJECT ",uValue}
ELSE
oTVI:=TreeViewItem{sName,cName+" nil ",uValue}
ENDIF
SELF:oDCtv:AddItem(sParentName,oTVI)
ELSEIF IsLogic(uValue)
oTVI:=TreeViewItem{sName,cName+" logic "+LTOC(uValue),uValue}
SELF:oDCtv:AddItem(sParentName,oTVI)
ELSEIF IsSymbol(uValue)
oTVI:=TreeViewItem{sName,cName+" symbol "+Symbol2String(uValue),uValue}
SELF:oDCtv:AddItem(sParentName,oTVI)
ELSEIF IsFloat(uValue)
oTVI:=TreeViewItem{sName,cName+" float "+NTrim(uValue),uValue}
SELF:oDCtv:AddItem(sParentName,oTVI)
ELSEIF IsLong(uValue)
oTVI:=TreeViewItem{sName,cName+" long "+NTrim(uValue),uValue}
SELF:oDCtv:AddItem(sParentName,oTVI)
ELSEIF IsNumeric(uValue)
oTVI:=TreeViewItem{sName,cName+" numeric "+NTrim(uValue),uValue}
SELF:oDCtv:AddItem(sParentName,oTVI)
ELSEIF IsDate(uValue)
oTVI:=TreeViewItem{sName,cName+" date "+DToC(uValue),uValue}
SELF:oDCtv:AddItem(sParentName,oTVI)
ELSEIF IsString(uValue)
oTVI:=TreeViewItem{sName,cName+" string "+CHR(34)+uValue+CHR(34),uValue}
SELF:oDCtv:AddItem(sParentName,oTVI)
ELSEIF IsArray(uValue)
oTVI:=TreeViewItem{sName,cName+" array",uValue}
SELF:oDCtv:AddItem(sParentName,oTVI)
IF SELF:nStopAdding<2
SELF:nStopAdding:=SELF:nStopAdding+1
FOR y=1 UPTO ALen(uValue)
SELF:doAttribute(sName,"["+NTrim(y)+"]",uValue[y],4)
NEXT
SELF:nStopAdding:=SELF:nStopAdding-1
ENDIF
ELSEIF IsObject(uValue)
cExp:=cName+" class "+Symbol2String(ClassName(uValue))
IF AScan(SELF:aObjectsInTree,uValue)>0
oTVI:=TreeViewItem{sName,cName+" class "+Symbol2String(ClassName(uValue))+" (circular reference)",uValue}
SELF:oDCtv:AddItem(sParentName,oTVI)
ELSEIF SELF:nStopAdding<2
SELF:nStopAdding:=SELF:nStopAdding+1
SELF:doObject(uValue,sParentName)
SELF:nStopAdding:=SELF:nStopAdding-1
ENDIF
ELSE // IF IsPtr(uValue)
oTVI:=TreeViewItem{sName,cName+" Other, assuming Pointer",uValue}
SELF:oDCtv:AddItem(sParentName,oTVI)
ENDIF
RETURN NIL
METHOD doObject(oObject,sParentName) CLASS ViewObjects
LOCAL sName,sAttrib AS SYMBOL
LOCAL y AS BYTE
LOCAL cExp AS STRING
LOCAL oTVI AS TreeViewItem
LOCAL aVarList:=IvarList(oObject)
cExp:="CLASS "+Symbol2String(ClassName(oObject))
sName:=String2Symbol(PadL(SELF:oDCtv:ItemCount,6,"0"))
AAdd(SELF:aObjectsInTree,oObject)
oTVI:=TreeViewItem{sName,cExp,oObject}
SELF:oDCtv:AddItem(sParentName,oTVI)
FOR y=1 UPTO ALen(aVarList)
sAttrib:=aVarList[y]
SELF:doAttribute(sName,Symbol2String(sAttrib),IVarGet(oObject,sAttrib),IVarGetInfo(oObject,sAttrib))
NEXT
RETURN NIL
METHOD TreeViewMouseButtonDoubleClick(oTreeViewMouseEvent) CLASS ViewObjects
LOCAL sName,sAttrib AS SYMBOL
LOCAL y AS BYTE
LOCAL uValue AS USUAL
LOCAL oTVI,oTVI2,oTVI3 AS TreeViewItem
LOCAL aVarList AS ARRAY
LOCAL oControl AS Control
oControl:=IF(oTreeViewMouseEvent==NULL_OBJECT,NULL_OBJECT,oTreeViewMouseEvent:Control)
SUPER:TreeViewMouseButtonDoubleClick(oTreeViewMouseEvent)
oTVI:=SELF:oDCtv:GetSelectedItem()
sName:=oTVI:NameSym
uValue:=oTVI:Value
IF(!IsArray(uValue).AND.!IsObject(uValue)).OR.IsObject(uValue).AND.AScan(SELF:aObjectsInTree,uValue)>0
RETURN NIL
ENDIF
oTVI2:=SELF:oDCtv:GetFirstChildItem(oTVI)
WHILE!oTVI2==NULL_OBJECT
IF IsArray(oTVI2:Value).OR.IsObject(oTVI2:Value).AND.AScan(SELF:aObjectsInTree,oTVI2:Value)>0
oTVI3:=SELF:oDCtv:GetFirstChildItem(oTVI2)
WHILE!oTVI3==NULL_OBJECT
IF IsArray(oTVI3:VALUE).AND.SELF:oDCtv:GetFirstChildItem(oTVI3:NameSym)==NULL_OBJECT
FOR y=1 UPTO ALen(oTVI3:VALUE)
SELF:doAttribute(oTVI3:NameSym,"["+NTrim(y)+"]",oTVI3:VALUE[y],4)
NEXT
ELSEIF IsObject(oTVI3:Value)
aVarList:=IvarList(oTVI3:VALUE)
FOR y=1 UPTO ALen(aVarList)
sAttrib:=aVarList[y]
SELF:doAttribute(oTVI3:NameSym,Symbol2String(sAttrib),IVarGet(oTVI3:Value,sAttrib),IVarGetInfo(oTVI3:Value,sAttrib))
NEXT
ENDIF
oTVI3:=SELF:oDCtv:GetNextSiblingItem(oTVI3)
ENDDO
ENDIF
oTVI2:=SELF:oDCtv:GetNextSiblingItem(oTVI2)
ENDDO
RETURN NIL
METHOD btnExit( ) CLASS ViewObjects
SELF:EndWindow()
RETURN NIL