创建你的第一个带有窗口的Windows程序

WPF Window窗口详细使用教程 WPF(Windows Presentation Foundation)是微软推出的基于 .NET 框架的 UI 框架,用于构建桌面应用程序。Window 是 WPF 应用程序中最基本的容器,用来创建和显示用户界面。 阅读详情

今天和大家说下如何创建一个带有窗口的程序,注意窗口的程序是以后写Windows程序的基础,今天的程序很重要,一定要看懂再去看以后的Windows程序

 

#include <windows.h>

 

/*

1.窗口消息处理程序WndProc

2.在32位的Windows中,WPARAM被定义为一个UINT,

而LPARAM被定义为一个LONG(这就是C中的long整数型态),

因此窗口消息处理程序的这两个参数都是32位的值

3.WndProc函数传回一个型态为LRESULT的值,

该值简单地被定义为一个LONG。

WinMain函数被指定了一个WINAPI型态(在表头文件中定义的所有Windows函数都被指定这种型态),

而WndProc函数被指定一个CALLBACK型态。

这两个标识符都被定义为_stdcall,

表示在Windows本身和使用者的应用程序之间发生的函数呼叫的呼叫参数传递方式

*/

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

 

int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,PSTR szCmdLine,int iCmdShow)

{

   

    static TCHAR szAppName[]=TEXT("Hello Window");

 

    HWND hwnd;                  //窗口句柄

 

    MSG msg;                    //消息结构

 

/*

MSG 的结构,查看的MSDN文档

typedef struct tagMSG {     // msg  
HWND   hwnd;       //决定消息是发给哪个窗口的,句柄用于标识窗口
UINT   message;    //定义消息的编号
    WPARAM wParam;     //由消息自身决定
    LPARAM lParam;     //由消息自身决定
    DWORD  time;       //决定消息发送的时间
    POINT  pt;         //记录消息发送时鼠标的位置
} MSG;

 

*/

 

    WNDCLASS wndclass;          //窗口类别结构

 

/*

WNDCLASS的结构,参考MSDN文档

 

typedef struct _WNDCLASSEX {

    UINT    cbSize;            //决定WNDCLASS的大小,便于系统获得结构

    UINT    style;             //决定样式,样式可以用|符号,复合多种

    WNDPROC lpfnWndProc;       //指向窗口处理函数的指针      

    int     cbClsExtra;        //

    int     cbWndExtra;

    HANDLE  hInstance;

    HICON   hIcon;             //窗口的ICON句柄

    HCURSOR hCursor;           //窗口的HCURSOR句柄  

    HBRUSH  hbrBackground;     //窗口的背景画刷句柄

    LPCTSTR lpszMenuName;      //窗口的菜单字符串的指针

    LPCTSTR lpszClassName;     //窗口类的指针

    HICON   hIconSm;           //窗口最小化时的ICON句柄

} WNDCLASSEX;

*/

 

/*

由于每个标识符都可以在一个复合值中设置一个位的值,

所以按这种方式定义的标识符通常称为「位旗标」。

通常我们只使用少数的窗口类别样式。

HELLOWIN中用到的这两个标识符表示

,所有依据此类别建立的窗口,

每当窗口的水平方向大小(CS_HREDRAW)或者垂直方向大小(CS_VREDRAW)改变之后,

窗口要完全重画。改变HELLOWIN的窗口大小,可以看到字符串仍然显示在窗口的中央,

这两个标识符确保了这一点。

不久我们就将看到窗口消息处理程序是如何得知这种窗口大小的变化的。   

*/

 

    wndclass.style = CS_HREDRAW | CS_VREDRAW ;

 

 

/*

这条叙述将这个窗口类别的窗口消息处理程序设定为WndProc,

即HELLOWIN.C中的第二个函数。

这个过程将处理依据这个窗口类别建立的所有窗口的全部消息。

在C语言中,像这样在结构中使用函数名时,真正提供的是指向函数的指针 

*/ 

 

    wndclass.lpfnWndProc = WndProc;

 

//下面两个字段用于在窗口类别结构和Windows内部保存的窗口结构中预留一些额外空间

 

    wndclass.cbClsExtra = 0;

 

    wndclass.cbWndExtra = 0;

 

//下一个字段就是程序的执行实体句柄(它也是WinMain的参数之一)

 

    wndclass.hInstance = hInstance;

 

 

    wndclass.hIcon = LoadIcon(NULL,IDI_APPLICATION);                  //图标句柄

 

    wndclass.hCursor = LoadCursor(NULL,IDC_ARROW);                    //鼠标光标句柄

 

/*

下一个字段指定依据这个类别建立的窗口背景颜色。

hbrBackground字段名称中的hbr前缀代表「handle to a brush(画刷句柄)」。

画刷是个绘图词汇,指用来填充一个区域的着色样式。

Windows有几个标准画刷,也称为「备用(stock)」画刷。

这里所示的GetStockObject呼叫将传回一个白色画刷的句柄  

*/

    wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);     //画刷句柄

 

//下一个字段指定窗口类别菜单。HElLOWIN没有应用程序菜单,所以该字段被设定为NULL

 

    wndclass.lpszMenuName=NULL;

 

//最后,必须给出一个类别名称。对于小程序,类别名称可以与程序名相同,即存放在szAppName变量中的「HelloWin」字符串

 

    wndclass.lpszClassName=szAppName;

 

/*

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

不同窗口可以依照同一种窗口类别建立。例如,Windows中的所有按钮窗口-包括按键、复选框,以及单选按钮-都是依据同一种窗口类别建立的。

窗口类别定义了窗口消息处理程序和依据此类别建立的窗口的其它特征。在建立窗口时,要定义一些该窗口所独有的特征。

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

此结构包括两个指向字符串的字段

*/

 

    if(!RegisterClass(&wndclass))

    {

        MessageBox(NULL,TEXT("This Programme requires Windows NT!!"),szAppName, MB_ICONERROR);

        return 0;

    }

 

/*

窗口类别定义了窗口的一般特征,因此可以使用同一窗口类别建立许多不同的窗口。

实际呼叫CreateWindow建立窗口时,可能指定有关窗口的更详细的信息。

Windows程序设计新手有时会混淆窗口类别和窗口之间的区别,

以及为什么一个窗口的所有特征不能被一次设定好。

实际上,以这种方式分开这些样式信息是非常方便的。

例如,所有的按钮窗口都可以依据同样的窗口类别来建立,与这个窗口类别相关的窗口消息处理程序位于Windows内部。

由窗口类别来负责处理按钮的键盘和鼠标输入,并定义按钮在屏幕上的外观形象。从这一点看来,所有的按钮都是以同样的方式工作的。

但是并非所有的按钮都是一样的。它们可以有不同的大小,不同的屏幕位置,以及不同的字符串。

后面的这样一些特征是窗口定义的一部分,而不是窗口类别定义的

 

*/

 

/*

传递给RegisterClass函数的信息会在一个数据结构中设定好,

而传递给CreateWindow函数的信息会在函数单独的参数中设定好。

下面是HELLOWIN.C中的CreateWindows呼叫,

每一个字段都做了完整的说明

*/

    hwnd=CreateWindow(szAppName,                //窗口类名称

 

        TEXT("The Hello Programme"),            //窗口标题

 

        WS_OVERLAPPEDWINDOW,                    //窗口样式         

 

        CW_USEDEFAULT,                          //窗口左上角x坐标 

 

        CW_USEDEFAULT,                          //窗口左上角y坐标

 

        CW_USEDEFAULT,                          //x方向窗口的大小

 

        CW_USEDEFAULT,                          //y方向窗口的大小

 

        NULL,                                   //父窗口句柄

 

        NULL,                                   //窗口菜单句柄

 

        hInstance,                              //执行体(自身)句柄     

 

        NULL);

 

/*

1.在CreateWindow呼叫传回之后,

Windows内部已经建立了这个窗口。

这就是说,Windows已经配置了一块内存,

用来保存在CreateWindow呼叫中指定窗口的全部信息跟一些其它信息,

而Windows稍后就是依据窗口句柄找到这些信息的   

 

2.第一个参数是刚刚用CreateWindow建立的窗口句柄。

第二个参数是作为参数传给WinMain的iCmdShow。

它确定最初如何在屏幕上显示窗口,是一般大小、最小化还是最大化。

在开始菜单中安装程序时,使用者可能做出最佳选择。如果窗口按一般大小显示,那么WinMain接收到后传递给ShowWindow的就是SW_SHOWNORMAL﹔

如果窗口是最大化显示的,则为SW_SHOWMAXIMIZED。而如果窗口只显示在工作列上,则是SW_SHOWMINNOACTIVE。

ShowWindow函数在显示器上显示窗口。

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

函数呼叫

 

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

后面,我们将说明WndProc如何处理这个消息

*/

 

    ShowWindow(hwnd,iCmdShow);

 

    UpdateWindow(hwnd);

 

 

/*

1.消息循环以GetMessage呼叫开始,它从消息队列中取出一个消息

 

2.这一呼叫传给Windows一个指针,指向名为msg的MSG结构。第二、第三和第四个参数设定为NULL或者0,表示程序接收它自己建立的所有窗口的所有消息。

Windows用从消息队列中取出的下一个消息来填充消息结构的各个字段,结构的各个字段包括:

 

hwnd 接收消息的窗口句柄。在HELLOWIN程序中,这一参数与CreateWindow传回的hwnd值相同,因为这是该程序拥有的唯一窗口。

 

message 消息标识符。这是一个数值,用以标识消息。对于每个消息,均有一个对应的标识符,这些标识符定义于Windows表头文件(其中大多数在WINUSER.H中),以前缀WM(「window message」,窗口消息)开头。例如,使用者将鼠标光标放在HELLOWIN显示区域之内,并按下鼠标左按钮,Windows就在消息队列中放入一个消息,该消息的message字段等于WM_LBUTTONDOWN。这是一个常数,其值为0x0201。

 

wParam 一个32位的「message parameter(消息参数)」,其含义和数值根据消息的不同而不同。

 

lParam 一个32位的消息参数,其值与消息有关。

 

time 消息放入消息队列中的时间。

 

pt 消息放入消息队列时的鼠标坐标。

 

只要从消息队列中取出消息的message字段不为WM_QUIT(其值为0x0012),GetMessage就传回一个非零值。WM_QUIT消息将导致GetMessage传回0

   

*/

    while(GetMessage(&msg,NULL,0,0))

    {

        //将msg结构传给Windows,进行一些键盘转换

 

        TranslateMessage(&msg);

 

        //又将msg结构回传给Windows

        DispatchMessage(&msg);

   

    }

    return msg.wParam;

 

 

   

}

 

/*

1.以上我们所讨论的都是必要的负担:注册窗口类别,建立窗口,然后在屏幕上显示窗口,程序进入消息循环,然后不断从消息队列中取出消息来处理。

实际的动作发生在窗口消息处理程序中。窗口消息处理程序确定了在窗口的显示区域中显示些什么以及窗口怎样响应使用者输入。

在HELLOWIN中,窗口消息处理程序是命名为WndProc的函数。窗口消息处理程序可任意命名(只要求不和其它名字发生冲突)。

一个Windows程序可以包含多个窗口消息处理程序。一个窗口消息处理程序总是与呼叫RegisterClass注册的特定窗口类别相关联。

CreateWindow函数根据特定窗口类别建立一个窗口。但依据一个窗口类别,可以建立多个窗口

 

2.窗口消息处理程序的四个参数与MSG结构的前四个字段是相同的。第一个参数hwnd是接收消息的窗口的句柄,它与CreateWindow函数的传回值相同。

对于与HELLOWIN相似的程序(只建立一个窗口),这个参数是程序所知道的唯一窗口句柄。

如果程序是依据同一窗口类别(同时也是同一窗口消息处理程序)建立多个窗口,则hwnd标识接收消息的特定窗口。

第二个参数与MSG结构中的message字段相同,它是标识消息的数值。

最后两个参数都是32位的消息参数,提供关于消息的更多信息。

这些参数包含每个消息型态的详细信息。有时消息参数是两个存放在一起的16位值,而有时消息参数又是一个指向字符串或数据结构的指针。

程序通常不直接呼叫窗口消息处理程序,窗口消息处理程序通常由Windows本身呼叫。

通过呼叫SendMessage函数,程序能够直接呼叫它自己的窗口消息处理程序。我们将在后面的章节讨论SendMessage函数

   

*/

 

 

LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam, LPARAM lParam)

{

    HDC hdc;                            //设备内容句柄

 

    PAINTSTRUCT ps;                     //绘图结构

 

/*

PAINTSTRUCT 结构的定义
typedef struct tagPAINTSTRUCT {
   HDC  hdc;                   //标识显式上下文
   BOOL fErase;                //背景是否重画
  RECT rcPaint;               //涂色矩形, 是BeginPaint返回的设备上下文句柄,有了从//BeginPaint获取的设备上下文句柄,就可以也只能在ps指出的//rcPaint的矩形内绘图,EndPaint调用使这一区域有效
   BOOL fRestore;              //系统保留的变量
   BOOL fIncUpdate;            //系统保留的变量
   BYTE rgbReserved[16];       //系统保留的变量
} PAINTSTRUCT;

 

 

*/

 

    RECT rect;                          //矩形结构

 

    switch(message)

    {

/*

窗口消息处理程序接收的第一个消息-也是WndProc选择处理的第一个消息-是WM_CREATE。

当Windows在WinMain中处理CreateWindow函数时,WndProc接收这个消息。

就是说,在HELLOWIN呼叫CreateWindow时,Windows将做一些它必须做的工作。

在这些工作中,Windows呼叫WndProc,将第一个参数设定为窗口句柄,第二个参数设定为WM_CREATE。

WndProc处理WM_CREATE消息并将控制传回给Windows。

Windows然后可以从CreateWindow呼叫中传回到HELLOWIN中,继续在WinMain中进行下一步的处理。

通常,窗口消息处理程序在WM_CREATE处理期间进行一次窗口初始化。

HELLOWIN对这个消息的处理中播放一个名为HELLOWIN.WAV的声音文件。

它使用简单的PlaySound函数来做到这一点。

该函数说明在/Platform SDK/Graphics and Multimedia Services/Multimedia Audio/Waveform Audio中,

而文件在/Platform SDK/Graphics and Multimedia Services/Multimedia Reference/Multimedia Functions中。

PlaySound的第一个参数是声音文件的名称(它也可能是在Control Panel的Sounds中定义的一种声音的别名,或者是一个程序资源)。

第二个参数只有当声音文件是一种资源时才被使用。第三个参数指定一些选项。在这个例子中,我指定第一个参数是一个文件名,

并且异步地播放声音,即PlaySound函数呼叫在声音文件开始播放时立即传回,而不会等待它的完成。

在这种方法下,程序能够继续初始化。

WndProc通过从窗口消息处理程序中传回0,结束了整个WM_CREATE的处理

 

*/

 

    case WM_CREATE:

        PlaySound(TEXT("Windows XP 启动"),NULL,SND_FILENAME | SND_ASYNC);

        return 0;

 

/*

1.WndProc处理的第二个消息为WM_PAINT。这个消息在Windows程序设计中是很重要的。

当窗口显示区域的一部分显示内容或者全部变为「无效」,以致于必须「更新画面」时,将由这个消息通知程序。

显示区域的显示内容怎么会变得无效呢?在最初建立窗口的时候,整个显示区域都是无效的,因为程序还没有在窗口上画什么东西。

第一条WM_PAINT消息(通常发生在WinMain中呼叫UpdateWindow时)指示窗口消息处理程序在显示区域上画一些东西。

在使用者改变HELLOWIN窗口的大小后,显示区域的显示内容重新变得无效。

读者应该还记得,HELLOWIN中wndclass结构的style字段设定为标志CS_HREDRAW和CS_VREDRAW,这样的格式设定指示Windows,

在窗口大小改变后,就把整个窗口显示内容当成无效。

然后,窗口消息处理程序将收到一条WM_PAINT消息。

当使用者将HELLOWIN最小化,然后再次将窗口恢复为以前的大小时,Windows将不会保存显示区域的内容。

在图形环境下,窗口显示区域涉及的数据量很大。因此,Windows令窗口无效,窗口消息处理程序接收一条WM_PAINT消息,

并自动恢复其窗口的内容。

在移动窗口以致其相互重迭时,Windows不保存一个窗口中被另一个窗口所遮盖的内容。

在这一部分不再被遮盖之后,它就被标志为无效。

窗口消息处理程序接收到一条WM_PAINT消息,以更新窗口的内容。

对WM_PAINT的处理几乎总是从一个BeginPaint呼叫开始

 

2.在这两个呼叫中,第一个参数都是程序的窗口句柄,第二个参数是指向型态为PAINTSTRUCT的结构指针。

PAINTSTRUCT结构中包含一些窗口消息处理程序,可以用来更新显示区域的内容。

我们将在下一章中讨论该结构的各个字段。现在我们只在BeginPaint和EndPaint函数中用到它。

在BeginPaint呼叫中,如果显示区域的背景还未被删除,则由Windows来删除。

它使用注册窗口类别的WNDCLASS结构的hbrBackground字段中指定的画刷来删除背景。

在HELLOWIN中,这是一个白色备用画刷。

这意味着,Windows将通过把窗口背景设定为白色来删除窗口背景。

BeginPaint呼叫令整个显示区域有效,并传回一个「设备内容句柄」。

设备内容是指实体输出设备(如视讯显示器)及其设备驱动程序。

在窗口的显示区域显示文字和图形需要设备内容句柄。

但是从BeginPaint传回的设备内容句柄不能在显示区域之外绘图,读者可以试一试。EndPaint释放设备内容句柄,使之不再有效。

如果窗口消息处理程序不处理WM_PAINT消息(这是很罕见的),它们必须被传送给DefWindowProc。

DefWindowProc只是依次呼叫BeginPaint和EndPaint,以使显示区域有效。

呼叫完BeginPaint之后,WndProc接着呼叫GetClientRect

 

3.DrawText可以输出文字(正如其名字所表明的一样)。由于该函数要输出文字,

第一个参数是从BeginPaint传回的设备内容句柄,第二个参数是要输出的文字,第三个参数是 -1,指示字符串是以字节0终结的。

DrawText最后一个参数是一系列位旗标,它们均在WINUSER.H中定义(虽然由于其显示输出的效果,使得DrawText像一个GDI函数呼叫,但它确实因为相当高级的画图功能而成为User模块的一部分。

此函数在/Platform SDK/Graphics and Multimedia Services/GDI/Fonts and Text中说明)。旗标指示了文字必须显示在一行上,水平方向和垂直方向都位于第四个参数指定的矩形中央。

因此,这个函数呼叫将让字符串「Hello, Windows 98!」显示在显示区域的中央。

一旦显示区域变得无效(正如在改变大小时所发生的情况一样),

WndProc就接收到一个新的WM_PAINT消息。

WndProc通过呼叫GetClientRect取得变化后的窗口大小,并在新窗口的中央显示文字

 

*/

 

    case WM_PAINT:

        hdc=BeginPaint(hwnd,&ps);

 

        GetClientRect(hwnd,&rect);

 

        DrawText(hdc,TEXT("Hello ,Windows XP!!"),-1,&rect,

            DT_SINGLELINE | DT_CENTER | DT_VCENTER);

 

        EndPaint(hwnd,&ps);

 

        return 0;

   

    case WM_DESTROY:

        PostQuitMessage(0);

        return 0;

 

    }

/*

呼叫DefWindowProc来为窗口消息处理程序不予处理的所有消息提供内定处理,这是很重要的。

不然一般动作,如终止程序,将不会正常执行。 

*/

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

 

}

 

/*

前缀指示该常数所属的类别

        CS      窗口类别样式

        CW      建立窗口

        DT      绘制文字

        IDI     图示ID

        IDC     游标ID

        MB      消息框

        SND     声音

        WM      窗口消息

        WS      窗口样式

*/

 

 

/*

经常用到的变量前缀

       c        char或WCHAR或TCHAR

       by       BYTE (无正负号字符)

       n        short

       i        int

       x, y     int分别用作x坐标和y坐标

       cx, cy   int分别用作x长度和y长度;C代表「计数器」

       b或f     BOOL (int);f代表「旗标」

       w        WORD (无正负号短整数)

       l        LONG (长整数)

       dw       DWORD (无正负号长整数)

       fn       function(函数)

       s        string(字符串)

       sz       以字节值0结尾的字符串

       h        句柄

       p        指针

*/

 

/*

Windows 程序设计的难点

 

即使有了对HELLOWIN的说明,读者对程序的结构和原理可能仍然觉得神秘。

在为传统环境编写简单的C程序时,整个程序可能包含在main函数中。

而在HELLOWIN中,WinMain只包含了注册窗口类别,建立窗口,从消息队列中取出消息和发送消息所必须的程序代码。

程序的所有实际动作均在窗口消息处理程序中发生。

在HELLOWIN中,这些动作不多,WndProc只是简单地播放了一个声音文件并在窗口中显示一个字符串。

但是在后面的章节中,读者将发现,Windows程序所作的一切,都是响应发送给窗口消息处理程序的消息。

这是概念上的主要难点之一,在开始写作Windows程序之前,必须先搞清楚。

 

 

别呼叫我,我会呼叫您

 

 

前面我们提到过,程序写作者已经熟悉了使用操作系统呼叫的做法。例如,C程序写作者使用fopen函数打开文件。

fopen函数最终通过呼叫操作系统来打开文件,这一点问题也没有。

但是Windows不同,尽管Windows有1000个以上的函数可供程序呼叫,

但Windows也呼叫使用者程序,比如前面定义的窗口消息处理程序WndProc。

窗口消息处理程序与窗口类别相关,窗口类别是程序呼叫RegisterClass注册的。

依据该类别建立的窗口使用这个窗口消息处理程序来处理窗口的所有消息。

Windows通过呼叫窗口消息处理程序对窗口发送消息。

 

在第一次建立窗口时,Windows呼叫WndProc。在窗口关闭时,Windows也呼叫WndProc。

窗口改变大小、移动或者变成图示时,从菜单中选择某一项目、挪动滚动条、按下鼠标按钮或者从键盘输入字符时,

以及窗口显示区域必须被更新时,Windows都要呼叫WndProc。

所有这些WndProc呼叫都以消息的形式进行。在大多数Windows程序中,程序的主要部分都用来处理消息。

Windows可以发送给窗口消息处理程序的消息通常都以WM开头的名字标识,并且都在WINUSER.H表头文件中定义。

 

实际上,从程序外呼叫程序内的例程这一种做法,在传统的程序设计中并非前所未闻。

C中的signal函数可以拦截Ctrl-C中断或操作系统的其它中断。

为MS-DOS编写的老程序中经常有拦截硬件中断的程序代码。

但在Windows中,这种概念扩展为包括一切事件。窗口中发生的一切都以消息的形式传给窗口消息处理程序。

然后,窗口消息处理程序以某种方式响应这个消息,或者将消息传给DefWindowProc,进行内定处理。

在HELLOWIN中,窗口消息处理程序的wParam和lParam参数除了作为传递给DefWindowProc的参数外,不再有其它用处。

这些参数给出了关于消息的其它信息,参数的含义与具体消息相关。

让我们来看一个例子。一旦窗口的显示区域大小发生了改变,Windows就呼叫窗口的窗口消息处理程序。

窗口消息处理程序的hwnd参数是改变大小的窗口的句柄(请记住,一个窗口消息处理程序能处理依据同一个窗口类别建立的多个窗口的消息。

参数hwnd让窗口消息处理程序知道是哪个窗口在接收消息)。

参数message是WM_SIZE。消息WM_SIZE的参数wParam的值是SIZE_RESTORED、SIZE_MINIMIZED、SIZE_MAXIMIZED、SIZE_MAXSHOW或SIZE_MAXHIDE (在WINUSER.H表头文件中分别定义为数字0到4)。

也就是说,参数wParam表明窗口是非最小化还是非最大化,是最小化、最大化,还是隐藏。

lParam参数包含了新窗口的大小,新宽度和新高度均为16位值,合在一起成为32位的lParam。WINDEF.H中提供了帮助程序写作者从lParam中取出这两个值的宏,我们将在下一章说明这个宏。

有时候,DefWindowProc处理完消息后会产生其它的消息。例如,假设使用者执行HELLOWIN,并且使用者最终单击了 Close按钮,

或者假设用键盘或鼠标从系统菜单中选择了 Close, DefWindowProc处理这一键盘或者鼠标输入,在检测到使用者选择了Close选项之后,

它给窗口消息处理程序发送一条WM_SYSCOMMAND消息。

WndProc将这个消息传给DefWindowProc。DefWindowProc给窗口消息处理程序发送一条WM_CLOSE消息来响应之。

WndProc再次将它传给DefWindowProc。DestroyWindow呼叫DestroyWindow来响应这条WM_CLOSE消息。

DestroyWindow导致Windows给窗口消息处理程序发送一条WM_DESTROY消息。

WndProc再呼叫PostQuitMessage,将一条WM_QUIT消息放入消息队列中,以此来响应此消息。

这个消息导致WinMain中的消息循环终止,然后程序结束。

 

 

队列化消息与非队列化消息

 

 

我们已经谈到过,Windows给窗口发送消息,这意味着Windows呼叫窗口消息处理程序。

 

但是,Windows程序也有一个消息循环,它呼叫GetMessage从消息队列中取出消息,

并且呼叫DispatchMessage将消息发送给窗口消息处理程序。

 

那么,Windows程序是依次等待消息(类似于普通程序中相同的键盘输入),

然后将消息送到某地方去的吗?或者,它是直接从程序外面接收消息的吗?实际上,两种情况都存在。

 

  消息能够被分为「队列化的」和「非队列化的」。

队列化的消息是由Windows放入程序消息队列中的。在程序的消息循环中,重新传回并分配给窗口消息处理程序。

非队列化的消息在Windows呼叫窗口时直接送给窗口消息处理程序

。也就是说,队列化的消息被「发送」给消息队列,而非队列化的消息则「发送」给窗口消息处理程序。

任何情况下,窗口消息处理程序都将获得窗口所有的消息--包括队列化的和非队列化的。窗口消息处理程序是窗口的「消息中心」。

队列化消息基本上是使用者输入的结果,以击键(如WM_KEYDOWN和WM_KEYUP消息)、击键产生的字符(WM_CHAR)、鼠标移动(WM_MOUSEMOVE)和鼠标按钮(WM_LBUTTONDOWN)的形式给出。

队列化消息还包含时钟消息(WM_TIMER)、更新消息(WM_PAINT)和退出消息(WM_QUIT)。

 

非队列化消息则是其它消息。在许多情况下,非队列化消息来自呼叫特定的Windows函数。

例如,当WinMain呼叫CreateWindow时,Windows将建立窗口并在处理中给窗口消息处理程序发送一个WM_CREATE消息。

当WinMain呼叫ShowWindow时,Windows将给窗口消息处理程序发送WM_SIZE和WM_SHOWWINDOW消息。

当WinMain呼叫UpdateWindow时,Windows将给窗口消息处理程序发送WM_PAINT消息。

键盘或鼠标输入时发出的队列化消息信号,也能在非队列化消息中出现。

例如,用键盘或鼠标选择了一个菜单项时,键盘或鼠标消息就是队列化的,而说明菜单项已选中的WM_COMMAND消息则可能就是非队列化的。

这一过程显然很复杂,但幸运的是,其中的大部分是由Windows解决的,不关我们的程序的事。

从窗口消息处理程序的角度来看,这些消息是以一种有序的、同步的方式进出的。窗口消息处理程序可以处理它们,也可以不处理。

当我说消息是以一种有序的同步的方式进出时,我是说首先消息与硬件的中断不同。

在一个窗口消息处理程序中处理消息时,程序不会被其它消息突然中断。

虽然Windows程序可以多线程执行,但每个执行绪的消息队列只为窗口消息处理程序在该执行绪中执行的窗口处理消息。

换句话说,消息循环和窗口消息处理程序不是并发执行的。

当一个消息循环从其消息队列中接收一个消息,然后呼叫DispatchMessage将消息发送给窗口消息处理程序时,

直到窗口消息处理程序将控制传回给Windows,DispatchMessage才能结束执行。

当然,窗口消息处理程序能呼叫给窗口消息处理程序发送另一个消息的函数。

这时,窗口消息处理程序必须在函数呼叫传回之前完成对第二个消息的处理。

那时窗口消息处理程序将处理最初的消息。例如,当窗口过程调用UpdateWindow时,Windows将呼叫窗口消息处理程序来处理WM_PAINT消息。

窗口消息处理程序处理WM_PAINT消息结束以后,UpdateWindow呼叫将把控制传回给窗口消息处理程序。

这也就是说窗口消息处理程序必须是可重入。在大多数情况下,这不会带来问题,但是程序写作者应该意识到这一点。

例如,假设您在窗口消息处理程序中处理一个消息时设置了一个静态变量,然后呼叫了一个Windows函数。

在这个函数传回时,您还能保证那个变量的值还是原来那个吗?难说--很可能您呼叫的Windows函数产生了另外一个消息,

并且窗口消息处理程序在处理这个消息时改变了该变量的值。

这也是在编译Windows程序时,有些编译最佳化选项必须关闭的原因之一。

在许多情况下,窗口消息处理程序必须保存它从消息中取得的信息,并在处理另一个消息时使用这些信息。

这些信息可以储存在窗口的静态(static)变量或整体变量中。

当然,读者将在下面几章对此有一个更清楚的了解,因为窗口消息处理程序将处理更多的消息。

 

 

行动迅速

 

 

Windows 98和Windows NT都是优先权式的多任务环境。这意味着当一个程序在进行一项长时间工作时,

Windows可以允许使用者将控制切换到另一个程序中。这是一件好事,也是现在的Windows优越于以前16位Windows的地方。

然而,由于Windows设计的方式,这种优先权式多任务并不总是以您希望的样子工作。

例如,假设您的程序花费一分钟左右来处理某一个消息。是的,使用者可以将控制切换到另一个程序,但是却无法对您的程序进行任何动作。

使用者无法移动您的程序窗口、缩放它、最小化、关闭它、什么都不能做。这是因为您的窗口消息处理程序正忙于进行一项长时间的作业。

表面上并不是窗口消息处理程序在执行它自己的移动和缩放操作,但实际上确实是它在做。这就是DefWindowProc部分的工作,它必须被考虑为您的窗口消息处理程序的一部分。

如果您的程序在处理某些消息时需要长时间的作业的话,可以选择我在第二十章里描述的那些方法来做得更有优雅一些。即使是在优先权式多任务环境中,也不应该让您的程序呆在屏幕上一动不动。

这会让使用者讨厌的,他们会认为您的程序中有bug、不标准的动作,说明文件没写好。最好让使用者觉得程序只停了一下子就把全部消息中快速料理完了

 

 

*/

 

VC++创建一个Windows窗口程序 本文为《VC++深入详解》第一章的笔记 创建一个完整的Windows窗口程序主要步骤为 WinMain函数的定义创建一个窗口编写消息循环编写窗口过程函数 窗口的创建: 消息循环: 在创建、显示、更新窗口之后,我们需要编写一个消息循环,不断从消息队列中取出消息,并进行响应。 GetMessage(),Peekmessage()从消息队列中取出消息,并保存在消息结构体中 阅读详情

相关推荐

C++调用API初步创建Windows窗口程序

首先在进行介绍前,先介绍一下api,个人理解,api是在windows.h中提供的一些封装好的函数。 创建一个窗口程序的一般步骤是先注册一个窗口类名,然后再创建一个窗口,传递信息进行处理(窗口的操作都是通过信息传递来实现的) 下面介绍几个要用到api函数 RegistetClass()  //该函数只有一个参数,是一个指向WNDCLASS结构的指针 //该函数用来注册一个窗口类 WNDC

Dang_boy的博客 8096

Windows --- 第一个Windows窗口程序

#include<Windows.h> //窗口程序的入口函数:WinMain //控制台窗口应用程序入口函数:main

weixin_60569662的博客 2096

Windows 窗口程序结构详解

文章目录引言一、一些基础知识的介绍二、Windows程序的结构三、第一个窗口程序的流程四、代码实现五、运行平台 引言 众所周知,我们只要使用Windows的操作系统,必然离不开Windows的界面程序,无论是用什么语言(c 、c++、汇编、或是易语言),什么编译器,只要是带图形界面的PE文件,运行时必然带有Windows子系统,也就是说,遵循Windows界面程序的结构。 那么,我们如果掌握了这...

peng1874的博客 2062

Windows程序设计-文本输出(二)

在Windows程序设计-文本输出一文中,给出了显示当前Windows硬件参数的一些文本信息,然而,在这个示例显示程序,有很大一个缺陷,除非有一个大屏幕跟高分辨率的显示卡,否则就无法看到系统尺度列表的最后几行。如果窗口太窄,甚至根本看不到值,下面我们来研究一下解决这些文本现实问题的办法。 1.显示区域的大小        我们通常使用GetClientRect函数来取得显示区域的大小,

dby3579的专栏 1134

第一个windows API窗口程序

步骤编写WinMain函数设计窗口类注册窗口类创建窗口显示并更新窗口编写消息循环编写窗口过程函数总结整体源码第一次写Windows窗口程序,第一次写博客,遇到很多问题。 编写WinMain函数 WinMian函数格式固定,可以直接粘贴复制 int WINAPI WinMain(HINSTANCE hInstance, //应用程序实例 HINSTANCE hPrevInstance, //上一个应用程序实例,不用 LPSTR lpCmdLine, //命令行参数 int nCmdShow //窗口显示

xianyudewo的博客 791

Windows程序设计14:Win32窗口程序的创建流程

Windows程序设计14:Win32窗口程序的创建流程。

qq_59940419的博客 1285

windows编程(1)-第一个窗口程序

windows编程学习 - 第一个窗口程序的创建,注册 创建 显示 刷新 消息循环 消息处理 完整代码

ylh的博客 874

win32-注册窗口类、创建窗口

win32-注册窗口类、创建窗口

Jackey_Song的博客 2453

Win32 程序开发:创建一个应用程序窗口

1)简单介绍创建应用程序的步骤 1.设计一个窗口类 2.注册这个窗口类 3.创建应用程序窗口 4.更新显示窗口 5.应用程序消息循环 2)下面根据这个步骤进行创建一个应用程序窗口吧(编译器为VS2017) /* 头文件 */ #include &lt;windows.h&gt; /* 全局变量 */ WCHAR g_lpszClassName[] = L"CLASSNAM...

南雨兮 4280

Windows应用程序窗口创建过程.rar

了解窗口的创建过程以及步骤 1、编写WinMain函数 2、设计窗口类 3、注册窗口类 4、创建窗口 5、显示及更新 6、编写消息循环 7、编写窗口过程函数

ADS1299寄存器配置与应用实战指南

本文提供了ADS1299高精度生物电信号采集芯片的寄存器配置与应用实战指南。详细解析了采样率、增益、转换模式等核心寄存器的设置方法,并分享了数据读取、高级功能配置及常见问题的调试心得,帮助开发者快速掌握ADS1299的配置技巧,实现稳定的ECG/EEG信号采集。

weixin_28278377的博客 194

Windows 程序设计:创建第一个窗口

超详细的Windows窗口创建教程!只要你有C语言基础就能轻松学会!文章末尾附带完整的Windows窗口创建代码!

先写一个BUG吧 1万+

ChatGPT Plus账号被降智?3个实用检测方法+终极解决方案(附国内支付指南)

本文针对ChatGPT Plus账号性能下降问题,提供了3个科学检测方法和系统级解决方案。通过详细分析症状、专业诊断技巧和优化策略,帮助用户有效应对GPT-4o等高级功能失效、响应质量下降等问题,特别包含国内用户关心的支付方案和账号管理建议。

weixin_42639633的博客 469

Windows编程基础,第一个Windows程序,注册窗口,创建窗口

Win32编程,指的是用Window提供的API(Application Programming Interface)为Window编写应用程序。Windows编程,大家也通常称为win32编程,或win32 SDK编程,其含义是针对32位Windows操作系统。 在这一章节中,我们会带大家认识Windows应用程序,编写第一个Windows程序,带大家学习注册窗口和创建窗口。

WdIg-2023的博客 1726

Linux下基于AHCI controller模块实现SATA Platform驱动附代码详细流程

本文主要是针对ARM A55集成了AHSATA controller模块开发sata驱动,通过Platform的形式在linux识别到SSD,本文详细讲述整个开发过程,同时对SATA Platform驱动进行讲解。1、配置SATA Phy的初始化;2、打开SATA Platform相关编译开关;3、配置设备树;4、linux启动识盘。

Luckiers的博客 2497

人工智能学习之机器人路径规划优化

A* AStar 人工智能 机器人路径规划优化

RongLin02的博客 7073

[Windows开发] 用C++创建第一个窗口程序

Win32 API(也称为Windows API)是Windows比较原生态的C++ API。 #ifndef UNICODE #define UNICODE #endif #include <windows.h> LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR

藏知阁 4916

避坑指南:RK3588上编译EtherCAT 1.6时遇到的权限问题和模块安装错误

本文详细解析了在RK3588平台上编译EtherCAT 1.6时常见的权限问题和模块安装错误,提供了两种实战解决方案和嵌入式开发的通用权限管理策略。通过优化内核配置和编译参数,显著提升实时性能,帮助开发者高效完成EtherCAT主站部署。

weixin_33724046的博客 453

【Windows编程】windows窗口创建过程详解

搞windows开发,必须熟悉vs自己的开发工具,这会提高你的开发效率;我用的是vs2013;感觉不错;

呋喃吖的博客 6484

一文读懂 CST 时/频域求解器

本文系统介绍了CST Studio Suite中的关键网格技术——六面体网格、四面体网格以及TLM求解器专用的八叉树非均匀网格技术。重点阐述了六面体网格的三项核心技术:理想边界拟合(PBA)技术可精确处理曲面边界,薄片技术(TST)能高效模拟薄层结构,自适应八叉树网格实现计算资源最优分配。同时详细对比了六面体网格与四面体网格的特点及适用场景,前者适用于系统级EMC/EMI分析等时域问题,后者擅长处理复杂曲面和异质材料的频域问题。

一只豌豆象的博客 463

华为OD机试2025C卷-双十一最大花费金额[100分](Java_Python3_C++_C语言_JsNode_Go)实现100%通过率

双十一最大花费金额 - 动态规划解法 摘要:本文介绍了华为OD机试中"双十一最大花费金额"问题的动态规划解法。该问题要求在给定N件商品价格和优惠券面额X的情况下,选择若干商品使总花费不超过X且尽可能大。核心解法采用布尔型动态规划数组,记录每个金额是否可被拼出。算法时间复杂度为O(N*X),空间复杂度为O(X)。通过倒序遍历避免重复选择同一商品,最后从X向下查找第一个可达金额作为答案。文中提供了C++和Java的实现代码,并解释了与经典0-1背包问题的关系。 关键点: 问题本质是0-1背包/子集和问题的变体

2503_91057718的博客 46

第一个C++窗口程序(Windows应用程序)

第一个C++窗口程序 //第一个C++窗口程序 //2020.10.15 #include<Windows.h> #include<tchar.h> static TCHAR szWindowClass[] = _T("WindowClass"); static TCHAR szTitle[] = _T("窗口程序"); //第五步:窗口函数中处理窗口消息 LRESULT CALLBACK WindowProc(HWND hWnd,UINT uMsg,WPARAM wParam,

学Java的LC的博客 2103

OSError We couldn‘t connect to https://huggingface.co to load file, couldn‘t find it chatglm3-6b解决方案

本文主要介绍了OSError: We couldn’t connect to https://huggingface.co to load this file, couldn’t find it in the cached files and it looks like THUDM/chatglm3-6b is not the path to a directory containing a file named config.json.解决方案,希望能对学习python的同学们有所帮助。

weixin_43178406的博客 5万+

一个简单的windows窗口程序

一个简单的windows窗口程序创建一个windows窗口程序的步骤具体实现代码实现效果 创建一个windows窗口程序的步骤 定义窗口类 注册窗口类 创建窗口 更新显示窗口 建立消息循环 处理消息 具体实现代码 #include "framework.h" #include "第一个windows窗口程序.h" #include <windows.h> #define MAX_LOADSTRING 100 // 全局变量: HINSTANCE hInst;

CHL的博客 6907

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

创建你的第一个Windows程序

快来创建你的第一个Windows程序吧!

因为热爱,所以坚持。 1289

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

Windows程序设计--创建第一个窗口

本篇文章讲如何创建一个基本的窗口。MessageBox弹出来的消息框就是一个窗口,但是窗口是怎样创建的呢?其实很简单,只要几个固定的步骤就行。而只要我们的窗口创建出来之后,只要把需要的函数填进去就能得到一个有功能的窗口。所以我觉得学习Windows就是学习API函数而已,懂得如何调用,各个参数的意思就行。说到参数,其类型都是从C语言的基本数据类型演变而来的,并不难。现在开始创建第一个窗口,该窗口并无

_ACME_的博客 1643

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

上一篇: 编写你的第一个Windows程序
下一篇: 在窗口显示文字和绘制图形
Cpp_Java_Man
博客等级 码龄21年 26粉丝 67原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值