How to Structure Your Windows Code

[Previous] [Next]

The Windows portion of the code for a typical application consists of the following code blocks:

  • Code to specify include files and to define globally unique identifiers (GUIDs)
  • WinMain, the function that sets up the main window and message handling
  • Code that defines the appearance of the main window (In RoadRage, this is performed by code in the CMyD3DApplication and CD3DApplication classes.)
  • A main window message handling function (In RoadRage, this is WndProc in d3dapp.cpp, which passes the messages to CMyD3DApplication::MsgProc and CD3DApplication::MsgProc.)

Let's walk through the implementation of each of these components of our Windows-related code. If you installed the sample code from the companion CD and want to follow the code that this chapter discusses, select Open Workspace from the File menu in Microsoft Visual C++, find and select roadrage.dsw, and click Open. Roadrage.dsw is the workspace for the entire book. To make Chapter 2's code the active project, select the FileView tab in the Workspace window, right-click Chap2 Files, and select Set As Active Project from the context menu.

Code to Specify Include Files and to Define GUIDs

Programs that use DirectX need certain GUIDs (globally unique IDs, such as IID_IDirectDraw7) to be defined so that they can compile successfully. GUIDs are global variables rather than constants, so you must define storage for them. One way to incorporate the required GUIDs into your program is to include dxguid.lib in your libraries when building the project. If you prefer, you can instead define the symbol INITGUID as follows in one of your source modules before you include the header files windows.h, ddraw.h, or d3d.h, and before you use any other #define directives:

 #define INITGUID 

If you forget to define INITGUID or include the dxguid.lib library, you'll be deluged with errors when you attempt to compile your DirectX code.

TIP
To compile a program that uses DirectX, you need to have your compiler use the directories containing the latest DirectX include and library files. To verify that Visual C++ is configured correctly, choose Options from the Tools menu and then click the Directories tab. Make sure that the path to the DirectX include files (something like C:\mssdk\include) is at the top of the directory list. Then use the Show Directories For control to switch to the Library Files section and make sure that the path to the DirectX library files (something like C:\mssdk\lib) is at the top of the directory list. If you don't do this, Visual C++ will use the DirectX header files and library files that shipped with Visual C++, which will usually be the wrong version.

The include files are specified at the top of the roadrage.cpp file (as they are in any C++ module). You can find the header files resource.h, d3dapp.h, and roadrage.hpp in this chapter's project directory. Resource.h defines the Windows resources that the RoadRage program uses. D3dapp.h is the header file for the d3dapp.cpp file that contains most of the code presented in this chapter and that will contain the main-event-handling code and rendering-related code added to RoadRage in later chapters.

The class CMyD3DApplication, defined in roadrage.hpp, will encapsulate the RoadRage code that handles all the main tasks, including 3D world creation and rendering as well as user control of the 3D world, sound, and multiplayer game play. CMyD3DApplication uses the DirectInput, DirectSound, and DirectPlay components of DirectX and inherits its basic attributes from the CD3DApplication class.

The CMyD3DApplication and CD3DApplication Classes

The following code (from roadrage.hpp and roadrage.cpp) defines the CMyD3DApplication class, a default constructor for the class, and the WinMain function (the main entry point for the application).

 #ifndef __ROADRAGE_H #define __ROADRAGE_H //------------------------------------------------------------------- // Name: class CMyD3DApplication // Desc: Application class. The base class provides just about all  //       the functionality we want, so we're just supplying stubs to  //       interface with the non-C++ functions of the application. //------------------------------------------------------------------- class CMyD3DApplication : public CD3DApplication { public:          CMyD3DApplication();     void    DisplayCredits(HWND hwnd);     void    DisplayRRStats(HWND hwnd);     void    DisplayLegalInfo(HWND hwnd);     LRESULT MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam,                                   LPARAM lParam );     static HRESULT hr;                  HINSTANCE hInstApp; }; #endif //__ROADRAGE_H ///------------------------------------------------------------------ // File: RoadRage.cpp // // Desc: The main RoadRage code. The CMyD3DApplication class handles //       most of the RoadRage functionality. // // Copyright (c) 1999 William Chin and Peter Kovach. All rights  // reserved. //------------------------------------------------------------------- #define STRICT #define D3D_OVERLOADS #include <math.h> #include <time.h> #include <stdio.h> #include "D3DApp.h"   //------------------------------------------------------------------- // Name: WinMain // Desc: Entry point to the program. Initializes everything, and goes  //       into a message-processing loop. Idle time is used to render  //       the scene. //------------------------------------------------------------------- INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE,                          LPSTR strCmdLine, INT ) {          CMyD3DApplication d3dApp;     d3dApp.hInstApp = hInst;     if( FAILED( d3dApp.Create( hInst, strCmdLine ) ) )         return 0;     d3dApp.Run();                  CoUninitialize();     return TRUE; } //------------------------------------------------------------------- // Name: CMyD3DApplication // Desc: Application constructor. Sets attributes for the  //       application. //------------------------------------------------------------------- CMyD3DApplication::CMyD3DApplication()                 {     m_strWindowTitle  = TEXT( "Chapter 2" );     pCMyApp = this; } 

The CD3DApplication class from which the CMyD3DApplication class is derived is part of the Direct3D Framework and provides several member variables we'll use. CD3DApplication also supplies methods for creating the new application and handling many of the Windows messages the application will receive. In this chapter, we'll use a simplified version of the CD3DApplication class rather than the full version as specified in the standard Direct3D Framework. The CD3DApplication class is defined in file d3dapp.h, which you'll find in this chapter's project folder, as follows:

 //------------------------------------------------------------------- // File: D3DApp.h // // Desc: Application class for the Direct3D samples framework  //       library // //------------------------------------------------------------------- #ifndef  D3DAPP_H #define  D3DAPP_H #define  D3D_OVERLOADS #include <d3d.h> //------------------------------------------------------------------- // Name: Class CD3DApplication // Desc: //------------------------------------------------------------------- class CD3DApplication {     // Internal variables and member functions     BOOL            m_bActive;     BOOL            m_bReady; protected:     HWND            m_hWnd;     // Overridable variables for the application     TCHAR*          m_strWindowTitle;     // Overridable power management (APM) functions     virtual LRESULT OnQuerySuspend( DWORD dwFlags );     virtual LRESULT OnResumeSuspend( DWORD dwData );  public:     // Functions to create, run, pause, and clean up the application     virtual HRESULT Create( HINSTANCE, LPSTR );     virtual INT     Run();     virtual LRESULT MsgProc( HWND hWnd,                               UINT uMsg,                               WPARAM wParam,                               LPARAM lParam );     // Accessor functions     HWND Get_hWnd()           { return m_hWnd; };     BOOL GetbActive()         { return m_bActive; };     BOOL GetbReady()          { return m_bReady; };     VOID SetbReady(BOOL val)  { m_bReady = val; };     VOID SetbActive(BOOL val) { m_bActive = val; };     // Class constructor        CD3DApplication(); }; #endif // D3DAPP_H 

Setting Up the Main Window and Message Handling

The WinMain routine is always the main function of a Windows-based application. The system calls this function as the initial entry point of the application.

Our implementation of WinMain uses CMyD3DApplication's default constructor to create the application's instance of the CMyD3DApplication class and then calls the class's Create method to generate an instance of the application's main window. CMyD3DApplication inherits its Create method from CD3DApplication and uses CD3DApplication's constructor. Here's the constructor for the CD3DApplication class:

 //------------------------------------------------------------------- // Name: CD3DApplication // Desc: Constructor for class CD3DApplication //-------------------------------------------------------------------    CD3DApplication::CD3DApplication() {     m_hWnd         = NULL;     m_bActive      = FALSE;     m_bReady       = FALSE;     m_strWindowTitle = _T( "Direct3D Application" );     g_pD3DApp = this; } 

This constructor initializes the member variables for our application whenever we create an instance of this class.

The CD3DApplication::Create method is defined as follows:

 //------------------------------------------------------------------- // Name: Create // Desc: Creates the application's main window //------------------------------------------------------------------- HRESULT CD3DApplication::Create( HINSTANCE hInst, CHAR* strCmdLine ) {     // Register the window class.     WNDCLASS wndClass = { 0, WndProc, 0, 0, hInst,                           LoadIcon( hInst,                               MAKEINTRESOURCE(IDI_MAIN_ICON) ),                           LoadCursor( NULL, IDC_ARROW ),                            (HBRUSH)GetStockObject(WHITE_BRUSH),                           NULL, _T("D3D Window") };     RegisterClass( &wndClass );     // Create the render window.     m_hWnd = CreateWindow( _T("D3D Window"), m_strWindowTitle,                            WS_OVERLAPPEDWINDOW|WS_VISIBLE,                            CW_USEDEFAULT,CW_USEDEFAULT, 640, 480, 0L,                            LoadMenu(hInst,MAKEINTRESOURCE(IDR_MENU)),                            hInst, 0L );          if (!m_hWnd)     {         LPVOID lpMsgBuf;         FormatMessage(              FORMAT_MESSAGE_ALLOCATE_BUFFER |              FORMAT_MESSAGE_FROM_SYSTEM |              FORMAT_MESSAGE_IGNORE_INSERTS,             NULL,             GetLastError(),         // Default language             MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),                 (LPTSTR) &lpMsgBuf,             0,             NULL);         //         // Process any inserts in lpMsgBuf.         // ...         // Display the string.         //         MessageBox( NULL, (LPCTSTR)lpMsgBuf, "Error",                      MB_OK | MB_ICONINFORMATION );         // Free the buffer.         LocalFree( lpMsgBuf );      }     UpdateWindow( m_hWnd );     // The application is ready to go.     m_bReady = TRUE;     return S_OK; } 

This Create method uses the WNDCLASS type to create a window class, specifies attributes for the window class, uses the RegisterClass function to register the window class, and creates a window based on the registered window class by using the CreateWindow function. The window class's attributes include items such as the window's cursor, its icon, and its menu. CD3DApplication::Create uses the following functions to fill in and register the WNDCLASS structure:

  • The LoadIcon function loads the icon, named IDI_MAIN_ICON, associated with the application.
  • The LoadCursor function loads the cursor. In this project, the standard arrow cursor is sufficient.
  • The GetStockObject function is called to acquire a handle to one of the predefined stock pens, palettes, brushes, and fonts. In this segment, we're requesting a white brush.

TIP
You don't need to delete stock objects using a call to DeleteObject, but if you do, no error will occur. Also keep in mind that you can't adjust stock brush origins and that NULL_BRUSH and HOLLOW_BRUSH objects are the same.

With the window class defined, the next step is to create the window. Creating a window is fairly straightforward, though the creation command includes a variety of parameters. CD3DApplication::Create uses the Windows function CreateWindow to create the main window. The CreateWindow function is defined as follows:

 HWND CreateWindow(     LPCTSTR lpClassName,     LPCTSTR lpWindowName,     DWORD dwStyle,     int x,     int y,     int nWidth,     int nHeight,     HWND hWndParent,     HMENU hMenu,     HINSTANCE hInstance,     LPVOID lpParam ); 

ParameterDescription
lpClassNamePointer to a null-terminated string containing a registered class name
lpWindowNamePointer to a null-terminated string containing the window name
dwStyleWindow style
xHorizontal position of window
yVertical position of window
nWidthWindow width
nHeightWindow height
hWndParentHandle to parent or owner window
hMenuMenu handle or child identifier
hInstanceHandle to application instance (ignored in Windows 2000)
lpParamWindow-creation data

Setting Up the Message-Processing Loop

In any Windows-based application, you need to define the message-processing loop that the application uses to process any Windows-based messages it receives. The routine that does this processing for RoadRage, CD3DApplication::Run, follows:

 //------------------------------------------------------------------- // Name: Run // Desc: Message-processing loop. Uses idle time to render the scene. //------------------------------------------------------------------- INT CD3DApplication::Run() {     // Load keyboard accelerators.     HACCEL hAccel = LoadAccelerators( NULL,          MAKEINTRESOURCE( IDR_MAIN_ACCEL ) );     // Now we're ready to receive and process Windows messages.     BOOL bGotMsg;     MSG  msg;     PeekMessage( &msg, NULL, 0U, 0U, PM_NOREMOVE );     while( WM_QUIT != msg.message  )     {         // Use PeekMessage if the application is active so that we          // can use idle time to render the scene. If the application         // isn't active, use GetMessage to avoid eating CPU time.         if( m_bActive )             bGotMsg = PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE );         else             bGotMsg = GetMessage( &msg, NULL, 0U, 0U );         if( bGotMsg )         {             // Translate and dispatch the message.             if( 0 == TranslateAccelerator( m_hWnd, hAccel, &msg ) )             {                 TranslateMessage( &msg );                 DispatchMessage( &msg );             }         }     }     return msg.wParam; } 

The first call in this routine, made before entering the message-processing loop, is to LoadAccelerators. Here's the function declaration for LoadAccelators:

 HACCEL LoadAccelerators(     HINSTANCE hInstance,     LPCTSTR lpTableName ); 

ParameterDescription
hInstanceThe handle of the application instance
lpTableNameThe address of the table name string

This routine is used to load the application's accelerator table. In Windows, an accelerator table defines the shortcuts used to access the various menu options.

Figure 2-1 shows the Microsoft Visual C++ resource editor window presented when you're constructing a menu.

click to view at full size.

Figure 2-1 Building the application's menu

To add a new menu in Visual C++, select Resource from the Insert menu; the Insert Resource dialog box shown in Figure 2-2 appears. In the Insert Resource dialog box, select Menu from the Resource Type control and click the New button. You can name the menu whatever you want.

Figure 2-2 Inserting a menu resource into an application's menu

Figure 2-3 shows the Visual C++ resource editor displaying the accelerator table defined for the RoadRage application. You can add or modify these entries if you want to. In the right pane, the ID field displays the names of the menu items that can be activated, the Key field shows the keystroke sequence for the shortcut, and the Type field displays the key type (either ASCII or VIRTKEY).

click to view at full size.

Figure 2-3 Setting the menu's shortcuts

After you've created the menu, you need to define how to handle the messages that this menu and the various window objects generate. The message retrieval and dispatch loop, which acts on each event that occurs, is the heart of the program. When the system receives the WM_QUIT message, it terminates the program, with WinMain returning the value passed in the message's wParam parameter. If the application terminates before entering the message loop, it returns a value of FALSE.

Once you've defined the accelerator table, you can begin the message-handling loop, which consists of the following steps:

  1. Call PeekMessage to see whether a message was received. Or, if the application isn't active, call GetMessage to wait until a message arrives.
  2. If a message was received, retrieve it, translate it, and dispatch it using TranslateAccelerator, TranslateMessage, and DispatchMessage.

Let's look at each of these commands individually to make sure that you understand their usage and the reason they're executed in this sequence.

PeekMessage

The PeekMessage function is declared as follows:

 BOOL PeekMessage(     LPMSG lpMsg,     HWND hWnd,     UINT wMsgFilterMin,     UINT wMsgFilterMax,     UINT wRemoveMsg ); 

ParameterDescription
lpMsgA pointer from the message queue to the message's structure.
hWndThe handle to the window whose message you want.
wMsgFilterMinThe value of the first message in the range of messages you want to examine.
wMsgFilterMaxThe value of the last message in the range of messages you want to examine.
wRemoveMsgThe removal flag: PM_NOREMOVE indicates that the message shouldn't be removed after the call to PeekMessage, and PM_REMOVE indicates that it should be removed.

PeekMessage is used to acquire a message from the message queue. The one argument that might need a bit of extra explanation is the final wRemoveMsg parameter. For nearly every application, you'll use the PM_REMOVE flag, which requests that the message be removed once it's handled. This flag is used to make sure that once a message is handled, it's removed from the queue so that the next message can be received. You'd rarely choose not to have the handled message removed. However, imagine that you've written an application such that when the code determines that it can't immediately process a message, but expects to be able to process the message within a short period of time, the code doesn't remove the message from the queue and simply tries to process the message again later (in a few milliseconds). That code would need to use the PM_NOREMOVE flag. GetMessage is just like PeekMessage except that if no messages are in the queue the function waits until a message appears. The reasoning for using both functions in this way will become clear in later chapters.

TranslateAccelerator, TranslateMessage, and DispatchMessage

The TranslateAccelerator function declaration follows:

 int TranslateAccelerator(     HWND hWnd,     HACCEL hAccTable,     LPMSG lpMsg ); 

ParameterDescription
hWndThe window whose message will be translated
hAccTableThe handle of the accelerator table loaded with LoadAccelerators
lpMsgPointer to the MSG structure that holds the message acquired from the calling thread's message queue using PeekMessage (or GetMessage)

This function processes the accelerator keys for the menu commands. TranslateAccelerator translates a WM_KEYDOWN or WM_SYSKEYDOWN message to a WM_COMMAND or WM_SYSCOMMAND message if an entry in the accelerator table indicates that this action should be taken. TranslateAccelerator then passes this translated message to the appropriate window procedure.

The function declaration for TranslateMessage follows:

 BOOL TranslateMessage(     CONST MSG * lpMsg ); 

This function is used to translate the virtual-key messages into character messages. These character messages are then posted to the calling thread's message queue, which is read the next time the thread calls PeekMessage or GetMessage. If the message is translated (so that a character message is posted to the thread's message queue), the function's return value will be nonzero; if it isn't translated, the return value will be 0. If the message is WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWN, or WM_SYSKEYUP, the return value will be 0 no matter what the translation is. When TranslateAccelerator returns a nonzero value, indicating that the message passed to it in the lpMsg parameter was processed, your application shouldn't process the message again with TranslateMessage.

The DispatchMessage function has a single parameter, lpMsg, which is a pointer to the MSG structure that holds the message acquired from the calling thread's message queue.

 LONG DispatchMessage(     CONST MSG * lpMsg ); 

This function dispatches the translated message to the window procedure. The return value of this function is the value the window procedure returns.

Setting Up the Application's Window Procedure

When creating the WNDCLASS structure, you specify a pointer to a window procedure in the structure's lpfnWndProc member. This procedure, which is named WndProc in our application (Microsoft documents it as WindowProc), is an application-defined callback function that processes messages sent to the window. The WindowProc function is declared as follows:

 LRESULT CALLBACK WindowProc(     HWND hWnd,     UINT uMsg,     WPARAM wParam,     LPARAM lParam ); 

ParameterDescription
hWndThe handle to the window
uMsgThe message identifier
wParamThe first message parameter
lParamThe second message parameter

D3dapp.cpp has a short WndProc function. It simply calls into g_pD3DApp->MsgProc to handle the message:

 //------------------------------------------------------------------- // Name: WndProc // Desc: Static message handler that passes messages to the  //       application class //------------------------------------------------------------------- LRESULT CALLBACK WndProc( HWND hWnd,                            UINT uMsg,                            WPARAM wParam,                            LPARAM lParam ) {     if( g_pD3DApp )         return g_pD3DApp->MsgProc( hWnd, uMsg, wParam, lParam );     return DefWindowProc( hWnd, uMsg, wParam, lParam );  } 

Since g_pD3DApp is of type CD3DApplication, you might think that this code would call CD3DApplication::MsgProc. But CMyD3DApplication has overridden the virtual function MsgProc, so CMyD3DApplication::MsgProc (in roadrage.cpp) gets called instead. CMyD3DApplication::MsgProc handles a few messages that are particular to RoadRage and passes the rest to CD3DApplication::MsgProc, which handles messages that are common to all programs that use the Direct3D Framework. Here's CMyD3DApplication::MsgProc:

 LRESULT CMyD3DApplication::MsgProc( HWND hWnd, UINT uMsg,                                      WPARAM wParam, LPARAM lParam ) {     HMENU hMenu;     m_hWnd = hWnd;              hMenu = GetMenu( hWnd );     switch( uMsg )     {         case WM_COMMAND:             switch( LOWORD(wParam) )             {                 case MENU_ABOUT:                     DialogBox(hInstApp, MAKEINTRESOURCE(IDD_ABOUT),                                hWnd, (DLGPROC)AppAbout);                     break;                 case IDM_EXIT:                     SendMessage( hWnd, WM_CLOSE, 0, 0 );                     DestroyWindow( hWnd );                     PostQuitMessage(0);                     exit(0);                 default:                     return CD3DApplication::MsgProc( hWnd, uMsg,                                                      wParam, lParam );             }             break;         case WM_GETMINMAXINFO:             ((MINMAXINFO*)lParam)->ptMinTrackSize.x = 100;             ((MINMAXINFO*)lParam)->ptMinTrackSize.y = 100;             break;         case WM_CLOSE:             DestroyWindow( hWnd );             PostQuitMessage(0);             return 0;         default:             return CD3DApplication::MsgProc( hWnd, uMsg, wParam,                                              lParam );     }     return DefWindowProc( hWnd, uMsg, wParam, lParam ); } 

The CD3DApplication::MsgProc method is defined as shown here:

 //------------------------------------------------------------------- // Name: MsgProc // Desc: Message-handling function //------------------------------------------------------------------- LRESULT CD3DApplication::MsgProc( HWND hWnd, UINT uMsg,                                    WPARAM wParam, LPARAM lParam ) {     HRESULT hr;     switch( uMsg )     {         case WM_PAINT:             // Handle paint messages when the application isn't              // ready.             break;         case WM_MOVE:             // If in windowed mode, move the Direct3D Framework's              // window.             break;         case WM_SIZE:             // Check to see whether you're losing the window.             if( SIZE_MAXHIDE==wParam || SIZE_MINIMIZED==wParam )                 m_bActive = FALSE;             else                 m_bActive = TRUE;             // A new window size requires a new back-buffer size,             // so you must change the 3D structures accordingly.             break;         case WM_SETCURSOR:             // Prevent a cursor in full-screen mode.             break;         case WM_ENTERMENULOOP:             // Pause the application when menus are displayed.             Pause(TRUE);             break;         case WM_EXITMENULOOP:             Pause(FALSE);             break;         case WM_ENTERSIZEMOVE:             // Halt frame movement while the application is sizing              // or moving.             if( m_bFrameMoving )                 m_dwStopTime = timeGetTime();             break;         case WM_EXITSIZEMOVE:             if( m_bFrameMoving )                 m_dwBaseTime += timeGetTime() - m_dwStopTime;             break;         case WM_CONTEXTMENU:             // Handle the application's context menu (via a right              // mouse click).              TrackPopupMenuEx(                     GetSubMenu(                          LoadMenu( 0, MAKEINTRESOURCE(IDR_POPUP) ),                          0 ),                     TPM_VERTICAL, LOWORD(lParam), HIWORD(lParam),                      hWnd, NULL );             break;         case WM_NCHITTEST:             // Prevent the user from selecting the menu in              // full-screen mode.             break;         case WM_POWERBROADCAST:             switch( wParam )             {                 case PBT_APMQUERYSUSPEND:                     // At this point, the application should save any                     // data for open network connections, files, and                      // so on and prepare to go into suspended mode.                     return OnQuerySuspend( (DWORD)lParam );                 case PBT_APMRESUMESUSPEND:                     // At this point, the application should recover                      // any data, network connections, files, and so                      // on and resume running from the point at which                     // the application was suspended.                     return OnResumeSuspend( (DWORD)lParam );             }             break;         case WM_SYSCOMMAND:             // Prevent moving or sizing and power loss in             // full-screen mode.             switch( wParam )             {                 case SC_MOVE:                 case SC_SIZE:                 case SC_MAXIMIZE:                 case SC_MONITORPOWER:                     // If not in windowed mode, return 1.                     break;             }             break;         case WM_COMMAND:             switch( LOWORD(wParam) )             {                 case IDM_TOGGLESTART:                     // Toggle frame movement.                     break;                 case IDM_SINGLESTEP:                     // Single-step frame movement.                     break;                 case IDM_CHANGEDEVICE:                     // Display the device-selection dialog box.                     return 0;                 case IDM_TOGGLEFULLSCREEN:                     // Toggle between full-screen and windowed mode.                     return 0;                 case IDM_ABOUT:                     // Display the About dialog box.                     Pause(TRUE);                     DialogBox( (HINSTANCE)GetWindowLong( hWnd,                                                       GWL_HINSTANCE ),                                MAKEINTRESOURCE(IDD_ABOUT), hWnd,                                 AboutProc );                     Pause(FALSE);                     return 0;                 case IDM_EXIT:                     // Received key/menu command to exit application                     SendMessage( hWnd, WM_CLOSE, 0, 0 );                     return 0;             }             break;         case WM_GETMINMAXINFO:             ((MINMAXINFO*)lParam)->ptMinTrackSize.x = 100;             ((MINMAXINFO*)lParam)->ptMinTrackSize.y = 100;             break;         // Close the window.         case WM_CLOSE:             DestroyWindow( hWnd );             return 0;         // The WM_DESTROY message is sent to the window procedure         // of the window being destroyed after the window is removed         // from the screen.          //         // This message is sent first to the window being destroyed          // and then to the child windows (if any) as they are          // destroyed. You can assume that while  the message is being         // processed, all child windows still exist.          case WM_DESTROY:             // Clean up 3D environment stuff here.             PostQuitMessage(0);             return 0;     }     //     // The DefWindowProc function calls the default window procedure      // to provide default processing for any window messages that an      // application doesn't process. This function ensures that every      // message is processed. DefWindowProc is called with the same      // parameters received by the window procedure.      return DefWindowProc( hWnd, uMsg, wParam, lParam ); } 

The CD3DApplication::MsgProc arguments describe the window (hWnd), the message received (uMsg), and any parameters associated with the message (wParam and lParam). The messages that follow are a few of the messages that the MsgProc method handles:

  • WM_SIZE Sent to the window after its size has been changed.
  • WM_MOVE Sent to the window after the window has been moved.
  • WM_SETCURSOR Sent to the window if the mouse causes the cursor to move within the window and mouse input isn't captured.
  • WM_PAINT Sent to the application when Windows or another program requests that the application repaint a portion of its window. This message is sent when the UpdateWindow and RedrawWindow functions are called.
  • WM_CLOSE Sent to the application when the user closes the application's window. WndProc processes this message by calling the DestroyWindow function.
  • WM_ENTERMENULOOP and WM_EXITMENULOOP Received when the menu modal loop has been entered or exited (when the user moves the pointer into or out of the menu).
  • WM_ENTERSIZEMOVE and WM_EXITSIZEMOVE Received when the user needs to halt the movement of objects in the 3D world when the application is resizing or moving.
  • WM_COMMAND Sent when the user selects an item from the menu, when one of the controls sends a message to its parent window, or when a user's accelerator keystroke is translated. A nested switch statement checks for each menu item. For now, handle only the Exit and About menu options. When the user selects the About menu item, the window shown in Figure 2-4 is displayed. When the user selects the Exit option, a WM_CLOSE message is sent to the application to close it.

The last step in the CD3DApplication's MsgProc method is to perform default processing of any window message that the application doesn't process. MsgProc calls DefWindowProc (with the same parameters that MsgProc received) for this default processing.

click to view at full size.

Figure 2-4 The About dialog box

DestroyWindow

The final step is to destroy the window when the code is complete, which usually occurs when the user selects Escape from the menu. To destroy the window, the DestroyWindow command is called. This command has one parameter, hWnd, which is the handle to the window to be destroyed. The DestroyWindow command is defined as follows:

 BOOL DestroyWindow(     HWND hWnd ); 

DestroyWindow does what it sounds like: destroys the window you pass to it. It also sends a WM_DESTROY message to the window procedure, so your application can destroy any resources that depend on the window.



Inside Direct3D
Inside Direct3D (Dv-Mps Inside)
ISBN: 0735606137
EAN: 2147483647
Year: 1999
Pages: 131

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net