2.2 窗口的创建

本书使用C语言编写Windows程序,因此需要搭建C语言开发环境,使用Visual Studio作为C语言的开发工具。

本节必须掌握的知识点:

        第8练:Windows程序模型

        第9练:注册窗口类

        第10练:创建、显示和更新窗口

        第11练:消息循环

        第12练:窗口过程

        第13练:处理WM_PAINT消息

        第14练:处理WM_DESTROY消息

        第15练:处理WM_CLOSE消息

        第16练:处理WM_SIZE消息

        第17练:处理WM_CHAR消息

        第18练:处理WM_LBUTTONDOWN消息

2.2.1 第8练:Windows程序模型

/*------------------------------------------------------------------------

 008 编程达人win32 API每日一练

     第8个例子HELLOWIN.C:Windows程序模型

 (c) www.bcdaren.com 编程达人

-----------------------------------------------------------------------*/

#include <windows.h>

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; //窗口过程-消息回调函数

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,

    PSTR szCmdLine, int iCmdShow) //PSTR非宽字符串指针char*

{

     static TCHAR szAppName[] = TEXT ("HelloWin") ; // 窗口类名

     HWND hwnd ;    //窗口句柄

     MSG msg ;      //消息 结构变量-F1

     WNDCLASS wndclass ; //窗口类结构变量

     //第一步:初始化窗口类

wndclass.style = CS_HREDRAW | CS_VREDRAW ; //窗口类风格-水平和垂直标识符-被

//改变时该类所有窗口重绘

     wndclass.lpfnWndProc = WndProc ; //窗口处理函数---函数指针

     wndclass.cbClsExtra = 0 ; //窗口扩展,cb表示字节数

     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("注册失败!"),szAppName, MB_ICONERROR);

          return 0;

      }

     //第三步:创建窗口

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

           TEXT ("创建窗口"),   // 窗口标题

           WS_OVERLAPPEDWINDOW, // 窗口样式

           CW_USEDEFAULT, // 窗口的初始水平位置

           CW_USEDEFAULT, // 窗口的初始垂直位置

           CW_USEDEFAULT, // 窗口的宽度(以设备单位为单位)

           CW_USEDEFAULT, // 窗口的高度(以设备单位为单位)

           NULL, // 父窗口句柄

           NULL, // 窗口菜单句柄

           hInstance,   // 程序实例句柄

           NULL) ;      // 创建参数

    //第四步显示和更新窗口

     ShowWindow (hwnd, iCmdShow) ; //显示窗口

     UpdateWindow (hwnd) ; //更新窗口

    //第五步:消息循环

     while (GetMessage (&msg, NULL, 0, 0)) //消息队列中获取消息,message为

//WM_QUIT则返回0,退出消息循环

      {

//将msg结构返还windows,并将虚拟键消息转换为字符消息

          TranslateMessage(&msg);

//再次将msg结构返还windows,分发一个消息给窗口程序

          DispatchMessage(&msg);

      }

//msg.wParam 来自一条表示退出的消息,返回这个值给系统,从而退出

     return msg.wParam ;

}

//窗口过程

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

{

     HDC hdc ; //外部环境句柄(设备上下文句柄)

     PAINTSTRUCT ps ; //用于绘制该应用程序拥有的窗口的客户区

     RECT rect ; //RECT结构通过其左上角和右下角的坐标定义一个矩形

     switch (message)

     {

     case WM_CREATE: //创建窗口消息         

           return 0 ;

     case WM_PAINT: //重绘窗口消息

           hdc = BeginPaint (hwnd, &ps);//开始画图(获取设备环境)-本例为显示器

           GetClientRect (hwnd, &rect) ; //该函数获取窗口客户区的大小

           DrawText (hdc, TEXT ("Hello, Windows!"), -1, &rect, //-1表示零字符串

                DT_SINGLELINE | DT_CENTER | DT_VCENTER) ; //绘制格式化:在指定

//的矩形文本,水平垂直居中单行显示

           EndPaint (hwnd, &ps) ;//标记指定窗口绘图的结束

           return 0 ; 

     case WM_DESTROY: //退出消息循环

           PostQuitMessage (0) ; //向系统指示线程已请求终止(退出)

           return 0 ;

     }

//调用操作系统默认窗口过程为应用程序未处理的任何窗口消息提供默认处理

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

}

       运行结果:【注】图标、标题和系统菜单都位于标题栏中,标题栏以下区域为客户区。

图2-1 创建窗口

       在HELLOWIN.C程序的功能是绘制一个自定义的窗口,并在窗口客户区输出文本字符串“Hello,Windows!”。

程序分为两个部分:主程序WinMain和窗口过程WndProc。

   ■主程序

   ●Winmain函数

Winmain函数是Windows窗口程序的入口函数,而控制台程序的入口函数是main函数。当创建项目时,选择的是桌面应用程序,因此“项目属性->链接器->系统->子系统”选项默认为“窗口”,如图。当然如果该选项设置为“未设置”,链接器会根据函数名“Winmain”来判断是窗口程序。

2-2 VS窗口子系统选项

       WinMain函数原型:

       int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,

PSTR szCmdLine,int iCmdShow);

    也可以是:

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,

                    LPSTR lpCmdLine, int nCmdShow);

       函数返回值类型为:int类型。返回值为0表示正常结束,非0为非正常结束。

       函数调用约定为:WINAPI,即stdcall调用约定。

       参数1 HINSTANCE hInstance为当前进程句柄,创建并初始化进程时由操作系统给定。

       参数2 HINSTANCE hPrevInstance:前一个进程句柄,这是Win16系统遗留的参数。Win16系统为16位单任务实模式操作系统,该参数用于判断前一个进程是否已经结束,只有前一个进程结束,才可以创建当前进程。

       参数3 PSTR szCmdLine:是一个长指针,指向一个NULL结尾的字符串,这个字符串中包含了命令行参数。

       参数4 int iCmdShow:一个整数值,决定主窗口如何显示。它可以是如下一些常量:

SW_HIDE:隐藏窗口,并激活另一个窗口。

SW_MAXIMIZE:最大化窗口,并激活它。

SW_MINIMIZE:最小化窗口,并激活下一个顶级窗口。

SW_RESTORE:激活并显示窗口。如果窗口是最小化或最大化,Windows会恢复其原始大小和位置。

SW_SHOW:在窗口的当前大小和位置下展示并激活它。

SW_SHOWDEFAULT:根据启动应用程序时传递给CreateProcess函数的STARTUPINFO结构体的wShowWindowfield来设置窗口的首次显示状态。

SW_SHOWMAXIMIZED:最大化窗口,并激活它。

SW_SHOWMINIMIZED:最小化窗口,并激活它。

SW_SHOWMINNOACTIVE:最小化窗口,但不激活它。

SW_SHOWNA:以窗口的当前状态显示窗口,但不把它激活。

SW_SHOWNOACTIVATE:以最近的大小和位置显示窗口,但不把它激活。

SW_SHOWNORMAL:和 SW_RESTORE 类似,根据窗口的默认位置和大小来显示并激活它。

默认是正常大小显示窗口。

       WinMain函数中需要定义4个变量:

     static TCHAR szAppName[] = TEXT ("HelloWin") ;定义一个窗口类名,通常也是进程的名称。

     HWND hwnd ;定义一个窗口句柄,通过句柄对窗口进行操作。

     MSG msg ; 定义一个消息结构变量。

     WNDCLASS wndclass ; 定义一个窗口类结构变量。在上一节中已经解释过窗口类,不再赘述。

如果我们想查看相关的帮助信息,可以选中后按F1键,查阅MSDN相关信息。也可以选中后点击鼠标右键,速揽定义或转到定义,VS操作非常方便。

       ●主程序创建窗口的五个步骤:

       步骤一:初始化窗口类。如果要创建一个自定义的窗口,必须要明确该窗口的相关信息,并传递给操作系统。简单点说就是初始化窗口类结构体的各个字段了。如果不需要或者没有的字段可以设置为NULL。但是,窗口风格、窗口过程和窗口类名这几个字段是必须要有的。

       步骤二:注册一个窗口类。当我们初始化一个新的窗口类之后,需要将窗口类向Winodws系统注册生效,调用API函数RegisterClass。

       步骤三:调用API函数CreateWindow创建窗口。

       步骤四:显示和更新窗口。

     ShowWindow (hwnd, iCmdShow) ; //显示窗口

     UpdateWindow (hwnd) ; //更新窗口

       步骤五:消息循环。

//消息队列中获取消息,message为WM_QUIT则返回0,退出消息循环

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

      { //将msg结构返还windows,并将虚拟键消息转换为字符消息

          TranslateMessage(&msg);

//再次将msg结构返还windows,分发一个消息给窗口程序

          DispatchMessage(&msg);

      }

       消息循环是一个While循环结构,调用GetMessage函数从窗口消息队列中获取消息,如果message为WM_QUIT则返回0,退出消息循环。否则交给TranslateMessage函数和DispatchMessage函数处理。

       主程序创建窗口的五个步骤是必须的,而且是固定的,可以作为Windows程序的模板使用。多数情况下直接拷贝使用就可以了。如果要增加子窗口或者处理控件消息或菜单快捷键消息时,稍微改一下就可以了。Windows程序设计的基础要比看起来要简单的多。

       ■窗口过程

       窗口过程负责处理Windows系统发送给窗口的各种消息。

LRESULT CALLBACK WndProc (HWND hwnd, UINT message,

WPARAM wParam, LPARAM lParam);

       我们会发现,窗口过程的四个参数就是MSG消息结构的前4个字段。这是Windows操作系统传递的消息参数。

参数hwnd:当前窗口的句柄。用于识别窗口或应用程序子窗口的唯一身份标识。

参数message:描述了窗口所需要处理的消息类型,例如WM_PAINT(需要重绘窗口)、WM_DESTROY(窗口将要销毁)等。

参数wParam:是消息特定的附加信息。其内容会根据具体的message类型而变化。

参数lParam:也是消息特定的附加信息。其内容会根据具体的message类型而变化。

返回值LRESULT则是消息处理后的返回信息,该返回信息根据消息类型各不相同。

每当有事件发生(如鼠标点击、键盘按键、计时器事件等)时,Windows会向相应的窗口发送一个消息(message),并通过WndProc函数进行消息的处理。

对于大部分消息,一旦WndProc处理了这些消息,它就应该返回0。对于某些消息,如WM_CREATE和WM_PAINT,返回的值可能包含某些信息。所有的这些信息都在每个消息的具体说明中有详细的解释。

如果WndProc没有处理某个消息,那么应该调用Windows系统默认的窗口过程DefWindowProc函数,让系统按照默认的方式处理。绝大多数消息都是由DefWindowProc函数处理的。

WndProc的调用约定是CALLBACK,和WINAPI相同,同样是stdcall调用约定。之所以使用CALLBACK,顾名思义,WndProc是一个回调函数,即Windows系统调用WndProc来处理各种消息。

WndProc窗口过程主要包含一个switch结构,根据message消息ID的不同分别进行处理,处理完之后,直接return 0 ;返回。没有处理的其他消息则交给DefWindowProc按照Windows系统默认方式处理。

       接下来我们将这个模板程序进行分解,一步一步实现窗口的创建过程,以便于我们分析窗口实现的过程。

2.2.2 第9练:注册窗口类

/*------------------------------------------------------------------------

 009 编程达人win32 API每日一练

     第9个例子RegisterClass.c:注册窗口类    

     WNDCLASS结构

     RegisterClass 函数    

 (c) www.bcdaren.com 编程达人

-----------------------------------------------------------------------*/

#include <windows.h>

/*****************************回调函数*****************************************

LRESULT:返回值,被宏定义为long型

CALLBACK:函数参数进栈顺序(从右到左,反汇编求证)---_stdcall调用约定

4个参数:为MSG结构体的前4个成员变量 ******************************************************************************/

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //消息回调函数

/*************************程序入口 *****************************************/

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,

    LPSTR lpCmdLine, int nCmdShow)

{   // 窗口类名---一个以\0结尾字符串数组,用来保存程序名称,

//即注册到操作系统的名称,一般为进程名称!

     static TCHAR szAppName[] = TEXT("HelloWin");                                                  

//***********************************1、设计窗口类*****************************

     WNDCLASS wndclass; //定义窗口变量

     wndclass.style = CS_HREDRAW | CS_VREDRAW; // 窗口类型:水平、垂直重绘,即改变窗口大小时自动发送WM_PAINT消息,而不必手动发送。被改变时该类所有窗口重绘

     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; // 窗口类名---程序的名字

     //******************************2、注册窗口类*****************************

     if (RegisterClass(&wndclass)) //成功注册一个窗口类

     {

          MessageBox(NULL, TEXT("爱达人---成功注册一个窗口类!"),

               szAppName, MB_ICONEXCLAMATION);

     }

     return 0;

}

 //**********************回调函数——消息处理过程******************************

 //窗口回调函数,此函数只有声明和定义,没有调用!这说明此函数确实是由操作系统调用的!

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

{

     return 0; //未处理任何窗口消息

}

/*

窗口总是基于窗口类创建的。窗口类确定了处理窗口消息的窗口过程。

在创建应用窗口之前,必须调用函数RegisterClass来注册窗口类。

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //消息回调函数

本例中未作任何消息处理,后面的例题中将会详细介绍

*******************************************************************

WNDCLASS结构:

typedef struct

{

    UINT style ;

    WNDPROC lpfnWndProc ;

    int cbClsExtra ;

    int cbWndExtra ;

    HINSTANCE hInstance ;

    HICON hIcon ;

    HCURSOR hCursor ;

    HBRUSH hbrBackground ;

    LPCTSTR lpszMenuName ; //LPCWSTR (Unicode版本)

    LPCTSTR lpszClassName ; //LPCWSTR (Unicode版本)

}

WNDCLASS, * PWNDCLASS ;

*******************************************************************************

RegisterClass 函数:注册一个窗口类,供以后在对CreateWindow或CreateWindowEx函数的调用中使用。

【注意】如果使用reateWindowEx函数创建创建,相应的需要使用RegisterClassEx注册窗口。但是,如果不需要设置类小图标,则仍然可以使用CreateWindow函数和RegisterClass函数。

ATOM RegisterClassA(

  const WNDCLASSA *lpWndClass //指向WNDCLASS结构的指针。在将结构传递给函数之前,必须用适当的类属性填充该结构。

);

返回值编辑

ATOM类型为Windows中定义的新数据类型,其即unsigned short类型,在<WinDef.h>中的定义如下:

typedef WORD ATOM;

typedef unsigned short WORD;

如果函数成功,返回值是唯一标识已注册的类的一个原子;如果函数失败,返回值为0。若想获得更多错误信息,请调用GetLastError函数。

*/

       运行结果:注册成功后,返回值非0,弹出一个对话框。

图2-3 注册窗口类

       毫无疑问,仅仅只是注册窗口类是无法完成窗口队的创建。

【注意】程序中的窗口过程WndProc并未做任何事情,直接返回操作系统。让我们继续进行下一步。

2.2.3 第10练:创建、显示和更新窗口

/*------------------------------------------------------------------------

 010 编程达人win32 API每日一练

     第10个例子CreateWindow.c:创建、显示和更新窗口---此例尚不能显示窗口    

     CreateWindow函数

     ShowWindow函数

     UpdateWindow函数

 (c) www.bcdaren.com 编程达人

-----------------------------------------------------------------------*/

#include <windows.h>

/****************************回调函数*****************************************/

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //消息回调函数

/*************************程序入口 ******************************************/

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,

        LPSTR lpCmdLine, int nCmdShow)

{

     static TCHAR szAppName[] = TEXT("HelloWin"); // 窗口类名

//***********************************1、设计窗口类*****************************

     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; // 窗口类名---程序名

     /********************************2、注册窗口类***************************/

     if (!RegisterClass(&wndclass)) //返回值为0,未成功注册

     {

          MessageBox(NULL, TEXT("注册失败!"),

               szAppName, MB_ICONEXCLAMATION);

          return 0;

     }

     /*****************************3、创建窗口********************************/

     HWND hwnd;

     hwnd = CreateWindow(szAppName,    //已注册的窗口类名

                         TEXT("我的第一个窗口程序!"),//窗体标题

                        WS_OVERLAPPEDWINDOW, //窗体风格(重叠窗口、标题栏和边框

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

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

                        CW_USEDEFAULT, //窗口的宽度

                        CW_USEDEFAULT, //窗口的高度

                        NULL,   //父窗口句柄,此处NULL

                        NULL,   //窗口菜单句柄,此处NULL

                        hInstance,//应用程序句柄

                        NULL    //指向一个值的指针,该值传递给窗口WM_CREATE消息

                                         );

/*创建窗口完成(只存在于内存中),系统会发送第一条消息WM_CREATE,注意此时,程序并未执行到消息循环处,操作系统绕过应用程序的消息队列,直接将WM_CREATE消息发送给窗口过程。该消息在所有的消息之前,目的是为了在程序程序执行之前可以有机会进行一些初始化工作或装载动态链接库等操作。

A) CreateWindow 的第一个参数就是窗口类名,通过这个名字可以找到刚才注册的窗口类,然后再根据它来创建窗口。

B) 显示器上的坐标与数学中的不同,显示器的左上角是坐标原点,从原点向右是x轴,向下是y轴,都是正坐标,没有负数。

C) 参数 hInstance 是通过主函数 WinMain 传入的。

【注意】通过 CreateWindows() 函数创建窗口后,仅仅是为窗口分配了内存空间,获得了句柄,但窗口并没有显示出来,所以还需要调用 ShowWindow() 函数来显示窗口。

*/

/**********************************4、显示窗口和更新窗口**********************/

     ShowWindow(hwnd, nCmdShow); //发送WM_SIZE和WM_SHOWWINDOW消息送入消息队列

/*1、单纯一个ShowWindow,照样会正确绘制出窗口内容,只不过是在消息队列取空之后才绘制的,有时我们希望窗口被立即重画,而不是去等待那个不确定的消息队列,此时就需要用到UpdateWindow。UpdateWindow的作用只有一个:假若当前存在被标记为重绘的区域(即无效区域,如果其不存在的话它什么也不做),那么立刻让Windows使用SendMessage的方式来对你的窗口发送WM_PAINT。

2、ShowWindow本身并不发送重绘消息,它的作用仅仅是把窗口显示出来。

所以还需要调用 UpdateWindow() 函数,生成 VM_PAINT 消息,将客户区中的各种控件绘制出来。不过,当窗口显示的时候,Windows会自动探测窗口的内容是否需要重绘、以及需要重绘的区域。

3、ShowWindow调用中会发送WM_SIZE和WM_SHOWWINDOW消息给窗口过程。

4、第一次调用和以后的调用是有区别的。第一次调用时,它的输入参数 nCmdShow 是需要输入WinMain 函数里传入来的nCmdShow 参数,而不能是其它参数。在此以后就不再需要nCmdShow 参数了。

 */ 

     UpdateWindow(hwnd); //发送WM_PAINT消息给窗口过程

/*执行UpdateWindow时会立即发送一条WM_PAINT消息,同样由操作系统直接发出。UpdateWindow执行时,先判断无效区域是否为空,不为空,则发送WM_PAINT消息,为空为不发送。可见,将上面的ShowWindow注释掉,则不发送。因为未显示出来,当然没有无效区域,即无效区域为空。

*/

/*UpdateWindow和Invalidate的区别:

1、UpdateWindow()的作用是使窗口立即重绘(因为操作系统绕过应用程序的消息队列,直接给窗口过程发WM_PAINT消息,从而导致窗口立即重绘。

   而Invalidate等函数(功能为使整个客户区无效)调用后窗口不会立即重绘,这是由于WM_PAINT消息的优先级很低,它需要等消息队列中的其它消息发送完后才能被处理。

2、UpdateWindow执行时,先判断无效区域是否为空,不为空才发送消息,Invalidate的无效区为整个客户区(注意与InvalidRect函数(功能为使指定的区域无效)的区别)

*/

     return 0;

}

 //*****************************回调函数——消息处理过程***********************

 //窗口过程回调函数,这里只有声明和定义,直接返回!这说明此函数确实是由操作系统调用的!

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

{

     return 0; //未处理任何窗口消息

}

/*****************************************************************************

CreateWindow函数:创建一个重叠窗口,弹出窗口或子窗口。它指定窗口类,窗口标题,窗口样式,以及(可选)窗口的初始位置和大小。

该函数还指定窗口的父级或所有者(如果有)以及窗口的菜单。

要使用CreateWindow支持的样式以及扩展窗口样式,请使用CreateWindowEx函数。

void CreateWindowA(

   lpClassName,//窗口的类名

   lpWindowName,//窗体标题名称

   dwStyle,    //指定创建窗口的风格。

   x,          //窗体左上角x坐标

   y,          //窗体左上角y坐标

   nWidth,     //窗口的宽度

   nHeight,    //窗口的高度

   hWndParent, //父窗口句柄

   hMenu,      //窗口菜单句柄

   hInstance,  //应用程序句柄

   lpParam     //指向一个值的指针,该值传递给窗口WM_CREATE消息。

);

*****************************************************************************

ShowWindow函数 :该函数设置指定窗口的显示状态。

BOOL ShowWindow(

  HWND hWnd,        //窗口的句柄。

  int  nCmdShow     //窗口显示的状态

);

返回值:如果窗口之前可见,则返回值为非零。如果窗口之前被隐藏,则返回值为零。

**************************************************************************

UpdateWindow函数:如果窗口的更新区域不为空,则UpdateWindow函数通过向该窗口过程发送WM_PAINT消息来更新指定窗口的客户区。

该函数绕过应用程序消息队列,直接将WM_PAINT消息发送到指定窗口的窗口过程。如果更新区域为空,则不发送消息。

UpdateWindow(

    HWND hWnd  //要更新的窗口句柄

);

*/

 

总结

       1.细心的读者可能会注意到,我们在初始化窗口类时已经给出了窗口风格,为何在调用CreateWindow函数创建窗口时还要再次给出窗口风格呢?

       这是因为,一个窗口类可以创建多个窗口,每个窗口都可以有属于自己的独特风格,这样设计更灵活。

       2.还有一个重复项,初始化窗口类时给定了窗口菜单,CreateWindow函数同样也有一个参数是菜单句柄。道理是相同的,如果该窗口类只创建了一个窗口,绑定菜单可以在初始化窗口类时进行,也可以在调用CreateWindow时进行。假设一个窗口类需要创建多个窗口时,则每个窗口都可以绑定不同的菜单。

       3.虽然此例程已经创建窗口、显示窗口和更新窗口,但新创建的窗口仅存在于内存中,并未真正显示出来。执行CreateWindow函数后,会产生程序的第一个消息WM_CREATE。只有当Windows系统将WM_CREATE消息直接发送给窗口过程WndProc并处理之后,才会真正在屏幕显示窗口。

    4.【注意】注释中解释了显示窗口的四个不同函数的区别:ShowWindow、UpdateWindow、Invalidate、InvalidRect。其中执行ShowWindow函数会产生两个消息WM_SIZE和WM_SHOWWINDOW并送入窗口消息队列。执行UpdateWindow函数会产生WM_PAINT消息无需送入窗口消息队列,而是直接交给窗口过程,立即更新窗口。

【注】此时,由于窗口过程尚未处理任何消息,屏幕仍无法显示窗口。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/686603.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【控制实践——二轮平衡车】【五】转动运动模型及控制方法

传送门 系列博客前言运动模型控制方法控制器设计 结论 系列博客 【控制实践——二轮平衡车】【一】运动分析及动力学建模 【控制实践——二轮平衡车】【二】实物设计和开源结构&代码 【控制实践——二轮平衡车】【三】基于PID的直立控制 【控制实践——二轮平衡车】【四】…

27 ssh+scp+nfs+yum进阶

ssh远程管理 ssh是一种安全通道协议&#xff0c;用来实现字符界面的远程登录。远程复制&#xff0c;远程文本传输。 ssh对通信双方的数据进行了加密。 用户名和密码登录 密钥对认证方式&#xff08;可以实现免密登录&#xff09; ssh 22 网络层 传输层 数据传输的过程中是…

Matlab 2024a 建模基础知识全面指南

一、Matlab简介 1. Matlab是什么&#xff1f; Matlab&#xff08;Matrix Laboratory&#xff09;是由MathWorks公司开发的一个高性能的数值计算环境和编程语言。它以其强大的矩阵运算能力、丰富的工具箱和便捷的数据可视化功能而闻名&#xff0c;广泛应用于科学研究、工程模拟…

【面试干货】什么是索引?

【面试干货】什么是索引&#xff1f; 1、索引的定义2、索引的工作原理3、索引在数据库管理系统中的作用 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 1、索引的定义 数据库索引是一个存储在磁盘上的数据结构&#xff0c;它以某种方式引用…

2024年全国青少信息素养大赛图形化编程挑战赛集训第一天编程题分享

大家如果不想阅读前边的比赛内容介绍,可以直接跳过:拉到底部看集训第一天题目 (一)比赛内容: 【小学低年级组】 1、图形化编程软件的使用:熟悉图形化编程软件中舞台区、角色列表区、功能区、脚本编 -3- 辑区的功能及使用。 2、基础功能模块的使用: a.运动模块:角…

ARM64汇编0A - thumb模式与IT块

本文主要讨论一下 32 位程序下的 thumb 模式相关东西&#xff0c;属于选读内容。 thumb模式 ARM模式的指令集宽度是32位而Thumb是16位宽度(但也可以是32位)。 Thumb也有很多不同的版本。不过不同的名字仅仅是为了区分不同版本的Thumb指令集而已(也就是对于处理器来说&#x…

10款必备软件,每款都是神器,赶快用起来吧!

AI视频生成&#xff1a;小说文案智能分镜智能识别角色和场景批量Ai绘图自动配音添加音乐一键合成视频https://aitools.jurilu.com/最近有很多小伙伴在咨询&#xff0c;我也抓紧时间整理了一些不错的软件和我陆续收到的&#xff0c;希望对大家有所帮助。 1. 全球鼠标——MouseI…

15_Vue3核心概念与实践

文章目录 Vue31. Vite2.使用Vite创建前端工程3.目录介绍4.SFC入门5.2.35.ViteVue样式导入方式6.响应式入门&&setup函数6.1 响应式数据6.2 省略setup(){} 省略default{}&#xff0c;return{}6.3 案例开发功能概述 7. 插值表达式8.文本渲染v-text/v-html9. 属性渲染v-bin…

基于Vue的前端瀑布流布局组件的设计与实现

摘要 随着前端技术的不断演进&#xff0c;复杂业务场景和多次迭代后的产品对组件化开发提出了更高的要求。传统的整块应用开发方式已无法满足快速迭代和高效维护的需求。因此&#xff0c;本文将介绍一款基于Vue的瀑布流布局组件&#xff0c;旨在通过组件化开发提升开发效率和降…

全球AI新闻速递6.7

1.智谱 AI 宣布全模型矩阵降价&#xff0c;开源 GLM-4-9B 系列模型。 2.复旦大学计划在2024-2025新学年推出至少100门。 3.思科&#xff1a;启动 10 亿美元 AI 基金&#xff0c;投资AI初创公司。 4.OpenAI和谷歌DeepMind员工联名发声&#xff1a;高级AI风险巨大&#xff0c;…

Acwing 786.第K个数

Acwing 786.第K个数 题目描述 786. 第k个数 - AcWing题库 运行代码 #include <iostream> #include <algorithm> using namespace std; const int N 100010; int q[N];int main() {int n, k;scanf("%d%d", &n, &k);for (int i 0; i < n; …

Mac屏幕截图软件

一、简介&#xff08;有小伙伴留言说想要mac的屏幕截图软件&#xff0c;今天给大家分享一个还不错的&#xff09; 1、一个功能丰富的功能丰富的截图工具&#xff0c;具有许多高级功能&#xff0c;免费。用于快速拍摄并将它们组织成集合。Snappy还支持注释&#xff0c;共享&…

Linux操作系统:Spark在虚拟环境下的安装及部署

将Spark安装到指定目录 // 通过wget下载Spark安装包 $ wget https://d3kbcqa49mib13.cloudfront.net/spark-2.1.1-bin-hadoop2.7.tgz // 将spark解压到安装目录 $ tar –zxvf spark-2.1.1-bin-hadoop2.7.tgz –C /usr/local/ // 重命名 $ mv /usr/local/spark-2.1.1-bin-hado…

1.音视频开篇

目录 音视频播放的原理 音视频数据格式YUV YUV数据存储比 ​编辑 YUV空间格式 RGB与YUV转换 音视频播放的原理 主要分为&#xff1a;解协议->解封装->解码->音视频同步->播放。当然&#xff0c;如果是本地播放&#xff0c;没有解协议这一步骤。 采集数据其实…

Cesium开发环境搭建(二)

由于win7搭建很费事&#xff0c;重新安装了OS&#xff0c;win10的。 记录一下&#xff0c;搭建步骤&#xff1a; 1.下载node.js。 百度搜索即可下载对应的版本。下载cesium。 2.安装node.js。 安装后&#xff0c;输入node -v&#xff0c;显示版本信息&#xff0c;表示安装…

Spring Cloud 微服务集成Sentinel实现服务熔断降级

文章目录 一、前言二、技术思路及方案2.1 实现思路2.2 实现方案2.2.1 nacos动态数据源实现类关系图 三、功能实现3.1 快速集成方案3.1.1 引入依赖3.1.2 服务端熔断降级3.1.3 feign调用降级 四、扩展4.1 SPI机制4.2 自定义Slot实现4.3 基于 Sentinel 实现 Feign 全局异常兜底4.3…

MySQL之查询性能优化(七)

查询性能优化 排序优化 无论如何排序都是一个成本很高的操作&#xff0c;所以从性能角度考虑&#xff0c;应尽可能避免排序或者尽可能避免对大量数据进行排序。前面已经提到了&#xff0c;当不能使用索引生成排序结果的时候&#xff0c;MySQL需要自己进行排序&#xff0c;如果…

【大事件】docker可能无法使用了

今天本想继续学习docker的命令&#xff0c;突然发现官方网站的文档页面打不开了。 难道是被墙了&#xff1f; 我用同事的翻了一下&#xff0c;能进&#xff0c;果然&#xff01; 正好手头的工作告一段落&#xff0c;将代码上传&#xff0c;然后通过jenkins将服务器自动部署到…

Python魔法之旅-魔法方法(20)

目录 一、概述 1、定义 2、作用 二、应用场景 1、构造和析构 2、操作符重载 3、字符串和表示 4、容器管理 5、可调用对象 6、上下文管理 7、属性访问和描述符 8、迭代器和生成器 9、数值类型 10、复制和序列化 11、自定义元类行为 12、自定义类行为 13、类型检…

代码随想录——删除二叉搜索树中的节点(Leetcode450)

题目链接 递归 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val val; }* TreeNode(int val, TreeNode left, TreeNode right) {* …