Windows程序设计:第一个窗口


目录:
1.窗口程序
2.分析
      注册窗口类别
      建立窗口
      显示窗口
      消息循环
      窗口消息处理程序  
3.注意事项  


窗口程序:

#include 
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
 
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int iCmdShow)
{
     static TCHAR szAppName[] = TEXT ("HelloWin") ;
     HWND         hwnd ;          //窗口句柄
      MSG          msg ;           //消息结构
      WNDCLASS     wndclass ;      //窗口类结构
      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 = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
     wndclass.lpszMenuName  = NULL ;
     wndclass.lpszClassName = szAppName ;
 
     //如果注册窗口失败,弹出错误对话框
      if (!RegisterClass (&wndclass))
     {
          //在Windows 98中,大多数Unicode函数无法执行,MessageBoxW是个例外
            MessageBox (NULL, TEXT ("This program requires Windows NT!"), 
                      szAppName, MB_ICONERROR) ;     
          return 0 ;
     }
 
     //建立窗口
      hwnd = CreateWindow (szAppName,                  // window class name
                          TEXT ("The Hello Program"), // window caption
                          WS_OVERLAPPEDWINDOW,        // window style
                          CW_USEDEFAULT,              // initial x position
                          CW_USEDEFAULT,              // initial y position
                          CW_USEDEFAULT,              // initial x size
                          CW_USEDEFAULT,              // initial y size
                          NULL,                       // parent window handle
                          NULL,                       // window menu handle
                          hInstance,                  // program instance handle
                          NULL) ;                     // creation parameters
   
    ShowWindow (hwnd, iCmdShow) ;  //显示窗口
     UpdateWindow (hwnd) ;          //重画显示区域
     //消息循环,用于从消息队列中取出消息,并做相应处理
     while (GetMessage (&msg, NULL, 0, 0))
     {
          TranslateMessage (&msg) ;
          DispatchMessage (&msg) ;
     }
     return msg.wParam ;
}
 
//窗口消息处理程序
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
     HDC         hdc ;
     PAINTSTRUCT ps ;
     RECT        rect ;
 
     switch (message)
     {
     case WM_CREATE:
          PlaySound (TEXT ("hellowin.wav"), NULL, SND_FILENAME | SND_ASYNC) ;
          return 0 ;
 
     case WM_PAINT:
          hdc = BeginPaint (hwnd, &ps) ; 
          //GetClientRect函数检索一个窗口的客户区坐标rect          
          GetClientRect (hwnd, &rect) ;        
          DrawText (hdc, TEXT ("Hello, Windows Vista!"), -1, &rect,
                    DT_SINGLELINE | DT_CENTER | DT_VCENTER) ;
          EndPaint (hwnd, &ps) ;
          return 0 ;
 
     case WM_DESTROY:
          PostQuitMessage (0) ;
          return 0 ;
     }
     return DefWindowProc(hwnd, message, wParam, lParam);
}

运行程序,将会出现一个窗口并发出响声,窗口中央有一行文字。如下图:
Windows程序设计:第一个窗口_第1张图片

分析:

1.注册窗口类别

窗口依照某一窗口类别建立,窗口类别用以标识处理窗口消息的窗口消息处理程序。

在为程序建立窗口之前,必须首先呼叫RegisterClass注册一个窗口类别。该函数只需要一个参数,即一个指向型态为WNDCLASS的结构指针。

WNDCLASS是一个窗口类结构:

typedef struct
{
     UINT        style ;                                   //窗口类别样式
     WNDPROC     lpfnWndProc ;                //设定消息处理程序
     int         cbClsExtra ;                            //用于在窗口类别结构和Windows内部保存的窗口结构中预留一些额外空间
     int         cbWndExtra ;                         //同上
     HINSTANCE   hInstance ;                    //程序的执行实体句柄
     HICON       hIcon ;                              //为所有依据这个窗口类别建立的窗口设置一个图标
     HCURSOR     hCursor ;                       //设置鼠标光标
     HBRUSH      hbrBackground ;             //指定依据这个类别建立的窗口背景颜色
     LPCTSTR     lpszMenuName ;             //指定窗口类别菜单
     LPCTSTR     lpszClassName ;             //必须给出一个类别名称
}
WNDCLASS, * PWNDCLASS ;

在WinMain中为WNDCLASS定义一个结构,通常像这样:

WNDCLASS wndclass;

然后,你就可以初始化该结构的10个字段,并呼叫RegisterClass。

在初始化该结构的10个字段后,HELLOWIN呼叫RegisterClass来注册这个窗口类别。该函数只有一个参数,即指向WNDCLASS结构的指针。

2.建立窗口

窗口类别定义了窗口的一般特征,因此可以使用同一窗口类别建立许多不同的窗口。呼叫CreateWindow建立窗口时,可以指定有关窗口的更详细的信息。

下面是HELLOWIN.C中的CreateWindows呼叫,每一个字段都做了完整的说明:

hwnd = CreateWindow (szAppName,                  // window class name
                     TEXT ("The Hello Program"), // window caption
                     WS_OVERLAPPEDWINDOW,        // window style
                     CW_USEDEFAULT,              // initial x position
                     CW_USEDEFAULT,              // initial y position
                     CW_USEDEFAULT,              // initial x size
                     CW_USEDEFAULT,              // initial y size
                     NULL,                       // parent window handle
                     NULL,                       // window menu handle
                     hInstance,                  // program instance handle
                     NULL) ;                     // creation parameters

此程序建立的窗口是一个普通的重迭式窗口。它含有一个标题列,标题列左边有一个系统菜单按钮,标题列右边有缩小、放大和关闭图示,四周还有一个表示窗口大小的边框。这是标准样式的窗口,名为WS_OVERLAPPEDWINDOW,出现在CreateWindow的「窗口样式」参数中。此样式是几种位旗标的组合:

#define WS_OVERLAPPEDWINDOW (WS_OVERLAPPED     | /
                             WS_CAPTION        | /
                             WS_SYSMENU        | /
                             WS_THICKFRAME     | /
                             WS_MINIMIZEBOX    | /
                             WS_MAXIMIZEBOX)

「窗口标题」是显示在标题列中的文字。

注释着「initial x position」和「initial y position」的参数指定了窗口左上角相对于屏幕左上角的初始位置。由于这些参数使用CW_USEDEFAULT标识符,指示Windows使用重迭窗口的内定位置。(CW_USEDEFAULT定义为0x80000000。)内定情况下,Windows依次对新建立的窗口定位,使各窗口左上角的垂直和水平距离在屏幕上按一定的大小递增。与此类似,注释着「initial x size」和「initial y size」的参数分别指定窗口的宽度和高度。同样使用了CW_USEDEFAULT标识符,表明希望Windows使用内定尺寸。

在建立一个「最上层」窗口,如应用程序窗口时,注释为「父窗口句柄」的参数设定为NULL。通常,如果窗口之间存在有父子关系,则子窗口总是出现在父窗口的上面。应用程序窗口出现在桌面窗口的上面,但不必为呼叫CreateWindow而找出桌面窗口的句柄。

因为窗口没有菜单,所以「窗口菜单句柄」也设定为NULL。「程序执行实体句柄」设定为执行实体句柄,它是作为WinMain的参数传递给这个程序的。最后,「建立参数」指标设定为NULL,可以用这个参数存取稍后程序中可能引用到的数据。

CreateWindow传回被建立的窗口的句柄,该句柄存放在变量hwnd中,后者被定义为HWND型态(「窗口句柄型态」)。Windows中的每个窗口都有一个句柄,程序用句柄来使用窗口。许多Windows函数需要使用hwnd作为参数,这样,Windows才能知道函数是针对哪个窗口的。如果一个程序建立了许多窗口,则每个窗口均有一个句柄。窗口句柄是Windows程序所处理最重要的句柄之一。

3.显示窗口

在CreateWindow呼叫传回之后,Windows内部已经建立了这个窗口。这就是说,Windows已经配置了一块内存,用来保存在CreateWindow呼叫中指定窗口的全部信息跟一些其它信息,而Windows稍后就是依据窗口句柄找到这些信息的。

然而,光是这样子,窗口并不会出现在视讯显示器上。您还需要两个函数呼叫,一个是:

ShowWindow (hwnd, iCmdShow);

第一个参数是刚刚用CreateWindow建立的窗口句柄。第二个参数是作为参数传给WinMain的iCmdShow。它确定最初如何在屏幕上显示窗口,是一般大小、最小化还是最大化。在开始菜单中安装程序时,使用者可能做出最佳选择。如果窗口按一般大小显示,那么WinMain接收到后传递给ShowWindow的就是SW_SHOWNORMAL﹔如果窗口是最大化显示的,则为SW_SHOWMAXIMIZED。而如果窗口只显示在工作列上,则是SW_SHOWMINNOACTIVE。

ShowWindow函数在显示器上显示窗口。如果ShowWindow的第二个参数是SW_SHOWNORMAL,则窗口的显示区域就会被窗口类别中定义的背景画刷所覆盖。函数呼叫:

UpdateWindow (hwnd);

这个函数会重画显示区域。它经由发送给窗口消息处理程序(即HELLOWIN.C中的WndProc函数)一个WM_PAINT消息做到这一点。

4.消息循环

呼叫UpdateWindow之后,窗口就出现在视讯显示器上。程序现在必须准备读入使用者用键盘和鼠标输入的数据。Windows为当前执行的每个Windows程序维护一个「消息队列」。在发生输入事件之后,Windows将事件转换为一个「消息」并将消息放入程序的消息队列中。

程序通过执行一块称之为「消息循环」的程序代码从消息队列中取出消息:
while (GetMessage (&msg, NULL, 0, 0)) 
{
     TranslateMessage (&msg) ;      
     DispatchMessage (&msg) ;
}
msg变量是型态为MSG的结构,型态MSG在WINUSER.H中定义如下:
typedef struct tagMSG
{
     HWND   hwnd ;                //接收消息的窗口句柄
     UINT   message ;             //消息标识符。这是一个数值,用以标识消息。
     WPARAM wParam ;          //一个32位的「message parameter(消息参数)」
     LPARAM lParam ;             //一个32位的消息参数,其值与消息有关
     DWORD  time ;                //消息放入消息队列中的时间
     POINT  pt ;                      //消息放入消息队列时的鼠标坐标
}
MSG, * PMSG ;
POINT数据型态也是一个结构,它在WINDEF.H中定义如下:
typedef struct tagPOINT
{
     LONG  x ;
     LONG  y ;
}
POINT, * PPOINT;

消息循环以GetMessage呼叫开始,它从消息队列中取出一个消息。第二、第三和第四个参数设定为NULL或者0,表示程序接收它自己建立的所有窗口的所有消息。
只要从消息队列中取出消息的message字段不为WM_QUIT(其值为0x0012),GetMessage就传回一个非零值。WM_QUIT消息将导致GetMessage传回0。

TranslateMessage将msg结构传给Windows,进行一些键盘转换。
DispatchMessage又将msg结构回传给Windows。然后,Windows将该消息发送给适当的窗口消息处理程序,让它进行处理。这也就是说,Windows将呼叫窗口消息处理程序。

5.窗口消息处理程序

以上我们所讨论的都是必要的负担:注册窗口类别,建立窗口,然后在屏幕上显示窗口,程序进入消息循环,然后不断从消息队列中取出消息来处理。实际的动作发生在窗口消息处理程序中。窗口消息处理程序确定了在窗口的显示区域中显示些什么以及窗口怎样响应使用者输入。

在此程序中,窗口消息处理程序是命名为WndProc的函数。窗口消息处理程序可任意命名(只要求不和其它名字发生冲突)。一个Windows程序可以包含多个窗口消息处理程序。一个窗口消息处理程序总是与呼叫RegisterClass注册的特定窗口类别相关联。CreateWindow函数根据特定窗口类别建立一个窗口。但依据一个窗口类别,可以建立多个窗口。

//窗口消息处理程序总是定义为如下形式
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 
{ 
     HDC         hdc ; 
     PAINTSTRUCT ps ; 
     RECT        rect ; 
 
     //在此程序中,WndProc只选择处理三种消息:WM_CREATE、WM_PAINT和WM_DESTROY。窗口消息处理程序的结构如下:
     switch (message) 
     { 
     case WM_CREATE: 
          //播放声音文件
            PlaySound (TEXT ("hellowin.wav"), NULL, SND_FILENAME | SND_ASYNC) ; 
          return 0 ; 
 
     case WM_PAINT: 
         //对WM_PAINT的处理几乎总是从一个BeginPaint呼叫开始,而以一个EndPaint呼叫结束,BeginPaint使显示区域变为有效。如果不呼叫BeginPaint和EndPaint(或者ValidateRect),则Windows不会使该区域变为有效。相反,Windows将发送另一个WM_PAINT消息,且一直发送下去。
           hdc = BeginPaint (hwnd, &ps) ;    
         //GetClientRect函数检索一个窗口的客户区坐标rect          
         GetClientRect (hwnd, &rect) ;      
         //DrawText可以输出文字。第一个参数是从BeginPaint传回的设备内容句柄;第二个参数是要输出的文字;第三个参数是-1,指示字符串是以字节终结的;DrawText最后一个参数是一系列位旗标,旗标指示了文字必须显示在一行上,水平方向和垂直方向都位于第四个参数指定的矩形中央。 
          DrawText (hdc, TEXT ("Hello, Windows Vista!"), -1, &rect, 
                    DT_SINGLELINE | DT_CENTER | DT_VCENTER) ; 
         EndPaint (hwnd, &ps) ;                
         return 0 ; 
 
     case WM_DESTROY: 
         //通过呼叫PostQuitMessage以标准方式响应WM_DESTROY消息
          PostQuitMessage (0) ; 
         return 0 ; 
     } 
     //呼叫DefWindowProc来为窗口消息处理程序不予处理的所有消息提供内定处理,这是很重要的。不然一般动作,如终止程序,将不会正常执行。
     return DefWindowProc(hwnd, message, wParam, lParam); 
}

程序通常不直接呼叫窗口消息处理程序,窗口消息处理程序通常由Windows本身呼叫。通过呼叫SendMessage函数,程序能够直接呼叫它自己的窗口消息处理程序。

3.注意事项

该程序使用了多媒体功能呼叫需要添加Windows多媒体链接库文件WINMM.LIB。可以在项目—>属性—>配置属性—>链接器—>输入—>附加依赖项里输入WINMM.LIB即可。

你可能感兴趣的:(Windows)