Tech Articles


Getting Internal Info for the Current Window at Runtime


TITLE – Window Internals at runtime

“OK, what window are you in?”

“The Spanner Controller window.”

So here I’m shooting for the browser, w_spanner_controller? Nope, no w_spanner anything. W_controller_spanner? Nope, no w_controller anything.

“OK, what’s in the titlebar?” I ask, my frustration growing.

“Untitled, what does that mean?”

Have you ever worked on a system that had literally hundreds of windows with names that are not intuitive? I’ve seen names like w_a13_6.

There are times that at runtime we would love to get information about a window from the user. If only there were a way…

This solution requires a two prong approach. It requires that you have a base class window and a base class menu (see the NOTE at the bottom). Let’s start with the window.

W_base.ue_internals

You need to create a user event that you will call from the menu (that’s why we start with the window, so the menu will have something to call).

I called my event ue_internals but of course you can call it whatever you like, flying_coat_hangers leaps to mind.

Now add the following code. Note that I’ve left out the header comments that I always provide. I did that so that you don’t have to wade through all that.

  1. string ls_display, ls_ancestors[]
  2. classDefinition lcd
  3. lcd = this.classDefinition
  4. classDefinition lcd_ancestor
  5. lcd_ancestor = create classDefinition

 

  1. string ls_name

 

  1. ls_name = lcd.name
  2. ls_display = "Internal structure of " + lcd.libraryname + "." + this.title + " (" + ls_name + ")~r~n~r~n"
  3. // window heirarchy
  4. ls_ancestors[1] = lcd.name
  5. lcd_ancestor = lcd.ancestor
  6. LS_display += "WINDOW HEIRARCHY~r~n"
  7. do while lcd_ancestor.name <> "window"
    1. ls_ancestors[upperbound(ls_ancestors) + 1] = lcd_ancestor.name
    2. lcd_ancestor = lcd_ancestor.ancestor
  8. loop

 

  1. int li_max, li_count
  2. li_max = upperbound(ls_ancestors)
  3. for li_count = li_max to 1 step -1
    1. ls_display += ls_ancestors[li_count]
    2. if li_count <> 1 then ls_display += "->"
  4. next
  5. ls_display += "~r~n~r~n"

 

  1. // Show the menu for this window
  2. ls_display += "Menu: " + this.menuname + "~r~n"

 

  1. // I added an instance array of string called is_debug_messages
  2. // so that at runtime I can display them here, cool?
  3. int li_messages, li_max_messages
  4. li_max_messages = upperbound(is_debug_messages)
  5. if li_max_messages > 0 then
    1. ls_display += "DEBUG:" + "~r~n"
    2. for li_messages = 1 to li_max_messages

i.ls_display += is_debug_messages[li_messages] + "~r~n"

  1. next
  2. ls_display += "~r~n"
  1. end if

 

  1. // Loop through the controls to find all the datawindows
  2. int li_control_count, li_current_control
  3. li_control_count = upperbound(control)
  4. datawindow ldw
  5. for li_current_control = 1 to li_control_count
    1. if control[li_current_control].typeOf() = datawindow! then

i.ldw = control[li_current_control]

ii.ls_display += "Datawindow: " + control[li_current_control].className() + "." + ldw.dataobject + "~r~n"

iii.ls_display += "Rows: [" + string(ldw.rowcount( )) + "]"

iv.ls_display += " Modified: " + string(ldw.modifiedcount( ))

v.ls_display += " Marked for deletion: " + string(ldw.deletedcount( )) + "~r~n~r~n"

  1. end if
  1. next

 

  1. // Final display. In my system I have a specialized kind of window that is prettier than a messagebox
  2. // and displays the text better with more flexibility.
  3. messagebox("Window Internals", ls_display)
  4. //openWithParm(w_messagebox, ls_display)

 

Let’s take a look at this code. I’ve numbered it so we can talk about it.

Line 3 defines a ClassDefinition object for this object (the window that you are in). That gives us some very handy information.

Line 4 gives me a ClassDefinition for my immediate ancestor.

You might notice that I create a classDefinition for the ancestor but I didn’t do that for the lcd that was populated in line 3. How is that? The classDefinition for THIS window comes with the window. It’s already in memory and doesn’t have to be created. That’s not so for the ancestor, it’s not in memory.

So now we come to line 8 where I start using ls_display. That’s a local string and I’m going to use it to display my internal structure. In line 8 I use that ClassDefinition (here called lcd) and I get the library where this window is stored. I add a dot and then the title of the window, followed by the name (that’s the name like w_client_update) in parenthesis. That gives me everything I need to identify and find that window. This is the first line in the figure that you see in the article. Do you notice that I put a “~r~n~r~n”? That gives me two ‘carriage return/new lines’

Now I go into a loop from lines 13-14. I use these to populate an array of window names that are all the ancestors for this window.

In lines 15-18 I loop through that array (backwards) and add each name to ls_display with a “->” between them

Line 21 gives me the name of my menu.

In my example I have no debug messages defined so they won’t appear. If I wanted to have them appear I could add this code wherever I choose:

Is_debug_messages[upperbound(is_debug_messages) + 1] = “This is a test”

 

Starting in line 28 I loop through each of the datawindows showing the pbl.name then info for each. This ends in line 33.

Finally in line 36 I show ls_display.

We open the base level menu. Add a top level item. I call mine invisibles. Set the visible attribute to FALSE. Now it and all of the submenus will not be visible to the user.

Add a submenu item. I call mine ‘internals’.

Here is the script for internals:

w_base lw

lw = parentWindow

lw.postevent("ue_internals")

The parentWindow is not likely to be a w_base as it is defined in the first line but that’s OK. We can define it as w_base as long as in this script we do not access anything that does not exist in a w_base.

The last thing is to give it a shortcut for ctrl-alt-d.

NOTE you don’t really need a base class menu but if you don’t then you need to add the invisible menu item to every menu that is used in a window wherein you want this functionality.

That’s all there is to it. Now when the user calls and says “I don’t know where I am or how I got there” you can just say, “hit ctrl-alt-D and send me a screen shot.”

Comments (1)
Tuesday, Jul 11 2017

Hi Rik

We have something similar in our applications that send the data to our support desk. We use this when error occur too as a self diagnosis tool. Mainly because users do not tell you they had an error and certainly cannot tell you what they were doing <img alt="smiley" src="https://community.appeon.com/profiles/commons/libraries/ckeditor/plugins/smiley/images/regular_smile.png" style="height:23px; width:23px" title="smiley" />

#7
0

Find Articles by Tag

WinAPI Database Table Configuration Database Table Schema TFS 32-bit Encoding SqlModelMapper Script ActiveX SQL SDK DevOps ODBC OrcaScript Visual Studio Trial Validation JSONParser API PowerServer Web Export .NET Std Framework XML SQL Server Encryption Icon Interface PowerBuilder Compiler Event Handler Debugging Messagging External Functions UI Modernization Testing PowerServer Mobile Open Source Database Connection C# PDF Export JSON DataType 64-bit Database Object Migration SqlExecutor Deployment BLOB PowerBuilder (Appeon) Repository SOAP Transaction Windows OS Database Painter Authentication OAuth 2.0 Outlook OLE iOS RibbonBar Builder PBNI Web Service Proxy TortoiseGit Filter SnapDevelop Branch & Merge Error Bug Design Jenkins TLS/SSL Event Windows 10 Icons CI/CD JSON DragDrop Excel PFC Database Table Data JSONGenerator Event Handling RibbonBar Linux OS DataWindow JSON Sort UI PostgreSQL Elevate Conference Performance Graph Installation Data WebBrowser Git Model CoderObject Window PBVM Source Control Oracle Array DLL Automated Testing Platform REST File .NET DataStore PowerBuilder Text Application Database Debugger Source Code Import JSON Database Profile Web API Import NativePDF .NET Assembly Menu IDE RichTextEdit Control Variable InfoMaker PowerScript (PS) UI Themes COM Syntax MessageBox Expression RESTClient Authorization Android OAuth TreeView Class DataWindow Mobile Azure Stored Procedure PBDOM Debug PostgreSQL ODBC driver Service SnapObjects CrypterObject PDFlib Resize GhostScript Charts HTTPClient SVN License