Welcome!

PowerBuilder Authors: Chris Pollach, Yeshim Deniz, Jayaram Krishnaswamy, Kevin Benedict, Avi Rosenthal

Related Topics: PowerBuilder

PowerBuilder: Article

Using Shared Objects to Graphically Display CPU and Memory Usage

Using Shared Objects to Graphically Display CPU and Memory Usage

If your application is long-running and uses PC resources intensively, it may be useful to display memory and processor usage in the application's window. It's a good idea to use graphics that look like those in the performance tab of the Windows NT Task Manager.
This article illustrates this technique.

It's clear that the main application window can't display resource usage; only the separate subtask can. I use the term subtask instead of thread to be exact. PowerBuilder implements multitasking through the use of shared objects. It spawns a new subtask whenever the SharedObjectRegister function is invoked. This subtask contains a separate system thread. You can be assured of it with the help of a utility that shows all threads of the process. I use a PSLIST freeware utility that can be downloaded from www.sysinternals.com.

So you've created the object belonging to the separate subtask, but you see the shared object is a nonvisual object. How do you make it manage visual GUI objects in the main window? The answer is child windows. If the shared object opens a child window, this window becomes part of the main window and, at the same time, it's managed by the independent subtask.

Multitasking Test
In my opinion the easiest way to understand some programming techniques is to study an example. That's why I've written the application called "Multitasking Test" (see Figure 1). In this test, I implement shared objects and some other useful tricks. You can download the PBL of the test from the PBDJ Web site at www.PowerBuilderJournal.com.

The test's main window contains three Statictext objects (I call them frames). They set coordinates where subtask's child windows will be placed to display memory and CPU usage and the subtask's timer. The main window also contains the information panel, which displays the PowerBuilder and Windows versions as well as your CPU type. Then there are a dw_1 Datawindow control (to retrieve data from the sample database) and a few buttons. The subtasks are created in the Open event script of w_perf main window. The window function wf_create_ object2 creates subtasks. The function wf_open_child2 passes the coordinates of the frames where the child window will be opened to the shared object. It also passes the other parameters, including the number of callback events (I used 1024, i.e., custom01). Note that almost all properties of the subtask's object are directly accessible from the main task (object is shared!) to the exclusion of w_main_obj - the main window object. This object can be passed as the instance of uo_parm user object. The of_open2 method is invoked through the function Post (see Listing 1).

After successfully opening a child window, the subtask sends a confirmation to the main task also using the Post method. After all subtasks notify the main task of a successful opening, the event link continues to initiate the main window, having initiated event opened. (See user object function of_open2 of uo_child_task object in Listing 2 and link event script of w_perf window in Listing 3.) All three subtasks (memory, timer, and CPU) are launched in the same way. The process of closing child windows is realized similarly with the use of the function of_close and the event link.

Synchronization of Timers
Some words about timers. In the second child window you can see two timers: one launched from the main task and the other from subtask. The subtask timer always runs independently of everything in the main window. The main task timer behaves differently.

If something is working in the main window - for example, the button Count 15 sec is pressed - the main timer doesn't show the time since the event of the timer doesn't have control when the processor is busy. But if both timers work, they should concur. The function wf_start_timer in Listing 4 is used to synchronize the timer ticks. This function launches timers after a set time period has passed.

Using DataWindow Objects to Show Graphs for CPU and Memory Usage
I've tried different ways to graphically display conditions using pictures from light and dark stripes - I'll call it the gauge. The most suitable and, I would say, elegant way is to use the DataWindow object. It can fulfill all the drawing work by itself, and most importantly, it changes the number of elements in the gauge automatically while changing its size. The DataWindow objects d_dashbar_mem and d_dashbar_proc are responsible for it in the test. Each of these objects contains five elements: two bitmaps - bar_dark and bar_light, two columns - total and current, and one computed field - used. Only the expressions for used are different. It's enough to execute only three statements to graphically display the current state of the measured value:

dw_1.object.current[1] = current_value
dw_1.object.total[1] = max_value
dw_1.SetRedraw(true)

The DataWindow will do the rest of the work by itself. For more details see the scripts of the timer event of w_graph_mem, inherited from the w_graph base class, in Listing 5. Of course, it's possible to construct the universal DataWindow object by adding some elements to it, but in this case we lose the performance, because the DataWindow object itself will waste more time. With the same purpose I used "first (x for page)" instead of "sum (x for all)" in the computed field expression. Through such maneuvers I managed to decrease CPU usage to 1-3% while drawing the gauge consisting of more than 200 elements.

Implementation of the Measure Algorithms of CPU Usage for Windows 95/NT
It's enough to call only one of the Windows API functions to obtain the value of memory usage (see the Timer event script for w_graph_mem). Unfortunately, obtaining the value of CPU usage is more complex. First of all, these are two different cases for Windows 95/98 and Windows NT/2000. In both cases you'll find these values in the Windows Registry. But if it's easy to do in the case of Windows 95/98 (see the Timer event script for w_graph_processor_95), you must become creative for Windows NT/2000. In the case of Windows NT/2000 you'll get not only one required value from Windows Registry but the whole block that contains a lot of various counters in which you must find the one you need. The search algorithm of the required counter in this block is rather complex. I've used C-language source code (many thanks to Oleg Bulychev) and translated it to PB script (see the GetCounter() window function of w_graph_processor_NT). You can find more information about performance counters in the Performance Data Helper section of MSDN and in the PDH.HLP file of the Windows NT Resource Kit.

A Simple Way to Resize/Move GUI Elements
My next intention was to provide a simple way to resize the child windows while the size of the main window is being changed. Thus, some window elements should change their size and others their place. So to solve the problem, in each tag property of the moveable and/or resizable element, I placed one or more descriptors: move-x, move-y, resize-x, and resize-y. For example, all frames that contain the child windows have the resize-y descriptor, and all the buttons have move-x, move-y descriptors, and dw_1 has move-y, resize-x descriptors. The frames also contain a Send descriptor that's followed by the handle value of the corresponding subtask (the Handle value returned by the subtask during opening is placed into the tag in the opened event script). Finally, there's one small script that provides all the necessary actions for all the elements that have descriptors (see Listing 6 of the Resize event script of w_perf). Pay attention to the way the child windows change their size. The Resize event script changes the frame's size, then sends a message to the subtask that changes the size of the child window.

Simple Progress Bar to Display the Records Retrieving Process
DataWindow control dw_1 in the bottom of the test's main window is included to demonstrate the changes of memory and CPU usage during the record retrieving process. I use Powersoft Demo DB V6 datasource for asynchronous retrieval of more than 8,000 records from the exam_xref_info table. There's one condition for performing asynchronous operations on the database: put something into the RetrieveRow event of the DataWindow object. This results in an appreciable delay of the record-reading process, but it allows you to show a progress meter.

It's best to use the progress bar. I use a pbar rectangle object together with t_rows StaticText and place them into the footer band of the dw_1. Adding the following lines of code into the RetrieveRow event script is enough to manage the progress bar.

If Mod (row, 100) = 0 Then
this.Object.pbar.width = &
Integer (Mod (row, il_row_max) / il_row_max * ii_pbar_max )
this.Object.t_rows.Text = String(row)
End If

Test Window Control Buttons
I placed some buttons into the main window to illustrate the behavior of the subtask's child windows:

  • Count 15 sec: A continuous loop loads the processor in 15 seconds. Loop counter changes are shown in every 1,000 cycles. The window timer doesn't run (display the time), since the timer event doesn't have control because the processor is busy. The timer of the subtask (thread) works and displays the time.
  • Yount 15 sec: This is the same as Count 15 sec, but with the addition of the Yield function into a loop. It enables the window timer event to take control and display time correctly.
  • Retrieve: Asynchronous reading of approximately 8,000 records from the table of a database. The Progress bar displays the process of reading. Both timers work.
  • Cancel: This interrupts the reading records from the table.
  • Clear: This clears the DataWindow of records that have been read.
  • Sleep 10 sec: This immerses a task into a sleeping condition by the call Sleep(10) function. The timer of the main window doesn't run. All interface elements of the main window aren't active. The subtask timer goes on working!
Conclusion
PowerBuilder's opportunities aren't limited to those described in the documentation. The use of shared objects for subtask creation is only one example of unusual applications. Any of the objects in MTest.pbl can be used, free, in your applications. Good luck.

More Stories By Andrew Khromtchenkov

Andrew Khromtchenkov, developing in PowerBuilder since 1997, specializes in client/server technologies and Oracle DBMS in Tula City, Russia. He's a member of the team developing and supporting RABIS-2 (Regional Automated Bank Information System) for the Bank of Russia.

Comments (0)

Share your thoughts on this story.

Add your comment
You must be signed in to add a comment. Sign-in | Register

In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.