Matt Woodward
Microsoft Corporation
July 1999
Summary: In addition to several ancillary Microsoft Windows® CE development techniques, the Visual Basic® Task Manager sample demonstrates three fundamental concepts: creation of a Windows CE Automation object in Visual C++®; use of the Windows CE Automation object from Visual Basic; and manipulation of system and task resources in Windows CE. (8 printed pages)
Click here to copy the CETaskMan sample files.
Developers familiar with building applications for the Windows desktop or server platforms will find that building applications for Windows CE involves little additional training. Windows CE contains a subset of the Win32® API found on traditional Windows machines, giving developers a similar programming model and interface for constructing compelling mobile solutions for the next generation of computing devices.
In addition to several ancillary Windows CE development techniques, the Visual Basic Task Manager sample demonstrates three fundamental concepts:
After building the user interface, an Automation object is used for interfacing with the Windows Toolhelp APIs to retrieve and manage system processes. Visual Basic’s Declare statement is then used to interface to custom functions that are defined in a DLL, as well as directly calling Win32 APIs. Using an appropriate combination of these technologies, you can create a wide variety of programs that utilize the full functionality of the Windows CE operating system.
Note The sample application does not work under Windows CE Emulation because the emulation environments do not support the Toolhelp APIs.
The sample program consists of a small window containing a tab control. On the first tab is the process list. Each process’s executable file name, process identifier thread count, and image base address are listed in a list view control. The list is automatically updated whenever a process is started or terminated, or process information changes.
The second tab is a memory monitor. A graph of current memory usage and total available memory is maintained on this tab. You can use the memory monitor tab for monitoring system memory usage and detecting memory leaks in your running programs. Please note that, because system memory usage is constantly changing, several passes over a potentially leaky piece of code may be required to determine whether a memory leak exists or not.
Figure 1. Task Manager sample application task and memory tabs
Finally, a pair of Visual Basic PictureBox controls are used to create a status bar at the bottom of the application. This status bar contains an updated display of the total memory available on the system, as well as the current memory usage.
Warning Terminating processes can result in unexpected results, including loss of data or machine lockups. With this program, you can terminate any process, so please be careful. If you do accidentally terminate a system process, you can press the reset button on your Windows CE device to return your device operation to normal.
Code for the Visual Basic Task Manager is written using a combination of the Windows CE Toolkit for Visual C++ (VCCE) and the Windows CE Toolkit for Visual Basic (VBCE).
When the program starts, the frames and controls on the frames are resized to fit the current geometry of the form. On devices that have larger displays, the program displays in one basic size. On other devices, including the Palm-Size PC, the form is displayed maximized in full-screen mode.
Windows CE Automation objects are COM controls that exist on the Windows CE device. Currently, only the Windows CE Toolkit for Visual C++ can be used to construct COM Automation objects for Windows CE-based devices. However, once created, Automation objects can be used from both Visual C++ and Visual Basic applications.
An Automation object can be created to interface to a hardware device, for example a traffic controller. A traffic light object might have methods such as LetEastWestGo, and LetNorthSouthGo. It might support events for the traffic sensors in the street such as EastWestTrafficWaiting and NorthSouthTrafficWaiting. Using these methods and events, a simple traffic light controller could be developed.
An Automation object can also be used to put a simple interface on a protocol. For example, an object could be designed that makes requests to a stock quote service using TCP/IP. Since network traffic can take time, a multithreaded object could be used to allow your user interface code to react while a request is being made. This object might have methods like GetQuote(Symbol As String) and fire events like QuoteReceived(Symbol As String, Quote as Currency). When a requested quote is received, the user interface can be updated.
Automation objects can be used to extend the Windows CE Toolkit for Visual Basic whenever a rich object interface to a piece of software, hardware, GUI, or anything else you can imagine is needed.
The process list is maintained within an Automation object called ProcInfo. The ProcInfo object is a multithreaded object that asynchronously fires events back to the Visual Basic program to notify the program that something in the process list has changed. Whenever this event is fired, the list view is updated with the new process information.
Figure 2. Automation task manager
The method CProcesses::ThreadFunc controls the thread that does most of the work in the ProcInfo object. Every four seconds, the thread wakes up, locks down its resources temporarily and rebuilds the process list. As it is rebuilding the list, it is looking for changes in the list. If any changes are detected, an event is fired by the control. This event is received in the Visual Basic program in the Module1 (taskman.bas) in the subroutine Processes_Changed. Processes_Changed calls the frmTask.DisplayProcesses that updates the process list in the form.
void CProcesses::ThreadFunc()
{
InternalAddRef();
while(1)
{
HRESULT hr;
Sleep(2000);
LockSnapshot();
if(m_fThreadTerminate)
{
m_fThreadTerminate = FALSE;
m_tidUpdate = 0;
UnlockSnapshot();
InternalRelease();
ExitThread(0);
return;
}
hr = BuildSnapshot();
UnlockSnapshot();
if(m_fNotifyChanges && hr == S_OK)
{
// Call ATL-generated code to fire event
Fire_Changed();
}
}
}
It should be noted that this notification is only fired when the process list changes, not every two seconds. This technique of notification can be used for many things, including external hardware devices that have a feedback mechanism.
In order to use ProcInfo in your Visual Basic program, you must first create it. Since ProcInfo fires events, you should use the CreateObjectWithEvents function. CreateObjectWithEvents is unique to Windows CE development in Visual Basic, and allows you to dynamically create objects that fire events and be able to receive those events in your Visual Basic code. You can see how ProcInfo is created in the Form_Load method.
‘ create the object which browses the processes
‘ Processes_ is the prefix of the event handler subroutines
Set Processes = CreateObjectWithEvents(“Processes.Processes.1”, _
“Processes_”)
For further convenience, you can enable Visual Basic’s IntelliSense® feature on your newly created object by having Visual Basic load the type library for your object. This file is created when you build the Automation object in Visual C++. The file ends with the .tlb extension. Choose Project->References, and click the Browse… button.
In the base of your ProcInfo project, if you have already built it, you will find the file ProcInfo.tlb. Open this file in the browse dialog. When you have completed this step, the ProcInfo object will have full IntelliSense support.
Note Since the ProcInfo.tlb file will not exist the first time you load your Visual Basic project, you may get an error message. Ignore this message and load the type library using the method described above.
The memory information provided in this sample is generated using the Win32 API GlobalMemoryStatus. You can see the implementation of the two functions called by Visual Basic in ProcInfo.cpp in the MemConsumed and MemTotal functions. In the MemConsumed function, for example, the GlobalMemoryStatus API is invoked, returning data on the amount of memory currently in use by the system.
long MemConsumed()
{
MEMORYSTATUS MemStatus;
MemStatus.dwLength = sizeof(MemStatus);
GlobalMemoryStatus(&MemStatus);
return MemStatus.dwTotalPhys - MemStatus.dwAvailPhys;
}
The task information is stored in the list view control. The key of the item is the actual process ID. The Visual Basic program passes this handle back to the ProcInfo object, where it is a simple task to use OpenProcess to terminate the process.
STDMETHODIMP CProcesses::TerminateProcess(long ProcessID)
{
HANDLE hProcess;
hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, ProcessID);
if(hProcess != NULL)
{
::TerminateProcess(hProcess, 0);
}
return S_OK;
}
Applications built using the Windows CE Toolkit for Visual Basic are able to call some DLL functions through the Declare syntax. Declare is used to describe to Visual Basic any function or subroutine written in a DLL. Since applications built using VBCE’s Declare syntax cannot pass and retrieve structures, some APIs, like GlobalMemoryStatus, can be difficult to access.
Formerly, an Automation object was required to access these APIs. While useful for some things, an Automation object is overkill for simple API access. To this end, writing small DLLs to access APIs that cannot be reached from Visual Basic programs can be extremely useful.
Typically, a DLL that contains a very small amount of code like MemConsumed and MemTotal can be less than 5 kilobytes (K) in size, providing compact and powerful access to Win32 API functions. MemConsumed and MemTotal have been included in the ProcInfo DLL only for convenience.
Creating a DLL like this is easily supported by VCCE. Simply run VCCE, select the New item from the File menu, and navigate to the Projects tab in the resulting dialog box. Then select the WCE Dynamic-Link Library project type. When VCCE asks what type of DLL to create, choose “A DLL that exports some symbols.”
This provides the base DLL functionality for you to add your own functions, such as MemConsumed and MemTotal. From Visual Basic, you can use Declare to access APIs or functions contained in user-created DLLs.
First, start Visual C++ and load the Workspace for the ProcInfo component.
To build the ProcInfo DLL, connect your Windows CE device to your desktop computer using Windows CE Services. Once you have a connection, choose the proper OS, CPU, and device in the ComboBoxes in the toolbar. From the build menu, select Build ProcInfo.DLL. The DLL will be automatically downloaded to your device and registered. It will then be ready for invocation from either a Visual Basic or a Visual C++ application.
In order to run the sample on your Windows CE-base device, you must first compile and download the application to the remote device. Remember, the application only works on a physical device, as emulation does not support the necessary APIs used in the sample.
Start Visual Basic and load the appropriate TaskMan project for your device class.
In project properties, make sure the proper device is selected. Press the Play button and wait a moment for the application to be downloaded and executed.