Documents
Resources
Learning Center
Upload
Plans & pricing Sign in
Sign Out

Background

VIEWS: 83 PAGES: 139

									Windows Systems Prog                           Background Info




                    Basic and Background Reading

                        Information for WSP




Created by Tony G                             Page 1
Windows Systems Prog                     Background Info


Contains.

What are resources - Menu’s
What are resources – Dialogues
Graphics Interface
Windows Parent – Child Architecture
Windows Input / Output Techniques.
Windows 16 – 32 Bit Migration Issues.




Created by Tony G                       Page 2
Windows Systems Prog                                                  Background Info


General Comments.

The Information contained here is important to CP3010 WSP.

It represents most of the prerequisite knowledge required for this module.


You are strongly advised to read and review all of this information to ensure you are
prepared for both the assessment work and the final examination.

Tony Grimer Module leader CP3010.




Created by Tony G                                                    Page 3
Windows Systems Prog                                                  Background Info


What are Window resources
Creating and using menus
Most Windows programs include an icon that is displayed when the program is
minimised. Some programs use customised cursors to represent different operations of
the program. Most Windows programs use menus and dialogue boxes.
Icons, cursors, menus and dialogue boxes are examples of resources. It would be possible
for a programmer to write code in the program to generate all these resources from
scratch. This would of course make Windows programs even bigger than they are
already! Because nearly all Windows programs use resources (they are part of the GUI
interface), Windows has embedded in it, the necessary code to generate the resources for
a program. All the program has to do is supply the necessary data to describe the resource
to Windows. Windows then interprets this data and generates the icon or menu. The data
is stored in a programs .EXE file. When Windows loads a program it usually leaves the
resources on disc. Only when a particular resource is required will Windows load the
resource data into memory.
A full list of resources is given below.
   Icons
   Cursors
   Bitmaps
   Character strings
   User-defined resources
   Menus
   Keyboard accelerators
   Dialogue boxes
   Fonts

Icons
An icon is a bitmap that is displayed when the program is minimised. They can produced
by various graphics editors including “paintbrush”. The Word icon is shown below.




Cursors
A cursor is a bitmap that can produced by various graphics editors. The arrow and
hourglass are two examples. Programmers can use customised cursors to represent
different operations of the program. Graphics programs use cursors to tell the user what
drawing tool is currently being used.



Created by Tony G                                                   Page 4
Windows Systems Prog                                                     Background Info


The example below is the fill cursor from the Paint program.




Bitmaps
Bitmaps are used for two major purposes.
(1)   To draw pictures on the display. The arrows in scroll bars and check marks in menus
      are examples of bitmaps. Programs often use bitmaps on buttons to show their use.
      Below is an example from Word.




(2)   To create brushes. Brushes are patterns of pixels used to fill an area of a window.

Character strings
Character strings are used mainly for “internationalisation” of a program. If a program is
to be sold in a foreign country then the text of the program (instructions to users, menu
items etc.) will need to be translated. The text can be stored as resource strings in the
resource script rather than being hard coded into a program thus easing the translation
process. To generate the foreign language version of the program, all that is needed is to
relink the program and the add the translated resources to the .EXE file.

User-defined resources
User-defined resources can be used to attach any sort of data you want to the .EXE file.
The data can be in any format - its up to you.

Menus
Menus allow the user to control the operation of the program from a set of drop down
menus using the mouse. They are part of the “standard” windows interface (often called
look & feel).




Keyboard accelerators
Allows the programmer to associate actions with specific keys or key combinations. This
gives the user the choice between using the menu system or the accelerator keys, although
there does not have to be menu items for all accelerator keys. The most common use of
accelerator keys is on a programs Edit menu. Many programs use the combinations


Created by Tony G                                                       Page 5
Windows Systems Prog                                                  Background Info


Ctrl+Z, Ctrl+X, Ctrl+C and Ctrl+V for undo, cut, copy and paste. Another common
accelerator key is F1 for help.


Dialogue boxes
Dialogue boxes are used for obtaining additional information from the user beyond what
can be easily managed through a menu. Menu items that invoke dialogue boxes usually
have ellipsis (…) added to the text of the item. Below is an example taken from an
assessment.




Storing Resources
Resources are stored in the EXE or DLL file. Resources are NOT loaded when the
program is loaded into memory. Instead Windows loads the resource when it is actually
required.
Compiling Resources
A programmer defines the resources used by the program using a simple scripting
language. These script programs are called resource scripts and are stored as ASCII text
files with the extension .rc. In the early days of Windows the programmer had to produce
the script file by hand - a long and laborious process if the program used many resources.
Modern Windows development environment provides a very useful tool called a
“Resource Editor”, Borland‟s offering under Version 3.1 is called “Resource Workshop”.
These tools allow the menus, dialogues etc. to be designed and laid out visually on the
screen. The editor saves the resulting resource script in a .rc file.
Resource are compiled (converting the script into data to be interpreted by Windows) by
the resource compiler (rc.exe) and added to the .EXE file by the linker.

Remember that the running of the compiler, linker and resource compiler will be done for
you when you build your project.




Created by Tony G                                                    Page 6
Windows Systems Prog                                                 Background Info

                     app.rc         app.c            app.h




                    Resource              C
                    Compiler            Compiler




                                                              Many Windows
                    app.res             app.obj               Libraries (.lib)




                                            Linker




                                        app.exe




                               Creating and Using Menus

A window‟s menu bar is displayed immediately below the window caption bar. Items
listed in the top-level menu almost always invoke a drop-down menu, which is often
called a popup menu or submenu.
Menu items in popups can be checked - Windows draws check mark (usually a tick) next
to the item.
Menu items in the top-level menu or in popups can be enabled or disabled. Disabled
items are usually in outline but not coloured at all. Windows only send messages to your
program for enabled menu items.




Created by Tony G                                                   Page 7
Windows Systems Prog                                                   Background Info

                        Caption bar




        Menu bar




                                                                  Enabled

                Popup                                             Disabled




Menus and dialogue boxes may be created by hand by using a text editor to create the
.resource file (.RC) file. Such resources are defined using simple script language. The
normal way however is to use the resource editor tools provided by Microsoft or Borland
with their development environments. These tools usually include:
 A graphics oriented visual resource editor that allows the dialogue box or menu to be
  designed by positioning the various control (buttons, radio buttons, edit boxes, list
  boxes…) visually using the mouse.
 A text editor for manipulating text descriptions of resources.
 A resource compiler.

Creating a Simple Menu – ex1

Program ex1. Files ex1.c, ex1.rc and resource.h.
The menu to be created is shown below. There is one item on the menu bar and one item
in the popup menu.




Below is the script to produce this simple menu. It would be stored in a text file with the
.RC extension (ex1.rc). As you will see when you get to the example on dialogue boxes,
producing resource scripts by hand is extremely tedious for any but the simplest of
applications. Remember that they are normally generated using a resource editor, not by
hand!


Created by Tony G                                                     Page 8
Windows Systems Prog                                                 Background Info


       #include "resource.h"
       MYMENU MENU
       BEGIN
           POPUP "&Edit"
           BEGIN
           MENUITEM "Cu&t", IDM_CUT
           END
       END
or
       #include "resource.h"

       MYMENU MENU
       {
          POPUP "&Edit"
          {
              MENUITEM "Cu&t", IDM_CUT
          }
       }

The section of the menu script below constructs the menu bar and gives it the name
MYMENU (it is up to you what name you use).

       MYMENU MENU
       BEGIN
           ....
       END

When the program registers the class for the main client window it can pass the name of
the menu bar to Windows as one of the class members. In this example it is the name
MYMENU that would be passed. When the program is run, Windows would look in the
resource part of the .EXE file for a menu with the name MYMENU. The words MENU,
BEGIN ({) and END (}) are keywords in the scripting language and are recognised by the
resource compiler.

The popup or drop-down menu is created by the section of resource script below.

              POPUP "&Edit"
              BEGIN
                  MENUITEM "Cu&t", IDM_CUT
              END

POPUP, BEGIN, MENUITEM and END are keywords in the scripting language. The
string "&Edit” is used to specify the name on the menu bar of the popup menu. The name
that appears will be Edit with the E underlined. The ampersand is used to specify the key

Created by Tony G                                                   Page 9
Windows Systems Prog                                                   Background Info


the user can use with the ALT key to cause the drop-down menu to appear. By pressing
ALT+E the drop down menu will appear.
The identifier IDM_CUT is very important. It is this identifier that is sent by Windows to
the window procedure (usually called WndProc()) to tell the program which menu item
has been clicked on by the mouse or selected using the keyboard. The resource editor puts
#define‟s into a header file for each identifier in the resource file. In this case the file
resource.h has been used.

Using the Menu in a Program
The usual way a program tells Windows about the menu bar is when it registers the
window class for the client window.
     WndClass.style = CS_HREDRAW | CS_VREDRAW;
     WndClass.lpfnWndProc = WndProc;
     WndClass.cbClsExtra = 0;
     WndClass.cbWndExtra = 0;
     WndClass.hInstance = hInstance;
     WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
     WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
     WndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
     WndClass.lpszMenuName = "MYMENU";
     WndClass.lpszClassName = szAppName;

     RegisterClass(&WndClass);

Alternatively the menu resource can be loaded into memory using the function
LoadMenu().

     hMenu = LoadMenu(hInstance, “MYMENU”);
The menu handle hMenu can then be used when creating the window.
   HWndMain = CreateWindow(szAppName, szAppName,
                      WS_OVERLAPPEDWINDOW,
                      CW_USEDEFAULT, CW_USEDEFAULT,
                      GetSystemMetrics(SM_CXSCREEN)/2,
                      GetSystemMetrics(SM_CYSCREEN)/2,
                      NULL,
                      hMenu,
                      hInstance,
                      NULL);

Handling Menu Item Messages
As has already been said, when the user selects a menu item, Windows sends a message
to the window procedure of the program to identify which particular menu item was
selected. The message that is sent is WM_COMMAND.
Created by Tony G                                                  Page 10
Windows Systems Prog                                                  Background Info


When Windows sends a message to the windows procedure it is made up of four
components. These are the four parameters that are passed to the window procedure when
Windows calls it. Below is part of the prototype for the windows procedure (WndProc())
used by the example program ex1.
     WndProc(HWND hWnd, UINT wMessage, WPARAM wParam, LPARAM
lParam)
The meaning of the parameters is:

hWnd            The handle of window to which the message is directed. Usually the window
                in which the event that caused the message occurred.
wMessage The message identifier.
wParam          A 32-bit message parameter in Win32 and 16-bit in Windows 3.1. The
                meaning and value depends upon the message.
lParam          A 32-bit message parameter dependent upon the message.

The meaning of wParam and wParam for WM_COMMAND messages is described
below.

wParam
  Contains the menu item (IDM_), control item (IDD_) or accelerator identifier (usually
  IDM_).

lParam
   Specifies whether the message is from a menu, an accelerator or a control (edit
   window, button, list box etc.). The low-order word contains zero if the message is
   from a menu. The high-order word contains 1 if the message is an accelerator
   message. If the message is from a control, the high-order word is contains the
   notification code. The low-order word is the window handle of the control sending the
   message. The notification code sent by a button control for example would tell the
   program whether the button had been “clicked” or “double-clicked”.
    The high and low words of lParam can be extracted using the macros HIWORD() and
    LOWORD().

The window procedure of example ex1 is shown below. Remember there was only one
menu item, the “Cut” command.

LRESULT CALLBACK WndProc(HWND hWnd, UINT wMessage, WPARAM
wParam, LPARAM lParam)
{
    WORD              wId;
    HDC          hDC;
    RECT              Rect;
  PAINTSTRUCT ps;
Created by Tony G                                                   Page 11
Windows Systems Prog                                                Background Info



       switch(wMessage)
       {
            case WM_PAINT:
                 hDC = BeginPaint(hWnd, &ps);
                 GetClientRect(hWnd, &Rect);
                 DrawText(hDC, "Hello Windows!", -1, &Rect,
                           DT_SINGLELINE | DT_CENTER | DT_VCENTER);
                 EndPaint(hWnd, &ps);
                 return 0;

              case WM_COMMAND:
                   wId = LOWORD(wParam);
                   switch (wId)
                   {
                        case IDM_CUT:
                              MessageBox(hWnd, CUT_MSG, szAppName, MB_OK);
                              return 0;
                   }
                   break;

              case WM_DESTROY:
                   PostQuitMessage(0);
                   return 0;
       }
       return DefWindowProc(hWnd, wMessage, wParam, lParam);
}

Although the switch statement seems to be redundant here (it could have been replaced by
an if statement), normally there will be a number of menu items to handle (see later
examples).

A Portability Problem
There is a portability problem if the same source is to be used under Windows 3.1 and
Windows 95/NT. In the case of the WM_COMMAND message, the data is not packed in
wParam and lParam in the same way. Handles under Win3.1 are 16 bits but under Win32
they are 32 bits.

The following diagram shows how this wParam and lParam are packed in Win32 and
Windows 3.1 for the WM_COMMAND message.




Created by Tony G                                                 Page 12
Windows Systems Prog                                                                    Background Info

                                               wParam                 lParam

                                               15          0   31                       0
                    Windows 3.1                      id             cmd          hwnd


                                    31                     0   31                       0
                    Win32                cmd        hwnd       2          hwnd




              Field         Use                                      Win 3.1            Win95/N
                                                                                           T
              Id            menu,control,accelerator                  16 bits            16 bits
                            identifier
              Cmd           Notification code                         16 bits            16 bits
              Hwnd          window handle                             16 bits            32 bits

You can overcome this problem by using conditional compilation. The identifier _WIN32
is defined when your compiler has been told to compile for the 32-bit environment
(Win32). It is undefined otherwise. The code extract below yields correct results, whether
you compile for the 16-bit or 32-bit environment:

      case WM_COMMAND:
#ifdef     _WIN32
           wId = LOWORD(wParam);
#else
           wId = wParam;
#endif
           switch (wId)
           {
                case IDM_CUT:
                      MessageBox(hWnd, CUT_MSG, szAppName, MB_OK);
                      return 0;
           }
           break;




Created by Tony G                                                                   Page 13
Windows Systems Prog                                                 Background Info


Source Files
File resource.h

#define IDM_CUT                    101

File ex1.c

#include <windows.h>
#include <stdarg.h>
#include "resource.h"
#define CUT_MSG                          "Cut requested - Menus"

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

char szAppName[] = "Menus Example 1";

#pragma argsused

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
{
     MSG          Msg;
     WNDCLASS WndClass;
     HWND         hWndMain;

       WndClass.style = CS_HREDRAW | CS_VREDRAW;
       WndClass.lpfnWndProc = WndProc;
       WndClass.cbClsExtra = 0;
       WndClass.cbWndExtra = 0;
       WndClass.hInstance = hInstance;
       WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
       WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
       WndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
       WndClass.lpszMenuName = "MYMENU";
       WndClass.lpszClassName = szAppName;

       RegisterClass(&WndClass);

       hWndMain= CreateWindow(szAppName, szAppName,
                             WS_OVERLAPPEDWINDOW,
                             CW_USEDEFAULT, CW_USEDEFAULT,
                             GetSystemMetrics(SM_CXSCREEN)/2,
                             GetSystemMetrics(SM_CYSCREEN)/2,
                             NULL, NULL, hInstance, NULL);

Created by Tony G                                                  Page 14
Windows Systems Prog                                             Background Info


       ShowWindow(hWndMain, nCmdShow);
       UpdateWindow(hWndMain);

       while (GetMessage(&Msg, NULL, 0, 0))
       {
            TranslateMessage(&Msg);
            DispatchMessage(&Msg);
       }

       return Msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT wMessage, WPARAM
wParam, LPARAM lParam)
{
    WORD              wId;
    HDC          hDC;
    RECT              Rect;
  PAINTSTRUCT ps;

       switch(wMessage)
       {
            case WM_PAINT:
                 hDC = BeginPaint(hWnd, &ps);
                 GetClientRect(hWnd, &Rect);
                 DrawText(hDC, "Hello Windows!", -1, &Rect,
                           DT_SINGLELINE | DT_CENTER | DT_VCENTER);
                 EndPaint(hWnd, &ps);
                 return 0;

              case WM_COMMAND:
                   wId = LOWORD(wParam);
                   switch (wId)
                   {
                        case IDM_CUT:
                              MessageBox(hWnd, CUT_MSG, szAppName, MB_OK);
                              return 0;
                   }
                   break;

              case WM_DESTROY:
                   PostQuitMessage(0);
                   return 0;
       }
       return DefWindowProc(hWnd, wMessage, wParam, lParam);

Created by Tony G                                              Page 15
Windows Systems Prog                                              Background Info


}


File ex1.rc

#include "resource.h"

MYMENU MENU
BEGIN
    POPUP "&Edit"
    BEGIN
        MENUITEM "Cu&t", IDM_CUT
    END

END


Adding Undo, Copy and Paste to the menu – ex2

Program ex2. Files ex2.c, ex2.rc and resource.h.
The example ex2, extends ex1 by adding some additional items to the Edit drop down
menu. The menu to be created is shown below.




                                  separator




Created by Tony G                                               Page 16
Windows Systems Prog                                                 Background Info


Below is the script to produce this simple menu.

       #include "resource.h"

       MYMENU MENU
       BEGIN
           POPUP "&Edit"
           BEGIN
               MENUITEM "&Undo", IDM_UNDO
               MENUITEM SEPARATOR
               MENUITEM "Cu&t", IDM_CUT
               MENUITEM "&Copy", IDM_COPY
               MENUITEM "&Paste", IDM_PASTE
           END

       END

Three extra menu items have been added. The line below is used to insert a line to break
the menu items into logical groups. It has no effect on the program code.

              MENUITEM SEPARATOR

As before the menu will be added to the program when class is registered.

              WndClass.lpszMenuName = "MYMENU";

The windows procedure has been extended to handle the new menu items.

       LRESULT CALLBACK WndProc(HWND hWnd, UINT wMessage,
                           WPARAM wParam, LPARAM lParam)
       {
           WORD            wId;
           HDC        hDC;
           RECT            Rect;
         PAINTSTRUCT ps;

              switch(wMessage)
              {
                   case WM_PAINT:
                        hDC = BeginPaint(hWnd, &ps);
                        GetClientRect(hWnd, &Rect);
                        DrawText(hDC, "Hello Windows!", -1, &Rect,
                                  DT_SINGLELINE | DT_CENTER | DT_VCENTER);
                        EndPaint(hWnd, &ps);
                        return 0;

Created by Tony G                                                  Page 17
Windows Systems Prog                                                Background Info



                    case WM_COMMAND:
                         wId = LOWORD(wParam);
                         switch (wId)
                         {
                              case IDM_CUT:
                              MessageBox(hWnd, CUT_MSG, szAppName, MB_OK);
                              return 0;

                              case IDM_COPY:
                              MessageBox(hWnd, COPY_MSG, szAppName, MB_OK);
                                    return 0;

                              case IDM_PASTE:
                              MessageBox(hWnd, PASTE_MSG, szAppName, MB_OK);
                                    return 0;

                              case IDM_UNDO:
                              MessageBox(hWnd, UNDO_MSG, szAppName, MB_OK);
                                    return 0;
                         }
                         break;

                    case WM_DESTROY:
                         PostQuitMessage(0);
                         return 0;
              }
              return DefWindowProc(hWnd, wMessage, wParam, lParam);
       }

The complete source of ex2
File resource.h

#define IDM_COPY 102
#define IDM_PASTE 103
#define IDM_UNDO 104
#define IDM_CUT 101

File ex2.c

#include <windows.h>
#include <stdarg.h>
#include "resource.h"

#define CUT_MSG                         "Cut requested - Menus"
Created by Tony G                                                 Page 18
Windows Systems Prog                                              Background Info


#define COPY_MSG                    "Copy requested - Menus"
#define PASTE_MSG                   "Paste requested - Menus"
#define UNDO_MSG                    "Undo requested - Menus"

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

char szAppName[] = "Menus Example 2";

#pragma argsused

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
{
     MSG          Msg;
     WNDCLASS WndClass;
     HWND         hWndMain;

       UNUSED_IDENTIFIER(lpszCmdLine);
       UNUSED_IDENTIFIER(hPrevInstance);

       WndClass.style = CS_HREDRAW | CS_VREDRAW;
       WndClass.lpfnWndProc = WndProc;
       WndClass.cbClsExtra = 0;
       WndClass.cbWndExtra = 0;
       WndClass.hInstance = hInstance;
       WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
       WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
       WndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
       WndClass.lpszMenuName = "MYMENU";
       WndClass.lpszClassName = szAppName;

       RegisterClass(&WndClass);

       hWndMain= CreateWindow(szAppName, szAppName,
                             WS_OVERLAPPEDWINDOW,
                             CW_USEDEFAULT, CW_USEDEFAULT,
                             GetSystemMetrics(SM_CXSCREEN)/2,
                             GetSystemMetrics(SM_CYSCREEN)/2,
                             NULL, NULL, hInstance, NULL);

       ShowWindow(hWndMain, nCmdShow);
       UpdateWindow(hWndMain);

       while (GetMessage(&Msg, NULL, 0, 0))
       {

Created by Tony G                                               Page 19
Windows Systems Prog                                         Background Info


              TranslateMessage(&Msg);
              DispatchMessage(&Msg);
       }

       return Msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT wMessage, WPARAM
wParam, LPARAM lParam)
{
    WORD              wId;
    HDC          hDC;
    RECT              Rect;
  PAINTSTRUCT ps;

       switch(wMessage)
       {
            case WM_PAINT:
                 hDC = BeginPaint(hWnd, &ps);
                 GetClientRect(hWnd, &Rect);
                 DrawText(hDC, "Hello Windows!", -1, &Rect,
                           DT_SINGLELINE | DT_CENTER | DT_VCENTER);
                 EndPaint(hWnd, &ps);
                 return 0;

              case WM_COMMAND:
                   wId = LOWORD(wParam);
                   switch (wId)
                   {
                        case IDM_CUT:
                              MessageBox(hWnd, CUT_MSG, szAppName, MB_OK);
                              return 0;

                        case IDM_COPY:
                              MessageBox(hWnd, COPY_MSG, szAppName, MB_OK);
                              return 0;

                        case IDM_PASTE:
                              MessageBox(hWnd, PASTE_MSG, szAppName, MB_OK);
                              return 0;

                        case IDM_UNDO:
                              MessageBox(hWnd, UNDO_MSG, szAppName, MB_OK);
                              return 0;
                    }

Created by Tony G                                          Page 20
Windows Systems Prog                                                 Background Info


                    break;

              case WM_DESTROY:
                   PostQuitMessage(0);
                   return 0;
       }
       return DefWindowProc(hWnd, wMessage, wParam, lParam);
}


File ex2.rc

#include "resource.h"

MYMENU MENU
BEGIN
    POPUP "&Edit"
    BEGIN
        MENUITEM "&Undo", IDM_UNDO
        MENUITEM SEPARATOR
        MENUITEM "Cu&t", IDM_CUT
        MENUITEM "&Copy", IDM_COPY
        MENUITEM "&Paste", IDM_PASTE
    END

END


Keyboard Accelerators - ex3

Program ex3. Files ex3.c, ex3.rc and resource.h.
The example ex3, extends ex2 by adding a keyboard interface to the menu system.

Keyboard accelerators are keyboard combinations that generate WM_COMMAND
messages to the program. They are often associated with menu items but do not have to
be. Keyboard accelerators give the user the chance to use the keyboard instead of the
menu system. The keyboard can often be faster to use than having to move through
several popup menus to get to the command you want to use. The keyboard accelerator
table is a resource that is defined in the resource file (ex3.rc).

In theory you can use almost any character key or virtual key in combination with the
Shift key, Ctrl key or Alt key. In practice it is better to be consistent with other
applications. It is also best to avoid keys such as Tab, Enter, Esc and Spacebar. An
example of consistency is the program ex3. Here we add accelerator keys to the Edit


Created by Tony G                                                  Page 21
Windows Systems Prog                                                   Background Info


menu items. The key combinations shown on the menu below have been chosen for
consistency with other Windows applications.




Defining Accelerator Keys
Each keyboard accelerator requires an entry in the accelerator table. Entries can be
defined in one of four ways.

   “char”,       wID          [,NOINVERT] [,SHIFT] [,CONTROL], [,ALT]
   “^char”,      wID          [,NOINVERT] [,SHIFT] [,CONTROL], [,ALT]
   code,         wID, ASCII   [,NOINVERT] [,SHIFT] [,CONTROL], [,ALT]
   code,         wID, VIRTKEY [,NOINVERT] [,SHIFT] [,CONTROL], [,ALT]

The fields enclosed in [..] are optional. SHIFT, CONTROL and ALT referring to the
modifier keys on the keyboard. wID is the value stored in wParam when the
WM_COMMAND message is sent to the windows procedure (WndProc()). “char” means
a single character enclosed in double quotes. “^char” means Ctrl+char, in which case the
CONTROL keyword would not be required. nCode is a number rather than a character in
quotes and is interpreted as either an ASCII code or a virtual key code. Virtual key codes
are used for keys for which there is no ASCII code (function and cursor keys for
example).

Below is the section of resource script from the resource file ex3.rc that defines the
accelerator keys Ctrl+C, Ctrl+V, Ctrl+X and Ctrl+Z. It was produced using Borland‟s
Resource Workshop.

     MYACCELERATORS ACCELERATORS MOVEABLE PURE
     {
       VK_F1,   IDM_UNDO,      VIRTKEY, CONTROL, NOINVERT
       "^C",  IDM_COPY,      ASCII, NOINVERT
       "^V",  IDM_PASTE,     ASCII, NOINVERT
       "^X",  IDM_CUT,      ASCII, NOINVERT
     }

The accelerator table name is MYACCELERATORS. It is this name that will be used by
the program to load the table into memory. From the table we can see that IDM_PASTE
Created by Tony G                                                  Page 22
Windows Systems Prog                                                 Background Info


is produced by the accelerator key Ctrl+V, IDM_UNDO by Ctrl+F1, IDM_CUT by
Ctrl+X and IDM_COPY by Ctrl+C.
Loading and Using the Accelerator Table
The only changes necessary to the C source file to use the accelerator table are in
WinMain(). The windows procedure does not need to be changed to handle the keyboard
accelerators. This is because the keyboard accelerators produce WM_COMMAND
messages and the identifier in wParam can be exactly the same as those produced by the
equivalent menu items.

The changes to WinMain() can be seen in the code below.

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
{
     MSG          Msg;
     WNDCLASS WndClass;
     HWND         hWndMain;
     HANDLE hAccelerator;

/*
 * code removed for clarity
 *
 * RegisterClass()
 * CreateWindow()
 * ShowWindow()
 * UpdateWindow()
 */

       hAccelerator = LoadAccelerators(hInstance, "MyAccelerators");

       while (GetMessage(&Msg, NULL, 0, 0))
       {
             if (!TranslateAccelerator(hWndMain, hAccelerator, &Msg))
      {
                   TranslateMessage(&Msg);
                   DispatchMessage(&Msg);
      }
       }
       return Msg.wParam;
}

The accelerator table is loaded from the EXE file into memory using the
LoadAccelerators() function and the handle returned is stored in hAccelerator for use by
the TranslateAccelerator() function.

Created by Tony G                                                  Page 23
Windows Systems Prog                                                   Background Info



To use the accelerator table the message loop has to be modified slightly. Below is the
“old” message loop.

       while (GetMessage(&Msg, NULL, 0, 0))
       {
            TranslateMessage(&Msg);
            DispatchMessage(&Msg);
       }

and here is the new version.

       while (GetMessage(&Msg, NULL, 0, 0))
       {
            if (!TranslateAccelerator(hWndMain, hAccelerator, &Msg))
      {
                  TranslateMessage(&Msg);
                  DispatchMessage(&Msg);
      }
       }

The TranslateAccelerator() function determines if the message stored in Msg is a
keyboard message. If it is, the function searches for a match in the accelerator table whose
handle is hAccelerator. If it finds a match it calls the windows procedure for the window
whose handle is hWndMain. When the windows procedure is called by
TranslateAccelerator() it will be passed the message WM_COMMAND with the
keyboard accelerator ID (IDM_PASTE, IDM_CUT etc.) in wParam.


Source Files

File resource.h

#define IDM_COPY 103
#define IDM_PASTE 104
#define IDM_UNDO 101
#define IDM_CUT 102

File ex3.c


#include <windows.h>
#include <stdarg.h>
#include "resource.h"


Created by Tony G                                                    Page 24
Windows Systems Prog                                             Background Info


#define CUT_MSG                    "Cut requested - Menus"
#define COPY_MSG                   "Copy requested - Menus"
#define PASTE_MSG                  "Paste requested - Menus"
#define UNDO_MSG                   "Undo requested - Menus"

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

char szAppName[] = "Menus Example 3";

#pragma argsused

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
{
     MSG          Msg;
     WNDCLASS WndClass;
     HWND         hWndMain;
     HANDLE hAccelerator;

       UNUSED_IDENTIFIER(lpszCmdLine);
       UNUSED_IDENTIFIER(hPrevInstance);

       WndClass.style = CS_HREDRAW | CS_VREDRAW;
       WndClass.lpfnWndProc = WndProc;
       WndClass.cbClsExtra = 0;
       WndClass.cbWndExtra = 0;
       WndClass.hInstance = hInstance;
       WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
       WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
       WndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
       WndClass.lpszMenuName = "MYMENU";
       WndClass.lpszClassName = szAppName;

       RegisterClass(&WndClass);

       hWndMain = CreateWindow(szAppName, szAppName,
                             WS_OVERLAPPEDWINDOW,
                             CW_USEDEFAULT, CW_USEDEFAULT,
                             GetSystemMetrics(SM_CXSCREEN)/2,
                             GetSystemMetrics(SM_CYSCREEN)/2,
                             NULL, NULL, hInstance, NULL);

       ShowWindow(hWndMain, nCmdShow);
       UpdateWindow(hWndMain);


Created by Tony G                                              Page 25
Windows Systems Prog                                                Background Info


       hAccelerator = LoadAccelerators(hInstance, "MyAccelerators");

       while (GetMessage(&Msg, NULL, 0, 0))
       {
             if (!TranslateAccelerator(hWndMain, hAccelerator, &Msg))
      {
                   TranslateMessage(&Msg);
                   DispatchMessage(&Msg);
      }
       }
       return Msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT wMessage, WPARAM
wParam, LPARAM lParam)
{
    WORD              wId;
    HDC          hDC;
    RECT              Rect;
  PAINTSTRUCT ps;

       switch(wMessage)
       {
            case WM_PAINT:
                 hDC = BeginPaint(hWnd, &ps);
                 GetClientRect(hWnd, &Rect);
                 DrawText(hDC, "Hello Windows!", -1, &Rect,
                           DT_SINGLELINE | DT_CENTER | DT_VCENTER);
                 EndPaint(hWnd, &ps);
                 return 0;

              case WM_COMMAND:
                   wId = LOWORD(wParam);
                   switch (wId)
                   {
                        case IDM_CUT:
                              MessageBox(hWnd, CUT_MSG, szAppName, MB_OK);
                              return 0;

                       case IDM_COPY:
                             MessageBox(hWnd, COPY_MSG, szAppName, MB_OK);
                             return 0;

                       case IDM_PASTE:
                             MessageBox(hWnd, PASTE_MSG, szAppName, MB_OK);

Created by Tony G                                                 Page 26
Windows Systems Prog                                             Background Info


                             return 0;

                         case IDM_UNDO:
                               MessageBox(hWnd, UNDO_MSG, szAppName, MB_OK);
                               return 0;
                    }
                    break;

              case WM_DESTROY:
                   PostQuitMessage(0);
                   return 0;
       }
       return DefWindowProc(hWnd, wMessage, wParam, lParam);
}


File ex3.rc

#include "resource.h"

MYMENU MENU DISCARDABLE
{
  POPUP "&Edit"
  {
    MENUITEM "&Undo\tCtrl+F1",              IDM_UNDO
    MENUITEM SEPARATOR
    MENUITEM "Cu&t\tCtrl+X",              IDM_CUT
    MENUITEM "&Copy\tCtrl+C",               IDM_COPY
    MENUITEM "&Paste\tCtrl+V",             IDM_PASTE
  }
}

MYACCELERATORS ACCELERATORS MOVEABLE PURE
{
  VK_F1,   IDM_UNDO,      VIRTKEY, CONTROL, NOINVERT
  "^C",  IDM_COPY,      ASCII, NOINVERT
  "^V",  IDM_PASTE,     ASCII, NOINVERT
  "^X",  IDM_CUT,      ASCII, NOINVERT
}




Created by Tony G                                              Page 27
Windows Systems Prog                                                  Background Info




A Last Menu Example Program - ex4

Program ex4. Files ex4.c, ex4.rc and resource.h.
Program ex4 demonstrates some other things you can do with menus such as cascaded
popups, no popups, several lines of menu items on the menu bar. It is left to you study the
program along with Borland‟s on help.




The complete source of ex4

File resource.h

#define IDM_ONE                   101
#define IDM_THREE                 103
#define IDM_FOUR                  104
#define IDM_FIVE                  105
#define IDM_SIX                   106
#define IDM_SEVEN                 107
#define IDM_EIGHT                 108
#define IDM_NINE                  109
#define IDM_TEN                   110
#define IDM_ELEVEN                111
#define IDM_TWELVE                112
#define IDM_THIRTEEN              113
#define IDM_FOURTEEN              114
#define IDM_FIFTEEN               115
#define IDM_SIXTEEN               116
#define IDM_EIGHTEEN              118
#define IDM_TWENTY                120
#define IDM_TWENTYONE                   121
#define IDM_TWENTYTWO                   122

Created by Tony G                                                   Page 28
Windows Systems Prog                                         Background Info



File ex4.c

/*
 * File: ex4.c
 *
 * Menus example 4
 *
 * Many menuitems, nested popups, CHECKED ...
 */

#include <windows.h>
#include <stdarg.h>
#include "resource.h"

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void MsgOut(LPCSTR Format, ...);

char szAppName[] = "Menus Example 4";
char *szIdString[] =
{
     "IDM_ONE", "IDM_TWO", "IDM_THREE", "IDM_FOUR", "IDM_FIVE",
"IDM_SIX",
     "IDM_SEVEN", "IDM_EIGHT", "IDM_NINE", "IDM_TEN", "IDM_ELEVEN",
     "IDM_TWELVE", "IDM_THIRTEEN", "IDM_FOURTEEN", "IDM_FIFTEEN",
     "IDM_SIXTEEN", "NONE", "IDM_EIGHTEEN", "NONE", "IDM_TWENTY",
     "IDM_TWENTYONE", "IDM_TWENTYTWO"
};

#pragma argsused

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
{
     MSG          Msg;
     WNDCLASS WndClass;
     HWND         hWndMain;

       WndClass.style = CS_HREDRAW | CS_VREDRAW;
       WndClass.lpfnWndProc = WndProc;
       WndClass.cbClsExtra = 0;
       WndClass.cbWndExtra = 0;
       WndClass.hInstance = hInstance;
       WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
       WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);

Created by Tony G                                          Page 29
Windows Systems Prog                                           Background Info


       WndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
       WndClass.lpszMenuName = "MYMENU";
       WndClass.lpszClassName = szAppName;

       RegisterClass(&WndClass);

       hWndMain = CreateWindow(szAppName, szAppName,
                             WS_OVERLAPPEDWINDOW,
                             CW_USEDEFAULT, CW_USEDEFAULT,
                             GetSystemMetrics(SM_CXSCREEN)/2,
                             GetSystemMetrics(SM_CYSCREEN)/2,
                             NULL, NULL, hInstance, NULL);

       ShowWindow(hWndMain, nCmdShow);
       UpdateWindow(hWndMain);

       while (GetMessage(&Msg, NULL, 0, 0))
       {
             TranslateMessage(&Msg);
             DispatchMessage(&Msg);
       }
       return Msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT wMessage, WPARAM
wParam, LPARAM lParam)
{
    WORD              wId;
    HDC          hDC;
    RECT              Rect;
  PAINTSTRUCT ps;

       switch(wMessage)
       {
            case WM_PAINT:
                 hDC = BeginPaint(hWnd, &ps);
                 GetClientRect(hWnd, &Rect);
                 DrawText(hDC, "Hello Windows!", -1, &Rect,
                           DT_SINGLELINE | DT_CENTER | DT_VCENTER);
                 EndPaint(hWnd, &ps);
                 return 0;

              case WM_COMMAND:
                   wId = LOWORD(wParam);
                   if (wId < IDM_ONE || wId > IDM_TWENTYTWO)

Created by Tony G                                         Page 30
Windows Systems Prog                                                Background Info


                           MsgOut("Invalid ID value %d", wId);
                    else
                           MsgOut("Menu item ID = %s", (LPSTR)szIdString[wId-
IDM_ONE]);
                    return      0;

              case WM_DESTROY:
                   PostQuitMessage(0);
                   return 0;
       }
       return DefWindowProc(hWnd, wMessage, wParam, lParam);
}


void MsgOut(LPCSTR Format, ...)
{
     va_list arg_ptr;
     char szBuffer[256];

       va_start(arg_ptr, Format);
       wvsprintf(szBuffer, Format, arg_ptr);
       MessageBox(NULL, szBuffer, szAppName, MB_OK);
}




Created by Tony G                                                 Page 31
Windows Systems Prog                            Background Info



File Ex4.rc

#include "resource.h"

MYMENU      MENU
{
    MENUITEM "&One", IDM_ONE
    POPUP      "&Two"
  {
        MENUITEM "&Ten",        IDM_TEN, CHECKED
        MENUITEM "&Eleven",     IDM_ELEVEN
        MENUITEM SEPARATOR
        MENUITEM "T&welve",     IDM_TWELVE,     INACTIVE
        MENUITEM "T&hirteen",   IDM_THIRTEEN
        MENUITEM "&Fourteen", IDM_FOURTEEN, MENUBREAK
        MENUITEM "F&ifteen",    IDM_FIFTEEN
        MENUITEM "&Sixteen",    IDM_SIXTEEN,    MENUBARBREAK
        POPUP "Se&venteen"
        {
            MENUITEM "&Twenty",          IDM_TWENTY
            MENUITEM "T&wenty-One",      IDM_TWENTYONE
            MENUITEM "Tw&enty-Two",      IDM_TWENTYTWO
        }
        MENUITEM "Ei&ghteen", IDM_EIGHTEEN, GRAYED
  }
    MENUITEM "Th&ree", IDM_THREE
    MENUITEM "&Four",       IDM_FOUR, INACTIVE
    MENUITEM "Fi&ve",       IDM_FIVE
    MENUITEM "Si&x",        IDM_SIX,     MENUBREAK
    MENUITEM "&Seven", IDM_SEVEN
    MENUITEM "&Eight", IDM_EIGHT, GRAYED
    MENUITEM "\a&Help", IDM_NINE,    HELP
}




Created by Tony G                             Page 32
Windows Systems Prog                                                 Background Info


What are Window resources
Dialogue boxes
                                 Dialogue Boxes

Dialogue boxes are usually used to obtain additional input from the user. The programmer
indicates that a menu item invokes a dialogue box by adding ellipsis (…) to the menu
item.

A dialogue generally takes the form of a popup window containing various child window
controls. Remember that all the controls are specialised windows. A typical dialogue box
is shown below.

                     Edit box                                                Radio buttons




  Check boxes
                                                  Buttons


The size and placement of these controls are specified in a dialogue box template in the
programs resource file. Windows is responsible for creating the dialogue popup window
and the child window controls and for providing a window procedure to process dialogue
box messages (including keyboard and mouse input).

The controls of a dialogue box communicate with your program by calling a function
within the program. The function is called a dialogue box procedure or a dialogue
procedure. This function is similar to a normal window procedure (WndProc()). The
dialogue procedure will initialise the child window controls, process messages from the
controls and end the dialogue box.




Created by Tony G                                                  Page 33
Windows Systems Prog                                                   Background Info


Dialogue boxes can be created by hand, but it is long and tedious process for a complex
layout with numerous controls. The co-ordinates of all the controls have to be worked out
by hand on paper. The data then has to be entered into the resource file as a dialogue box
template, compiled and tested. If it does not look right then back round the loop. To save
considerable time a resource editor should be used. Borland‟s Resource Workshop was
used to create the dialogue boxes in the following example programs.

Modal and Modeless Dialogue Boxes
A dialogue box can be either modal or modeless.
Modal

    When your program displays a modal dialogue box, the user cannot switch to another
    window in your program. The user must end the dialogue box (usually by clicking an
    Ok or Cancel button) before the user can switch another window in the application.
    The file open common dialogue is a typical example of a modal dialogue box.

Modeless

    Modeless dialogue boxes allow the user to switch between the dialogue box and the
    window that created it. For instance WORD uses a modeless dialogue box for the
    Find dialogue. If the Find dialogue box were modal, the user would have to choose
    Find from the menu, enter the string to be found, end the dialogue box to return to the
    document, and then repeat the entire process to search for another occurrence of the
    same string. Allowing the user to switch between the document and the dialogue box
    as can be done with a modeless dialogue box is much more convenient.




Created by Tony G                                                    Page 34
Windows Systems Prog                                                     Background Info



A Simple Dialogue Box – ex1

This first example popups the “About” dialogue box below when the About menu item is
clicked. It has three controls, a push button and two text static controls. Additionally there
is an icon. You will see how complex the resource file is if you have a look at ex5.rc. It
was created using Borland‟s Resource Workshop.

                                  Icon          Static text controls




                                      Push button control



The complete program ex1 is a few pages further on.

The Dialogue Box Template
Below is a stripped down version of the dialogue box template used to produce the About
dialogue box.

       AboutBox DIALOG 25, 19, 128, 66
       STYLE WS_POPUP | WS_DLGFRAME
       {
           CTEXT        "About Dialogue Demo",              -1,       20, 28, 88, 8
           DEFPUSHBUTTON "Ok",                                    IDD_OK, 48, 46, 31, 14
           CTEXT        "Example 1",                        -1,       47, 9, 33, 8
           ICON               "EX1AICON",                         -1,         6, 6, 13, 13
           ICON               "EX1BICON",                         -1,         108, 6, 13, 13
       }


The words STYLE, DIALOG, BEGIN, CTEXT, DEFPUSHBUTTON and ICON are all
reserved words recognised by the resource compiler.
The first line gives the dialogue box a name (in this case AboutBox) and the position and
size relative to it parent window. This dialogue box will have its top left corner at

Created by Tony G                                                      Page 35
Windows Systems Prog                                                      Background Info


coordinates (35, 33) relative to the client area of the parent window and be 128 wide by
66 high. The coordinates are NOT in pixels but in units based on the size of the system
font - yet another reason for using a resource editor!
The STYLE line tells Windows that it will be popup window with a dialogue frame
around it.
The lines enclosed within BEGIN..END define the child window controls. The format of
these statements is:
control-type “text” nID, xPos, yPos, xWidth, yHeight, dwStyle
control-type        specifies the type of the child window control. In the case of the About
                    dialogue box three types are used: CTEXT (centered text), ICON (an
                    icon) and DEFPUSHBUTTON (a default push button).
nID                 is used by the child to identify itself when sending message (usually
                    WM_COMMAND) to its parent. The parent window of the control is
                    the dialogue box itself, which causes these messages to be sent a
                    dialogue procedure (similar to a windows procedure) in the program.
                    Some control such as text and icon do not send messages to their parent
                    so the nID value is set to -1.
xPos, yPos          position of control relative to parent (dialogue box window)
xWidth, yHeight size of control.
CTEXT defines a centered text child window control.
DEFPUSHBUTTON defines a default push button. The default push button in a set of
buttons is the one that is clicked if the enter key is pressed, even if focus is on one of the
other buttons. If there is no default button then the ID IDOK is generated.
ICON specifies a bitmap that defines the shape of the icon to be used for a given
application. The bitmap can be stored in the resource file or in an external file.

The Dialogue Box Procedure
The dialogue box procedure in the program receives messages from child window
controls in a dialogue box. In actual fact Windows supplies the windows procedure for
dialogue boxes. It is this procedure that sends the messages to the dialogue procedure in
the program. Dialogue box procedures along with windows procedures are often called
callback procedures - Windows calls the program via the callback function when it has a
message for the program.




Created by Tony G                                                       Page 36
Windows Systems Prog                                                  Background Info


Below is the dialogue procedure for program ex1.

BOOL CALLBACK AboutDlgProc(HWND hDlg, UINT wMessage,
                        WPARAM wParam, LPARAM lParam)
{
  WORD wId;

    switch (wMessage)
    {
      case WM_INITDIALOG:
         return TRUE;

      case WM_COMMAND:
        wId = LOWORD(wParam);
        switch(wId)
        {
           case IDD_OK:
             EndDialog(hDlg, 0);
             return TRUE;
        }
        break;
    }
    return FALSE;
}

The parameters are the same as for a windows procedure (WndProc()). The basic
differences are:
 Windows procedure return a long; dialogue procedures return a BOOL.
 A window procedure calls DefWindowProc() if it does not process a message; a
  dialogue procedure returns TRUE if it processes a message and FALSE if it does not.
 A dialogue box procedure does not need to process WM_PAINT messages; repainting
  of child window controls is done by the windows procedure in Windows. A dialogue
  box procedure does not receive a WM_CREATE message when the dialogue window
  is created, instead it receives a WM_INITDIALOG message. This message can be
  used by the program to initialise the state of controls - fill list boxes with text, set
  check boxes etc..

The dialogue procedure must call EndDialog() to terminate the dialogue box otherwise it
will not return control to WndProc().




Created by Tony G                                                   Page 37
Windows Systems Prog                                                Background Info


Invoking the Dialogue Box
The dialogue procedure handles messages sent to it by the various child window controls
in the dialogue box. Before Windows can send messages to a dialogue procedure it needs
to know its address. When the program invokes a dialogue using the function
DialogBox(), one of its parameters is the program instance, another of its parameters is
the address of the dialogue procedure. lParam contains a pointer to a CREATESTRUCT
when WndProc() is called by Windows. The member hInstance of this structure is the
program instance.

The dialog box is invoked using DialogBox() when the WM_COMMAND message is
received by the windows procedure with wParam set IDM_ABOUT.

       DialogBox(hInstance, "AboutBox", hWnd, AboutDlgProc);

hInstance is the instance handle of the program, “AboutBox” is the name of the dialogue
box in the resource file (ex1.rc), hWnd is the parent window and AboutDlgProc is
address of the dialogue procedure.
The relevant sections of code can be seen in the WndProc() below.

    LRESULT CALLBACK WndProc(HWND hWnd, UINT wMessage, WPARAM
    wParam, LPARAM lParam)
    {
       static HANDLE           hInstance;
       ....
       switch(wMessage)
       {
            case WM_CREATE:
                hInstance = ((LPCREATESTRUCT) lParam)->hInstance;
                return 0;
                ....
            case WM_COMMAND:
                wId = LOWORD(wParam);
                switch (wId)
                {
                   case IDM_ABOUT:
                   DialogBox(hInstance, "AboutBox", hWnd, AboutDlgProc);
                       return 0;
                }
                break;
                ....
       }
       return DefWindowProc(hWnd, wMessage, wParam, lParam);
    }


Created by Tony G                                                 Page 38
Windows Systems Prog                                                   Background Info


Windows 3.1

Things are a bit more complicated in Windows 3.1!
The dialogue procedure handles messages sent to it by the various child window controls
in the dialogue box. Before Windows can send messages to a dialogue procedure it needs
to know its address. When the program invokes a dialogue using the function
DialogBox(), one of its parameters is the address of dialogue procedure. In actual fact
that‟s not quite right, its not the address of the dialogue procedure but an a pointer to an
instance thunk associated with the dialogue procedure.

During processing of WM_CREATE in the windows procedure WndProc(), ex5 gets the
program‟s instance handle and stores it in a static variable (static variables retain their
values from one call to another of a function). The program now calls
MakeProcInstance() using the instance handle and the address of the dialogue procedure
as parameters. MakeProcInstance() returns a pointer to an instance thunk which the
program stores in another static variable for later use when invoking dialogue boxes.

       static FARPROC      lpfnAboutDlgProc;
       ....
       ....
       hInstance = ((LPCREATESTRUCT) lParam)->hInstance;
       lpfnAboutDlgProc = MakeProcInstance((FARPROC)AboutDlgProc,hInstance);

MakeProcInstance() assures that the dialogue procedure AboutDlgProc() gets the correct
data segment address for this instance of ex5.

The dialog box is invoked using DialogBox() when the WM_COMMAND message is
received by the window procedure with wParam set IDM_ABOUT.

    DialogBox(hInstance, "AboutBox", hWnd, lpfnAboutDlgProc);

hInstance is the instance handle of the program, “AboutBox” is the name of the dialogue
box in the resource file (ex5.rc), hWnd is the parent window and lpfnAboutDlgProc is the
effective pointer to the dialogue procedure.



Source Files
File resource.h

#define IDD_OK   101
#define IDM_ABOUT    102




Created by Tony G                                                    Page 39
Windows Systems Prog                                       Background Info



File ex1.c

#include <windows.h>
#include <stdarg.h>
#include "resource.h"

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
BOOL CALLBACK AboutDlgProc(HWND hDlg, UINT wMessage,
               WPARAM wParam, LPARAM lParam);

char szAppName[] = "Dialogues Example 1";

#pragma argsused

int WINAPI WinMain(HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
{
     MSG          Msg;
     WNDCLASS WndClass;
     HWND         hWndMain;

       WndClass.style = CS_HREDRAW | CS_VREDRAW;
       WndClass.lpfnWndProc = WndProc;
       WndClass.cbClsExtra = 0;
       WndClass.cbWndExtra = 0;
       WndClass.hInstance = hInstance;
       WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
       WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
       WndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
       WndClass.lpszMenuName = "MYMENU";
       WndClass.lpszClassName = szAppName;

       RegisterClass(&WndClass);

       hWndMain= CreateWindow(szAppName, szAppName,
                             WS_OVERLAPPEDWINDOW,
                             CW_USEDEFAULT, CW_USEDEFAULT,
                             GetSystemMetrics(SM_CXSCREEN)/2,
                             GetSystemMetrics(SM_CYSCREEN)/2,
                             NULL, NULL, hInstance, NULL);

       ShowWindow(hWndMain, nCmdShow);
       UpdateWindow(hWndMain);


Created by Tony G                                        Page 40
Windows Systems Prog                                              Background Info


       while (GetMessage(&Msg, NULL, 0, 0))
       {
            TranslateMessage(&Msg);
            DispatchMessage(&Msg);
       }

       return Msg.wParam;
}

#pragma argsused

LRESULT CALLBACK WndProc(HWND hWnd, UINT wMessage, WPARAM
wParam, LPARAM lParam)
{
    WORD                    wId;
    HDC               hDC;
    RECT                    Rect;
  PAINTSTRUCT         ps;
    static HANDLE     hInstance;

       switch(wMessage)
       {
            case WM_CREATE:
                 hInstance = ((LPCREATESTRUCT) lParam)->hInstance;
                 return 0;

              case WM_PAINT:
                   hDC = BeginPaint(hWnd, &ps);
                   GetClientRect(hWnd, &Rect);
                   DrawText(hDC, "Hello Windows!", -1, &Rect,
                             DT_SINGLELINE | DT_CENTER | DT_VCENTER);
                   EndPaint(hWnd, &ps);
                   return 0;

              case WM_COMMAND:
                   wId = LOWORD(wParam);
                   switch (wId)
                   {
                        case IDM_ABOUT:
              DialogBox(hInstance, "AboutBox", hWnd, AboutDlgProc);
                              return 0;
                   }
                   break;

              case WM_DESTROY:

Created by Tony G                                               Page 41
Windows Systems Prog                                             Background Info


                    PostQuitMessage(0);
                    return 0;
       }
       return DefWindowProc(hWnd, wMessage, wParam, lParam);
}

#pragma argsused

BOOL CALLBACK AboutDlgProc(HWND hDlg, UINT wMessage,
                   WPARAM wParam, LPARAM lParam)
{
   WORD wId;

       UNUSED_IDENTIFIER(lParam);
       switch (wMessage)
       {
            case WM_INITDIALOG:
                 return TRUE;

              case WM_COMMAND:
                   wId = LOWORD(wParam);
                   switch(wId)
                   {
                        case IDD_OK:
                              EndDialog(hDlg, 0);
                              return TRUE;
                   }
                   break;
       }
       return FALSE;
}


File ex1.rc

#include "resource.h"

MYMENU MENU DISCARDABLE
{
  POPUP "\a&Help", HELP
  {
    MENUITEM "&About", IDM_ABOUT
  }
}


Created by Tony G                                              Page 42
Windows Systems Prog                                             Background Info


// Stripped down template
AboutBox DIALOG 25, 19, 128, 66
STYLE WS_POPUP | WS_DLGFRAME
{
       CTEXT         "About Dialogue Demo",   -1,       20, 28, 88, 8
       DEFPUSHBUTTON "Ok",                          IDD_OK, 48, 46, 31, 14
       CTEXT         "Example 1",             -1,       47, 9, 33, 8
       ICON               "EX1AICON",               -1,       6, 6, 13, 13
       ICON               "EX1BICON",               -1,       108, 6, 13, 13
}

// bit map in text form
EX1AICON ICON
{
       ...
}

EX1BICON ICON "ex1.ico"




Created by Tony G                                              Page 43
Windows Systems Prog                                                     Background Info


Another Dialogue Box using an Edit Control – ex2

Program ex2. Files ex2.c, ex2.rc and resource.h.
The dialogue box in program ex2 has an edit control plus two buttons. The program will
retrieve the text typed in the edit control. The dialogue box is invoked when the Find
menu item of the Search drop down menu is clicked. The main window and dialogue box
are shown below.




An edit control allows the user to type in text, move the cursor, select portions of text,
delete selected text to the clipboard or insert text from the keyboard. A simple use of the
edit control is for single line entry fields, maybe to get a file name or search pattern from
the user.

The Dialogue Template
Below is a stripped down version of the dialogue box template used to produce the About
dialogue box.

       FindDialogue DIALOG 53, 45, 127, 53
       STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
       CAPTION "Find Dialogue"
       {
            EDITTEXT IDD_FINDTEXT, 45, 9, 74, 12
            DEFPUSHBUTTON "Ok", IDOK, 46, 32, 29, 14
            PUSHBUTTON "Cancel", IDCANCEL, 90, 32, 29, 14
            RTEXT "&Text to find:", -1, 4, 11, 38, 8
       }

The edit window identifier is IDD_FINDTEXT.

Created by Tony G                                                      Page 44
Windows Systems Prog                                                  Background Info


Invoking the Dialogue Box
As with program ex1, the callback for the dialogue procedure is set up when the
WM_CREATE message is received by the window procedure.

       hInstance = ((LPCREATESTRUCT) lParam)->hInstance;

The dialog box is invoked using DialogBox() when the WM_COMMAND message is
received by the window procedure with wParam set IDM_FIND.

       DialogBox(hInstance, "FindDialogue", hWnd, FindDlgProc);

Initialising the Edit Window
When the dialogue box is invoked, the first message the dialogue procedure
FindDlgProc() get is WM_INITDIALOG. The program uses this to initialise the contents
of the edit box otherwise it would be empty.

       case WM_INITDIALOG:
            hWndCtrl = GetDlgItem(hDlg, IDD_FINDTEXT);
            SendMessage(hWndCtrl, WM_SETTEXT, 0, (LPARAM)(LPSTR)szText);
            return TRUE;

A program can send messages to a child window controls to configure it or change its
state. Check boxes and radio buttons can be “checked”, edit boxes filled with text, and so
on. The function SendMessage() is used to send messages to windows. Its prototype is:

LRESULT SendMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM
lParam);

       hWnd Window handle. Identifies the window whose window procedure will
         receive the message.
       Msg          Specifies the message to be sent.
       wParam Specifies additional message-specific information.
       lParam Specifies additional message-specific information.

The return value specifies the result of the message processing and depends on the
message sent.
The function GetDlgItem() is used to get the window handle of the child window control
identified by the second parameter (IDD_FINDTEXT identifies the edit window - see
ex2.rc). The first parameter is the window handle of the dialogue box (it was passed to
the dialogue box procedure when it was invoked).
An alternative to using SendMessage() to set the text in a window is to use
SetWindowText().

Created by Tony G                                                   Page 45
Windows Systems Prog                                                 Background Info


       SetWindowText(hWndCtrl, szText);

This function actually uses GetDlgItem() and SendMessage().

Retrieving the Text Typed in the Edit Window
When the Ok button is clicked a WM_COMMAND message is sent to the dialogue
procedure with wParam set to IDOK (see ex2.rc). The text is retrieved by sending a get
text message, WM_GETTEXT, to the edit control requesting it to copy the text into a
buffer.

  hWndCtrl = GetDlgItem(hDlg, IDD_FINDTEXT);
  if (SendMessage(hWndCtrl, WM_GETTEXT,
MAX_TEXT,(LPARAM)(LPSTR)szText)==0)
          MsgOut("No find text entered");
  else
          MsgOut("Find text = \"%s\"", (LPSTR)szText);

wParam is set to MAX_TEXT, the size of the buffer szText and lParam is a pointer to the
buffer. The casts (LPARAM)(LPSTR) are to stop the compiler warning about
incompatible types. When SendMessage() is used to retrieve text from a window its
return value is the number of characters copied into the buffer.

An alternative to the SendMessage() function call is to use GetWindowText().

       GetWindowText(hWndCtrl, szText, MAX_TEXT-1)

If the IDCANCEL message is received then the program just terminates the dialogue
using EndDialog(hDlg, 0).

Source Files
File resource.h

#define IDM_CUT            101
#define IDM_COPY           102
#define IDM_PASTE          103
#define IDM_UNDO           104
#define IDD_FINDTEXT       106
#define IDM_FIND           107




Created by Tony G                                                  Page 46
Windows Systems Prog                                               Background Info




File ex2.c

#include <windows.h>
#include <stdarg.h>
#include "resource.h"

#define CUT_MSG                    "Cut requested - Dialogues"
#define COPY_MSG                   "Copy requested - Dialogues"
#define PASTE_MSG                  "Paste requested - Dialogues"
#define UNDO_MSG                   "Undo requested - Dialogues"
#define MAX_TEXT                   64

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
BOOL CALLBACK FindDlgProc(HWND hDlg, UINT wMessage,
                      WPARAM wParam, LPARAM lParam);
void MsgOut(LPCSTR Format, ...);

char szAppName[] = " Dialogues Example 2";
char szText[MAX_TEXT] = "some text";

#pragma argsused

int WINAPI WinMain(HANDLE hInstance, HANDLE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
{
     MSG          Msg;
     WNDCLASS WndClass;
     HWND         hWndMain;
     HANDLE hAccelerator;

       WndClass.style = CS_HREDRAW | CS_VREDRAW;
       WndClass.lpfnWndProc = WndProc;
       WndClass.cbClsExtra = 0;
       WndClass.cbWndExtra = 0;
       WndClass.hInstance = hInstance;
       WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
       WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
       WndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
       WndClass.lpszMenuName = "MYMENU";
       WndClass.lpszClassName = szAppName;

       RegisterClass(&WndClass);


Created by Tony G                                             Page 47
Windows Systems Prog                                                Background Info


       hWndMain = CreateWindow(szAppName, szAppName,
                             WS_OVERLAPPEDWINDOW,
                             CW_USEDEFAULT, CW_USEDEFAULT,
                             GetSystemMetrics(SM_CXSCREEN)/2,
                             GetSystemMetrics(SM_CYSCREEN)/2,
                             NULL, NULL, hInstance, NULL);

       ShowWindow(hWndMain, nCmdShow);
       UpdateWindow(hWndMain);

       hAccelerator = LoadAccelerators(hInstance, "MyAccelerators");

       while (GetMessage(&Msg, NULL, 0, 0))
       {
             if (!TranslateAccelerator(hWndMain, hAccelerator, &Msg))
      {
                   TranslateMessage(&Msg);
                   DispatchMessage(&Msg);
      }
       }
       return Msg.wParam;
}


LRESULT CALLBACK WndProc(HWND hWnd, UINT wMessage, WPARAM
wParam, LPARAM lParam)
{
    WORD                wId;
    HDC           hDC;
    RECT                Rect;
  PAINTSTRUCT ps;
    static HANDLE hInstance;

       switch(wMessage)
       {
            case WM_CREATE:
                 hInstance = ((LPCREATESTRUCT) lParam)->hInstance;
                 return 0;

              case WM_PAINT:
                   hDC = BeginPaint(hWnd, &ps);
                   GetClientRect(hWnd, &Rect);
                   DrawText(hDC, "Hello Windows!", -1, &Rect,
                             DT_SINGLELINE | DT_CENTER | DT_VCENTER);
                   EndPaint(hWnd, &ps);

Created by Tony G                                                 Page 48
Windows Systems Prog                                                 Background Info


                    return 0;

              case WM_COMMAND:
                   wId = LOWORD(wParam);
                   switch (wId)
                   {
                        case IDM_CUT:
                              MessageBox(hWnd, CUT_MSG, szAppName, MB_OK);
                              return 0;

                         case IDM_COPY:
                               MessageBox(hWnd, COPY_MSG, szAppName, MB_OK);
                               return 0;

                         case IDM_PASTE:
                               MessageBox(hWnd, PASTE_MSG, szAppName, MB_OK);
                               return 0;

                         case IDM_UNDO:
                               MessageBox(hWnd, UNDO_MSG, szAppName, MB_OK);
                               return 0;

                         case IDM_FIND:
                               DialogBox(hInstance, "FindDialogue", hWnd, FindDlgProc);
                               return 0;
                    }
                    break;

              case WM_DESTROY:
                   PostQuitMessage(0);
                   return 0;
       }
       return DefWindowProc(hWnd, wMessage, wParam, lParam);
}

#pragma argsused

BOOL CALLBACK FindDlgProc(HWND hDlg, UINT wMessage,
                         WPARAM wParam, LPARAM lParam)
{
   WORD wId;
   HWND hWndCtrl;

       UNUSED_IDENTIFIER(lParam);
       switch (wMessage)

Created by Tony G                                                  Page 49
Windows Systems Prog                                                            Background Info


       {
               case WM_INITDIALOG:
           /* Get handle of edit control in dialogue box         */

                    hWndCtrl = GetDlgItem(hDlg, IDD_FINDTEXT);

                    /* Initialise text in edit control. If not initialised it will                */
                    /* be blank.                                                                  */

                 SendMessage(hWndCtrl, WM_SETTEXT, 0,
(LPARAM)(LPSTR)szText);
/*
 * An alternative way of setting the text in a window is to use SetWindowText().
 * The statement below would work for this program.
 *
 *               SetWindowText(hWndCtrl, szText);
 */
                 return TRUE;

              case WM_COMMAND:
                   wId = LOWORD(wParam);
                   switch(wId)
                   {
                        case IDOK:
                              hWndCtrl = GetDlgItem(hDlg, IDD_FINDTEXT);
                              if (SendMessage(hWndCtrl, WM_GETTEXT,
                                    MAX_TEXT, (LPARAM)(LPSTR)szText) == 0)
                              {
                                    MsgOut("No find text entered");
                              }
                              else
                        MsgOut("Find text = \"%s\"", (LPSTR)szText);

/*
 * An alternative way of getting the text from a window is to use
 * GetWindowText().
 *
 * The SendMessage() would be replaced by the function call below.
 *
 *          GetWindowText(hWndCtrl, szText, MAX_TEXT-1)
 */
                             EndDialog(hDlg, 0);
                             return TRUE;

                          case IDCANCEL:

Created by Tony G                                                             Page 50
Windows Systems Prog                                        Background Info


                             EndDialog(hDlg, 0);
                             return TRUE;
                    }
                    break;
       }
       return FALSE;
}

void MsgOut(LPCSTR Format, ...)
{
     va_list arg_ptr;
     char szBuffer[256];

       va_start(arg_ptr, Format);
       wvsprintf(szBuffer, Format, arg_ptr);
       MessageBox(NULL, szBuffer, szAppName, MB_OK);
}


File ex2.rc

#include "resource.h"

MYMENU MENU DISCARDABLE
BEGIN
  POPUP "&Edit"
  BEGIN
    MENUITEM "&Undo\tCtrl+Z",                 IDM_UNDO
    MENUITEM SEPARATOR
    MENUITEM "Cu&t\tCtrl+X",                 IDM_CUT
    MENUITEM "&Copy\tCtrl+C",                  IDM_COPY
    MENUITEM "&Paste\tCtrl+V",                IDM_PASTE
  END
  POPUP "&Search"
  BEGIN
    MENUITEM "&Find",                     IDM_FIND
  END
END

MYACCELERATORS ACCELERATORS MOVEABLE PURE
BEGIN
    "^V",     IDM_PASTE, ASCII
    VK_Z,     IDM_UNDO, VIRTKEY, CONTROL
    "^X",     IDM_CUT,       ASCII
    "^C", IDM_COPY, ASCII

Created by Tony G                                         Page 51
Windows Systems Prog                            Background Info


END

FindDialogue DIALOG 53, 45, 127, 53
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Find Dialogue"
{
     EDITTEXT IDD_FINDTEXT, 45, 9, 74, 12
     DEFPUSHBUTTON "Ok", IDOK, 46, 32, 29, 14
     PUSHBUTTON "Cancel", IDCANCEL, 90, 32, 29, 14
     RTEXT "&Text to find:", -1, 4, 11, 38, 8
}




Created by Tony G                             Page 52
Windows Systems Prog                                                 Background Info


A More Complex Dialogue Box – ex3

Program ex3. Files ex3.c, ex3.rc and resource.h.
Example 3 demonstrates the basic of radio buttons and check boxes. It extends example 3
by add dialogue to allow the user to select case sensitive search, whole word search and
forward/backward search.

                                          Edit Box




                         Check Box    Radio Buttons



Two other frequently used controls are radio buttons and check boxes. Radio buttons in a
group are mutually exclusive - only one can be checked. With check boxes there is no
limit on how many are checked.

The Dialogue Template
Below is a stripped down version of the dialogue box template used to produce the Find
dialogue box. See ex3.rc for the full template.
The radio buttons and check boxes are of “Button” class but the styles
BS_AUTOCHECKBOX and BS_AUTORADIOBUTTON change the way Windows
displays them. There are also the styles BS_CHECKBOX and BS_RADIOBUTTON. The
AUTO versions of the radio button and check box manage themselves. If an auto check
box is clicked it will visually toggle it‟s state but the non auto version will have to be
checked or unchecked by the program. Both types of radio buttons and check boxes send
messages to the dialogue procedure when they are clicked; the messages would be
WM_COMMAND messages with wParam set to the ID of the control. Because we are
using the auto version in this program we only need to handle the Ok and Cancel button
messages.
One of other point of interest is the use of WM_TABSTOP in the style field. The tab key
can be used to move from one control to another in a dialogue box. Those controls that
can be moved to using the tab key must have the WM_TABSTOP attribute set. This can
be done in the Resource Editor when defining the controls. Additionally, the order in

Created by Tony G                                                  Page 53
Windows Systems Prog                                                   Background Info


which they are visited by the tab key is determined by the order in the dialogue script.
This can be set using the Resource Editor.

FindDialogue DIALOG 86, 83, 179, 60
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Find Dialogue"
BEGIN
     EDITTEXT IDD_FINDTEXT, 45, 10, 74, 12, ...
     CONTROL "&Case sensitive", IDD_CASE, "BUTTON", ..., 3, 26, 55, 13
     CONTROL "&Whole word", IDD_WHOLEWORD, "BUTTON", ..., 3, 41, 54, 12
     CONTROL "Search &foward", IDD_FORWARD, "BUTTON", ..., 64, 26, 66, 12
     CONTROL "Search &backward", IDD_BACKWARD, "BUTTON", ..., 64, 41, 66,
12
     DEFPUSHBUTTON "Ok", IDOK, 139, 9, 29, 14, ...
     PUSHBUTTON "Cancel", IDCANCEL, 139, 37, 29, 14, ...
     CONTROL "&Text to find:", -1, "STATIC", ..., 2, 12, 38, 8
END

Initialising the Edit Window
When the dialogue box is invoked, the first message the dialogue procedure
FindDlgProc() get is WM_INITDIALOG. The program uses this to initialise the contents
of the edit box and which radio button is checked.

      case WM_INITDIALOG:
          hWndCtrl = GetDlgItem(hDlg, IDD_FINDTEXT);
          SendMessage(hWndCtrl, WM_SETTEXT, 0, (LPARAM)(LPSTR)szText);
          SendMessage(hDlg, IDD_FORWARD), BM_SETCHECK, 1, (LPARAM)0);

GetDlgItem() is used to get the handle of the control identified by IDD_FORWARD (the
forward search radio button). When the message BM_SETCHECK is sent to this control
it activated it.

An alternative way to check the radio button is to use SendDlgItemMessage().

      SendDlgItemMessage(hDlg, IDD_FORWARD, BM_SETCHECK, 1, (LPARAM)0);

Retrieving the State of a Radio Button or Check Box
The state of a radio button or check box (auto or otherwise) is retrieved by sending the
BM_GETCHECK message to the control asking for its state. The values of wParam and
lParam are ignored.

      if (SendMessage(GetDlgItem(hDlg, IDD_CASE), BM_GETCHECK, 0, (LPARAM)
0))
         wsprintf(szMessage+lstrlen(szMessage), "Case sensitive find\n");

Created by Tony G                                                    Page 54
Windows Systems Prog                                                  Background Info


    else
        wsprintf(szMessage+lstrlen(szMessage), "Case insensitive find\n");

An alternative way of getting the "state" of a check box or radio button is to use
SendDlgItemMessage(). The state of the "Case sensitive" check box could have been got
using the function call below.

    SendDlgItemMessage(hDlg, IDD_CASE, BM_GETCHECK, 0, (LPARAM) 0);

As has already been said, an alternative to waiting for the Ok button to be clicked and
then getting the state of the various controls would have been to have included a case
statement for each control ID in the dialogue procedure. As each control was clicked a
WM_COMMAND message would be sent to the procedure. with the ID in wParam.

Source Files
File resource.h

#define IDM_CUT                    101
#define IDM_COPY                    102
#define IDM_PASTE                   103
#define IDM_UNDO                    104
#define IDD_FINDTEXT                  106
#define IDM_FIND                   107
#define IDD_CASE                   108
#define IDD_WHOLEWORD                    109
#define IDD_FORWARD                    110
#define IDD_BACKWARD                    111

File ex3.c


#include <windows.h>
#include <stdarg.h>
#include "resource.h"

#define CUT_MSG                   "Cut requested - Dialogues"
#define COPY_MSG                  "Copy requested - Dialogues"
#define PASTE_MSG                 "Paste requested - Dialogues"
#define UNDO_MSG                  "Undo requested - Dialogues"
#define MAX_TEXT                  64

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
BOOL CALLBACK FindDlgProc(HWND hDlg, UINT wMessage,
               WPARAM wParam, LPARAM lParam);
Created by Tony G                                                   Page 55
Windows Systems Prog                                                 Background Info



char     szAppName[] = "Resources Example 7";
char     szText[MAX_TEXT] = "some text";
char     szMessage[256];

#pragma argsused

int WINAPI WinMain(HANDLE hInstance, HANDLE hPrevInstance,
              LPSTR lpszCmdLine, int nCmdShow)
{
    MSG     Msg;
    WNDCLASS WndClass;
    HWND       hWndMain;
    HANDLE hAccelerator;

    WndClass.style = CS_HREDRAW | CS_VREDRAW;
    WndClass.lpfnWndProc = WndProc;
    WndClass.cbClsExtra = 0;
    WndClass.cbWndExtra = 0;
    WndClass.hInstance = hInstance;
    WndClass.hIcon = LoadIcon(hInstance, "EX7ICON");
    WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
    WndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
    WndClass.lpszMenuName = "MYMENU";
    WndClass.lpszClassName = szAppName;

    RegisterClass(&WndClass);

    hWndMain = CreateWindow(szAppName, szAppName,
                   WS_OVERLAPPEDWINDOW,
                   CW_USEDEFAULT, CW_USEDEFAULT,
                   GetSystemMetrics(SM_CXSCREEN)/2,
                   GetSystemMetrics(SM_CYSCREEN)/2,
                   NULL, NULL, hInstance, NULL);

    ShowWindow(hWndMain, nCmdShow);
    UpdateWindow(hWndMain);

    hAccelerator = LoadAccelerators(hInstance, "MyAccelerators");

    while (GetMessage(&Msg, NULL, 0, 0))
    {
        if (!TranslateAccelerator(hWndMain, hAccelerator, &Msg))
      {
             TranslateMessage(&Msg);

Created by Tony G                                                  Page 56
Windows Systems Prog                                           Background Info


              DispatchMessage(&Msg);
      }
    }
    return Msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT wMessage, WPARAM
wParam, LPARAM lParam)
{
  WORD              wId;
  HDC            hDC;
  RECT           Rect;
  PAINTSTRUCT       ps;
  static HANDLE hInstance;

    switch(wMessage)
    {
       case WM_CREATE:
           hInstance = ((LPCREATESTRUCT) lParam)->hInstance;
           return 0;

         case WM_PAINT:
             hDC = BeginPaint(hWnd, &ps);
             GetClientRect(hWnd, &Rect);
             DrawText(hDC, "Hello Windows!", -1, &Rect,
                     DT_SINGLELINE | DT_CENTER | DT_VCENTER);
             EndPaint(hWnd, &ps);
             return 0;

         case WM_COMMAND:
             wId = LOWORD(wParam);
             switch (wId)
             {
                case IDM_CUT:
                    MessageBox(hWnd, CUT_MSG, szAppName, MB_OK);
                    return 0;

                    case IDM_COPY:
                        MessageBox(hWnd, COPY_MSG, szAppName, MB_OK);
                        return 0;

                    case IDM_PASTE:
                        MessageBox(hWnd, PASTE_MSG, szAppName, MB_OK);
                        return 0;


Created by Tony G                                            Page 57
Windows Systems Prog                                                            Background Info


                    case IDM_UNDO:
                        MessageBox(hWnd, UNDO_MSG, szAppName, MB_OK);
                        return 0;

                    case IDM_FIND:
                        DialogBox(hInstance, "FindDialogue", hWnd, FindDlgProc);
                        return 0;
              }
              break;

         case WM_DESTROY:
             PostQuitMessage(0);
             return 0;
    }
    return DefWindowProc(hWnd, wMessage, wParam, lParam);
}

#pragma argsused

BOOL CALLBACK FindDlgProc(HWND hDlg,UINT wMessage,WPARAM
wParam,LPARAM lParam)
{
  WORD wId;
  HWND hWndCtrl;

    switch (wMessage)
    {
       case WM_INITDIALOG:

              /* Get handle of edit control in dialogue box */

              hWndCtrl = GetDlgItem(hDlg, IDD_FINDTEXT);

              /* Initialise text in edit control. If not initialised it    */
              /* will be blank                                        */

              SendMessage(hWndCtrl, WM_SETTEXT, 0, (LPARAM)(LPSTR)szText);

/*
 * An alternative way of setting the text in a window is to use
 * SetWindowText().
 *
 *          SetWindowText(hWndCtrl, szText);
 */


Created by Tony G                                                          Page 58
Windows Systems Prog                                                    Background Info


              /* Check the "Forward find" radio button on */

       SendMessage(GetDlgItem(hDlg, IDD_FORWARD), BM_SETCHECK,1,
(LPARAM)0);

/*
 * An alternative way to check the radio button is to use SendDlgItemMessage().
 *
 *          SendDlgItemMessage(hDlg, IDD_FORWARD, BM_SETCHECK, 1,
(LPARAM)0);
*/
            return TRUE;

         case WM_COMMAND:
             wId = LOWORD(wParam);
             switch(wId)
             {
                case IDOK:
                /* Get the handle of the text control in the dialogue box       */

                        hWndCtrl = GetDlgItem(hDlg, IDD_FINDTEXT);

                    /* text (if any) from the dit control   */

                        if (SendMessage(hWndCtrl, WM_GETTEXT, MAX_TEXT,
                                    (LPARAM)(LPSTR)szText))
                             wsprintf(szMessage, "Find text = \"%s\"\n",
                                (LPSTR)szText);
                        else
                             wsprintf(szMessage, "No find text\n");
/*
 * An alternative way of getting the text from a window is to
 * use GetWindowText().
 *
 * The SendMessage() was replaced by the function call below.
 *
 *      GetWindowText(hWndCtrl, szText, MAX_TEXT-1)
 */
                   if (SendMessage(GetDlgItem(hDlg, IDD_CASE), BM_GETCHECK,
                                                      0, (LPARAM) 0))
                        wsprintf(szMessage+lstrlen(szMessage),
                               "Case sensitive find\n");
                   else
                        wsprintf(szMessage+lstrlen(szMessage),
                                   "Case insensitive find\n");

Created by Tony G                                                     Page 59
Windows Systems Prog                                            Background Info



           if (SendMessage(GetDlgItem(hDlg, IDD_WHOLEWORD),
BM_GETCHECK,
                                 0, (LPARAM) 0))
                wsprintf(szMessage+lstrlen(szMessage), "Whole word find\n");
           else
                wsprintf(szMessage+lstrlen(szMessage), "String find\n");

           if (SendMessage(GetDlgItem(hDlg, IDD_FORWARD),
BM_GETCHECK,
                                 0, (LPARAM) 0))
                wsprintf(szMessage+lstrlen(szMessage), "Forward find\n");
           else
                wsprintf(szMessage+lstrlen(szMessage), "Backward find\n");

                       MessageBox(NULL, szMessage, szAppName, MB_OK);
/*
 * An alternative way of getting the "state" of a check box or radio button
 * is to use SendDlgItemMessage(). The state of the "Case sensitive" check
 * box could have been got using the function call below.
 *
 *       SendDlgItemMessage(hDlg, IDD_CASE, BM_GETCHECK, 0, (LPARAM) 0)
 */
                   EndDialog(hDlg, 0);
                   return TRUE;

                    case IDCANCEL:
                        EndDialog(hDlg, 0);
                        return TRUE;
              }
              break;
    }
    return FALSE;
}


File ex3.rc

#include "resource.h"

MYMENU MENU
BEGIN
  POPUP "&Edit"
  BEGIN
    MENUITEM "&Undo\tCtrl+Z", IDM_UNDO

Created by Tony G                                             Page 60
Windows Systems Prog                                    Background Info


     MENUITEM SEPARATOR
     MENUITEM "Cu&t\tCtrl+X", IDM_CUT
     MENUITEM "&Copy\tCtrl+C", IDM_COPY
     MENUITEM "&Paste\tCtrl+V", IDM_PASTE
   END

   POPUP "&Search"
   BEGIN
     MENUITEM "&Find", IDM_FIND
   END

END
MYACCELERATORS ACCELERATORS
BEGIN
  "^V", IDM_PASTE
  VK_Z, IDM_UNDO, VIRTKEY, CONTROL
  "^X", IDM_CUT
  "^C", IDM_COPY, ASCII
END

FindDialogue DIALOG 86, 83, 179, 60
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Find Dialogue"
BEGIN
  EDITTEXT IDD_FINDTEXT, 45, 10, 74, 12,
               ES_LEFT | WS_CHILD | WS_VISIBLE | WS_BORDER |
WS_TABSTOP
  CONTROL "&Case sensitive", IDD_CASE, "BUTTON",
               BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE |
WS_TABSTOP,
               3, 26, 55, 13
  CONTROL "&Whole word", IDD_WHOLEWORD, "BUTTON",
               BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE |
WS_TABSTOP,
               3, 41, 54, 12
  CONTROL "Search &foward", IDD_FORWARD, "BUTTON",
               BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE |
WS_TABSTOP,
               64, 26, 66, 12
  CONTROL "Search &backward", IDD_BACKWARD, "BUTTON",
               BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE |
WS_TABSTOP,
               64, 41, 66, 12
  DEFPUSHBUTTON "Ok", IDOK, 139, 9, 29, 14, WS_CHILD | WS_VISIBLE |
WS_TABSTOP

Created by Tony G                                     Page 61
Windows Systems Prog                                                     Background Info


  PUSHBUTTON "Cancel", IDCANCEL, 139, 37, 29, 14,
            WS_CHILD | WS_VISIBLE | WS_TABSTOP
  CONTROL "&Text to find:", -1, "STATIC",
            SS_RIGHT | WS_CHILD | WS_VISIBLE | WS_GROUP |
WS_TABSTOP,
            2, 12, 38, 8
END
EX3ICON ICON
{
    ....
}

A Note about Radio Buttons and Groups
In example ex3 we had two radio buttons. As each was clicked it was checked and all
others (if there were more than two) were unchecked. A more typical dialogue would
have several groups of radio buttons with the radio buttons in each group being mutually
exclusive. The GROUPBOX control would be used to group the sets of radio buttons
together. The radio buttons in each GROUPBOX would be mutually exclusive.

Common Dialogues

Windows 3.1 onwards includes the common dialogue box library. This library consists of
a number functions that invoke standard dialogue boxes the include such functionality as
opening and saving files, searching and replacing, choosing colours, choosing fonts and
printing .

To use these functions, you basically initialise the fields of a structure and pass a pointer
to the structure to a function in the common dialogue box library. The function creates
and displays the dialogue box. When the user has finished with the dialogue box, the
function you called returns control to your program, and you get information from the
structure you passed to it.




Created by Tony G                                                      Page 62
Windows Systems Prog     Background Info




Created by Tony G      Page 63
Windows Systems Prog                                                    Background Info


The Graphics Interface and Mouse Events

In our review of the architecture of a Windows application and its structure we have so
far only considered simple PAINTING of text in response to WM_PAINT messages.

Here we will look at the EVENTS associated with GUI interaction i.e. Mouse and
shortcut keys etc. Plus the available graphical API‟s, the idea of sizing, mapping and
drawing modes.

It is important to realise that we have to look at this subject in a physical device
independent manner. In any architecture in which the user has the ability to change the
physical appearance of the display. Not only the size but the default background colour
schemes etc., if the application is written with specific attributes to the manner in which
information is displayed or the manner of display of a particular piece of information, a
particular screen format size etc. then it will only be a matter of time before the
application will run on a system were these application derived attributes will render the
use of the system worthless.

First therefore we must look at the concepts associated with device independence.

Before we begin it must be emphasised that the HCI aspects of the GUI interface for a
particular application is not being considered, we are looking at the systems architecture
associated with a particular GUI design not how the GUI‟s HCI has been derived.

                                  DEVICE CONTEXT.

We have seen that within the processing of the BeginPaint() .... EndPaint () we used
something called the Device Context. Now we need to look at the relevance of this
Device Context, what it offers and how use of the associated API‟s provide the device
independence we require.

This forbidding name Device Context (DC) has two important aspects related to the
ability of a particular application to communicated with specific physical devices.

First - any DC is associated with a particular physical device, the actual software interface
to for instance a SVGA display adapter is configured into a particular installation of
Windows 3.1.x, „95 or „NT. This physical drive software is of no relevance to the
architecture of a windows application. An application must be designed with the full
understanding that it is to communicate all its graphical output in the case of a display
adapter through a DC. Any application must not attempt as was done extensively in the
past under DOS to fix attributes and constraints on the output by attempting to interface
directly with the physical device.


Created by Tony G                                                     Page 64
Windows Systems Prog                                                   Background Info


Other DC‟s exist in any system the other most common DC is the one associated with the
printer. NOTE. To select which physical device to direct output to the application selects
the correct DC. We will see later that you can create a compatible DC (compatible with
for instance the display), environments in Memory allowing output to be drawn and
formatted in memory and the BLATED (fast copied to the DC associated with the actual
physical device).

The Device Driver or Interface to the physical display etc. is the piece of software within
a windows system designated as a VxD and will generally have the file extension .DRV.
As mentioned above the internal structure and operation of the VxD is not relevant to the
architecture of an application. Within this course we will not consider VxD‟s although
later in this document we do spend some time looking at device driver design
considerations, we will rather simply use VxD's indirectly i.e. by selecting a DC for the
particular device.

It is however worth noting that significant differences in the form the VxD have evolved
through Windows 3.1.x, „95 and „NT. Initially VxD‟s and therefore the physical devices
configured and characteristics for a particular installation of Windows, had to be loaded
during Windows start up, the configuration of devices was fixed meaning that only
specific DC‟s were available. Later this constraint was removed and VxD‟s could be
loaded/unloaded and modified by applications allowing far for flexibility. This increase in
flexibility does not mean applications attempt to modify the physical devices directly, it
simply implies that the DC for a physical device can be replaced dynamically, and may
then offer possibly a different physical interface operation for a particular graphics API.

The second major function of the DC is a place holder and container for storage of a set
of graphical attributes such as colour, pen size, brushes (fill patterns) and so on.
We will see later that we can modify the attributes once the application has access to the
DC these modifications will generally be temporary since we are in multi task
environment and the DC will be acquired by all applications. So when for instance screen
output is required, and since no application ever owns the DC, the application will
acquire the DC for the screen and it can not assume it is the same as when it was last
acquired. An important concept is that an application will acquire the DC to perform
output then it should release the DC so that another applications can acquire it and on
release it should return the DC to a known configuration or to the configuration when it
was acquired.

In order to understand the concept of DC we can think of it as an artist being instructed to
do something on to a canvas. The application itself does not know how to put the physical
paint on the canvas, the artist takes the instructions from the application and using the
equipment he /she is holding performs the actual visual actions..




Created by Tony G                                                    Page 65
Windows Systems Prog                                    Background Info




The DC at work ... Artist paints onto the canvas - Physical Device




Created by Tony G                                     Page 66
Windows Systems Prog                                                       Background Info


GRAPHIC OBJECTS.

Graphics objects are surprisingly abstract entities.
What are they ?
How are they used?

How do they relate to the DC.

WHY ARE GRAPHICS OBJECTS NECESSARY?

The functions within Windows to actually draw graphics shapes are comparatively
simple. The function to draw a rectangle, for example, takes five arguments, four of
which define the dimensions whilst the fifth specifies the DC for the operation.

This fifth argument therefore is the encapsulation of every aspect of the drawing process
required, width, colour of outline and pattern of the interior and the destination i.e. screen
or printer etc. If this encapsulation did not exist then the function to draw even a simple
line would become complex and unwieldy since all this additional information would
need to be provided each time.

The concept of the encapsulation is not without its problems, the fact that an application
sets the line colour attribute for instance to Blue in the DC before a given graphics
operation then all subsequent operations using that DC will have a line colour of Blue
until the attribute is once again changed. This interposes a new design discipline, as
indicated the DC should be restored by each application prior to release and no
application should assume any particular graphics object attribute is correct as it acquires
the DC prior to performing output.

WHAT ARE THE OBJECTS?

Groups of attributes are encapsulated into objects that are then stored within the DC.
These abstract entities are called graphics objects
Drawing a line for instance is controlled by an object called a PEN.
Filling the interior of an object is controlled by an object called a BRUSH.

To modify or select a particular characteristic of an object, the application has to either
use a STOCK object, one that exists within Windows, or a new set of characteristic are
CREATED to form the specific object characteristics for the application. Once such an
object exists it has to be selected into the DC that is going to use it. It is then used for the
particular graphics output operation. So in all cases it is basically a two-stage operation,
select the characteristics, and update the characteristics into the DC.




Created by Tony G                                                        Page 67
Windows Systems Prog                                                    Background Info


                                    Common Objects.

PENS.

The basic characteristics for a PEN are


Line Style
Line width
Colour

There are STOCK Pens - BLACK_PEN, WHITE_PEN and NULL_PEN. The
advantages of these are they already exist and do not require any additional system
resource. If the application uses CREATE, to formulate a unique PEN a nearly infinite
variety of PENS can be produced, but system resource in terms of memory requirements
for these specific PENS has to be considered.

The important item to remember is; if an application creates PENS it is using system
resource and it is the responsibility of the application to delete the resource when it is no
longer required, otherwise the resource may be left over even when the application
terminates and this is the basis of the Windows System problem called a Memory Leak.

BRUSHES

The basic characteristics for a BRUSH are

Solid or Hatch Pattern
Colour

As with PENS there are STOCK brushes for instance - BLACK_BRUSH,
WHITE_BRUSH and NULL_BRUSH etc. Again the advantages of these is they already
exist and do not require any additional system resource. If the application uses CREATE
a nearly infinite variety of BRUSHES can be produced specific to that applications need.
The same resource leakage problem exists, specifically created brushes must be deleted
when no longer required.

COLOUR

Obviously the availability of colour offers an exciting difference on GUI based systems
such as Windows.

However, careful consideration to the colour capability of the system the application is
likely to run on is important, even today when nearly 100% of PC‟s have some form of
colour display adapter. It is common practice to combine the applications code associated
with graphical output between the display and the printer. Now the screen may have a
Created by Tony G                                                     Page 68
Windows Systems Prog                                                   Background Info


colour palette capability of 256 or more were a colour printer may only have a palette of
16.

Windows 3.1.x, „95 and „NT address the problem of specifying a colour hue that can not
be dealt with by the physical device by modifying the colour to a best match. Windows
will also dither the colour from a solid in an attempt to imitate the requirement. Dithering
produces a characteristic textured look to the colour in question and some colour hues
appear better than others do, so applications need to bear this in mind. Colour images or
colour printing is specified by considering the amount of each of the three primary light
colours RED, GREEN and BLUE.

Typically a phosphor screen on a display has a repeating pattern similar to

RED GREEN BLUE




                                      PIXEL

So a Pixel consists of one or more of these groupings of each of the three primary colours
in this characteristic format. Colour is derived by the intensity of the three colours. On a
printer three dots are printed with varying intensity.

It is a simple matter to get RED the red dot is maximum and the other two are off.
Magenta is formed by RED and BLUE at maximum and GREEN off.

To specify the colour within Windows it is necessary to define the intensity of the three
primary colours. This is done by using the COLORREF ( this is not a typo US spelling of
colour). COLORREF changes between flavors of Windows. In the 16 bit 3.1.x version it
is a DWORD - double word i.e. 2 x 16 bit words. In the 32 bit „95 and „NT it is an
WORD - 1x 32 bit word.




Created by Tony G                                                    Page 69
Windows Systems Prog                                                    Background Info


COLORREF is split into 4 x 8 bit bytes - one for each colour and one that is not used.

Not Used               Blue                   Green                     Red



COLORREF - 32 bit value
3 x 8 bit values specify the primary colour intensity.

The value of each 8 bit colour intensity can be from 0 to 255 so this gives rise to the
availability of 256 shades of red mixed with 256 shades of green and 256 shades of blue a
total of 16,777,216 hues of colour.

In order to simplify the selection and set up of COLORREF a macro exists RGB that
constructs the COLORREF 32 bits correctly.

Typical RGB Values.

Colour                Red                    Green                  Blue

Black                 0                      0                      0
Red                   255                    0                      0
Green                 0                      255                    0
Blue                  0                      0                      255
Yellow                255                    255                    0
Magenta               255                    0                      255
Cyan                  0                      255                    255
Dark Gray             128                    128                    128
Olive                 128                    128                    0
Orange                255                    128                    0
White                 255                    255                    255


WARNING

If an application specifies an RBG(175,37,76) it is very lightly unless the system running
the application has a sophisticated display adapter that this will be translated to something
far simpler RGB(255,0,128) and possibly dependant on the display adapter some
dithering may occur. The moral to this warning is; unless you are doing photographic
manipulation applications don‟t try and select complex colour hues they probably won‟t
be available on the majority of systems.

The last topic before we look at HOW we interface with the user allowing graphical input
MOUSE events etc.


Created by Tony G                                                     Page 70
Windows Systems Prog                                                   Background Info


                               What are drawing modes?

If an application specifies draw a line, it expects this line to appear on for instance the
screen (we will assume the DC specified is the screen). The line should appear in the line
style, width and colour specified by the PEN selected into the DC. However, the world of
graphic drawing is not as simple as that. What about the background ?

This may be a variety of colours either solid or banded and the line may disappear in
certain regions because the PEN colour and the background colour are the same.

What do we do?

The answer in Windows and many other graphical based GUI‟s is to have Drawing
Modes or Raster Operations. This is another set of attributes held in the DC which
determine how the graphical operation is to be performed.

ROP‟s - Raster Operation

There are vast arrays of these some are very obscure but looking at the most common we
can identify

ROP Constant                                 Line Drawn on background Colour

R2_Black                                     Always Black
R2_White                                     Always White
R2_NOP                                       Same as background so invisible
R2_NOT                                       Inverse of background so always visible
R2_COPYPEN                                   Same as Pen Colour (default)
R2_NOTCOPYPEN                                Inverse of Pen Colour


The most common is R2_COPYPEN, (the default) simply uses the Pen Colour specified.

R2_NOT is very important it draws as the inverse of the background so is always visible
no matter what colour or range of colours on the background. Of course the inverse of
Black is White and the inverse of White is Black, but things do get complicated when the
background is some other colour.

An interesting feature of R2_NOT is that if we draw one line then draw over that line
again it will disappear since the inverse of the inverse get us back to the original
background colour. This is a very simple but important idea, that we will investigate more
fully when we look in a moment at acquiring drawing input from the user via the Mouse
events.



Created by Tony G                                                    Page 71
Windows Systems Prog                                                  Background Info


Summary so far

Pens and Brushes are selected into the DC they can either be STOCK items within
Windows or CREATED. They are no more than a set of characteristics that define the
manner in which lines or shapes etc., are drawn. The DC isolates the applications
requirement for drawing from the physical characteristics of the device on which the
drawing appear.

The colour of the graphical drawing can be controlled by defining the intensity of the
RED, GREEN and BLUE within a Pixel. Although 16,777,216 options exist any
particular physical device may not be able to support all these and a simpler code or
dithering may take place.

The raster operation code defines how the foreground drawing will appear on the
background.

                                 Drawing Input Events.

The Mouse and User Events

Having dealt with graphics input we must now look at User Input. You use the mouse or
tracker ball to indicate points on the screen o draw and drag graphics objects on the
screen to different locations within the application window. If we did not have this
pointing device then all co-ordinates on the screen would have to be entered by hand and
the whole idea of a GUI interface would be lost in graphics programs.

We have already seen that by encapsulating the attributes for the actual drawing features
Windows can provide a simple set of API‟s to place geometric elements; lines, ellipses
and rectangles on the screen. In some cases none of these elements is quite what is needed
so in addition you can place individual pixels on the screen.

Such a technique requires patience on the part of the user but with the right mouse
interface the user can drag a shape outline onto the screen simply by holding down a
mouse button and pulling the cursor around the outline of the shape required. The API
function will draw the pixel at the screen co-ordinate specified.

In order to accomplish this we will require a message from Windows each and ever time
the mouse is moved over the application Window. It is important to understand it is over
the applications window not the border or any other window on the desktop.

The message we get is a WM_MOUSEMOVE.




Created by Tony G                                                   Page 72
Windows Systems Prog                                                 Background Info



We will first look at this message in the 16 bit Windows 3.1.x environment, the contents
of the parameters lParam and wParam has changed in „95 and „NT simply because of the
32 bit environment, the changes are significant in terms of where values can be found but
trivial in terms of functionality change.

lParam in the 16-bit environment contains in the low word the X-co-ordinate and the high
word the Y-co-ordinate.

                    16 bits                         16 bits




         lParam
         High Word Y-co-ordinate                    Low Word X-co-ordinate

Two standard macros are available to extract these unsigned integer value correctly
LOWORD() and HIWORD()

wParam indicates carries information on whether the user has pressed mouse keys or is
holding down any special keys.




Created by Tony G                                                  Page 73
Windows Systems Prog                                                        Background Info



WParam Constant                                 Meaning in WM_MOUSEMOVE

MK_LBUTTON                                      Left Button was down
MK_RBUTTON                                      Right Button was down
MK_MBUTTON                                      Middle Button was down
MK_CONTROL                                      CTRL key on keyboard down
MK_SHIFT                                        SHIFT key on keyboard down

So typically you can test wParam with code of the form

if (wParam & MK_LBUTTON)

This will be true if the mouse moved while the user held the left button.

To light a single Pixel on the screen when the user moves the mouse with the left button
held we would need a piece of code of the form

         if (wParam & MK_LBUTTON)
         {
                hDC = GetDC(hWnd); // Get the default screen DC for this Window
                                     // hWnd is the windows handle returned during
                                     // create window.
                SetPixel (hDC, LOWORD(lParam), // X-coordinate
                               HIWORD(lParam), // Y-coordinate
                               RGB(0,0,0)); // Colour for Pixel in this case Black
                ReleaseDC(hWnd,hDC); // Release the DC
         }

This code being the action to take on receipt of a WM_MOUSEMOVE message in the
message handling procedure for the particular window.

Drawing a Line with a MOUSE

Now we could simply use the Pixels to draw the line but this would be tedious for the
user and it does not offer the user the chance to change the direction of the line. The most
common technique employed is rubber-band lines Here the line appears to stretch from
its starting point, in fact the starting point is the only fixed aspect of the line until the user
indicates they want to terminate an fix the line into that position.

Sounds very complicated but the code is quite simple.




Created by Tony G                                                         Page 74
Windows Systems Prog                                                Background Info



if (wParam & MK_LBUTTON)
       {
             hDC = GetDC(hWnd); // Get the default screen DC for this Window
                                    // hWnd is the windows handle returned during
                                    // create window.
             PtNew = MAKEPOINT (lParam);
                                  // Ptnew is a standard structure of type POINT
                                  // which using the MAKEPOINT macro
                                  // will store the X-coordinate and Y-coordinate
             SetROP2(hDC, R2_NOT); // we have seen this is draw mode
                                         // in which the colour is inverse of
                               // background
             MoveTo(hDC,Original.x, Original.y)// What is Original ?
                                                   // We will see we get this set by
                                          // another message
             LineTo(hDC, PtOld.x,PtOld.y);         // What is PtOld its were we drew
                                         // the line last time
             MoveTo(hDC,Original.x, Original.y)// What is Original ?
                                                   // We will see we get this set by
                                          // another message
             LineTo(hDC, PtNew.x,PtNew.y); // Draw the new line in...
             ReleaseDC(hWnd,hDC); // Release the DC
             PtOld = PtNew;                       // Update for next time .
       }


Now as indicated this is the code for WM_MOUSEMOVE messages, but we also need a
very simple bit of code for the message generated when the user first presses the left
button.

Windows provides a WM_LBUTTONDOWN message sent when the user first presses
the left button.

The code for this would be no more than

         Original = MAKEPOINT (lParam); // set up and save start point
         PtOld = PtNew;


The above would work but we have no way of completing the line and making it
permanent this would occur when the user releases the left button

Windows provides a WM_LBUTTONUP message sent when the user releases the left
button.

Created by Tony G                                                 Page 75
Windows Systems Prog                                                   Background Info



The code for this would be no more than


                    hDC = GetDC(hWnd); // Get the default screen DC for this Window
                                        // hWnd is the windows handle returned during
                                        // create window.
                    MoveTo(hDC,Original.x, Original.y); // back to start
                    LineTo(hDC, PtNew.x,PtNew.y); // Finish the line
                    ReleaseDC(hWnd,hDC);

Note we use the default ROP2 here of copy pen we do not need to look at were the mouse
is because the WM_MOUSEMOVE would have sorted the line out all we are doing is
just redrawing the line from its original position to the last stored position.

This technique can be employed to rubber band rectangle and circles.




Created by Tony G                                                   Page 76
Windows Systems Prog                                                  Background Info


                    The Architecture of Parent / Child
Any application running on a Windows System is unlikely to have only a single Window.
However, an application can only have a single Main Window. It is from this Main
Window that child Windows can be created and the hierarchy of the parent / child nature
of the Windows system architecture is formulated.

Options are available to have child windows that are not constrained to stay within the
client area of the main window.

Having reviewed the use of Dialog boxes when looking at the use of resources, then in a
special kind of way we have already looked at the Parent / Child architecture. However,
although Dialog Boxes use the same philosophy as Child Windows, a layer of insulation
between the raw Data manipulation at System Level and the applications retrieval of
Dialog data is provided by the Dialog Manager services of the environment.




Created by Tony G                                                   Page 77
Windows Systems Prog                                                   Background Info



                       WHAT IS A REAL CHILD WINDOW?

In simple terms it is any registered Windows Class used by an application, which is not
created as the Main Window of that application. In fact we could take this definition a
stage further, any application launched in a Windows environment is launched from
nominally a Window so that any Window is created is a Child of the SYSTEM level
Window on the GUI. Although the latter is true Microsoft certainly does not consider this
to be the case.

Any child window can be considered as simply an extension of the overall concept of the
Windows system; it will have message handling capability and will interact in the
message passing strategy of the system. For the purposes of this discussion we will look
at various forms of the child window; controls, application specific and MDI (Multiple
Document interfaces. Each has a unique place within the Windows architecture.

From a systems architecture view point with the exception of the main applications
Windows and in the case of the MDI the MDICLIENT form of a Window all elements
used or viewed in the application are child windows - YES even Static Text.

The most common child windows are the various active controls used to retrieve
information from the USER: PushButtons, CheckBoxes, RadioButtons, Lists, Combo‟s
etc., equally any application can derive its own Child Windows to suit its requirements.

In order to maintain compatibility with the overall event handling philosophy there has to
be a ChildWinProc() for each Child created. This procedure deals with all the messages
associated with this Child Window.

Having this facility it is possible for the Child to send messages to the Parent or visa
versa. This is consistent with the system concepts of Windows that allows information
interchange between Windows.

HOW is this accomplished?
In order to send messages l that is needed is for the sending Window to have the
HANDLE for the Window it wishes to communicate with. Any Child window can or for
that matter any Window has access to various API‟s to determine handles. In the case of a
Child wanting access to its parent .

         hwndParent = GetParent(hwd);

hwd is the handle returned by the CreateWindow() call as the child was generated.


Created by Tony G                                                    Page 78
Windows Systems Prog                                                 Background Info


Once hwndParent is recovered by this process, ( the parent will in most cases be the main
- parent - application window created in WinMain()), the child can then send a message to
the parent

         SendMessage (hwndParent,message,wParam,lParam);

The message sent can mimic a standard message on the system or can be derived as
application specific.

It is also possible to RegisterMessage(message) this API will ensure that any application
specific message will be made available to other applications on the system. An important
aspect of application specific messages in that they are unique, in order to do this the
message must be tagged to be beyond the range of standard messages and since in 3.1.x,
„95,‟NT the range is variable we must make use of the inherent constant
USER_MESSAGE. It is often the case that wParam will contain some form if ID for the
child possibly the unique identifier required in a Child Window create call, or unique
special data is used by allocation of extra information again in the Create structure. The
lParam will generally be used to transfer information between the Child and the Parent.
Although the wParam and lParam could be used for other purposes but it would make
user specific messages unique and outside the normal message structure for Windows.

This message mechanism should be familiar to you, it mimics the system messages such
as WM_CREATE, WM_PAINT etc., used in a Windows system.




Created by Tony G                                                  Page 79
Windows Systems Prog                                                    Background Info



                                        Controls

With this definition of a child it should now be obvious that all Controls in either dialog
boxes or in Windows are in fact no more than Child Windows.

The mechanism of using Controls is generally termed input information manipulation, it
allows applications to offer a USER interface to the system that is cleaner and more
effective.

The fact that an application we can use STANDARD controls in both Dialog Boxes s you
well as in Parent Windows etc., offers a high degree of consistency both from an
implementation and user viewpoint.

Although an application can create controls to customize the application to meet a
particular requirement. The flexibility offered by using the STANDARD Windows
Controls offers advantages and speed of development.

The standard Controls are simply predefined and registered Windows Classes, each has a
specific message handling procedure and in many cases it is possible to use this without
the need for extensive modification.

Using Standard Controls offers

            Buttons
            Check Boxes
            Radio Buttons
            Edit Boxes
            List Boxes
            Combo Boxes
            Static Text Strings
            Scroll Bars

Within 3.1.x the application can make use of 3DCNTRL.DLL to offer more advanced
graphical effects, however, this DLL has been obsolete in „95 and „NT were the standard
controls are all updated top offer 3D formats. In addition in „95 and then in „NT ver 4.0
following the changes from the use of the program manager to the shell format new
controls have been added such Spin, Tabbed, Progress Bars etc., each of these offers
new variations of format for user input but the basic philosophy on Child Window
Controls remains the same.




Created by Tony G                                                     Page 80
Windows Systems Prog                                                    Background Info


                                    A Simple Control.

To add a button on the left-hand side of an applications Window, requires only that a
single call to CreateWindow() of type “button” then possibly followed by a
MoveWindow() to locate it at the required position.

Once the button is created it can in most cases be considered as an autonomous item, its
own message procedure will take care of the operation, the application need not be
concerned by the mouse logic or the painting of the button and animation that users have
become accustomed to.

The application simply has to monitor for WM_COMMAND messages in its WinProc()
since this is the mechanism by which the Parent can determine that a message has come
from a Child.

DOES THE ARCHITECTURE MAKE IT REALLY THIS SIMPLE..?


1. In any application the majority of controls are grouped into Dialog Boxes. However
   there are times that a Control is needed in the Client Area - the rectangle within the
   main applications Window.

2. All intercommunications will be via messages of the form WM_COMMAND.

3. In order to place a Control in an application Window, first the application window
   class must registered and the instance of the class i.e. the window created. Once
   created the parent handle will be known or it can be recovered as discussed previously.

4. To us a predefined control there is no need to register the class simply create a
   Window from one of the following classes

                       button
                       static
                       scrollbar
                       edit
                       listbox
                       combobox

or one of the newer classes in „95 and „NT.

5. The control can then be created by using CreateWindow() and positioned using
   MoveWindow()

Once the application has got this far use of the Child is based on 99% use the predefined
message procedure. In fact in 99.9% of cases this may be all that is necessary.
Created by Tony G                                                    Page 81
Windows Systems Prog                                                    Background Info



Use of this predefined procedure eliminates the “reinvent the wheel” design and again
adds to the consistency of any windows based application.

We must be aware that use of Child Controls directly on the main - parent - application
Window does require a far greater involvement with the actual predefined class than is
necessary when dealing with such a Control in a Dialog Box. This is because the Dialog
manager, the piece of software within Windows that Creates and Controls Dialog Box
resources puts a layer of monitoring and insulation between the application and the lower
lever of Child Control manipulation.

SO WHY USE CONTROLS IN APPLICATION WINDOWS ?

In certain cases the use of a dialog box may detract from the overall FEEL of the
application. This is often the case in SYSTEM TOOLS. So information control from the
user is required is via a Child Window Control.

                       What this thing called FOCUS ?

This need to understand the lower level manipulation of Child Windows is best illustrated
by the „FOCUS‟ of controls.

In any Dialog box the focus is shifted by the TAB key and during the CreateDialog() it is
possible to define the initial focus and the subsequent order of focus flow.

However, any Control directly laid onto the application window can be given the focus,
but once the input focus has been shifted from the application window to the control,
effectively the application has lost control of its Input!

The subject of focus is not simple.

Lets first look at a simple application in which we create various styles of the Button in a
client area. This is not intended as a clever application rather it demonstrates the creation
of Controls directly onto an application Window.




Created by Tony G                                                     Page 82
Windows Systems Prog                                                  Background Info


/*----------------------------------------
          BTNLOOK.C -- Button Look Program
  ----------------------------------------*/
// Can‟t get rid of 10 Warnings on Array Initialisation...
#include <windows.h>
struct fred
    {
    long style ;
    char *text ;
    }
  button [ ] =
           {
          { BS_PUSHBUTTON,                        "PUSHBUTTON" },
           { BS_DEFPUSHBUTTON, "DEFPUSHBUTTON" },
           { BS_CHECKBOX,                 "CHECKBOX" },
           { BS_AUTOCHECKBOX, "AUTOCHECKBOX"},
           { BS_RADIOBUTTON, "RADIOBUTTON"},
           { BS_3STATE,               "3STATE"},
           { BS_AUTO3STATE,                "AUTO3STATE"},
           { BS_GROUPBOX,                 "GROUPBOX" },
           { BS_AUTORADIOBUTTON, "AUTORADIO"},
           { BS_OWNERDRAW,                   "OWNERDRAW"}
           };

#define NUM (sizeof button / sizeof button [0])
int CALLBACK WndProc (HWND, UINT, UINT, LONG) ;
#pragma argsused
int APIENTRY WinMain (HANDLE hInstance, HANDLE hPrevInstance,
              LPSTR lpszCmdLine, int nCmdShow)
    {
    static char szAppName[] = "BtnLook" ;
    HWND          hwnd ;
    MSG         msg ;
    WNDCLASS wndclass ;
    if (!hPrevInstance)
        {
        wndclass.style    = CS_HREDRAW | CS_VREDRAW ;
        wndclass.lpfnWndProc = WndProc ;
        wndclass.cbClsExtra = 0 ;
        wndclass.cbWndExtra = 0 ;
        wndclass.hInstance = hInstance ;
        wndclass.hIcon     = LoadIcon (NULL, IDI_APPLICATION) ;
        wndclass.hCursor    = LoadCursor (NULL, IDC_ARROW) ;
        wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
        wndclass.lpszMenuName = NULL ;

Created by Tony G                                                   Page 83
Windows Systems Prog                                         Background Info


       wndclass.lpszClassName = szAppName ;
       RegisterClass (&wndclass) ;
// Standard Winmain ...
       }
    hwnd = CreateWindow (szAppName, "Button Look",
                WS_OVERLAPPEDWINDOW,
                CW_USEDEFAULT, CW_USEDEFAULT,
                CW_USEDEFAULT, CW_USEDEFAULT,
                NULL, NULL, hInstance, NULL) ;
    ShowWindow (hwnd, nCmdShow) ;
    UpdateWindow (hwnd) ;
    while (GetMessage (&msg, NULL, 0, 0))
       {
       TranslateMessage (&msg) ;
       DispatchMessage (&msg) ;
       }
    return msg.wParam ;
    }
// Normal create of the main etc...

long FAR PASCAL _export WndProc (HWND hwnd, UINT message, UINT wParam,
                                   LONG lParam)
   {
   static char szTop [ ] = "message     wParam     lParam",
            szUnd [ ] = "_______      ______    ______",
            szFormat [ ] = "%-16s%6X%8X-%04X",
            szBuffer [50] ;
   static HWND hwndButton [NUM] ; // Array for Child handles.
   static RECT rect ;
   static int cxChar, cyChar ;
   HDC          hdc ;
   PAINTSTRUCT ps ;
   int       i;
   TEXTMETRIC tm ;

    switch (message)
       {
       case WM_CREATE:
          hdc = GetDC (hwnd) ;
          SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ;
          GetTextMetrics (hdc, &tm) ;
          cxChar = tm.tmAveCharWidth ;
          cyChar = tm.tmHeight + tm.tmExternalLeading ;
          ReleaseDC (hwnd, hdc) ;


Created by Tony G                                          Page 84
Windows Systems Prog                                                Background Info


           for (i = 0 ; i < NUM ; i++)
              hwndButton [i] = CreateWindow ("button", button[i].text,
                      WS_CHILD | WS_VISIBLE | button[i].style,
                      cxChar, cyChar * (1 + 2 * i),
                      20 * cxChar, 7 * cyChar / 4,
                      hwnd, i,
                      ((LPCREATESTRUCT) lParam) -> hInstance, NULL) ;
           return 0 ;
          // Create the different types of Buttons and set them up...
        case WM_SIZE:
           rect.left = 24 * cxChar ;
           rect.top = 2 * cyChar ;
           rect.right = LOWORD (lParam) ;
           rect.bottom = HIWORD (lParam) ;
           return 0 ;

       case WM_PAINT:
          InvalidateRect (hwnd, &rect, TRUE) ;
          hdc = BeginPaint (hwnd, &ps) ;
          SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ;
          SetBkMode (hdc, TRANSPARENT) ;
          TextOut (hdc, 24 * cxChar, cyChar, szTop, sizeof szTop - 1);
          TextOut (hdc, 24 * cxChar, cyChar, szUnd, sizeof szUnd - 1);
          EndPaint (hwnd, &ps) ;
          return 0 ;
       case WM_COMMAND:
       case WM_DRAWITEM:
          ScrollWindow (hwnd, 0, -cyChar, &rect, &rect) ;
          hdc = GetDC (hwnd) ;
          SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ;
          TextOut (hdc, 24 * cxChar, cyChar * (rect.bottom / cyChar - 1),
          szBuffer,
           wsprintf (szBuffer, szFormat, (LPSTR)
           (message == WM_COMMAND ? "WM_COMMAND" :
                "WM_DRAWITEM"),
                wParam, HIWORD (lParam), LOWORD (lParam))) ;
          ReleaseDC (hwnd, hdc) ;
          ValidateRect (hwnd, NULL) ;
          return 0 ;
       case WM_DESTROY:
          PostQuitMessage (0) ;
          return 0 ;
       }
    return DefWindowProc (hwnd, message, wParam, lParam) ;
    }

Created by Tony G                                                 Page 85
Windows Systems Prog                                                    Background Info


In this example each time you click on a button, the WM_COMMAND sent to the parent
is displayed in the Parent via the WM_PAINT option.

BTNLOOK creates the structure button; this is a definition of all the available Windows
Button styles except USE DEFINED. Notice that after creation no more procedure code
for these Child windows is needed since we have the WM_COMMAND messages from
the child windows to indicate something has happened sent to the parent.

The input focus is indicated as you click on any button by the dashed line.

So although this is a very simple piece of code it demonstrates many of the fundamental
aspects of the child window architecture in a highly visible form.

                                THE FOCUS PROBLEM

ButtonLook highlights the focus problem, once a pushbutton has the input focus,
indicated by the dotted line the parent has lost the input focus and is effectively no longer
in control of the input process.

If we run ButtonLook again we would find that the spacebar mimics the mouse click,
(this is standard in such control manipulations), but we have no means of stepping
between buttons. This is because we do not have the standard TAB function; the parent
Window can‟t get the keyboard-input messages because a Button has the input focus all
of the time!

Possible Solutions..

There is a message generated WM_KILLFOCUS

These occur as the focus shifts from Parent to Child. The message is sent to the message
handler of the Window that has just lost focus. In the message wParam indicates the
hwnd the window handle for the window that is about to get the focus.

Also there is the message WM_SETFOCUS

This is sent to the window that is just being granted input focus, the wParam this time is
set to the hwnd the window handle of the window that has just lost focus.

A NULL param in wParam for either message informs the application that Focus has
either shifted to no Window or that previously no input focus was assigned.

Using this we can prevent any child from gaining FOCUS by monitoring messages in the
Parent. For instance if the parent has an array of Child hwnd called hwndChild[ ], such an
array is created while the Child windows are initially created and shown.

Created by Tony G                                                     Page 86
Windows Systems Prog                                                  Background Info


Then either

case WM_KILLFOCUS
      for (i=0;i<numberchildren,i++)
      if (hwndChild [ i ] == wParam)
              {
                    SetFocus (hwnd) ; // force focus back to Parent
                    break;
              }
or

case WM_KILLFOCUS
      if (hwnd == GetParent(wParam)
             SetFocus (hwnd) ; // force focus back to Parent
             break;

However these have shortcomings....

Once again they will not allow the user to TAB between Controls since the focus is not
passed around the Controls its always with the Parent, this means we can‟t tell were the
focus should be !!!

The approach accepted as correct and one that with most problems associated with Input
Focus can be adopted, is to SUBCLASS the Child Window, allowing the application to
preprocess all messages to the Child before passing them on to the predefined windows
procedure.

The subject of SUBCLASSING is fundamental to Windows System Level programming,
“it is one that many people find difficult to grasp” Microsoft Windows Systems Group
1994. The basic concept is to intercept messages before they get to the message
procedure for a particular Windows Class. Having intercepted the message the application
can preprocess the message, this may involve killing it, modifying it or simply passing it
on unchanged to the correct message handler.



Windows message                                                               Windows Class message
                                    SUBCLASS MESSAGE
                                                                              handler
                                    HANDLER CAN
                                    1. KILL MESSAGE
Windows                             2. MODIFY MESSAGE
                                    3. PASS IT THROUGH




                                                Normal Message Handle



Created by Tony G                                                   Page 87
Windows Systems Prog                                                   Background Info



                    So how can such a technique be implemented ?

The address of the message procedure is held in the Class Structure.

        wndclass.style    = CS_HREDRAW | CS_VREDRAW ;
        wndclass.lpfnWndProc = WndProc ;
        wndclass.cbClsExtra = 0 ;
        wndclass.cbWndExtra = 0 ;
        wndclass.hInstance = hInstance ;
        wndclass.hIcon     = LoadIcon (NULL, IDI_APPLICATION) ;
        wndclass.hCursor    = LoadCursor (NULL, IDC_ARROW) ;
        wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
        wndclass.lpszMenuName = NULL ;
        wndclass.lpszClassName = szAppName ;

So the procedure must be buried somewhere inside Windows itself as are the
STANDARD procedures for all STANDARD CONTROLS.

If we could find the address of the procedure, remove it from the structure, replace it with
an application specific address i.e. a procedure that will intercept the messages to the
standard control and then process, modify or pass on the message to the original we will
have satisfied our need and produced what is called a SUB CLASSED window.

Since we are about to replace a procedure that is buried in the heart of the windows
system it is important that we ensure the Data Segment is correctly aligned. This is
accomplished by the use of the MakeProcInstance() API This is only relevant to
discussions on 3.1.x since we will see later in the course that the concept of a data
instance of an application changes when we are dealing with ‘95 and ‘NT.

Assume we have a procedure called NewButtonProc ( ) which has been written to
handle the keyboard etc., allowing the focus control etc., to be corrected, in our
application code.

lpfnNewButtonProc = MakeProcInstance( (FARPROC) NewButtonProc,hInstance);

Creates a FAR pointer to the function with the corrected data segment address etc.

To extract the old procedure address we will make use of GetWindowLong ( ) API.

lpfnOldButtonProc =
      (FARPROC) GetWindowLong (hwndbButton[1], GWL_WNDPROC);

We extract the pointer from the window class for the first button, this button had a handle
stored in hwndButton[1]. storing the old procedure address at lpfnOldButtonProc.
Created by Tony G                                                    Page 88
Windows Systems Prog                                                    Background Info




We can then replace the newly created pointer lpfnNewButtonProc in its place by using
the SetWindowLong ( ) API.

         SetWindowLong( hwndButton[1], GWL_WNPROC, lpfnNewButtonProc);

Once in place all message processing will now pass through this application specific filter
first.

Finally we have to ensure that if this new filter either modifies or ignores the message it is
passed on to the original message handler and this is accomplished by a call to

                    CallWindowsProc ( )

Typically this new procedure would therefore have the structure..

long FAR PASCAL _export NewButtonProc(HWND hwnd, UINT message, UINT ,
                                         wParam, Long lParam)
{
       switch (message)
       {
       case

        case etc
        }
return CallWindowProc ( lpfnOldButtonProc, hwnd,message,wParam,lParam);
// send message to original
}




Created by Tony G                                                     Page 89
Windows Systems Prog                                                    Background Info


MDI Multi Document Interface

The MDI is a specification of how to handle in an application the Interface between
multiple documents of either the same or different types within a single application.

The specification describes a window structure and user interface that allow the user to
work in this multiple document environment within a single application. Simply put as
Windows maintains multiple applications windows within a single screen, MDI maintains
multiple document windows in a single client area.

The Elements of MDI

The main application window of an MDI compliant program is conventional.
It has Title bar, menu, sizing border, system menu and min/max and in the case of „95 and
„NT a close icon. The client area however, is called a “workspace” and is not directly
used to display program output. Within the workspace you can have zero or more child
windows within each the application could define a particular document format.

These child windows look much like normal application windows. They will have title
bars, a system menu icon, borders, max and min icons etc., they may or may not have
scroll bars dependant on the document or type of data being displayed within them.
NOTE: None of these windows will have a menu, the menu items on the main
application window applies to these MDI child windows.

Obviously to be consistent with windows architecture only one of these document
windows is active at a time. The active window is indicated by the title bar being
highlighted. The active window will appear IN FRONT of any other document window.
The entire document child Windows are clipped to within the client area and can not be
moved outside the bounds of the client area.

SO WHAT IS UNIQUE TO MDI ?

At first glance the MDI specification seems straightforward, the application consists of a
parent with a number of child windows. This makes the main application window a
simple parent and the documents windows specific children. However, this is only half
the story, if you have used any MDI based application you will be aware that an MDI
application has other feature.

 Any MDI document Window can be minimized. Its ICON appears at the bottom of
  the Workspace. (Normally MDI applications will use different icons for different type
  of documents.)

 Any MDI document Window can be maximized. In this case the Title bar of the
  document window is removed (it would normally have shown the document file
  name), it is replaced by the application title bar being modified to incorporate the file
Created by Tony G                                                     Page 90
Windows Systems Prog                                                  Background Info


   name. The system level menu icon on the document becomes the first item in the
   application level menu bar. Another additional menu item is added to the application
   menu bar; an icon to restore the document window to its original or default size.

 The system keyboard accelerator to close a document window is the same as that to
  close a main window except that rather than Alt - F4 , Ctrl - F4 will close any
  document and Alt F4 will close the application. Cntrl - F6 is used to step between
  document windows.

 The movement around the menu bars using the cursor keys changes. Normally if you
  start at the application system menu then the cursor key will move you next to the first
  level of the application menu. In MDI this sequence is modified so that starting at the
  application systems menu, you will move to document system menu and then onto the
  first level of the applications menu.

 If the application has the ability to support many different document types, then the
  main application menu will be modified each time a different document window type
  becomes active. We will return for an overview of OLE and its implications on
  composite documents in a later lecture, this once again has an effect on menu layout in
  an MDI environment.

 The top-level menu in an MDI application has the additional item Window. By
  convention this should be the last item except for Help. The sub menu‟s in this will
  generally have options to arrange the view of the document windows. Normally
  document window can be cascaded from the upper left of the client area or they can be
  tilled so that each window is visible. This submenu by convention should also contain
  a list of the currently open document windows, selection of a particular window moves
  that document window to the foreground and selects it.

The support for the above features has been available fully since 3.1.x, obviously
some overhead within the application is required it isn’t anywhere near the amount
of code that would be needed to support these types of features directly!

Some new terminology is necessary at the systems level for an MDI application. The
main applications window is called the “Frame Window”. It is normally constructed as
with most application windows as WS_OVERLAPPEDWINDOW style.

An MDI will then construct a “Client Window” based on the predefined class
MDICLIENT. This is created from the standard class as a WS_CHILD. However the last
parameter in the CreateWindow() is a pointer to a structure of type
CLIENTCREATESTRUCT. This window covers the client area of the “Frame” and is
responsible for most of the support for MDI.

The document windows are by convention called “child windows”. You create these by
initialising a MDICREATESTRUCT and sending the Client Window a
Created by Tony G                                                   Page 91
 Windows Systems Prog                                                   Background Info


 WM_MDICREATE message. This message includes a pointer to the
 MDICREATESTRUCT.

 So the document window is a child of the client and the client is a child of the frame.




                             Frame Window




                                Client window




Child 1                            Child 2                                 Child 3




 Document Windows



 The frame window requires a WindowProc() and each type of document window that the
 application will support.

 The client Window is standard and hence needs no additional support for its functionality.

 In addition to the MDICLIENT class and the CLIENTCREATESTRUCT and
 MDICREATESTRUCT there are other new functions to support the MDI interface. In
 place of the DefWindowProc() normal for all unprocessed messages in an application.

 Created by Tony G                                                    Page 92
Windows Systems Prog                                                                Background Info


There are two special functions DefFrameProc() for all unprocessed Frame window
messages and DefMDIChildProc() for all unprocessed document window messages.

There are additional functions for handling Keyboard Accelerator tables but these will be
left for the example.

The simple example program illustrates MDI and identifies the special MDI messages,
which start with the prefix WM_MDI.....

These listings highlight the import aspects of MDI only.

/*--------------------------------------------------------
           MDIDEMO.C -- MDI simple Demo Tony Grimer 1998.
  --------------------------------------------------------*/
// This is a simple 16 bit MDI example
#include <windows.h>
#include <stdlib.h>
#include "mdidemo.h"
#define min(a,b) (((a) < (b)) ? (a) : (b))
#define max(a,b) (((a) > (b)) ? (a) : (b))
long FAR PASCAL _export FrameWndProc (HWND, UINT, UINT, LONG) ;
BOOL FAR PASCAL _export CloseEnumProc (HWND, LONG) ;
long FAR PASCAL _export HelloWndProc (HWND, UINT, UINT, LONG) ;
long FAR PASCAL _export RectWndProc (HWND, UINT, UINT, LONG) ;
                                 // structure for storing data unique to each Hello child window
typedef struct
             {
             short nColor ;
             COLORREF clrText ;
             }
             HELLODATA ;
typedef HELLODATA NEAR *NPHELLODATA ;
                                 // structure for storing data unique to each Rect child window
typedef struct
             {
             short cxClient ;
             short cyClient ;
             }
             RECTDATA ;
typedef RECTDATA NEAR *NPRECTDATA ;
                                 // global variables
char szFrameClass [] = "MdiFrame" ;
char szHelloClass [] = "MdiHelloChild" ;
char szRectClass [] = "MdiRectChild" ;
HANDLE hInst ;
HMENU hMenuInit, hMenuHello, hMenuRect ;
HMENU hMenuInitWindow, hMenuHelloWindow, hMenuRectWindow ;


Notice the data structures for the document types.




Created by Tony G                                                                 Page 93
Windows Systems Prog                                                        Background Info

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
                                                     LPSTR lpszCmdLine, int nCmdShow)
        {
        HANDLE hAccel ;
        HWND hwndFrame, hwndClient ;
        MSG       msg ;
        WNDCLASS wndclass ;
        hInst = hInstance ;
        if (!hPrevInstance)
                         { // Register the frame window class
                         wndclass.style        = CS_HREDRAW | CS_VREDRAW ;
                         wndclass.lpfnWndProc = FrameWndProc ;
                         wndclass.cbClsExtra = 0 ;
                         wndclass.cbWndExtra = 0 ;
                         wndclass.hInstance = hInstance ;
                         wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
                         wndclass.hCursor        = LoadCursor (NULL, IDC_ARROW) ;
                         wndclass.hbrBackground = COLOR_APPWORKSPACE + 1 ;
                         wndclass.lpszMenuName = NULL ;
                         wndclass.lpszClassName = szFrameClass ;
                         RegisterClass (&wndclass) ;
                          // Register the Hello child window class
                         wndclass.style        = CS_HREDRAW | CS_VREDRAW ;
                         wndclass.lpfnWndProc = HelloWndProc ;
                         wndclass.cbClsExtra = 0 ;
                         wndclass.cbWndExtra = sizeof (LOCALHANDLE) ;
                         wndclass.hInstance = hInstance ;
                         wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
                         wndclass.hCursor        = LoadCursor (NULL, IDC_ARROW) ;
                         wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
                         wndclass.lpszMenuName = NULL ;
                         wndclass.lpszClassName = szHelloClass ;
                         RegisterClass (&wndclass) ;
                          // Register the Rect child window class
                         wndclass.style        = CS_HREDRAW | CS_VREDRAW ;
                         wndclass.lpfnWndProc = RectWndProc ;
                         wndclass.cbClsExtra = 0 ;
                         wndclass.cbWndExtra = sizeof (LOCALHANDLE) ;
                         wndclass.hInstance = hInstance ;
                         wndclass.hIcon         = NULL ;
                         wndclass.hCursor        = LoadCursor (NULL, IDC_ARROW) ;
                         wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
                         wndclass.lpszMenuName = NULL ;
                         wndclass.lpszClassName = szRectClass ;
                         RegisterClass (&wndclass) ;
                         }
                        // Obtain handles to three possible menus & submenus
        hMenuInit = LoadMenu (hInst, "MdiMenuInit") ;
        hMenuHello = LoadMenu (hInst, "MdiMenuHello") ;
        hMenuRect = LoadMenu (hInst, "MdiMenuRect") ;
        hMenuInitWindow = GetSubMenu (hMenuInit, INIT_MENU_POS) ;
        hMenuHelloWindow = GetSubMenu (hMenuHello, HELLO_MENU_POS) ;
        hMenuRectWindow = GetSubMenu (hMenuRect, RECT_MENU_POS) ;

         Register the Classes and set up for Menu Alteration in the event of a maximise.


Created by Tony G                                                         Page 94
Windows Systems Prog                                                          Background Info

// Create the frame window
  hwndFrame = CreateWindow (szFrameClass, "MDI Demonstration",
 WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
 CW_USEDEFAULT, CW_USEDEFAULT,
 CW_USEDEFAULT, CW_USEDEFAULT,
  NULL, hMenuInit, hInstance, NULL) ;
  hwndClient = GetWindow (hwndFrame, GW_CHILD) ;
  ShowWindow (hwndFrame, nCmdShow) ;
  UpdateWindow (hwndFrame) ;
.... /// Then would follow the normal message loop for the frame window..
....
....
     /// plus the exit conditions
}

Normal start up of the application the message loop and exit is as per normal.

long FAR PASCAL _export FrameWndProc (HWND hwnd, UINT message, UINT wParam,
                                                                          LONG lParam)
        {
        static HWND      hwndClient ;
        CLIENTCREATESTRUCT clientcreate ;
        FARPROC          lpfnEnum ;
        HWND            hwndChild ;
        MDICREATESTRUCT mdicreate ;
        switch (message)
                      {
                      case WM_CREATE:              // Create the client window
                                      clientcreate.hWindowMenu = hMenuInitWindow ;
                                      clientcreate.idFirstChild = IDM_FIRSTCHILD ;
                                    hwndClient = CreateWindow ("MDICLIENT", NULL,
                                      WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE,
                                      0, 0, 0, 0, hwnd, 1, hInst, (LPSTR) &clientcreate) ;
                                      return 0 ;
                      case WM_COMMAND:
                                      switch (wParam)
                                                 {
                               case IDM_NEWHELLO:             // Create a Hello child window
                                                mdicreate.szClass = szHelloClass ;
                                                mdicreate.szTitle = "Hello" ;
                                                mdicreate.hOwner = hInst ;
                                                mdicreate.x       = CW_USEDEFAULT ;
                                                mdicreate.y       = CW_USEDEFAULT ;
                                                mdicreate.cx      = CW_USEDEFAULT ;
                                                mdicreate.cy      = CW_USEDEFAULT ;
                                                mdicreate.style = 0 ;
                                                mdicreate.lParam = NULL ;
        hwndChild = (HWND) SendMessage (hwndClient, WM_MDICREATE, 0, (long)
                      LPMDICREATESTRUCT) &mdicreate) ;
                                       return 0 ;


Standard creation the creation of the other type of Doc Window is the same,

                                  case IDM_CLOSE:     // Close the active window
                    hwndChild = LOWORD (SendMessage (hwndClient, WM_MDIGETACTIVE,

Created by Tony G                                                           Page 95
Windows Systems Prog                                                           Background Info

                                                        0, 0L)) ;
                    if (SendMessage (hwndChild, WM_QUERYENDSESSION, 0, 0L))
                            SendMessage (hwndClient, WM_MDIDESTROY, hwndChild, 0L) ;
                            return 0 ;


Closure of a document window.


                                    case IDM_EXIT:            // Exit the program
                                   SendMessage (hwnd, WM_CLOSE, 0, 0L) ;
                                   return 0 ;
 // Messages for arranging windows
                                    case IDM_TILE:
                                   SendMessage (hwndClient, WM_MDITILE, 0, 0L) ;
                                   return 0 ;
                                    case IDM_CASCADE:
                                   SendMessage (hwndClient, WM_MDICASCADE, 0, 0L) ;
                                   return 0 ;
                                    case IDM_ARRANGE:
                                   SendMessage (hwndClient, WM_MDIICONARRANGE, 0, 0L) ;
                                   return 0 ;
                                    case IDM_CLOSEALL:             // Attempt to close all children
                                   lpfnEnum = MakeProcInstance ((FARPROC) CloseEnumProc,
                                                                                hInst) ;
                                   EnumChildWindows (hwndClient, lpfnEnum, 0L) ;
                                                                         FreeProcInstance (lpfnEnum) ;
                                   return 0 ;
                                    default:         // Pass to active child
                                   hwndChild = LOWORD (SendMessage (hwndClient,
                                                     WM_MDIGETACTIVE, 0, 0L)) ;
                         if (IsWindow (hwndChild))
                               SendMessage (hwndChild, WM_COMMAND, wParam, lParam) ;
                                  break ;       // and then to DefFrameProc
                                    }
                         break ;
                          case WM_QUERYENDSESSION:
                          case WM_CLOSE:                      // Attempt to close all children
                                           SendMessage (hwnd, WM_COMMAND,
                                                                        IDM_CLOSEALL, 0L) ;
                                           if (NULL != GetWindow (hwndClient, GW_CHILD))
                           return 0 ;
                         break ; // ie, call DefFrameProc ;
                          case WM_DESTROY :
                                           PostQuitMessage (0) ;
                                           return 0 ;
                          }
                // Pass unprocessed messages to DefFrameProc (not DefWindowProc)
         return DefFrameProc (hwnd, hwndClient, message, wParam, lParam) ;
         }

Basic Handling of frame messages


BOOL FAR PASCAL _export CloseEnumProc (HWND hwnd, LONG lParam)
       {

Created by Tony G                                                            Page 96
Windows Systems Prog                                                 Background Info

           if (GetWindow (hwnd, GW_OWNER))     // check for icon title
                          return 1 ;
           SendMessage (GetParent (hwnd), WM_MDIRESTORE, hwnd, 0L) ;
           if (!SendMessage (hwnd, WM_QUERYENDSESSION, 0, 0L))
                          return 1 ;
           SendMessage (GetParent (hwnd), WM_MDIDESTROY, hwnd, 0L) ;
                          return 1 ;
           }


Enumerate through child windows and close when found.

long FAR PASCAL _export HelloWndProc (HWND hwnd, UINT message, UINT wParam,
                                                                     LONG lParam)
        {
        static COLORREF clrTextArray [] = { RGB (0, 0, 0), RGB (255, 0, 0),
        RGB (0, 255, 0), RGB ( 0, 0, 255),
        RGB (255, 255, 255) } ;
        static HWND hwndClient, hwndFrame ;
        HDC         hdc ;
        HMENU          hMenu ;
        LOCALHANDLE hHelloData ;
        NPHELLODATA npHelloData ;
        PAINTSTRUCT ps ;
        RECT         rect ;

           switch (message)
                             {
                             case WM_CREATE:
                   // Allocate memory for window private data
                  hHelloData = LocalAlloc (LMEM_MOVEABLE | LMEM_ZEROINIT, sizeof
                                             (HELLODATA)) ;
                  npHelloData = (NPHELLODATA) LocalLock (hHelloData) ;
                  npHelloData->nColor = IDM_BLACK ;
                  npHelloData->clrText = RGB (0, 0, 0) ;
                  LocalUnlock (hHelloData) ;
                  SetWindowWord (hwnd, 0, hHelloData) ;
                   // Save some window handles
                  hwndClient = GetParent (hwnd) ;
                  hwndFrame = GetParent (hwndClient) ;
                  return 0 ;
                   case WM_COMMAND:
                            switch (wParam)
                              {
                              case IDM_BLACK:
                              case IDM_RED:
                              case IDM_GREEN:
                              case IDM_BLUE:
                              case IDM_WHITE:
                              // Change the text color
                             hHelloData = GetWindowWord (hwnd, 0) ;
                             npHelloData = (NPHELLODATA) LocalLock (hHelloData) ;
                             hMenu = GetMenu (hwndFrame) ;




Created by Tony G                                                  Page 97
Windows Systems Prog                                                           Background Info

                     CheckMenuItem (hMenu, npHelloData->nColor,
                                                  MF_UNCHECKED) ;
                     npHelloData->nColor = wParam ;
                     CheckMenuItem (hMenu, npHelloData->nColor,
                                                  MF_CHECKED) ;
                     npHelloData->clrText = clrTextArray [wParam - IDM_BLACK] ;
                     LocalUnlock (hHelloData) ;
                     InvalidateRect (hwnd, NULL, FALSE) ;
                                                           }
                    return 0 ;
                     case WM_PAINT:
                              // Paint the window
                              hdc = BeginPaint (hwnd, &ps) ;
                             hHelloData = GetWindowWord (hwnd, 0) ;
                             npHelloData = (NPHELLODATA) LocalLock (hHelloData) ;
                             SetTextColor (hdc, npHelloData->clrText) ;
                             LocalUnlock (hHelloData) ;
                             GetClientRect (hwnd, &rect) ;
                             DrawText (hdc, "Hello, World!", -1, &rect,
                              DT_SINGLELINE | DT_CENTER | DT_VCENTER) ;
                             EndPaint (hwnd, &ps) ;
                             return 0 ;
                     case WM_MDIACTIVATE:
                              // Set the Hello menu if gaining focus
                                       if (wParam == TRUE)
                                         SendMessage (hwndClient, WM_MDISETMENU, 0,
                                                MAKELONG (hMenuHello, hMenuHelloWindow)) ;
                              // check or uncheck menu item
                             hHelloData = GetWindowWord (hwnd, 0) ;
                             npHelloData = (NPHELLODATA) LocalLock (hHelloData) ;
                             CheckMenuItem (hMenuHello, npHelloData->nColor,
                             wParam ? MF_CHECKED : MF_UNCHECKED) ;
                             LocalUnlock (hHelloData) ;
                              // Set the Init menu if losing focus
                                       if (wParam == FALSE)
                                         SendMessage (hwndClient, WM_MDISETMENU, 0,
                                                MAKELONG (hMenuInit, hMenuInitWindow)) ;
                             DrawMenuBar (hwndFrame) ;
                             return 0 ;
                              case WM_QUERYENDSESSION:
                              case WM_CLOSE:
                                       if (IDOK != MessageBox (hwnd, "OK to close window?", "Hello",
                                                         MB_ICONQUESTION | MB_OKCANCEL))
                                         return 0 ;
                             break ; // ie, call DefMDIChildProc
                              case WM_DESTROY:
                                                hHelloData = GetWindowWord (hwnd, 0) ;
                                                LocalFree (hHelloData) ;
                                                return 0 ;
                              }
                                                // Pass unprocessed message to DefMDIChildProc

           return DefMDIChildProc (hwnd, message, wParam, lParam) ;
           }

Thats the procedure for one type of Document window the other is similar...

Created by Tony G                                                            Page 98
Windows Systems Prog                                                   Background Info


What can we see from these listings?

The system overhead for MDI is not high; the application support is minimal.

One of the important aspects is a request to close the application. If any Child windows
exist they must be identified and any clean up actioned.

This is accomplished by the enumerate child function and the call back which in this case
does very little but in a major application would concern itself with file saves etc.

                                  SYSTEM METRICS

Another important aspect of user interaction is the presentation of information. Here I am
thinking not of the HCI but the FONTS etc., and the way the Client area is used.

In DOS you had little control, you could force FONT changes but not in a very simple
manner. In Windows the system aspects of display need to be understood by both System
and application programmers.

Within Window you can extract a great deal of information, which because of the nature
of the environment itself, relates specifically to the desktop and machine currently being
used. So gone are the days of having various configuration options that must be right to
get an application to run on a particular desktop.

/*-----------------------------------------------------
          SYSMETS.C -- System Metrics Display Program
 -----------------------------------------------------*/
#include <windows.h>
#include "sysmets.h"
#define min(a,b) (((a) < (b)) ? (a) : (b))
#define max(a,b) (((a) > (b)) ? (a) : (b))
long FAR PASCAL _export WndProc (HWND, UINT, UINT, LONG) ;
#pragma argsused
int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
               LPSTR lpszCmdLine, int nCmdShow)
    {
    static char szAppName[] = "SysMets" ;
    HWND            hwnd ;
    MSG          msg ;
    WNDCLASS wndclass ;
    if (!hPrevInstance)
        {
        wndclass.style          = CS_HREDRAW | CS_VREDRAW ;
        wndclass.lpfnWndProc = WndProc ;
        wndclass.cbClsExtra = 0 ;
Created by Tony G                                                    Page 99
Windows Systems Prog                                         Background Info


        wndclass.cbWndExtra = 0 ;
        wndclass.hInstance = hInstance ;
        wndclass.hIcon     = LoadIcon (NULL, IDI_APPLICATION) ;
        wndclass.hCursor    = LoadCursor (NULL, IDC_ARROW) ;
        wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
        wndclass.lpszMenuName = NULL ;
        wndclass.lpszClassName = szAppName ;
        RegisterClass (&wndclass) ;
        }

   hwnd = CreateWindow (szAppName, "System Metrics",
                  WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL,
                  CW_USEDEFAULT, CW_USEDEFAULT,
                  CW_USEDEFAULT, CW_USEDEFAULT,
                  NULL, NULL, hInstance, NULL) ;
   ShowWindow (hwnd, nCmdShow) ;
   UpdateWindow (hwnd) ;
   while (GetMessage (&msg, NULL, 0, 0))
       {
       TranslateMessage (&msg) ;
       DispatchMessage (&msg) ;
       }
   return msg.wParam ;
 } // standard WinMain
long FAR PASCAL _export WndProc (HWND hwnd, UINT message, UINT wParam,
                                      LONG lParam)
   {
   static short cxChar, cxCaps, cyChar, cxClient, cyClient, nMaxWidth,
             nVscrollPos, nVscrollMax, nHscrollPos, nHscrollMax ;
   char       szBuffer[10] ;
   HDC           hdc ;
   short       i, x, y, nPaintBeg, nPaintEnd, nVscrollInc, nHscrollInc ;
   PAINTSTRUCT ps ;
   TEXTMETRIC tm ;                // Metric array
   switch (message)
       {
       case WM_CREATE:
          hdc = GetDC (hwnd) ;
          GetTextMetrics (hdc, &tm) ;
          cxChar = tm.tmAveCharWidth ;
          cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2 ;
          cyChar = tm.tmHeight + tm.tmExternalLeading ;
          ReleaseDC (hwnd, hdc) ;
          nMaxWidth = 40 * cxChar + 22 * cxCaps ;
          return 0 ;

Created by Tony G                                          Page 100
Windows Systems Prog                                              Background Info


        case WM_SIZE:
           cxClient = LOWORD (lParam) ;
           cyClient = HIWORD (lParam) ;
           nVscrollMax = max (0, NUMLINES + 2 - cyClient / cyChar) ;
           nVscrollPos = min (nVscrollPos, nVscrollMax) ;
           SetScrollRange (hwnd, SB_VERT, 0, nVscrollMax, FALSE) ;
           SetScrollPos (hwnd, SB_VERT, nVscrollPos, TRUE) ;
           nHscrollMax = max (0, 2 + (nMaxWidth - cxClient) / cxChar) ;
           nHscrollPos = min (nHscrollPos, nHscrollMax) ;
           SetScrollRange (hwnd, SB_HORZ, 0, nHscrollMax, FALSE) ;
           SetScrollPos (hwnd, SB_HORZ, nHscrollPos, TRUE) ;
           return 0 ;
        case WM_VSCROLL:
           switch (wParam)
               {
               case SB_TOP:
                  nVscrollInc = -nVscrollPos ;
                  break ;
               case SB_BOTTOM:
                  nVscrollInc = nVscrollMax - nVscrollPos ;
                  break ;
               case SB_LINEUP:
                  nVscrollInc = -1 ;
                  break ;
               case SB_LINEDOWN:
                  nVscrollInc = 1 ;
                  break ;
               case SB_PAGEUP:
                  nVscrollInc = min (-1, -cyClient / cyChar) ;
                  break ;
               case SB_PAGEDOWN:
                  nVscrollInc = max (1, cyClient / cyChar) ;
                  break ;
               case SB_THUMBTRACK:
                  nVscrollInc = LOWORD (lParam) - nVscrollPos ;
                  break ;
               default:
                  nVscrollInc = 0 ;
               }
           nVscrollInc = max (-nVscrollPos,
          min (nVscrollInc, nVscrollMax - nVscrollPos)) ;
           if (nVscrollInc != 0)
               {
               nVscrollPos += nVscrollInc ;
               ScrollWindow (hwnd, 0, -cyChar * nVscrollInc, NULL, NULL) ;

Created by Tony G                                               Page 101
Windows Systems Prog                                              Background Info


               SetScrollPos (hwnd, SB_VERT, nVscrollPos, TRUE) ;
               UpdateWindow (hwnd) ;
               }
           return 0 ;
        case WM_HSCROLL:
           switch (wParam)
               {
               case SB_LINEUP:
                  nHscrollInc = -1 ;
                  break ;
               case SB_LINEDOWN:
                  nHscrollInc = 1 ;
                  break ;
               case SB_PAGEUP:
                  nHscrollInc = -8 ;
                  break ;
               case SB_PAGEDOWN:
                  nHscrollInc = 8 ;
                  break ;
               case SB_THUMBPOSITION:
                  nHscrollInc = LOWORD (lParam) - nHscrollPos ;
                  break ;
               default:
                  nHscrollInc = 0 ;
               }
           nHscrollInc = max (-nHscrollPos,
           min (nHscrollInc, nHscrollMax - nHscrollPos)) ;
           if (nHscrollInc != 0)
               {
               nHscrollPos += nHscrollInc ;
               ScrollWindow (hwnd, -cxChar * nHscrollInc, 0, NULL, NULL) ;
               SetScrollPos (hwnd, SB_HORZ, nHscrollPos, TRUE) ;
               }
           return 0 ;
        case WM_KEYDOWN:
           switch (wParam)
               {
               case VK_HOME:
                  SendMessage (hwnd, WM_VSCROLL, SB_TOP, 0L) ;
                  break ;
               case VK_END:
                  SendMessage (hwnd, WM_VSCROLL, SB_BOTTOM, 0L) ;
                  break ;
               case VK_PRIOR:
                  SendMessage (hwnd, WM_VSCROLL, SB_PAGEUP, 0L) ;

Created by Tony G                                               Page 102
Windows Systems Prog                                                 Background Info


                 break ;
              case VK_NEXT:
                 SendMessage (hwnd, WM_VSCROLL, SB_PAGEDOWN, 0L) ;
                 break ;
              case VK_UP:
                 SendMessage (hwnd, WM_VSCROLL, SB_LINEUP, 0L) ;
                 break ;
              case VK_DOWN:
                 SendMessage (hwnd, WM_VSCROLL, SB_LINEDOWN, 0L) ;
                 break ;
              case VK_LEFT:
                 SendMessage (hwnd, WM_HSCROLL, SB_PAGEUP, 0L) ;
                 break ;
              case VK_RIGHT:
                 SendMessage (hwnd, WM_HSCROLL, SB_PAGEDOWN, 0L) ;
                 break ;
              }
           return 0 ;
        case WM_PAINT:
           hdc = BeginPaint (hwnd, &ps) ;
           nPaintBeg = max (0, nVscrollPos + ps.rcPaint.top / cyChar - 1) ;
           nPaintEnd = min (NUMLINES,
           nVscrollPos + ps.rcPaint.bottom / cyChar) ;

           for (i = nPaintBeg ; i < nPaintEnd ; i++)
              {
              x = cxChar * (1 - nHscrollPos) ;
              y = cyChar * (1 - nVscrollPos + i) ;
              TextOut (hdc, x, y,
                              sysmetrics[i].szLabel,
                              lstrlen (sysmetrics[i].szLabel)) ;
              TextOut (hdc, x + 22 * cxCaps, y,
                     sysmetrics[i].szDesc,
                     lstrlen (sysmetrics[i].szDesc)) ;
              SetTextAlign (hdc, TA_RIGHT | TA_TOP) ;
              TextOut (hdc, x + 22 * cxCaps + 40 * cxChar, y,
                     szBuffer,
                     wsprintf (szBuffer, "%5d",
                     GetSystemMetrics (sysmetrics[i].nIndex))) ;
               SetTextAlign (hdc, TA_LEFT | TA_TOP) ;
              }
           EndPaint (hwnd, &ps) ;
           return 0 ;
        case WM_DESTROY:
           PostQuitMessage (0) ;

Created by Tony G                                                  Page 103
Windows Systems Prog                                           Background Info


            return 0 ;
       }
    return DefWindowProc (hwnd, message, wParam, lParam) ;
    }




Created by Tony G                                            Page 104
Windows Systems Prog                                                     Background Info


        INPUT / OUTPUT PROGRAMMING IN WINDOWS

Introduction

The whole subject of controlling I/O devices in a Windows environment needs very
careful planning many of the common development environments, VB (Visual Basic)
ignore the subject totally and it is necessary to implement I/O facility using other tools
and techniques.

Why is this?

The complex nature of the Windows environment itself requires that any I/O device
programming requires an understanding of the protected mode and privilege level control
aspects of the 80x86 family of CPU's. The protected mode option was first implemented
fully in the 80386 and subsequent designs have simply enhanced the facility. Within the
protected mode all I/O functions are Virtualised, in going to such a scheme we find that
no application actually owns any of the I/O ports or any part of the Interrupt structure. As
any application attempts to read or write an I/O ports, or attach itself to an Interrupt vector
the actual operation is modified by an arbitration scheme using the protection control
afforded by the CPU's Task Control Blocks allowing the Virtual Machine Manager
(VMM) within Windows to either grant or not access to the operation.

This causes even on simple polled I/O requests a slight overhead, however, when we
investigate the implementation of Interrupt driven I/O, with the requirement to attach to a
specific vector etc., we find that irrespective of the technique employed in implementing
the attachment to a physical interrupt, the system level overhead of the arbitration and
routing of the actual Interrupt to the application, can be excessive.

Brief review of the overall architecture of Interrupts on the 80x86 family.

Whatever flavour of Windows considered 3.1.x,'95,NT the manner of handling interrupts
is based on the design of the Intel 8Ox86 family of processors.

All Intel 8Ox86 CPU recognises several kinds of interrupts:

       Software interrupts triggered by an explicit INT instruction in the executing code.
       In the earlier CPU designs these were simply considered and acted upon in a similar
       manner to hardware interrupts, from the 80386 they still perform the same basic
       function but are considered to be part of the group called exceptions

       MaskableHardware interrupts trigger the CPU via the INTR pin of the CPU, but
       only if the interrupt enabled flag bit of the EFLAGS register is set to 1. When the
       interrupt enabled flag bit is 0, any interrupt coming in on the INTR pin is ignored.

Created by Tony G                                                     Page 105
Windows Systems Prog                                                    Background Info


       The clear interrupt flag (CLI) and set interrupt flag (STI) instructions are used to
       clear and set the interrupt flag bit, respectively.

       NonMaskable interrupts (NMI) trigger the CPU via the NMI pin. Intel has
       reserved entry 2 in the interrupt table for handling NMIs.

       Exceptions consisting of faults, traps, and aborts, are detected by the processor as a
       result of executing an instruction. Software interrupts from the 80386 and above
       CPU's are considered as exceptions.

In protected mode, the CPU uses the interrupt descriptor table (IDT), which resides at the
memory pointed to by the CPU's interrupt descriptor table register; in real mode, it uses
the interrupt vector table (IVT), which resides at segmented address 0:0.

A further complication to the scheme is the addition of the 8259A programmable
interrupt controller (PIC) this is used to multiplex and prioritise interrupts through to
the CPU's single INTR pin. Originally the PC used a single 8259A but now there are two
organised into a Master / Slave configuration giving a total of 15 external interrupt
stimulus options. The inclusion of this PIC device complicated the handling of hardware
interrupts even in the REAL mode, an interrupt service routine (ISR) cannot simply be
installed, wait for an interrupt, and then call IRET. It must after installing an appropriate
handler start the interrupt flow by unmasking the appropriate bit in the PIC's interrupt
mask register (IMR), and at interrupt time, issue an "EOI to the PIC," which is
programmer-speak for 'notify the PIC' with an end-of-interrupt identifier.

Since the beginning DOS has provided functions to install and remove interrupt handlers:
               Interrupt 21 h Function 25h-Set Interrupt Vector
               Interrupt 21 h Function 35h-Get Interrupt Vector

It was in Real mode a simple task to install an ISR, you first called Get Interrupt Vector
to retrieve the address of the previously installed handler, followed by then installing your
handler with Set Interrupt Vector.

Now consider what happens when a Windows system is interposed into the equation. .
Regardless of what Windows environment is running, the Windows kernel gets its paws
dirty in the interrupt handling game.

All the underlying previously called DOS vectors hooked via the Interrupt 21h are
intercepted so that older application or developments from older programming tools can
be fully screened, (intercepted). From the discussion above the two very important
functions Set Interrupt Vector and Get Interrupt Vector are among the most important
to be screened and modified, in some cases the kernel will ignored completely any
attempt by an application to install its own ISR into a Windows System.



Created by Tony G                                                     Page 106
Windows Systems Prog                                                     Background Info



The basic mechanism for the screening is:

Application attempts to hook (attach) a particular Interrupt vector, the call is intercepted
and a decision is made to allow, disallow, or virtualize the hook.


Table 1 Interrupts an Application can not Hook


Vector                                       Basic Purpose
2lh                                          MS-DOS function dispatcher.
24h                                          MS-DOS critical-error handler.

Table 2. Interrupts Instanced by Kernel on an Application (Per -Task) Basis.

 Vector                                        Basic Purpose
 00h                                           Divide-by-zero
 02h                                           NMI (nonmaskable interrupt)
 04h                                           Overflow
 06h                                           Reserved
 07h                                           Reserved
 3Eh                                           WIN87EM in Windows
 75h                                           IRQ13 numeric coprocessor

If the interrupt trying to be hooked is not listed in Table 1, the kernel will install the new
ISR as the first ISR in the protected mode handler chain. Keep in mind that the exalted
status as 'number 1' in the chain may only be temporary as soon as some other dynamic
link library (DLL) hooks the same interrupt, the first ISR is bumped down to 'number 2'
in the chain. This can be related to the Real Mode chain were new ISR's would normally
after execution call the default or pervious handler. The old will remove the TSR
problem. TSR's can be removed if they are the first on an Interrupt chain if they are not it
was virtually impossible to find them!

Now back to the Windows environment, you can rest assured that even if the interrupt
was previously being handled by a real-mode ISR, the BIOS or a special resident TSR or
device driver, it will now be handled by your new ISR first before any of these real-mode
ISR ever gets even a chance to see it. NOTE: As we will see later it is now important to
consider very carefully if after handling the interrupt in the new ISR we wish to pass it
down to any real mode handlers, the overhead on swapping from protected to real is
excessive.

In Table 2 the interrupt vectors listed are those that the kernel maintains on an instance
basis. Any application can hook these vectors, but this new hook (ISR) will only be
active when the instance is active. This is very important to consider during the design

Created by Tony G                                                      Page 107
Windows Systems Prog                                                     Background Info


because ISR's executable instances of the application must be active to work. So if the
application is inactive the ISR installed by that instance of the application is also inactive
and will not be available to handle the interrupts.

In Windows, a mechanism, (undocumented fully as far as I am aware), exists that is
executed by the kernel task itself on exit from user supplied ISR, this can be detected by
the IRET etc., causing a protection violation. Its exact purpose can only be guessed at, but
in most cases it will perform as a safety mechanism to ensure that the CPU's enable
Interrupt flag is not left inactive by a user supplied ISR.

Remember this small task is apparently totally undocumented, a brief mention is made of
its existence on the Microsoft's Developer Network CD for Windows 3.1.x, also D.Lang
makes mention of it in his famous paper on the Tao of Interrupts for Windows 3.1.x.
From various sources the implication is, that a similar technique is used in both '95 and
'NT. So development of an ISR's for use across platforms should be aware of its nature
and to ensure that any design understands the implications of its action, obviously since
its undocumented you can't be sure when and how it is will react so Don't try and make
use of it !

The code has the form:
              push ax
              pushf
              pop ax
              test   ah, 2
              pop    ax
              jnz SHORT Dont_Do_STI
              sti
  Dont_Do_STI:
              iret

The reason it is included here is that it illustrates a performance improvement that can be
used in any code. The actual 'STI' instruction uses a lot of clock cycles in 80386
protected mode when the currently executing code is less privileged than the CPU
privilege level (that is, CPL > IOPL), this is quite normal for a Windows application.

The other main reason to look at the functionality is to realise that the kernel will try not
to let an application leave interrupts disabled, so if an ISR attempts to disable CPU level
interrupt recognition this code will simply switch them back on. The lesson to learn from
this is never rely on any previous knowledge of the interrupt flag, all code that needs to be
aware of the state of the flag must check it.

Although we could now briefly look at Real Mode and Standard VM Mode interrupt handling,
the whole purpose of these subjects was for use by Windows 3.0 and although some of the
techniques can be employed using Windows 3.1.x it is not a subject which lends itself to '95 or
'NT. The Windows environment has evolved and matured into '95 and 'NT, coupling this with

Created by Tony G                                                     Page 108
Windows Systems Prog                                                  Background Info


the hardware extensions of the 80x86 family it now only seems relevant to be concerned with the
handing of interrupts in protected mode.

In coming to such a conclusion, an understanding of the implications of Virtual interrupt
structures is vital. In all cases hardware interrupts on the PC will first be routed to the VMM
(Virtual Machine Manager). In going to such an approach we have imposed an overhead even
before any ISR routine is entered. The fastest method of handling an Interrupt once within the
VMM is by use of a virtual device (VxD), alternatively a slightly less sophisticated approach is to
allow it to be reflected (or "simulated") into a virtual machine (VM) for processing by a
protected mode DLL service routines, in some cases both approaches may be relevant for single
interrupt.

Within the Virtual structure we must now have a VPIC (Virtual PIC Device) which virtualises all
hardware IRQs that have not been previously virtualised, (an IRQ can be virtualised during the
loading of specific VxDs), at system load.

The term virtualised in this context means: Windows tricks a VM instance into thinking it is
really running on its own 8086 or 80286 CPU, in Windows 3.1.x. In '95 and 'NT since we are
dealing with an implementation which is described as offering a full Multi-Task / Multi-Thread
pre-emptive environment the required tricks are much simpler.

These aspects of virtualisation are made possible by the ring protection features of the
80x86 processors available from the 80386 onwards. Once any interrupt is reflected into
a virtual machine (including the Windows system VM), the interrupt will first be
processed by the protected-mode interrupt-handler chain for the VM, and then, if the last
handler in that chain passes the interrupt on, Windows switches the CPU into Virtual
8086 mode and lets the real-mode interrupt handler chain process the interrupt as well.




Created by Tony G                                                   Page 109
Windows Systems Prog                                                   Background Info



   Hardware Interrupt


                                       Check VxD chain at Ring 0



        VMM
                                               VxD1            VxD2               VxDn

                                      VMM switch CPU to Ring 3 mode


     Protected Mode ISR
                                               ISR1           ISR2              Default
          Chain

                                       VMM switch CPU to Real



 Real Mode ISR Chain
                                               ISR




As complicated as the above looks with the switching from Ring protection to Ring
protection level whilst the handler for the Interrupt is being found it is only an overview
of the complex series of events that are needed, the above diagram is based on Windows
3.1.x formats, however although the implementation has changed in '95 and 'NT the basic
philosophy has remained for compatibility.

It should be very clear, just how much processing has to occur between the time of the
hardware interrupt and the time it is handled by a real or protected mode ISR. The delay
between the time of an interrupt and the time of its processing is called the interrupt
latency, and it is the price paid for virtualisation of hardware.

Increased interrupt latency means less data throughput for interrupt-driven devices such
as the communications port, naturally leading to the fact that the maximum baud rate
achievable under Windows is less than what can be achieved in a nonvirtualised
environment.

Although the diagram is correct in showing the various paths an interrupt might follow, it
is not very representative of what normally occurs.

Created by Tony G                                                    Page 110
Windows Systems Prog                                                Background Info



The next two diagrams show more typical cases of interrupt flow through the system.

Interrupt handled by the VxD


 Interrupt




   VMM                             VxD Consumes
                                     Interrupt



The simplicity of this illustrates why interrupt throughput is so much faster when
handled completely in a VxD (as opposed to handling it in protected or a real-mode ISR).
However, the development complexity of a VxD 32 bit ring 0 ISR is daunting.

A more common solution for many Interrupt systems is the design of a 16 bit or 32 bit
protected mode DLL ISR handler.


 Interrupt




   VMM                              VPIC


                Switch to Ring 3

 Protected
 Mode
                           Interrupt consumed in the DLL
 16 or 32 Bit
  DLL


The VPIC handles the hardware interrupt by scheduling a VMM event to reflect the interrupt
into the system VM. Once reflected, the protected-mode ISR for that IRQ gets to process it.
This ISR has to be part of a Windows DLL and has to be installed by calling Interrupt 21h
Created by Tony G                                                 Page 111
Windows Systems Prog                                                   Background Info


Function 25h within the Windows System VM. This technique holds for all versions of
Windows up to now! At this point we need to investigate how the interrupt reflection is
handled and how Windows can keep track of who owns what interrupt. To achieve a basic
understanding requires some background information into the architecture of Intel's 80x86
protected mode.

The Effect of Privilege Levels

All CPU's after the 80386 and have a protection mechanism, which uses four levels of task
privilege numbered 0 to 3, with 0 being the most privileged and 3 being the least privileged.
The architects devised this ring system so that operating systems could run at ring 0 (most
privileged), operating system services such as device drivers could run at rings 1 and 2 (less
privileged), and applications could run at ring 3 (least privileged). This technique should
and in most cases does protect the operating system from ill behaved applications.

Code segments running at higher privilege levels, (that is, at a lower I/O privilege level (IOPL) ), have
access to I/O instructions that lower privilege levels don't have. Whenever the CPU performs I/O
operations, it checks the current privilege level (CPL) against the I/O privilege level (IOPL) and
generates a general-protection fault if the code has less privilege than the I/O permission level
(that is, CPL > IOPL).

The IOPL sensitive instructions.

The following instructions generate general-protection exceptions if CPL > IOPL. This affects both 16-
bit and 32-bit Windows libraries because they run with IOPL==0 and CPL==3. VxDs run with
IOPL==0 and CPL==0 so that they can execute these instructions without triggering exceptions.

     Instruction                 Meaning

     IN                               Input
     INS                              Input string
     OUT                              Output
     OUTS                             Output string
     CLI                              Clear interrupt-enable flag
     STI                               Set Interrupt-enable flag

What's important about ring protection (for this discussion, anyway) is that it allows the
I/O space to be trapped, making possible all sorts of checks and balances, such as the
right to declare war, to approve budgets and confirm High Court judgements, in this way
the system is able to validate who gets access to which I/O ports, and to assign ownership
of IRQS.




Created by Tony G                                                    Page 112
Windows Systems Prog                                                  Background Info


Masking and Unmasking Interrupts

By taking advantage of the ring-protection features of the 80386 and above CPU's,
Windows can keep track of the masking and unmasking of interrupts. Windows separates
the concept of a virtual IRQ from a physical IRQ in the following way:

       A physical IRQ is an actual hardware interrupt request. It's what an MS-DOS based
       application's ISR works with outside of the Windows environment.

       A virtual IRQ is a per VM representation of a hardware interrupt. It's what an MS-
       DOS-based application's ISR gets when it is run in an MS-DOS box in Windows,
       and it is also what a Windows DLL's ISR receives.

By default, VPIC sets all previously masked IRQs to what's called auto masking. Auto
masking means that the system performs what is essentially a multi VM logical 'AND' of
the mask state. If all virtual machines have masked the virtual IRQ, the system masks the
physical IRQ. Another way to say it is: If at least one VM needs to receive the interrupt,
VPIC keeps the physical IRQ unmasked.

CLI and STI

By now it should be obvious that STI and CLI are special and since we are in the hands of
the VMM we should not be able to get into a mess.

WRONG !!!

Let's begin with the obvious: What do you think will happen when you run the following
program in an MS-DOS box?

     void main (void)
         asm cli

Behaviorally, it appears that nothing happens. If you single-step through it in a debugger,
however, you'll see that it does clear the interrupt flag, but Windows seems not to mind.

What about the next program?

     void main(void)
         while(l);        Infinite loop

This program hangs the MS-DOS box but doesn't hang the system. You can still move
the mouse around, so you know that interrupts are enabled and the CPU isn't hung. So up
to here, it appears that Windows really does protect itself from these kind of bad-
application lockups.


Created by Tony G                                                   Page 113
Windows Systems Prog                                                         Background Info


What about combining the previous approaches into one program?

     void main(void)
        asm cli
         while(1);   Infinite loop

Don't try this !!!!

It will hang the machine, (a trial got 100% system lock up in 20 attempts), your only way
out is a total reboot on Windows 3.1.x, and on '95 and 'NT we should be able to close
down the specific task, (using Ctrl Alt Del), but beware this is not always the case ? You
can see that it's important to use CLI and STI properly, so some guidelines follow.

In Windows unlike the nonvirtualized environment of MS-DOS you cannot assume that a
POPF (Pop Flags) instruction will reenable interrupts, ( assuming we pushed the flags
with Interrupts enabled), such an idea was common practice at DOS levels.

     ; An Example of INCORRECT Interrupt-time Code
     ;
     ; (Assume interrupts are enabled at this point.)
     ;
     pushf ; Save the CPU flags.
     cli     ; Clear interrupt flag.
     popf    ; Restore CPU flags, assuming this will reenable interrupts.
              ; WRONG! Interrupts are still off.

The right way to do it is like this:

     ; Corrected Interrupt-time Code ;
     ; (Assume interrupts are enabled at this point.)

         pushf       ; Save the CPU flags.
         cli         ; Clear interrupt flag.
         sti         ;This correctly reenables interrupts.
         popf       ; Restore the CPU flags for your VM etc..

Likewise, the IRET instruction does not restore the interrupt flag, (IRET not only recovers the IP
etc., it also restores the flags to the state prior to the start of the Interrupt), so just as in the previous
example, you must explicitly reenable interrupts before calling IRET.




Created by Tony G                                                         Page 114
Windows Systems Prog                                                      Background Info


                            Now Let’s look at How to Design and write ISR's

In this section we will look at the issues involved in writing interrupt service routines
(ISRS) for any of the Microsoft Windows operating system. Although we will base most
of the discussion on aspects not directly related to virtual devices (VxDs), we will be
reviewing the advantages and disadvantages of writing a VxD instead of a dynamic-link
library (DLL).

In fact we shall be looking at the following key topics.

    ·     Choosing between DLLs and VxDs for your interrupt handler

    ·     Prototyping and profiling your ISR in a DLL

    ·     Guidelines for 16-bit interrupt service routines, although this could be expanded to
          32 bit ISR's all the basic concepts can be fully explored at 16-bits and the
          discussion is significantly easier.

Introduction

So you want to write an interrupt handler? Or maybe you've been told you have to?
Interrupt service routines (ISR's) are the bread and butter of device drivers. In either
case, the Windows operating system adds considerable complexity to the ISR developers
tasks. Issues that you would never even imagine worrying about in programming for a
basic DOS ISR become commonplace in Windows; for example:

       Tweaking FIXED vs. MOVEABLE vs. DISCARDABLE attributes in module
        definition (.DEF) files. In some of the newer development tools Visual C++ ver 4.0
        definition files are becoming less important and can be ignored. However, the
        complexity of the Compiler and Linker Command lines when no definition file is
        used complicates the development environment.

       Considering whether a given selector-to-segment translation will be handled
        implicitly by Windows or explicitly by your code. Since many aspects of an ISR can
        be handled more effectively in Assembler it may be necessary to actually work out the
        physical address of Code or Data.

       Worrying about whether interrupts must be handled in ring 0 or is it better to use ring
        3.

       The choice between writing embedded, installable, or conventional Device Drivers vs.
        Virtual Device Driver (VxDs)

Choices, Choices, Choices


Created by Tony G                                                       Page 115
Windows Systems Prog                                                     Background Info


As you begin planning an ISR for Windows, realise that you have several choices for how
you implement the ISR. Also realise that you lost one of the basic choices previously
available at MS-DOS level that of including your ISR as part of your application, you can
not do this under Windows.

This raises the question why ?

Windows allows a FIXED Code Segment, just what any ISR needs. The answer is in the
definition of an executable i.e. a .EXE which is defined as having FIXED Code
Segment fixed does not mean fixed and locked it actually means relocatable and
moveable when applied to an executable, clever use of words don't you think!

The Windows architecture requires interrupt handlers to be placed in libraries, not in
applications. In a Windows Library the use of a FIXED Code Segment means just that
fixed and locked in memory

Given that you must place the ISR in a library, what type of library ?

              1.    A regular dynamic-link library (DLL)?
              2.    A Windows driver (.DRV file)?
              3.    An installable driver?
              4.    A virtual device (VxD)?

Some of these choices will involve more effort than others. The two ends of the difficulty
spectrum offer these two possibilities:

    Relatively easy :

You could write a very simple DLL that contains an ISR dedicated to your Windows
based application, and treat the DLL quite simply as an extension of your application.

    Relatively difficult:

You could write a VxD to fully virtualize the hardware among all virtual machines (VMs)
in the system. VxDs are enhanced mode only libraries that have full access to the
privileged ring 0 services of the virtual machine manager (VMM) and other VxDs.

What's so difficult About Writing a VxD?

VxD development is difficult because it involves:

     None of the Windows functions you've grown to know and love. VxDs run
      underneath the Windows application programming interface (API) and thus remain
      ignorant of Windows functions. A VxD is not completely isolated, however; once


Created by Tony G                                                   Page 116
Windows Systems Prog                                                 Background Info


    your VxD accumulates enough data, it can post a message to a Windows-based
    application by scheduling an event for the system VM.

   A whole new API. This API consists of hundreds of new functions that use a
    completely different calling convention, which is based on CPU registers and flags
    and designed for assembly-language programming.

   A completely different set of development tools.

   More difficult debugging. You are no longer debugging within the total Windows
    environment, you are debugging the Windows Environment itself since VxD have to
    be considered as part of the system not part of the application !




Created by Tony G                                                 Page 117
Windows Systems Prog                                                   Background Info


What's so good About VxDs?

We have already briefly looked at this when we considered Ring protection etc., but to
summarise ..

   Faster routing of interrupts to your ISR. An interrupt handled in a VxD is serviced
    shortly after it is vectored through the interrupt descriptor table (IDT), and is handled
    in ring 0 code in the context of the current VM. In contrast, an interrupt handled in a
    Windows DLL must be simulated into the system VM. Simulating interrupts involves
    a lot of overhead, especially if the interrupt occurs while the current VM is not the
    system VM.

   Ability to virtualize the hardware so that it appears to be shared by Windows based
    applications and MS-DOS based applications. If sharing doesn't make sense for your
    hardware, you may still consider using a VxD to mediate exclusive access to your
    hardware.

   Less overhead for I/O. The IN and OUT instructions run about twice as fast because
    the executing code's current privilege level is equal to the I/O privilege level (CPL ==
    IOPL).

   No more silly games with 16-bit segment registers. Okay, so maybe that's not a
    compelling reason but if you're going to code in Intel 8Ox86 Assembler, you'll be
    much happier taking advantage of the full power of the 80386 (and above) register
    and instruction set in a 32-bit flat model.

   VxDs can do magical things that no Windows based application or DLL can do. This
    may be the most compelling reason for writing a VxD not because you need a device
    driver, but because your application needs services that only a VxD can access. For
    example ;
                VxDs can manipulate the address space visible in any or all VMs or they
                can create regions of address space that are unique or instanced per VM.

After all this glorious praise for VxDs, I'm about to propose a development strategy
that attempts to circumvent, or at least delay, writing a VxD well no one said they it
was easy to weight up the pro's and con's, experience has to be a factor !

There is an alternative to taking the plunge straight away, why not prototype your ISR in
a DLL If (a) you're beginning a device driver project, (b) you're already familiar with
programming for Windows, and (c) you haven't already committed to writing a virtual
device especially if you've never written one before the following approach offers a first
step in the design process.




Created by Tony G                                                    Page 118
Windows Systems Prog                                                  Background Info


    1 . Begin by writing your ISR as part of a DLL. Think of this as a prototyping phase:
        You're getting the kinks out of your design in a relatively rapid turnaround
        environment before investing in the development overhead of writing a VxD.
        You may choose to make it a regular DLL (essentially an embedded driver) or an
        installable driver.

   2.    Benchmark the performance of your driver. If it runs quickly and reliably enough
         for your hardware, you won't have to convert it to a VxD. The same code will
         work for 3.1.x, '95 and probably Windows NT.

    3.   On the other hand, you may find that ISR performance does not meet the
         demands of your hardware. In this case, you will have to decide if you can
         optimise the performance to meet your needs. This may not be possible due to
         the inherent architectural constraints of interrupt reflection.

    4.   If performance is inadequate and can't be optimised, now is the time to write
         that VxD you were having nightmares about. But take consolation in the
         knowledge that you did the initial prototyping and some of the optimisation in
         a more familiar environment than the VxD environment. Tracking down bugs
         in VxDs is painfully difficult compared with DLL's.

One final note about the advantage of this strategy: If your ISR works successfully in a
DLL in a Windows environment there is close to a 100% chance it will work in any other,
VxD's on the other hand have separate DDK kits for 3.1.x.,'95, and 'NT. So you will have
to build and debug three versions if you wish to offer complete across platform support.
Also when the next flavour of windows arrives, it is most likely that DLL ISR support
will be maintained but for VxD's it could very well be start again.

Guidelines for DLL ISRs

Regardless of the form it takes Windows device driver, installable driver, or DLL.
A driver is still a protected mode DLL that must follow the guidelines discussed below.

Ride with MS-DOS

Do not write into the interrupt table directly! Instead, install your interrupt handler with MS-DOS
Interrupt 21h Function 25h (Set Interrupt Vector) after retrieving the address of the previously
installed handler, using Interrupt 21h Function 35h (Get Interrupt Vector). Windows intercepts the
MS-DOS Set Interrupt Vector call and registers your ISR as the protected-mode handler for that
interrupt. When interrupts occur, Windows "reflects" the interrupt to your protected-mode handler even
if the processor was running in real mode or V86 mode at the time the interrupt occurred.




Created by Tony G                                                   Page 119
Windows Systems Prog                                                  Background Info


Many of the run-time libraries supplied with common 'C' compilers have a problem associated with get
and set vector in the Windows environment, so to this end it is a good idea to code these as your own
functions, below is a typical implementation, also notice the use of DOS3Call which in 99% of cases is
missing in the header file 'windows.h' or 'windowsx.h', DOS3Call is the preferred mechanism for
execution of any INT 21h call from within Windows.

    // FUNCTION DECLARATIONS
    // (note that DOS3Call is not prototyped in WINDOWS.H)
void my_dos_setvect(urisigned intnum, void( _cdecl _interrupt __far *handler)());
void _cdecl _interrupt __far * my-dos-getvect(unsigned intnum))();
extern void FAR PASCAL DOS3Call(void);
// FUNCTION DEFINITIONS
//
//**************************************************************
// FUNCTION: my_dos_setvect
//
// PURPOSE:
// Substitute for the C run-time dos setvect function (which is not compatible with
// Windows).
//
void my_dos_setvect(unsigned intnum, void (__cdecl _nterrupt __far *handler)())
{
         asm
         {
         push ds         ;Save DS 'cause we're gonna trash it.
         lds      dx, handler ; trash it
         mov ah, 25h          ;
         mov al, byte ptr intnum ;
         call     DOS3Call
         pop      ds
         }
}
// FUNCTION: my_dos_getvect
//
//PURPOSE:
//Substitute for C run-time dos getvect function.

void (__cdecl __interrupt __far *my_dos_getvect(unsigned intnum))()
{
void (__ cdecl __interrupt __far *OldVector)();
       asm
       {
       mov ah, byte ptr 35h
       mov al, byte ptr intnum
       call    DOS3Call

Created by Tony G                                                  Page 120
Windows Systems Prog                                                     Background Info


        mov word ptr [OldVector], bx
        mov word ptr [OldVector+2],es
        }
return OldVector;
}

Fly high

Avoid using the memory address space under the 1 MB line. This real mode addressable
(also called 'MS-DOS addressable') area of memory is a scarce and very important
resource for the system, this is particularly true of 3.1.x, in '95 and 'NT resource tables
have moved above this line, but there are still significant data tables and structures in this
area. Particularly in 3.1.x if too many libraries or applications use up this address space,
Windows will be unable to launch new Windows based applications even if plenty of
virtual memory is available. This is because each new process requires some real mode
addressable memory for task database (TDB) items such as MS-DOS file handles.

Your DLL's code segments never need to reside below the 1 MB line, and your DLL's
data segments should never need to store variables within this space.

If you are interfacing with an existing TSR, which by its very nature will be in this space
and it has already allocated a buffer for its own use before Windows started up. The
access to this data by the DLL can be made using either kernels exported selectors
_A000h, _B000h, and so on or by synthesising a selector in the actual implementation
code.

Stick with fixed

Your ISR code must reside in a FIXED code segment of a DLL. Both of these
requirements FIXED and a DLL must be met because:

If the ISR is in a DLL's MOVEABLE or DISCARDABLE segment, it may not be
available when the interrupt occurs. Placing it in a FIXED segment tells Windows to load
it as page locked code. As indicated previously if we attempted to put the ISR in a FIXED
code segment of an application, it will not truly be FIXED. This is because the FIXED
attribute is ignored for Windows based applications (it's treated as MOVEABLE or
RELOCATABLE).


Don't touch slippery objects

Your ISR code must not touch any data that is not fixed (page locked). This means that if
the ISR uses any data stored in DGROUP (which is the DLL's default data segment), the
DLL's module definition file must specify DATA PRELOAD FIXED.


Created by Tony G                                                     Page 121
Windows Systems Prog                                                  Background Info


If a DLL allocates memory by calling GlobalAlloc with the GMEM_FIXED flag, it will
get fixed; page locked global memory suitable for use in the ISR. However, if an
application attempts to do the same thing, it will end up with movable, pageable memory.
This means that the DLL cannot rely on the application to provide buffers for use in the
ISR, unless the buffers are explicitly page locked with the GlobalPageLock function. The
DLL should allocate the buffers itself or explicitly page lock the buffers that it receives
from the application.
Watch your tongue

Your ISR must be extremely careful when deciding which functions to call; it cannot call
functions that are not re-entrant. The only Windows functions that your ISR may call at
interrupt time are:

       PostMessage

       PostAppMessage

and the following multimedia system functions:

 ·     timeGetSystemTime

 ·     timeGetTime

 ·     timeSetEvent

 ·     timeKillEvent

 ·     midiOutShortMsg

 ·     midiOutLongMsg

 ·     OutputDebugStr (this is not the same as OutputDebugString; it is a function that
       exists only in the debugging version of the MMSYSTEM library)

Consideration of the issue of re-entrancy, it vital, interrupts can be nested, functions
called at interrupt time must be coded to allow for re-entrancy before they return. Only
the functions listed above are guaranteed to be re-entrant.

Also don't call any of the application's functions, not even its exported call-back
functions. No application function is safe at interrupt time. Instead, the ISR should post
a message to the application, as described in the next section.




Created by Tony G                                                   Page 122
Windows Systems Prog                                                     Background Info


Don't call me, I'll call you

Because of time constraints and re-entrancy limitations, your ISR and its clients must
agree on a method for handling events in a robust manner. Your ISR cannot call
functions in client applications, nor can it touch the application's data segments unless
some special steps are taken to explicitly page lock the segments beforehand.

 1.      The ISR receives interrupts, processes them, and stores or retrieves data to or
         from its internal buffers.

 2.      The ISR signals the application with a call to PostMessage or
         PostAppMessage

Other Advanced ISR Techniques

So far, we've discussed the guidelines for basic interrupt handling. Here is an advanced
technique to add to your repertoire.

"I'll fix it later",'

At load time, your DLL may not know whether its ISR will be needed. In this case, it is a
waste of system resources (page locked memory) to have FIXED PRELOAD code and
data segments in memory for the lifetime of the DLL. They may never be needed.

To save resources, you can dynamically re configure your DLL's segments to make them
'ISR-ready' during run time. This involves creating LOADONCALL MOVEABLE
DISCARDABLE code segments that you dynamically re configure when you're about to
start up the ISR. The GlobalRealloc function lets you change the attributes of a specified
segment, and the GlobalPageLock function lets you lock it down tight. Don't forget to
page lock any data segments that the ISR will touch.




Created by Tony G                                                     Page 123
Windows Systems Prog                                                   Background Info


                              The Migration Problem


Although Windows '95 and 'NT are the 32 bit environments for which new applications
will be targeted in the future, there are still many 16 bit applications designed for
Windows 3.1.x which will need to be ported across as and when required. These in the
main will be specialist solutions which, as the installed base of '95 or 'NT environments
grows will be upgraded and ported.

Obviously these applications could be totally revised and re implemented, such an option
will be, and is being used for the larger and more popular general purpose applications,
(this is ongoing by many of the leading software suppliers). However, in contrast to the
marking hype from Microsoft, there are many applications, which represent vast man
years of development investment, for which the cost of a total design development
exercise would be prohibitive.

So we find two kind of programmers: Some are saying "Oh boy, 32 bit Windows is here
just look at all the clever things I can make the application do now!", and the others who
are groaning, " Oh no, look at the changes I have to make to this application to port it
across".
It is the second group who have to come to terms with the migration problem, they are the
ones who need to understand what is absolutely necessary and what would be nice to do if
time allows.

We will explore first the absolutely necessary changes, the bare minimum that must be
looked at to port a 16 bit application into the 32 bit world. We will not and can not take
the laid back attitude that since the 32 environments have the ability to run a 16 bit
application, we can leave well alone. This option is taken for granted by USERS, 16 bit
applications in a 32 bit world are slow, prone to strange problems and are certainly not
making effective use of the resource available.

Having dealt with the bare minimum we will also look at the "Nice Changes", that can
generally be made without throwing too much resource at the porting exercise, we can
then conclude the discussion finally with a "Improving Performance Review ", here we
can identify changes which would give the application the true Windows '95 and 'NT feel
and take advantage of many aspects of the underlying architecture of the environment.




Created by Tony G                                                    Page 124
Windows Systems Prog                                                         Background Info


Necessary Changes

What must you absolutely, positively do to a Windows 3.1.x application? Not as much as
you probably first think! Only a handful of the underlying 'C' API's have been radically
changed. The area to consider being those that have involve the communication port, the
I/O system and access to DOS functionality.

The remainder of the changes to the API merely identify alterations to use 32 bit
parameters everywhere, rather than a mixture of 16 and 32. These require only a few
elementary changes to the Source files. There are a number or Porting Tools, which
identify for you were such changes have to be made. The Microsoft offering is
PORTTOOL.EXE, this scans the source a file at a time, tokens are compared and where
necessary comments are inserted to recommend what changes are needed.

Now what's all this about LONG FILE NAMES ?

In the '95 and 'NT environment filenames are no longer limited to the 8.3 format of
Windows 3.1.x. They can now be 255 characters in length, and can include
 .~'!@#$%^()_-+={}[ ] ;' and spaces. They still have a 3 character extension but they
may only have 1 or 2.

With the inclusion of the period in the filename we can find users naming files as

                        file.old.addinfo from lastyear into 1997.txt

So all kind of old 8.3 format file name assumptions are gone.

This change is not only limited to file names directories, or as they are called in the 32 bit
world Folders can also have long names.

Actually the first thing to realise is that the 255 limit applies to the
                        complete path + filename.

So in the root of the c: drive we would have c:\ plus up to a 252 character maximum for
a file name. Were as if say the path was nested through say 50 long name folders there
may only be room left for 3 or 4 characters for the filename. The permutations are endless
any application will have to be changed to deal with them all ! Consider the following :-




Created by Tony G                                                          Page 125
Windows Systems Prog                                                    Background Info



   any application will have to be able to display a reasonable amount of the file name
    including path information within the user interface. It will need to provide a scroll
    horizontally, if space does not allow the display of a full 255 character string in one
    go.

   Buffers for filenames will need to allocate 256 characters, (255 plus the trailing
    NULL) If they can change it once who says they won't change it again so a better
    solution is _MAX_PATH + 1.

   File name parsing will now have to look for the .3 extension from the name, instead
    of looking from the old standard of the ninth character or the first period.

   Filename comparison is now not a trivial exercise.

If the application uses the Common Dialogue Boxes then FILE OPEN - FILE SAVE it
will automatically solve most of these problems. If these were not used in the original
implementation maybe they need to be used after this porting exercise, it will save time in
the future if other changes are made.

32 Bit EVERYTHING

In '95 nearly everything, (data types), have become 32 bits, in 'NT they all have ! So
this includes all the data types; int, BOOL's, HANDLEs, etc. and of course we must not
forget that FAR pointers have become NEAR pointers. The only data types that have
not become 32 bits are 'BYTE and char' (still 8 bits) and 'short and WORD' ( they are
still 16 bits). To cope with UNICODE an alternative to ASCII for international character
sets a WCHAR now exists which is 16 bits. So what do we need to look for as potential
problems in data type conversion?




Created by Tony G                                                    Page 126
Windows Systems Prog                                                    Background Info


HANDLE's

All handle's have become 32 bits (HWND, HANDLE, HDC etc., ) Some older 16 bit
applications used the generic type WORD for storage of handle's rather than any windows
specific derived type.

For instance a piece of code in 16 bits could be typically formatted as ..

DrawWindow ( WORD hWnd)
{
  WORD hDc = GetDC (hWnd)
......

The equivalent 32 bit code would be

DrawWindow ( HWND hWnd)
{
  HDC hDc = GetDC (hWnd)
......

This is of course the better way to do it anyway, because using the derived types means
this revised code will work when compiler for either 16 or 32 Bits or who know 64 bit
Windows in the near future !! A major problem exists with the two API functions
GetWindowWord and GetClassWord commonly used to get handle's etc. from the
Windows data structures. These will have to be replaced with GetWindowLong and
GetClassLong to ensure you get full 32 bit values.

POINTERS

In '95 and 'NT all pointers are 32 bit. It again was quite common practice in 16 bits to use
a declaration such as

short      pWindowPos;

This could be replaced with

LPWINDOWPOS           pWindowPos;

However to offer complete 16 and 32 bit compatibility

WINDOWPOS * pWindowPos.

Because all pointers are 32 bit the FAR or _far and NEAR or _near, are now obsolete,
they can be removed most compilers have defined these to NOTHING for 32 bit


Created by Tony G                                                    Page 127
Windows Systems Prog                                                  Background Info


compilations. However, inclusion in a 32 bit application source will only lead to
confusion so they should be removed.

Graphics Co-ordinates

This is an area that can cause some concern between '95 and 'NT ( differences do exist
between the GDI system on '95 and 'NT). NT is truly a 32 GDI, '95 uses several thunks
fixes to allow 32 bit parameters to call 16 bit code. This brief review is not meant to
cover all aspects just to highlight the main concerns between 16 bit and 32 bit
applications.

With the co-ordinate system now stored in 32 bits, several changes have needed to be
made in the GDI. Several older functions returned in the 16 bit system a DWORD (
Double Word yes 32 bits ). But the application extracted the parameters of the co-
ordinates by use of the HIWORD and LOWORD macros. Changes based on this idea
would have meant use of a 64 bit value which was felt not to be acceptable, instead the
API's now return a BOOL indicating success or failure of the API. The actual values are
now returned in a new parameter. So this will require the inclusion of new code to handle
the change.

Consider the example below.      Original 16 bit code.

MoveToCenter (HDC hDC, short left, short top)
{
DW      dwSize;
dwSize = GetWindowExt (hDC);
// Return coordinates in dwSize
MoveTo ( hDC, left + LOWORD(dwSize), top + HIWORD(dwSize));
....


                              Modified 32 bit code

MoveToCenter (HDC hDC, int left, int top)
{
BOOL bSuccess;
LPSIZE pSize;
LPPint pPoint;
bSucess = GetWindowExt (hDC, pSize);
// Return success in bSuccess
// Psize will point to a structure with the details
if (bSucess)
        {
        MoveToEx ( hDC, left + pSize->cx/2, top + pSize->cy/2,pPoint);
....

Created by Tony G                                                  Page 128
Windows Systems Prog                                                  Background Info



It is important that an application porting exercise reviews all the GDI functions used and
all are modified for the 32 bit environment of '95 and 'NT.

MESSAGE PARAMETERS

Message IDs , WPARAM and LPARAM are now 32 bit values in the '95 and 'NT world.
Also LPARAM can now contain HANDLE'S of 32 bits and this creates a problem with
messages that had information in LPARAM in addition to HANDLE'S ( in the 16 bits
world the Handle and the information were separated in the application by the HIWORD
and LOWORD macros), now that the handle makes full use of LPARAM, this other
information has had to go somewhere and that's back into WPARAM.

This is the most significant change that needs to be made in any Windows 3.1.x
application, which is being ported. It will mean changes to message handling in nearly
100% of cases.

It‟s not possible here to look at all messages let‟s simply consider WM_COMMAND,
WM_VSCROLL and WM_HSCROLL. During modification of Message Handler
function in an application it is generally estimated that 75% of the code will have to
change.

LRESULT WndProc ( UINT uMsg , WPARAM wParam, LPARAM lParam)
{
      switch (uMsg)
      {
      case WM_COMMAND:
                    return OnCommand
(wParam,LOWORD(lparam),HIWORD(lParam);
      case WM_VSCROLL
                    return OnVScroll
(wParam,LOWORD(lparam),HIWORD(lParam))
      case WM_HSCROLL
                    return OnHScroll
(wParam,LOWORD(lparam),HIWORD(lParam));

         ......
         }
}

LRESULT OnCommand (WORD idItem, HWND hwnCtl, WORD wNotify )
{
....
}
LRESULT OnVScroll (WORD wScrollC, int nPos, HWND hwnCtl)

Created by Tony G                                                   Page 129
Windows Systems Prog                                                  Background Info


{
.....
}
LRESULT OnHScroll (WORD wScrollC, int nPos, HWND hwnCtl)
{
.....
}

In the 32 bit Message dispatcher we would be coding for the same functions something :-

LRESULT WndProc ( UINT uMsg , WPARAM wParam, LPARAM lParam)
{
     switch (uMsg)
     {
     case WM_COMMAND:
            return OnCommand (LOWORD(wParam),HIWORD(wParam),lParam);
     case WM_VSCROLL
            return OnVScroll  (LOWORD(wParam),HIWORD(wParam),lParam);
     case WM_HSCROLL
            return OnHscroll  (LOWORD(wParam),HIWORD(wParam),lParam);

         ......
         }
}

LRESULT OnCommand (WORD idItem, WORD wNotify, HWND hwnCtl, )
{
....
}
LRESULT OnVScroll (WORD wScrollC, short nPos, HWND hwnCtl)
{
.....
}
LRESULT OnHScroll (WORD wScrollC, short nPos, HWND hwnCtl)
{
.....
}


There are a few messages that have changed (or haven't changed) in unexpected ways!
In mouse messages - mouse positions are not 32 Bit for '95 only! they are in 'NT.

So lParam LOWORD is the x position and HIWORD is the y position.
Since in 99% of cases if these positions are being used, its to allow calculations to be
performed, they will have to be cast to the correct 32 integer value since we will normally

Created by Tony G                                                   Page 130
Windows Systems Prog                                                      Background Info


be dealing with 32 bit integer arithmetic. Unfortunately, the wrong cast could yield an
unsigned value and 'oh dear what does that do to the sign !'

In order to ensure this is not a problem first cast to a short and then to an int this will
ensure sign extension is maintained.

The WM_CTLCOLOR message has gone, it has been broken down to specific messages
for each Control.

Finally In addition to all of this it is a good idea to search the source for all type casts
and check them especially explicit casts to WORD and short.
Obviously implicit casts in the source can cause a real problem !

Shared Memory

With both '95 and 'NT each process is now run totally in its own memory space. This
means that unlike 3.1.x it is now impossible for a process to corrupt another memory
space. However, this leads to a porting problem were in 3.1.x applications were using
shared memory. This was generally the case in multiple instance applications were shared
memory could have been used to transfer data from any one instance to another.

To redesign the application to overcome this new limitation within '95 and 'NT we have
to consider one of the following alternatives. However, don't get into these concepts if the
application doesn't absolutely need to have multiple instances running.

Note: In order to find out if an application is running another instance you have to find
that instance, the parameter in WinMain ... hPrevInstance is maintained only for
backwards compatibility, in '95 and 'NT it will always have a value of NULL.

You must use the API 'FindWindow' with the particular Window & Class Name, if it is
found the Instance handle will be returned if it is not found the return value is NULL.




Created by Tony G                                                      Page 131
Windows Systems Prog                                                    Background Info


The options available for shared memory in '95 & 'NT are :

   To use a Common DLL to store this shared data, each instance of the application can
    then use the DLL's Data Segment to pass this data about, but of course you will need
    to consider the usual synchronisation and mutual exclusion problems associated with
    such shared access.

   Alternatively you can share memory with some form of memory mapped file.
    Memory mapped files allow an application to access data as if it were memory
    locations. The file's content is mapped into a particular process's virtual address space.
    This technique is intended to offer fast file access to an application. For instance when
    an application reads 200 bytes from a file using standard file management techniques,
    in fact what happens is that a 4K Byte chunk of the file is read into actual memory,
    then the 200 bytes requested are copied into the application provided buffer. If on the
    other hand the application reads 200 bytes from a memory mapped file, although the
    4K Byte swap is made from the swap file to the actual memory, the application is then
    allowed to read the 200 bytes from this actual memory itself, rather than the data
    being copied to a buffer allocated in the application. Shared Memory is not the
    primary reason for the inclusion of memory mapped files within '95 and 'NT they are
    there to provide a faster and more efficient method of disk access, however the
    mechanism offers a compact and relatively fast shared memory alternative.

IF YOU HAVE USED...

A few aspects of the system have changed pretty radically between 3.1.x and '95 & 'NT. If
your application have previously used the 'COM port I/O API' functions, sound,
Dos3Call or customised ICON painting techniques some very special design /
implementation changes will have to take place.

   The communications port API's have been replaced by considering the COM port as
    just another file structure, so access is now via the CreateFile, ReadFile etc.

   Most of the sound functions have been replaced by changes within the API
    PlaySound.

   The old idea of using the uniform Dos3Call interface, (which was in fact not formally
    documented in the 3.1.x development kit ) , to access INT21 etc. has been totally
    removed. It is now rationalised in '95 and 'NT to the use of the file I/O functions of
    Win32 API to gain access to low level features.

   Since on a '95 and 'NT ver 4, the desktop does not have ICONS available for
    minimised running applications, (OK small ICONS do exist on the status bar),
    these applications will not receive WM_PAINTICON messages. So the animated
    techniques employed in 3.1.x for such ICONS can no longer be supported.


Created by Tony G                                                     Page 132
Windows Systems Prog                                                      Background Info


With the changes highlighted any old 3.1.x application can be ported and running on a '95
or 'NT desktop. However, to many users such a revision of the application will not appear
to be 100% functional, many of the expected options USERS are familiar with in '95 and
'NT have not been considered.

NICE CHANGES TO MAKE

OLE

Every '95, 'NT application sitting on a desktop should support as a minimum drag and
drop OLE. Microsoft define everything in '95, 'NT as an object with properties and as
appropriate methods, so that by definition any application, (object), should be able to be
embedded into any other object, (application). Users quickly become used to dragging
things around, dropping them into folders etc. Any application that does nothing when the
USER tries to drag it will not look functional.

If the application previously supported cut and paste, the changes are not too complex.

However, if it did not there is quite a lot of redesign work to undertake.

At the very least during the porting an application should offer either

   Allow objects to be embedded and linked within it, and embed objects dropped onto
    it.

   Be capable of embedding itself into other applications and recognise when it has been
    embedded.

The first kind of application is called a container, whilst the second is an automation
server ( object).

There is also the question of inplace edit, i.e. the user double clicks on an embedded
object and the original application starts to deal with this request.

THE REGISTRY

The '95,'NT Registry now manages a vast array of information, applications specific, user
specific and also machine or desktop specific. So it hold hardware details, software
initialisation, user preferences etc. etc. It has replaced and augmented the .INI file,
autoexec,config file concepts of 3.1.x. So any application should store and retrieve its
information from the registry.

The Registry is very large and complex and so has a clearly defined structure for
application specific information. The Registry uses a hierarchy of keys and subkeys with
the added flexibility that subkeys can store multiple values. In general the keys which are

Created by Tony G                                                    Page 133
Windows Systems Prog                                                   Background Info


most important to an application are HKEY_LOCAL_MACHINE and
HKEY_CURRENT_USER.

If we assume we are the Company Wolvs Soft, if we have an application that needs user
specific information stored for initialisation then the usual place would be ..

HKEY_CURRENT_USER/ Software/Wolvs Soft/ App name...

If we also have particular machine dependant information defaults etc., it would go in

HKEY_LOCAL_MACHINE/Software/Wolvs Soft/App name..

A number of API's are available to interact with the registry. Because the Registry is in
fact, two data base files, new and special DATA types have been introduced such as
REG_SZ for a string and REG_DWORD for a 32 bit number etc., ensuring very tight
type checking when interacting with the Registry specific API's in Win 32.




Created by Tony G                                                    Page 134
Windows Systems Prog                                                    Background Info


USER INTERFACE

Although the user interface on '95 and 'NT from version 4 looks significantly different
from 3.1.x most of the changes are in the frame and format of windows elements and will
migrate automatically unless the application draws its own window ( not a very usual
situation ).

There are of course, a few exceptions.

      16 x 16 icons : Whenever an application is minimised, it goes to the desk top status
       bar, where the now defined as small ICON is used (16 x16) along with the
       application title. If the application only provides the normal 32 x 32 ICON as a
       resource the small Icon will be automatically shrunk. However, the detail in the
       shrunk form is normally very poor, looking like a "swashed bug". It is advisable to
       generate a small version ICON you like, to register this if the application uses the
       WNDCLASSEX rather than WNDCLASS structure an entry for hIconSm is given
       to register the ICON. WNDCLASSEX is used in the API RegisterClassEX rather
       than the older RegisterClass, which still uses WNDCLASS.

      GetSysMetrics and GetSysColor: Since USERS can set preferences for there
       particular desktop environment, (personal profiles) any application should use these
       system API's to obtain the currently set Color and Resolution. More critical than
       colour and resolution is in fact FONT's on the desktop (size). USERS get very
       frustrated if a new application is hard coded and causes titles and text to become
       illegible. The information is all stored in the REGISTRY and for 100%
       compatibility the application should respond to WN_WININICHANGED messages.
       yes we know WIN.INI is no longer used but the message has stayed and refers now
       to user changes to the desktop etc. in the REGISTRY database.

SetUp Guidelines

Any new application should conform to a standard installation and SetUp procedure.
Although Microsoft will only propose use of their standard MSETUP which can be built
from the tool kit of many of the Microsoft Development tools. Many other variants are
available which have the same functionality but a slightly less Microsoft GUI. The most
important aspect is the SetUp .INI or .DEF file and the fact the application will be
automatically register etc.

Help

Although again help files in '95 and 'NT version 4 or later have a different feel with a
more general appearance and some new functionality aspects, this is achieved by a change
to the Help system and the help compiler rather than complex changes to the original RTF
files used to prepare and develop the Help.


Created by Tony G                                                    Page 135
Windows Systems Prog                                                   Background Info


The only real major change is the new ability to be able to actually launch an application
from within Help.

COMMON DIALOG BOXES

We have already briefly mentioned the use of Common Dialog when we looked at Long
filenames. Several new Dialogue boxes have been added to '95 and 'NT version 4 or later.

The now available dialogs are : ChooseColor, ChooseFont, FindText, GetFileTitle,
GetOpenFileName, GetSaveFilename, PrintDlg and ReplaceText. Using these can save a
great deal of time, they are already LongFileName aware, they conform to User
requirements and much as they could in 3.1.x they can be customised to provide either
more controls or some aspects of the controls can be disabled.


RIGHT MOUSE BUTTON

Users get used to the use of the right mouse button to bring up contextual pop up menus.
In 3.1.x the mouse button was used by some applications for specific functions this
should be avoided in '95 and 'NT and the right mouse button simply used for this feature.

The API TrackPopupMenu will display the menu at the co ordinates given. For an
effective example of right mouse actions, consider a document with an embedded sound
object, clicking the right mouse button on the object should bring up a menu offering
PLAY - RECORD etc.

If an application has got this far in a porting excersise it must be obvious that should we
need to maintain backwards compatibility to 3.1.x it will be necessary to either hold two
complete development environments, (source code, resources etc.) one for 16 bit and
another for 32 bit.

This is of course not a very practical option so the application will need to be ported with
both conditional compilation options and specific conditional code for different platform
execution.

Conditional Compilation.

#ifdef _WIN32
.......
....... set up version info etc for '95 or 'NT
#else
......
......  set up vbersion info for 3.1.x
#endif


Created by Tony G                                                    Page 136
Windows Systems Prog                       Background Info


conditional code execution

switch (version_info)
{
case WINNT : // Specific NT API etc.
case WIN32s : // Specific WIN32s API's
etc..




Created by Tony G                        Page 137
Windows Systems Prog                                                      Background Info


Windows '95 and 'NT offers some features which if incorporated into an application will
greatly enhance performance etc. This of course requires far more radical changes to an
application that is being ported over from 3.1.x than those previously identified to 'get it
working and make it feel right for the USER'

Flat Memory Model

Application now have available a 4 GB virtual memory address space, in fact it is more
realistic to look at this as 2 GB since the other 2 are reserved for various aspects of the
windows environment. So gone is the old 64KB segment limit, therefore if the application
was constrained o n buffers etc., to this older limitation now is the time to look at the
memory allocation and revise as necessary.

Multithread

Within '95 and 'NT it is now possible to do more than one thing at a time within a given
application. Don't go made with threads each thread within an application takes up system
resource and too many can lead to a very poor response.

Multithreading is not as simple as it might first appear, the synchronisation aspects for the
threads coupled with such common multi tasking problems as Reader / Writer or the more
generic Producer / Consumer interchange must now be considered within this particular
application rather than being the stuff of interapplication transfers only.

Both '95 and 'NT have objects and event techniques that can be adapted to solve the
problems, but never think its an easy task to take a design solution for a single thread
sequential application solution and split it into a multithread environment.

Overlapped I/O

Within '95 and 'NT any application that spends significant time on file access can benefit
greatly from the available functions to perform overlapped I/O.

The concept is simple, once an application tries to Read or Write a file rather than going
to sleep effectively until the I/O request is finished, control is returned to the application
straight away, Windows then calls back a defined routine when the I/O operation is
actually finished.

Structured Exception Handling

Within both '95 and 'NT an application can raise an exception from anywhere in its code
so a great deal of exception handling specific code is removed making error handling
much faster.



Created by Tony G                                                      Page 138
Windows Systems Prog                                                    Background Info


When an exception is found, it is passed to the nearest handler or to the system handler if
no specific application handler has been defined. Most of the standard Windows Win32
API's do not make use of the ability yet, so applications still have to check return values
for now, but at least once found the exception can be handled by one common routine.

Unicode

This again was briefly mentioned in the Data Types. The concept is to make character
handling simpler and offer applications accessibility internationally.

The idea is that character rather than being 8 bits are now 16, this enables messages etc.,
to be translated to any language since Unicode has been defined for all modern world
wide languages.

Taking this concept to a logical conclusion provided you use Resource string tables for all
text within an application, these string tables should be Unicode, then to change the
language of the application all that needs to happen is to rebind the new resource to the
application.

Resources in Memory

Within '95 and 'NT resources no longer have to be locked to keep them in memory when
you have finished with them, but may need them gain quickly. All memory allocation for
resources is handled automatically.


OK, so its easier said than done to port an older 3.1.x application to '95 or 'NT. However,
it could have been a lot harder than it actually is, the redeclaration of API's in Win32
compared to Win16 shows almost a 80% no change.

The major item as identified is the changes in the Message dispatching that will 100% be
needed unless the application does very little at the USER or message interface.

The porting can be incremental, the must do list has to be considered to get the 32 bit
version running, the nice to do list can be phased in slowly making the application more
acceptable to the USER.

The performance enhancements need only be considered if the performance of the ported
application is not acceptable. In fact if this is the case then to accomplish the performance
enhancements will in 80 to 90% of cases require a complete rethink and possibly the
redesign / implementation of the application anyway.




Created by Tony G                                                    Page 139

								
To top