SysInfo

Abstract

This sample demonstrates the following:
Creating a dialog with the GUI Builder
Working with resources
Creating a project and generating an executable
Using external resource scripts
Integrating Resource Compilation
 
This page contains step-by-step instructions on how to implement a complete dialog-based application that uses resources and a custom icon. 
Additional topics: Control reframing, separating development-time and runtime code

 

Creating the dialog 

Enter the code: 

Click on File|Browse Classes to open a Class Hierarchy Browser on the dialog. Add the code that fills the tree view to the initWindow method.

Create a Resource Script

Create a folder with the application name. Here, the application name is SysInfo, so you create a SysInfo folder that will hold the project and resources.

Generate a resource script

Open the GUI Builder, click on File|New and select Resource Script. Select the parent directory (SysInfo) into which you want the resources to be placed. The builder will create a Res subdirectory under the specified folder.

In the check prompter, check 

Click Ok to generate the files:

File Description
dummy.def Dummy file required to compile a resource DLL 
makefile Makefile for NMAKE
stmsg.bin Message file
sysinfo.ico Application icon
sysinfo.rc Resource script

You can now run nmake in the res subdirectory to build the resource DLL.

Create a Project

Open the Project Browser and drag the dialog class to the project contents pane. Save the project under the SysInfo folder.

Create a winmain.sm method that defines the entry point for your application:

!ApplicationProcess * methods!
winMain: hModule with: hPrevInstance with: cmdLineArgs with: nCmdShow
"
Public - Calls initialization function.
"
" init common controls if we use them "
WINAPI InitCommonControls.

" initialize COM "
IClassFactory coInitialize.

" open a modal dialog "
SystemInfoDialog new openOn: 0.

" uninitialize COM "
IClassFactory coUninitialize.
! !

The first line initializes the common control library (otherwise the TreeView could not be created). The second line initialize COM (the system info class uses COM to query properties). Finally, the third line opens the dialog. On exit we uninitialize COM.

You can now build and test the executable. 

Generating an RC file

Once the dialog is stable, generate an RC file as follows: 

  1. Open the GUI Builder on the dialog
  2. Click on File|Save As and select Dialog and Resource Template.
  3. Select the res folder as a target directory and enter the name of the dialog resource, e.g., dialog.rc.

The builder generates the file dialog.rc and modifies the dialog class so that it loads the dialog from the resource rather than from the in-memory template. The #initTemplate method is not used anymore.

Add the line below to the resource script SysInfo.rc:

RCINCLUDE dialog.rc

This line includes the dialog resource into the resource DLL. You can now recompile the DLL using NMAKE. When you build the executable, the dialog is loaded from the compiled resource. 

Modifying the dialog so that it loads from the resource DLL

After performing the steps above, the dialog works in the executable but not anymore in the development image, because the dialog attempts to load the resource DLL from the current module (which is stdev.dll by default). The solution is to load the resource DLL in the initialize method:

initialize
  m_module := HModule loadLibrary: 'sysinfo.dll'.
  ^super initialize 

After adding the method the dialog loads in the development image but will require sysinfo.dll at runtime, so clearly we don't want to execute this code at runtime. One possibility is to use conditional statements: 

_IS_DEVELOPMENT == TRUE ifTrue: [
    m_module := HModule loadLibrary: 'sysinfo.dll'.
]

The constant _IS_DEVELOPMENT is set to 1 (TRUE) in the development image and to 0 at runtime. The compiler optimizes the statement so that at development, the code is the same as in our first initialize method. At runtime the initialize method becomes:

initialize
  ^super initialize

Obviously the code to which the #initialize method defaults at runtime is not very useful, the best would be to remove it entirely. This is what the category .INTERNALDEV does: any method under this category is excluded from runtime. 

Therefore, the solution in this case is to set the #initialize category to .INTERNALDEV. However, if you performed other initializations in the method you would have to use the _IS_DEVELOPMENT test to separate runtime and development-only code.

Adding a custom icon

To add a custom icon and / or menu to a dialog, you must first register the dialog class. 

Registering the window class

This is done using the message #registerClass:. Behind the scenes, the implementation for dialog boxes is a little different. The registration code in DialogBox queries the parameters for the default dialog class, then replaces the icon, sets a new class name and registers the class. 

Add the following methods: 

registerClass
  |hmod|
  hmod := HModule loadLibrary: 'sysinfo.dll'.
  self registerClass: hmod.
  hmod close.

This method registers the class in the development environment. At runtime, this method is not called so we place it under the .INTERNALDEV category. The startup code in winmain.sm performs the registration at runtime:

!ApplicationProcess * methods!
winMain: hModule with: hPrevInstance with: cmdLineArgs with: nCmdShow
"
Public - Calls initialization function.
"
" init common controls if we use them "
WINAPI InitCommonControls.

" initialize COM "
IClassFactory coInitialize.

" register window class "
SystemInfoDialog registerClass: hModule.

" open a modal dialog "
SystemInfoDialog new openOn: 0.

" uninitialize COM "
IClassFactory coUninitialize.
! !

 

windowClassIcon
  ^IDI_APP

 

windowClassName
  ^'SysInfo'

Modifying the dialog template

The dialog template must specify the class name, which is done by adding the CLASS statement to the RC script: 

IDD_DIALOG1 DIALOGEX 0, 0, 301, 169
STYLE WS_POPUP|WS_CAPTION|WS_SYSMENU|WS_THICKFRAME|WS_VISIBLE|DS_MODALFRAME|DS_NOIDLEMSG|DS_SETFONT
CAPTION "System Info"
CLASS "SysInfo"
FONT 8, "MS Sans Serif"
BEGIN
CONTROL "",IDC_LIST1, "SysTreeView32", WS_CHILD|WS_BORDER|WS_VISIBLE|WS_TABSTOP|TVS_HASBUTTONS|TVS_HASLINES|TVS_LINESATROOT, 7,6,289,156, 
END

Integrating Resource Compilation with the Build 

By default, the builder binds the resources in the DLL in the Res subdirectory, if one is present. You can direct the builder to automatically build the DLL: 

  1. uncheck Use precompiled resource DLL in the Build properties
  2. enter the application name into the edit box above

This also has the advantage of generating a new message file with SysInfo (the application name entered into the edit box) as the aplication title.