Copyright: Copyright © MASM32 1998 - 2003 All Rights Reserved


In Memory Dialogs

A system for coding dialogs without a dialog editor

Introduction

What Are In Memory Dialogs ?

How Do They Work ?

Dialog Base Units

Loading The Common Control Library

Sending Extra Data

Register Preservation

Reference

Dialog

Controls

CallModalDialog

DlgProc

Example

Bare Bones Dialog

Code To Frame Dialogs

Putting A Dialog In A Library Module

Setting The Colour Of A Dialog


What Are In Memory Dialogs ?

In memory dialogs are an operating system defined method of building dialog boxes at runtime in memory rather than creating a resource definition file manually or using a dialog editor. This technique has the advantage that it can be created wholly in code without the need for an external file and this makes it suitable for prebuilt dialogs that are contained in library modules.

The technique uses ANSI string data that is stored in the initialised data section and converted on the fly to UNICODE so there is less storage required as ANSI string data is half the size of UNICODE data.

Dialog editors are not always the best for accurately laying out a dialog box as they are dependent on visual control where a fully coded dialog box can be aligned perfectly because it has position control in dialog units in each statement that creates either the dialog or a control.


How Do They Work ?

To create a dialog in memory means allocating enough memory for a dialog template and then writing data to that template for the dialog box and for each control that is required in the dialog box.

When all of the dialog and control data is written to the template, an API function "DialogBoxIndirectParam" that creates a MODAL dialog is called that uses the dialog engine in the operating system to run the dialog.

The logic required occurs in this order.

1.	The dialog definition statement.

2.	Control definition statement(s).

3.	The statement that calls the operating system to load the dialog template.

4.	Write a DlgProc procedure to handle messages for the dialog and controls.

The statements that are used are macros written in MASM that allocate the memory for the template, write the dialog definition, write any control definitions and finally call the function that uses the operating system dialog engine to load the dialog.

Example

; The dialog definition statement

    Dialog "Dialog Caption","MS Sans Serif",10, \

            WS_OVERLAPPED or WS_SYSMENU or DS_CENTER, \

            2, \

            50,50,150,80, \

            1024

; Control definition statements

    DlgButton "OK",WS_TABSTOP,48,40,50,15,IDCANCEL

    DlgStatic "Dialog Written In MASM32",SS_CENTER,2,20,140,9,100

; Call the operating system to load the dialog template

    CallModalDialog hInstance,0,dlgproc,NULL

; The DlgProc message handling procedure

dlgproc proc hWin:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD

    .if uMsg == WM_INITDIALOG

      invoke SendMessage,hWin,WM_SETICON,1,FUNC(LoadIcon,NULL,IDI_ASTERISK)

    .elseif uMsg == WM_COMMAND

      .if wParam == IDCANCEL

        jmp quit_dialog

      .endif

    .elseif uMsg == WM_CLOSE

      quit_dialog:

      invoke EndDialog,hWin,0

    .endif

    xor eax, eax

    ret

dlgproc endp

Example


Dialog Base Units

Dialogs are different from windows created with CreateWindowEx in that they use what are called dialog base units instead of direct screen based pixels to set the size of the dialog window and the controls used in the dialog.

The advantage of using dialog base units is that they will have the same appearance on screens that have different resolutions and aspect ratios.

The method of determining the size of a dialog base unit is based on the average size of the font used in the dialog. A horizontal unit is one quarter of the average width of the font used and a vertical unit is one eighth of the average height of the font used.


Loading The Common Control Library

To use the common controls, you must load the common control library. Sme of the earlier versions can be loaded with the API function InitCommonControls but later versions require the extended version that can specify the type of control to be loaded.

Example

    LOCAL icce:INITCOMMONCONTROLSEX

    mov icce.dwSize, SIZEOF INITCOMMONCONTROLSEX

    mov icce.dwICC,  ICC_DATE_CLASSES

    invoke InitCommonControlsEx,ADDR icce

In the dwICC member of the INITCOMMONCONTROLSEX structure there are a range of styles you can use to specify what controls should be loaded from the common control library

    ICC_ANIMATE_CLASS       Load animate control class.

    ICC_BAR_CLASSES         Load toolbar, status bar,

                            trackbar, and tooltip control classes.

    ICC_COOL_CLASSES        Load rebar control class.

    ICC_DATE_CLASSES        Load date and time picker control class.

    ICC_HOTKEY_CLASS        Load hot key control class.

    ICC_INTERNET_CLASSES    Load IP address class.

    ICC_LISTVIEW_CLASSES    Load list view and header control classes.

    ICC_PAGESCROLLER_CLASS  Load pager control class.

    ICC_PROGRESS_CLASS      Load progress bar control class.

    ICC_TAB_CLASSES         Load tab and tooltip control classes.

    ICC_TREEVIEW_CLASSES    Load tree view and tooltip control classes.

    ICC_UPDOWN_CLASS        Load up-down control class.

    ICC_USEREX_CLASSES      Load ComboBoxEx class.

    ICC_WIN95_CLASSES       Load animate control, header, hot key,

                            list view, progress bar, status bar, tab,

                            tooltip, toolbar, trackbar, tree view,

                            and up-down control classes.


Sending Extra Data

Part of the operating system for modal dialogs is the capacity to pass extra data to the message handling procedure. The macro CallModalDialog has as its last parameter, the lpExtra member that can be used to pass a single DWORD sized value to the DlgProc style procedure that handles the messages for the dialog.

The way to pass multiple values with this single DWORD value is to put all of the values that need to be passed into an array and pass the address of the array. The array can be created in different ways depending on what needs to be passed to the dialog, it can be done by simply passing the address of the first parameter on the stack which is effectively an array of parameters.

You can make a local DWORD array and fill it with the values you require and pass its address or you can dynamically allocate memory with a function like GlobalAlloc() and do the same.

A concept that will be familiar to most C programmers is  "indirection". When an address is placed into a variable, the address of that variable is an "indirect" reference to the address that is contained in that variable. This establishes what is called a level of indirection which in this case is one level of indirection. If you keep track of the levels of indirection, you can have multiple levels that allow a large range of data to be passed in a single variable.

In operation, this is simpler than it sounds, if you load an address into a variable with methods like,

    lea eax, stack_variable

or

    mov eax, OFFSET data_section_variable

    push eax

or in an invoke statement,

    ADDR variable

when you receive the parameter at the other end in a procedure, it is an "indirect" reference to the address that was originally passed. To access this address, you need to dereference the information that is received in the procedure to use it.

In the case of a DlgProc style procedure for a modal dialog box, the extra data is accessed within the DlgProc through the lParam of the WM_INITDIALOG message that is sent to the DlgProc at startup.

    .if uMsg == WM_INITDIALOG

      mov eax, lParam     ; put address in EAX

      mov ecx, [eax]      ; dereference it for 1st parameter

    ; value of 1st parameter is now in ECX

      mov edx, [eax+4]    ; 2nd parameter

    ; value of 2nd parameter is now in EDX

If this information is required for the life of the dialog and not just when it starts, it can be written to the "GWL_USERDATA" memory address in the dialogs memory with a call to SetWindowLong as follows.

    invoke SetWindowLong,hWin,GWL_USERDATA,lParam

When it is required later in the life of the dialog, this value can be retreived by the alternate API function GetWindowLong as follows.

    invoke GetWindowLong,hWin,GWL_USERDATA

Then you simply dereference the return value in EAX to get the value you are after.

Example

If the calling procedure for the DlgProc message handler has for example 3 parameters,

ProcName proc hParent:DWORD,hIcon:DWORD,buffer:DWORD

If you pass the address of the first parameter "hParent" in the member of the  CallModalDialog statement lpExtra, as follows,

    CallModalDialog hInstance,hParent,DlgProc,ADDR hParent

Once it is written to the window memory location GWL_USERDATA in the WM_INITDIALOG processing, you get back individual values as follows.

    invoke GetWindowLong,hWin,GWL_USERDATA

    mov ecx, [eax]    ; hParent

    mov edx, [eax+4]  ; hIcon

    mov ebx, [eax+8]  ; address of buffer


Register Preservation

Under normal circumstances, the macros that are used to define and create the dialog handle the preservation of registers correctly and there is no need to perform any other register preservation. The exception occurs if any code is manually written between the Dialog macro and the CallModalDialog macro as this may interfere with the two registers used by the macros.

The Dialog macro preserves the ESI and EDI registers which are subsequently used in the dialog definition code and the control definition code. If either of these registers are modified before the CallModalDialog macro is called, there is a risk of a serious crash that is often fatal in win9x. The CallModalDialog macro restores the two registers after the dialog has closed.

It is not recommended that code be written between the two macros but if there is some need to write code in this area, it is critical that the ESI and EDI registers are preserved and restored correctly.

    push esi

    push edi

    ; write your code here

    pop edi

    pop esi

This will ensure that the data contained in the two registers is not corrupted.


Dialog

The "Dialog" macro preserves the ESI and EDI registers, allocates the memory for the dialog template and writes the definition of the dialog to the template. The top X Y coordinates and the width and height are specified in dialog base units.

    Dialog "MASM32 Modal Dialog", \    ; dialog title

           "MS Sans Serif",8, \        ; font name & point size

           WS_OVERLAPPED or \

           WS_SYSMENU or DS_CENTER, \  ; window style

           3, \                        ; control count

           50,50, \                    ; top X Y coordinates

           150,80, \                   ; width and height

           1024                        ; buffer size to allocate

Note that if the DS_CENTER style is used that the top X Y coordinates are ignored and the dialog is centred at the width and height set in the following two parameters.


Dialog Controls
Controls

The available controls break up into two seperate groups, those that are supplied directly by the operating system and those that are provided in the common control library. Note that the top X Y coordinates and the width and height are specified in dialog base units.

Normal controls

      DlgBitmap     BmpID,tx,ty,ctlID

      DlgButton     quoted_caption,dstyle,tx,ty,wd,ht,ctlID

      DlgCheck      quoted_caption,dstyle,tx,ty,wd,ht,ctlID

      DlgCombo      dstyle,tx,ty,wd,ht,ctlID

      DlgEdit       dstyle,tx,ty,wd,ht,ctlID

      DlgGroup      quoted_caption,tx,ty,wd,ht,ctlID

      DlgIcon       IconID,tx,ty,ctlID

      DlgList       dstyle,tx,ty,wd,ht,ctlID

      DlgRadio      quoted_caption,dstyle,tx,ty,wd,ht,ctlID

      DlgScroll     dstyle,tx,ty,wd,ht,ctlID

      DlgStatic     quoted_caption,dstyle,tx,ty,wd,ht,ctlID

Common Controls

      DlgAnimate    dstyle,tx,ty,wd,ht,ctlID

      DlgDateTime   dstyle,tx,ty,wd,ht,ctlID

      DlgHeader     dstyle,tx,ty,wd,ht,ctlID

      DlgHotkey     dstyle,tx,ty,wd,ht,ctlID

      DlgIPAddress  dstyle,tx,ty,wd,ht,ctlID

      DlgListView   dstyle,tx,ty,wd,ht,ctlID

      DlgMonthCal   dstyle,tx,ty,wd,ht,ctlID

      DlgProgress   dstyle,tx,ty,wd,ht,ctlID

      DlgRebar      dstyle,tx,ty,wd,ht,ctlID

      DlgRichEdit   dstyle,tx,ty,wd,ht,ctlID

      DlgStatus     ctlID

      DlgTabControl dstyle,tx,ty,wd,ht,ctlID

      DlgTreeView   dstyle,tx,ty,wd,ht,ctlID

Loading The Common Control Library

Parameter types.

1. quoted_caption for controls of the type that accept text, the text must be enclosed in quotation marks. EXAMPLE : "Caption"

2. dstyle Window styles for seperate controls. These are documented in the Microsoft literature under "CreateWindow" for the normal controls and with the reference material for the later common controls in MSDN and the PlatformSDK.

3. tx, ty, wd, ht The four (4) coordinates for the control in dialog units. Note that wd and ht are the WIDTH and HEIGHT of the control.

4. ctlID The ID number for each control. Equates can be used but they must resolve to a number, named ID's are not supported.

5. IconID The resource ID number of the icon that is stored in the file resources. Note that you cannot use an icon handle for this parameter.

6. BmpID The resource ID number of the bitmap that is stored in the file resources. Note that you cannot use an bitmap handle for this parameter.


CallModalDialog

The "CallModalDialog" macro loads the template into the operating system dialog engine using the API function "DialogBoxIndirectParam" , releases the memory allocated in the Dialog macro after the dialog is closed and restores the ESI and EDI registers.

Example

    CallModalDialog hInstance, \   ; the Instance handle

                    hParent, \     ; the parent handle

                    DlgProc, \     ; name of dialog proc

                    ADDR lpArgs \  ; optional argument(s)

If the dialog is the main window in the application, the parent handle should be zero, the DlgProc parameter is the NAME of the message handling procedure for the dialog and the optional parameter can contain whatever you require. If you don't require any additional information passed to the message handler, this parameter should be NULL.


DlgProc

The DlgProc is the procedure where messages are processed in a modal dialog box. The name DlgProc is a place holder for any unique name you require for your application.

Example

MyDlgProc proc hWin:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD

    .if uMsg == WM_INITDIALOG

      ; if you have passed a value in the last

      ; parameter of the CallModalDialog macro,

      ; it is received here as the "lParam"

      ; associated with the WM_INITDIALOG

      ; message.

      ; ------------------------------------

      ; process any other messages required

      ; for your application here.

      ; .elseif uMsg == WM_COMMAND etc ...

      ; ------------------------------------

    .elseif uMsg == WM_CLOSE

      quit_dialog:

      invoke EndDialog,hWin,0

    .endif

    xor eax, eax

    ret

MyDlgProc endp


Bare Bones Dialog

For writing a quick dialog, just copy this code below into Quick Editor, save it as a ASM file name and it will build into a basic dialog file.

When you paste this code into Quick Editor, check to ensure that the formatting is correct.

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

      .486                      ; create 32 bit code

      .model flat, stdcall      ; 32 bit memory model

      option casemap :none      ; case sensitive

;     include files

;     ~~~~~~~~~~~~~

      include \masm32\include\windows.inc

      include \masm32\include\masm32.inc

      include \masm32\include\gdi32.inc

      include \masm32\include\user32.inc

      include \masm32\include\kernel32.inc

      include \masm32\include\Comctl32.inc

      include \masm32\include\comdlg32.inc

      include \masm32\include\shell32.inc

      include \masm32\include\oleaut32.inc

;     libraries

;     ~~~~~~~~~

      includelib \masm32\lib\masm32.lib

      includelib \masm32\lib\gdi32.lib

      includelib \masm32\lib\user32.lib

      includelib \masm32\lib\kernel32.lib

      includelib \masm32\lib\Comctl32.lib

      includelib \masm32\lib\comdlg32.lib

      includelib \masm32\lib\shell32.lib

      includelib \masm32\lib\oleaut32.lib

      FUNC MACRO parameters:VARARG

        invoke parameters

        EXITM <eax>

      ENDM

      include \masm32\include\dialogs.inc

      dlgproc PROTO :DWORD,:DWORD,:DWORD,:DWORD

    .data?

        hInstance dd ?

    .code

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

start:

      mov hInstance, FUNC(GetModuleHandle,NULL)

      call main

      invoke ExitProcess,eax

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

main proc

    Dialog "Bare Bones Dialog", \           ; caption

           "MS Sans Serif",10, \            ; font,pointsize

            WS_OVERLAPPED or \              ; styles for

            WS_SYSMENU or DS_CENTER, \      ; dialog window

            2, \                            ; number of controls

            50,50,150,80, \                 ; x y co-ordinates

            1024                            ; memory buffer size

    DlgButton "Cancel",WS_TABSTOP,48,40,50,15,IDCANCEL

    DlgStatic "Bare Bones Dialog Written In MASM32", \

              SS_CENTER,2,20,140,9,100

    CallModalDialog hInstance,0,dlgproc,NULL

    ret

main endp

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

dlgproc proc hWin:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD

    .if uMsg == WM_INITDIALOG

      invoke SendMessage,hWin,WM_SETICON,1,

                         FUNC(LoadIcon,NULL,IDI_ASTERISK)

    .elseif uMsg == WM_COMMAND

      .if wParam == IDCANCEL

        jmp quit_dialog

      .endif

    .elseif uMsg == WM_CLOSE

      quit_dialog:

      invoke EndDialog,hWin,0

    .endif

    xor eax, eax

    ret

dlgproc endp

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

end start


Code To Frame Dialogs

To create a frame around the outer border of a dialog box, allocate the following LOCAL variable in the Dlgproc message handling procedure for the dialog.

    LOCAL hDC   :DWORD

    LOCAL ps    :PAINTSTRUCT

    LOCAL rct   :RECT

Process the WM_PAINT message with the following code to draw the frame around the outer border of the dialog box

    .elseif uMsg == WM_PAINT

      invoke BeginPaint,hWin,ADDR ps

      mov hDC, eax

      invoke GetClientRect,hWin,ADDR rct

      invoke DrawEdge,hDC,ADDR rct,EDGE_ETCHED,BF_RECT

      invoke EndPaint,hWin,ADDR ps


Putting A Dialog In A Library Module

Dialogs coded dynamically have the advantage that they can be placed in a library module for reuse. It is not uncommon to use the same dialog in a number of different programs and rather than re-inventing the wheel each time or having to re-create a dialog in a dialog editor, having the dialog prebuilt means you need to do no more than make a library call to the dialog procedure that is already built in a library.

If the dialog requires resources like icons or bitmaps, these will still have to be passed to the dialog through normal parameters as they cannot normally be stored within a library module. There are a few considerations that are different to coding a normal dialog within an application.

For reasons of scope, you cannot access things like the handle of the parent dialog or the instance handle or other values that are set as GLOBAL values within an application. You can obtain both the instance and parent handles dynamically with the API calls, GetParent() for the parent handle and GetModuleHandle() for the instance handle but it is generally easier to pass the values as parameters because they can also be passed to the DlgProc with a single address.

It is a viable technique to store the parameters passed to the dialog in the window memory for user defined data. The following API function called from the WM_INITDIALOG shows how it is done.

      invoke SetWindowLong,hWin,GWL_USERDATA,lParam

If you do need to have a variable with scope between the dialog creation procedure and the DlgProc or a subclass of one of the controls on the dialog, you can write the value in the .DATA or .DATA? section of the library module so that it is fully encapsulated within the module.

The purpose of this technique is to keep the dialog fully self contained so that the only data you need to manage when you use the dialog from a library module is the prototype and this is easy to do in an include file.

Below is the skeleton of a library module. In MASM32 you build a library of one or more modules with a batch file that contains the following lines.

\masm32\bin\ml /c /coff *.asm

\masm32\bin\lib *.obj /out:yourlib.lib

Place you module(s) in a directory with no other assembler files except the library module(s) you wish to build and run the batch file. You will need to code an include file that has the prototype(s) for the module(s) contained within the library.

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

      .486                      ; create 32 bit code

      .model flat, stdcall      ; 32 bit memory model

      option casemap :none      ; case sensitive

    ; include files

    ; macros

    ; prototypes etc ...

.data?

    variable dd 0 ?

.code

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

MyDialog proc parameters etc ...

MyDialog endp

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

DlgProc proc hWin:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD

    ; message processing code

    ret

DlgProc endp

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

end


Setting The Colour Of A Dialog

Place the WM_CTLCOLORDLG message in the DlgProc for the dialog and return a brush to the operating system bypassing the default DlgProc processing.

      case WM_CTLCOLORDLG

        invoke CreateSolidBrush,00FFAAAAh

        ret

The value for the CreateSolidBrush API is a DWORD sized COLORREF format value which works as follows,

    00BBGGRRh

where RR is the RED value, GG is the GREEN value and BB is the BLUE value.