The ClassDefinition object was introduced in PowerBuilder 6.0 a long time ago. It allows you to retrieve information for an object at runtime. Most of us didn't pay too much attention to this object and it only attracts our attention when we see it in the debugger.
In this article I provide an overview of the ClassDefinition object and related objects and explain the most important properties of these objects. I also include a step-by-step guide on how to build a simple object browser. This browser has a limited functionality like the browser included in the PowerBuilder runtime environment and can't replace products like PBLPeeper by Terry Voth or PBBrowser by OOWidgets.
The ClassDefinition Object Hierarchy
The classdefinitionobject is a descendant of the pbtocppobject. It's the ancestor of all the other objects used to describe the PowerBuilder objects. Each object in PowerBuilder has one property named ClassDefinition that references the ClassDefinition of this object.
The objects serve the following purpose:
- ArrayBounds: Information about the boundary of an array variable
- EnumerationItemDefinition: Name and value of an enumerated value
- ScriptDefinition: All information about a script
- ClassDefinition: Information about a class
- EnumerationDefinition: Information concerning an enumerated data type
- SimpleTypeDefinition: Information about the type of a scalar variable
- VariableCardinalityDefinition: Provides information to the cardinality of a variable
- VariableDefinition: All information of a variable, property, or argument
At first glance, this looks quite complex as we didn't even touch the properties of the various classes. To get a good start we need to take a closer look at the ClassDefinition object.
Create an application with a window. To access the ClassDefinition object of any object, without having to instantiate the object, use the function FindClassDefinition. The first argument of the function is the name of the class; the second argument is an optional array of strings defining a library list. If the second argument is omitted, the current library list is searched. Place the following code in the open event of the window. It gets the ClassDefinition object of the window using the name of the window class and displays the library name where your window object is stored.
This.Title = FindClassDefinition( ClassName() ).LibraryName
You can either run the window or open it in the application object. Running the application allows you to debug and inspect additional properties of the ClassDefinition object. I would categorize them into two groups. The first group contains properties describing the object. These are properties like Name, DataTypeOf, IsVisualType, etc. In the other group are properties that describe the data "within" the object such as NestedClassList, ScriptList, or VariableList and ParentClass for a NestedClass.
Add a treeview control to the window, check LinesAtRoot, and put the code from Listing 1(link at the bottom of the article) into the open event of the window. First, we retrieve the ClassDefinition object and use the property name to add the window name as a first entry in the treeview. Now we loop over every variable of the window (stored in VariableList) and add the name of the variable as a child to the window name entry. With these few lines we already see some information regarding the variables of this window.
In the script from Listing 1 we used the VariableList property (an array of VariableDefinitionObjects) that lists all the variables of a class. For a closer look at the nested classes of the window, use the NestedClassList property of the ClassDefinition object. The NestedClassList is an array of ClassDefinition objects describing the nested classes of an object (the treeview control in our example window). The code from Listing 2 shows the nested classes in the treeview. Play around by adding some more controls to the window. If you replace the NestedClassList in the script with the ScriptList property, which is an array of ScriptDefinition objects, the events and functions of the window are shown in the treeview.
Let's use the script from Listing 1 and add the initial value of the variable to the treeview label. Add the following code for the label in the InsertItemLast function and run the application.
+ ': ' + String( lcd_Window.VariableList[ll_Counter].InitialValue)
Behind each variable you'll see the initial value of the variable as long as the variable is a simple type and not an enumerated type, for example, toolbaralignment.
After getting an idea on how the ClassDefinition object works, we start working on the browser (see Figure 2). The application is an MDI application that allows analyzing more than one PowerBuilder application at once.
First we create a MDI frame window (w_frame) with a menu (m_frame) and a sheet window (w_main). Choose either an application icon or a library-related icon for the windows. The sheet requires no menu on its own. The main menu should include an entry File/Open and File/Exit. You can either create this from scratch or use the Template Application Wizard in the PowerBuilder IDE.
Getting the Library List
Before we can analyze an application we have to choose one and determine its library list. The application target file (PBT) contains the information that's needed. First we create an event on the frame window called ue_opentarget. Place the code from Listing 3 in this event. The user can choose a target file in this event and the window w_main will be opened with the file name passed to it as an argument. Call the event ue_opentarget from the frame menu File/Open menu item. On the window w_main, declare an instance variable called il_LibList of type string array. Next, create a function of_ParseLibList to get the application name and library list from the target file. The PBT file is a text file that contains the definition of a target. The line begins with a keyword followed by a space and the data, whereas the data is enclosed in double quotes. The following lines show the PBT file for the object browser.
Save Format v3.0(19990112)
For our application we need the keyword appname to determine the name of the application and the keyword liblist for the library list. In Listing 4 we parse the PBT file by looping through each line and check for the key appname. As soon as we find it, we remove the double quotes and set the title of the window to the name of the application. We continue looping until the key liblist is found. Having found the key, we can leave the loop and start parsing the line found. Each PBL entry is separated by a semicolon. We split the line and store each library as an entry in the il_LibList array together with the directory where it's located. Using PFC would have made some tasks easier but I want to keep the application small.
Place a treeview control with LinesAtRoot checked on the left half of the window w_main and a multilineedit control on the right half of the window. You may add some code to resize them dynamically when the window size changes. We need quite a lot of treeview pictures in the application. Set the property PictureMaskColor to silver and define the pictures in the treeview and the picture constants in the instance variables section according to Table 1 (in the listings file). Since we used the last picture of the control as an overlay picture, place the following code in the constructor event of the treeview control:
In the open event of the window w_main call the function of_ParseLibList. We pass message.stringparm as an argument to this function as this is the target file passed to the window w_main on opening it. The code to call the function and fill the treeview can be found in Listing 5. The name of the library is shown without path information as the path information is stored in the data property of the treeviewitem. The children property is set to TRUE to show the plus sign in front of the library entry. This allows the coding the drill down in the itemexpanding event.
In the next article we'll continue to work on the browser. We'll read the objects from the PBL and drill down on them.
Download all the source from our website at: https://www.dropbox.com/s/odksu9rsgttqxzy/SourceClassDefinition.txt?dl=1