在创建DirectX应用程序之前我们要知道如何创建一个简单的Win32窗口程序. 在安装好 Microsoft Visual Studio .NET 2003 和 Microsoft DirectX 9.0c August 2006 后运行 Microsoft Visual Studio .NET 2003 建立一个Win32项目的空工程, 再新建一个C++源文件(建议名称使用Main.cpp或工程名.cpp), 我们以下的工作将在这个基础上实现. 要创建一个Win32窗口程序只需要以下几个步骤:

  1. 定义和注册一个窗口类, 描述我们想要创建的窗口类型.
  2. 创建窗口, 设定窗口风格.
  3. 创建消息循环更新窗口.
  4. 创建一个消息句柄或窗口处理器响应窗口消息.

以下为创建窗口程序的主要代码, 用于创建简单的Win32窗口, 你也可以扩展相应的功能. 详细使用说明见 MSDN .

//******************************************************************** 
//      Filename: Main.cpp 
//        Author: Chinafish 
//      Modifier: Chinafish 
//       Created: 2008-2-29 10:56 
//       Updated: 2008-2-29 10:56 
//            QQ: 149200849 
//           MSN: china_fish@msn.com     
//       Purpose: Create a sample window. 
//==================================================================== 
//  Copyright(C) 2004-2008 by Chinafish. All Rights Reserved. 
//******************************************************************** 
 
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include "windows.h"

#define WINDOW_TITLE "www.csinx.org - Creating A Sample Window (DX9+VC7)"
#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 600

//----------------------------------------------------------------------------- 
// GLOBALS 
//----------------------------------------------------------------------------- 
HWND g_hWnd = NULL;

//----------------------------------------------------------------------------- 
// PROTOTYPES 
//----------------------------------------------------------------------------- 
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nCmdShow);
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

//----------------------------------------------------------------------------- 
// Name: WinMain() 
// Desc: The application's entry point 
//----------------------------------------------------------------------------- 
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
    WNDCLASSEX winClass;
    MSG        uMsg;
    memset(&uMsg,0,sizeof(uMsg));
    
    winClass.lpszClassName = "CLASS_CREATE_WINDOW";
    winClass.cbSize        = sizeof(WNDCLASSEX);
    winClass.style         = CS_HREDRAW | CS_VREDRAW;
    winClass.lpfnWndProc   = WindowProc;
    winClass.hInstance     = hInstance;
    winClass.hIcon         = LoadIcon(hInstance, NULL);
    winClass.hIconSm       = LoadIcon(hInstance, NULL);
    winClass.hCursor       = LoadCursor(NULL, IDC_ARROW);
    winClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    winClass.lpszMenuName  = NULL;
    winClass.cbClsExtra    = 0;
    winClass.cbWndExtra    = 0; 
    
    if( !RegisterClassEx(&winClass) )
        return FALSE;
    
    g_hWnd = CreateWindowEx( NULL, "CLASS_CREATE_WINDOW",
        WINDOW_TITLE,
        WS_OVERLAPPEDWINDOW | WS_VISIBLE,CW_USEDEFAULT, CW_USEDEFAULT,
        WINDOW_WIDTH, WINDOW_HEIGHT, NULL, NULL,
        hInstance, NULL );
    
    if( g_hWnd == NULL )
        return FALSE;
    
    // Adjust Window
    RECT rect = { 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT };
    AdjustWindowRect( &rect, GetWindowLong( g_hWnd, GWL_STYLE ), FALSE );
    SetWindowPos( g_hWnd, 0, 0, 0, rect.right - rect.left, rect.bottom - rect.top,
        SWP_NOZORDER | SWP_NOMOVE );
    
    // Show Window 
    ShowWindow( g_hWnd, nCmdShow );
    UpdateWindow( g_hWnd );
    
    while( uMsg.message != WM_QUIT ) 
    {
        if( PeekMessage( &uMsg, NULL, 0, 0, PM_REMOVE ) ) 
        { 
            TranslateMessage( &uMsg );
            DispatchMessage( &uMsg );
        }
        else
        {
            // Render a frame
        }
    }
    UnregisterClass( "CLASS_CREATE_WINDOW", winClass.hInstance );
    return uMsg.wParam;
}

//----------------------------------------------------------------------------- 
// Name: WindowProc() 
// Desc: The window's message handler 
//----------------------------------------------------------------------------- 
LRESULT CALLBACK WindowProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
    switch( msg )
    {
    case WM_KEYDOWN:
        {
            switch( wParam )
            {
            case VK_ESCAPE:
                PostQuitMessage(0);
                break;
            }
        }
        break;
    case WM_CLOSE:
        {
            PostQuitMessage(0);
        }
    case WM_DESTROY:
        { 
            PostQuitMessage(0);
        }
        break;
    default:
        {
            return DefWindowProc( hWnd, msg, wParam, lParam );
        }
        break;
    }
    return 0;
}
				

在以上代码中 WinMain 函数是Win32窗口程序的入口点. 首先我们要定义一个窗口类填充 WNDCLASSEX 结构, 这个结构告诉系统 我们要创建的窗口属性, 详见 MSDN 说明. 要注册一个窗口类只要调用 RegisterClassEx 即可, 注册的窗口类将用于创建窗口. 我们可以通过 CreateWindowCreateWindowEx 创建一个窗口并指定 窗口风格 .

通常我们创建的窗口是包含标题, 菜单和窗口边框的大小, 实际上需要调整客户区为指定的大小. 可以通过 AdjustWindowRect 调节, 然后通过 SetWindowPos 更新窗口大小.

AdjustWindowRect

 

更新窗口大小后我们就可以通过调用 ShowWindowUpdateWindow 显示窗口.

然后消息循环将一直更新消息直到用户想退出程序. 为了更新效率我们将在消息队列中没有消息时才更新和渲染物体. 通过 PeekMessage 检查是否有消息在队列中, 如果有消息我们就调用 TranslateMessageDispatchMessage 转换虚拟键消息到字符消息并分配消息到窗口处理器.

窗口处理器 WindowProc 用于处理窗口收到的消息, 你可以定义为其他你想用的名称, 但是在 WNDCLASSEX 结构中的 lpfnWndProc 也要对应你定义的名称. 在窗口处理器中我们可能收到很多的消息, 在本例中我们只处理了 WM_KEYDOWN , WM_CLOSE , WM_DESTROY 消息并在收到 ESC 键按下, 窗口关闭消息和窗口销毁消息后通过调用 PostQuitMessage 通过应用程序关闭. 其他消息将通过调用 DefWindowProc 发送给系统处理.

下图为本例代码执行效果:

[返回目录][我有话说]