简明现代魔法 -> C/C++ -> 建立自己的程序窗口

建立自己的程序窗口

2010-08-08

MessageBox函数非常有用,但我们不能在消息框中显示图形,而且也不能在消息框中添加菜单。要添加这些对象,就需要建立自己的窗口,现在就开始。

建立窗口很简单,只需调用CreateWindow函数即可。

虽然建立窗口的函数的确名为CreateWindow,而且您也能在/Platform SDK/User Interface Services/Windowing/Windows/Window Reference/Window Functions找到此文件,但您将发现CreateWindow的第一个参数就是所谓的「窗口类别名称」,并且该窗口类别连接所谓的「窗口消息处理程序」。在我们调用CreateWindow之前,有一点背景知识会对您大有帮助。

总体结构

进行Windows程序设计,实际上是在进行一种面向对象的程序设计(OOP)。这一点在Windows中使用得最多的对象上表现最为明显。这种对象正是Windows之所以命名为「Windows」的原因,它具有人格化的特征,甚至可能会在您的梦中出现,这就是那个叫做「窗口」的东西。

桌面上最明显的窗口就是应用程序窗口。这些窗口含有显示程序名称的标题列、菜单甚至可能还有工具列和滚动条。另一类窗口是对话框,它可以有标题列也可以没有标题列。

装饰对话框表面的还有各式各样的按键、单选按钮、复选框、清单方块、滚动条和文字输入区域。其中每一个小的视觉对象都是一个窗口。更确切地说,这些都称为「子窗口」或「控件窗口」或「子窗口控件」。

作为对象,使用者会在屏幕上看到这些窗口,并通过键盘和鼠标直接与它们进行交互操作。更有趣的是,程序写作者的观点与使用者的观点极其类似。窗口以「消息」的形式接收窗口的输入,窗口也用消息与其它窗口通讯。对讯息的理解将是学习如何写作Windows程序所必须越过的障碍之一。

这有一个Windows的消息范例:我们知道,大多数的Windows程序都有大小合适的应用程序窗口。也就是说,您能够通过鼠标拖动窗口的边框来改变窗口的大小。通常,程序将通过改变窗口中的内容来响应这种大小的变化。您可能会猜测(并且您也是正确的),是Windows本身而不是应用程序在处理与使用者重新调整窗口大小相关的全部杂乱程序。由于应用程序能改变其显示的样子,所以它也「知道」窗口大小改变了。

应用程序是如何知道使用者改变了窗口的大小的呢?由于程序写作者习惯了往常的文字模式程序,操作系统没有设置将此类消息通知给使用者的机制。问题的关键在于理解Windows所使用的架构。当使用者改变窗口的大小时,Window给程序发送一个消息指出新窗口的大小。然后程序就可以调整窗口中的内容,以响应大小的变化。

「Windows给程序发送消息。」我们希望读者不要对这句话视而不见。它到底表达了什么意思呢?我们在这里讨论的是程序代码,而不是一个电子邮件系统。操作系统怎么给程序发送消息呢?

其实,所谓「Windows给程序发送消息」,是指Windows调用程序中的一个函数,该函数的参数描述了这个特定消息。这种位于Windows程序中的函数称为「窗口消息处理程序」。

无疑,读者对程序调用操作系统的做法是很熟悉的。例如,程序在打开磁盘文件时就要使用有关的系统调用。读者所不习惯的,可能是操作系统调用程序,而这正是Windows面向对象架构的基础。

程序建立的每一个窗口都有相关的窗口消息处理程序。这个窗口消息处理程序是一个函数,既可以在程序中,也可以在动态链接库中。Windows通过调用窗口消息处理程序来给窗口发送消息。窗口消息处理程序根据此消息进行处理,然后将控制传回给Windows。

更确切地说,窗口通常是在「窗口类别」的基础上建立的。窗口类别标识了处理窗口消息的窗口消息处理程序。使用窗口类别使多个窗口能够属于同一个窗口类别,并使用同一个窗口消息处理程序。例如,所有Windows程序中的所有按钮均依据同一个窗口类别。这个窗口类别与一个处理所有按钮消息的窗口消息处理程序(位于Windows的动态链接库中)联结。

在面向对象的程序设计中,对象是程序与数据的组合。窗口是一种对象,其程序是窗口消息处理程序。数据是窗口消息处理程序保存的信息和Windows为每个窗口以及系统中那个窗口类别保存的信息。

窗口消息处理程序处理给窗口发送消息。这些消息经常是告知窗口,使用者正使用键盘或者鼠标进行输入。这正是按键窗口知道它被「按下」的奥妙所在。在窗口大小改变,或者窗口表面需要重画时,由其它消息通知窗口。

Windows程序开始执行后,Windows为该程序建立一个「消息队列」。这个消息队列用来存放该程序可能建立的各种不同窗口的消息。程序中有一小段程序代码,叫做「消息循环」,用来从队列中取出消息,并且将它们发送给相应的窗口消息处理程序。有些消息直接发送给窗口消息处理程序,不用放入消息队列中。

如果您对这段Windows架构过于简略的描述将信将疑,就让我们去看看在实际的程序中,窗口、窗口类别、窗口消息处理程序、消息队列、消息循环和窗口消息是如何相互配合的。这或许会对您有些帮助。

编写自己的窗口

建立一个窗口首先需要注册一个窗口类别,那需要一个窗口消息处理程序来处理窗口消息。处理窗口消息对每个Windows程序都带来了些负担。下面程序中整个做的事情差不多就是料理这些事情。

/*------------------------------------------------------------
   CreateWindow.C -- Displays "Welcome to www.nowamagic.net" in client area
                 (c) Gonn, www.nowamagic.net, 2010
  ------------------------------------------------------------*/

#include <windows.h>
#include "StdAfx.h"

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

/* main函数 */
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int iCmdShow)
{
     static TCHAR szAppName[] = TEXT ("HelloWin") ;
	 /* 系统调用WndProc函数时是认为有hwnd参数的 */
     HWND         hwnd ;
     MSG          msg ;
     WNDCLASS     wndclass ;

     wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
	 /* wndClass.lpfnWndProc = WndProc;只是让系统知道消息函数的地址在哪里 */
     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))
     {
          MessageBox (NULL, TEXT ("This program requires Windows NT!"), 
                      szAppName, MB_ICONERROR) ;
          return 0 ;
     }
     
	 /* CreateWindow之前是没有消息的,所以不会调用WndProc函数 */
     hwnd = CreateWindow (szAppName,                  // window class name
                          TEXT ("欢迎来到简明现代魔法"), // 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); 之前是不会调用WndProc的 */
          DispatchMessage (&msg) ;
     }
     return msg.wParam ;
}

/* WndProc这个函数就是告诉系统,接收到消息后就这个函数来处理 */
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 (hwnd, &rect) ;
          
		  /* 参数:设备描述表句柄、将要绘制的字符串、字符串的长度、指向矩形结构RECT的指针、正文的绘制选项 */
          DrawText (hdc, TEXT ("Welcome to www.nowamagic.net"), -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) ;
}

程序建立一个普通的应用程序窗口,在窗口显示区域的中央显示「Welcome to www.nowamagic.net」。

Create Windows

如果您认真思考一下,将会发现虽然只有80行程序代码,这个窗口却令人惊讶地具有许多功能。您可以用鼠标按住标题列,在屏幕上移动窗口;可以按住大小边框,改变窗口的大小。在窗口大小改变时,程序自动地将「Hello, Windows 98!」字符串重新定位在显示区域的中央。您可以按最大化按钮,放大HELLOWIN以充满整个屏幕;也可以按最小化按钮,将程序缩小成一个图示。您可以在系统菜单中执行所有选项(就是按下在标题列最左端的小图示);也可以从系统菜单中选择 Close选项,或者单击标题列最右端的关闭按钮,或者双击标题列最左端的图标,来关闭窗口以终止程序的执行。

随机文章推荐
网站分类


注:如需转载本文,请注明出处(原文链接),谢谢。更多精彩内容,请进入简明现代魔法首页。

进入新博客
喜欢本文,就分享它吧
给我留言
您的名字:
您的邮件:
您的网站:


 

copyright © 2009 简明现代魔法    学习、分享、进步

power by Gonn 感谢所有关心和支持本站的朋友们