• Windows程序运行机制


    写在前面

    首先理解下两个名词: API 和 SDK.

    API(Application Programming Interface), 译为应用程序编程接口. 是Windows系统提供给开发人员的函数的简称.
    SDK(Software Development Kit), 译为软件开发包. 是API库, 使用手册, 帮助说明, 其他辅助工具等资源, 即开发所需资源的一个集合.

    一个Windows程序至少包括一个与用户交互的窗口(即主窗口), 窗口可以分为客户区和非客户区.

    客户区是窗口的一部分, Windows应用程序一般在客户区显示和绘制.

    标题, 菜单, 状态栏, 最大/小框等属于非客户区, 它们有Windows操作系统来管理.

    窗口可以有一个父窗口, 有父窗口的窗口则为子窗口.

    在Windows应用程序中, 窗口通过窗口句柄(HWND)来标识, 对窗口的操作也都需借由窗口句柄来进行.

    Windows程序是一种基于消息的程序设计方式, 每个Windows程序都会有一个消息循环来捕获用户在窗口上操作, 然后交由窗口的 "窗口过程"处理.

    一个Windows应用程序一般会有以下步骤:
    ①设计一个窗口类
    ②注册窗口类
    ③创建窗口
    ④显示/刷新窗口
    ⑤进入消息循环

    设计

    Windows操作系统会为每个窗口类维护一个WNDCLASS结构体, 如下:

    //ANSI版本
    typedef struct tagWNDCLASSA {
        UINT        style;			//窗口类型. 例CS_HREDRAW | CS_VREDRAW表示水平垂直尺寸发生变化时重绘窗口
        WNDPROC     lpfnWndProc;	//窗口过程, 原型:typedef LRESULT (CALLBACK* WNDPROC)(HWND, UINT, WPARAM, LPARAM);
        int         cbClsExtra;		//类附加内存,一般用于存储类的附加信息,该窗口类的所有窗口共享该内存;0 表示不使用
        
        //窗口附加内存,Windows系统为每一个窗口(实例对象)管理一个内部数据结构,在注册一个窗口类时,应用程序能够指定一定字节数的附加内存空间,称为窗口附加内存,注意区别于类附加内存
        //在创建该类窗口时,Windows系统就为窗口的结构(即Windows为窗口管理的内部数据结构)分配和追加指定书目的窗口附加内存空间,应用程序可用这部分内存存储窗口特有的数据。
        //Windows系统把该部分初始化为0,表示不使用。如果应用程序用WNDCLASS结构注册对话框,则必须给DLGWINDOWEXTRA设置这个成员。
        int         cbWndExtra;	
        HINSTANCE   hInstance;		//包含窗口过程的应用程序的实例句柄
    
    	//指定窗口类的图标句柄, 为NULL则为系统默认图标
    	//可使用LoadIcon函数来加载自定义图标资源, 原型如下:参数一为要加载的图标句柄,为NULL则加载标准图标;参数二为图标名称,LPCTSTR类型,可使用MAKEINTRESOURCE宏将ID转换成LPCTSTR
    	//HICON LoadIcon(HINSTANCE hInstance, LPCTSTR lpInconName);	
        HICON       hIcon;
    	
    	//指定窗口类的光标句柄, 这个成员必须是一个关闭资源的句柄。如果为NULL,则无论何时鼠标进入应用程序窗口中,应用程序都必须明确地设置光标的形状
    	//可使用LoadCursor函数来加载一个光标资源.函数原型如下:参数一为要加载的光标句柄,为NULL则加载标准光标;参数二为光标名称,可使用MAKEINTRESOURCE宏将ID转换成LPCTSTR
    	//HCURSOR LoadCursor(HINSTANCE hInstance, LPCTSTR lpCursorName);
        HCURSOR     hCursor;
    
    	//指定窗口类的背景画刷, 当窗口重绘时使用该画刷擦除窗口的背景
    	//可使用GetStockObject函数获取画刷句柄,该函数还可用来获取画笔、字体和调色板的句柄,原型如下:
    	//HGDIOBJ GetStockObject(int fnObject); 参数指定要获取的对象类型,例BLACK_BRUSH表示获取黑色的画刷; 还需注意GetStockObject可获取多种资源的句柄,因此返回值是一个通用的HGDIOBJ(void*),可强制类型转成所需类型
        HBRUSH      hbrBackground;
        LPCSTR      lpszMenuName;		//指定窗口类的菜单资源, 菜单的ID一般都是UINT,可使用MAKEINTRESOURCE宏转换成LPCTSTR
        LPCSTR      lpszClassName;		//指定窗口类的名称
    } WNDCLASSA, *PWNDCLASSA, NEAR *NPWNDCLASSA, FAR *LPWNDCLASSA;
    
    //Unicode版本
    typedef struct tagWNDCLASSW {
        UINT        style;
        WNDPROC     lpfnWndProc;
        int         cbClsExtra;
        int         cbWndExtra;
        HINSTANCE   hInstance;
        HICON       hIcon;
        HCURSOR     hCursor;
        HBRUSH      hbrBackground;
        LPCWSTR     lpszMenuName;
        LPCWSTR     lpszClassName;
    } WNDCLASSW, *PWNDCLASSW, NEAR *NPWNDCLASSW, FAR *LPWNDCLASSW;
    
    #ifdef UNICODE
    typedef WNDCLASSW WNDCLASS;
    #else
    typedef WNDCLASSA WNDCLASS;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    例这里设计一个窗口如下:

    	WNDCLASS wndcls;	
    	wndcls.style = CS_HREDRAW | CS_VREDRAW;						//水平垂直尺寸发生变化时重绘窗口
    	wndcls.lpfnWndProc = WinProc;								//指定该窗口的窗口过程
    	wndcls.cbClsExtra = 0;										//不使用类附加内存
    	wndcls.cbWndExtra = 0;										//不使用窗口附加内存
    	wndcls.hInstance = hInstance;								//使用WinMain中hInstance参数初始化
    	wndcls.hIcon = LoadIcon(NULL, IDI_INFORMATION); 			//使用信息图标
    	wndcls.hCursor = LoadCursor(NULL, IDC_CROSS);				//使用十字光标
    	wndcls.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);	//BLACK_BRUSH表示获取黑色的画刷
    	wndcls.lpszMenuName = NULL;									//这里不指定菜单
    	wndcls.lpszClassName = "WinPro";							//将自己设计的窗口类命名为WinPro
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    窗口过程WinProc如下:

    	//窗口过程
    	//窗口过程函数
    LRESULT CALLBACK WinProc(
    	HWND hwnd,
    	UINT msg,
    	WPARAM wParam,
    	LPARAM lParam
    	)
    {
    	switch (msg)
    	{
    	case WM_CHAR:
    		char szChar[20];
    		sprintf_s(szChar, sizeof(szChar), "char code is %d", wParam);
    		MessageBox(hwnd, szChar, "char", 0);
    		break;
    
    	case WM_LBUTTONDOWN:
    		MessageBox(hwnd, _T("mouse clicked"), _T("message"), 0);
    		HDC hdc;
    		hdc = GetDC(hwnd);	//不能在WM_PAINT的响应中调用GetDC函数,在WM_PAINT响应中必须且只能使用BeginPaint(HWND, PAINTSTRUCT*)
    		TextOut(hdc, 0, 50, _T("mouse clicked"), strlen("mouse clicked"));
    		ReleaseDC(hwnd, hdc);
    		break;
    
    	case WM_PAINT:
    		HDC hDC;
    		PAINTSTRUCT ps;		//用于接收绘制的信息
    		hDC = BeginPaint(hwnd, &ps);
    		TextOut( hDC, 0, 0, _T("WM_PAINT.."), _tcslen(_T("WM_PAINT..")) );
    		EndPaint(hwnd, &ps);
    		break;
    
    	case WM_CLOSE:
    		if (IDYES == MessageBox(hwnd, "是否关闭窗口?", "message", MB_YESNO))
    		{
    			DestroyWindow(hwnd);
    		}
    		break;
    
    	case WM_DESTROY:
    		PostQuitMessage(0);
    		break;
    
    	default:
    		return DefWindowProc(hwnd, msg, wParam, lParam);
    	}
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    注册

    使用RegisterClass注册窗口,原型如下:

    typedef WORD                ATOM;   //BUGBUG - might want to remove this from minwin
    
    //参数: WNDCLASS类的指针, 在将结构传递给函数之前,必须使用适当的类属性填充结构。
    //返回值: 如果函数成功,则返回值是一个类原子,用于唯一标识正在注册的类。如果函数失败,则返回值为零。
    ATOM RegisterClassA(
      [in] const WNDCLASSA *lpWndClass
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    例: 这里注册上面设计的WinPro窗口类

    RegisterClass(&wndcls);	
    
    • 1

    创建

    使用CreateWindow函数创建一个窗口示例, 原型如下:

    void CreateWindowA(
      [in, optional]  lpClassName,		//设计窗口时WNDCLASS中的lpszClassName成员
      [in, optional]  lpWindowName,		//窗口名称。即窗口标题
      [in]            dwStyle,			//正在创建的窗口的样式。注意区别于WNDCLASS中的style, 以IPhone为例, WNDCLASS中style指定的像是苹果手机的模具(刘海屏), 而创建时的dwStyle则可以指定使用该模具实际生产时的颜色
      [in]            x,				//窗口的初始水平位置。以父窗口为基准的坐标
      [in]            y,				//窗口的初始垂直位置。同样以父窗口为基准的坐标
      [in]            nWidth,			//窗口的宽度
      [in]            nHeight,			//窗口的高度
      [in, optional]  hWndParent,		//正在创建的窗口的父窗口或所有者窗口的句柄。
      [in, optional]  hMenu,			//菜单的句柄,或根据窗口样式指定子窗口标识符。
      [in, optional]  hInstance,		//要与窗口关联的模块实例的句柄。
      [in, optional]  lpParam			//指向要通过WM_CREATE消息的参数所指向的创建结构结构(lp创建参数成员)传递到窗口的值的指针, 一般设置为NULL。
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    例这里创建上面注册的WinPro窗口:

    HWND hwnd;
    hwnd = CreateWindow(_T("WinPro"), _T("这是一个标题"), WS_OVERLAPPEDWINDOW, 300, 200, 600, 400, NULL, NULL, hInstance, NULL);
    
    • 1
    • 2

    显示/刷新

    使用ShowWindow显示, 使用UpdateWindow刷新, 原型如下:

    //返回值: 如果窗口以前可见,则返回值为非零值。如果窗口以前是隐藏的,则返回值为零。
    BOOL ShowWindow(
      [in] HWND hWnd,		//要显示的窗口句柄
      [in] int  nCmdShow	//显示方式. 注意: 第一次显示窗口时应指定SW_SHOWNORMAL
    );
    
    
    //返回值: 如果函数成功,则返回值为非零值。如果函数失败,则返回值为零。
    BOOL UpdateWindow(
      [in] HWND hWnd		//要刷新的窗口句柄
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    例这里显示和刷新上面创建的窗口:

    	ShowWindow(hwnd, SW_SHOWNORMAL);	//第一次显示窗口时应制定SW_SHOWNORMAL
    	UpdateWindow(hwnd);
    
    • 1
    • 2

    消息循环

    作为一个窗口应用程序, 必定会有与用户的交互, 而对用户的操作的相应处理, 放在应用程序的消息循环中处理.

    可使用GetMessage函数从应用程序的消息队列中取出消息,函数原型如下:

    //lpMsg: 保存从消息队列中取出的消息,hwnd制定接收哪个窗口的消息,为NULL则接收所有窗口的消息, wMsgFilterMin指定要获取消息的最小值, wMsgFilterMax指定要获取消息的最大值
    //wMsgFilterMin和wMsgFilterMax均指定为0, 则接收所有消息
    //返回值: GetMessage接收除WM_QUIT外的消息均返回非零值,对于WM_QUIT消息,返回0。如果出现了错误,则返回-1,例当hwnd为无效窗口句柄时或lpMsg为无效指针时,返回-1
    BOOL GetMessage(LPMSG lpMsg, HWND hwnd, UINT wMsgFilterMin, UINT wMsgFilterMax);	
    
    • 1
    • 2
    • 3
    • 4

    消息结构体MSG定义如下:

    typedef struct tagMSG {
      HWND   hwnd;			//窗口过程的句柄,其窗口过程接收消息。当消息是线程消息时,此成员为 NULL。
      UINT   message;		//消息标识符。应用程序只能使用低字;高音由系统保留。
      WPARAM wParam;		//有关消息的其他信息。确切的含义取决于消息成员的值。
      LPARAM lParam;		//同wParam
      DWORD  time;			//发布消息的时间。
      POINT  pt;			//发布消息时光标位置(以屏幕坐标表示)。
      DWORD  lPrivate;		//
    } MSG, *PMSG, *NPMSG, *LPMSG;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    然后使用TranslateMessage将虚拟键消息转换成字符消息.

    注意: TranslateMessage用于将虚拟键消息转换成字符消息,然后再使用投递到msg.hwnd窗口的消息队列中。TranslateMessage并不会修改原有的消息,它只产生新的消息并投递到对应窗口的消息队列中.

    原型如下:

    BOOL TranslateMessage(
      [in] const MSG *lpMsg		//指向 MSG 结构的指针,该结构包含使用“获取消息”或“扫视消息”函数从调用线程的消息队列中检索到的消息信息。
    );
    
    • 1
    • 2
    • 3

    最后使用DispatchMessage分派消息到对应窗口的窗口过程. DispatchMessage实际上是将消息回传给操作系统,由操作系统调用对应窗口过程进行处理(响应).
    原型如下:

    //lpMsg: 指向包含消息的结构的指针。
    //返回值: 指定窗口过程返回的值。尽管其含义取决于要调度的消息,但返回值通常会被忽略。
    LRESULT DispatchMessage(
      [in] const MSG *lpMsg
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5

    编写上面创建窗口所属示例的消息循环:

    	while (bRet = GetMessage(&msg, NULL, 0, 0))
    	{
    		if (bRet == -1)
    		{
    			return -1;
    		}
    		else
    		{
    			if (TranslateMessage(&msg))
    			{
    				OutputDebugString(_T("Translate"));
    			}
    			DispatchMessage(&msg);
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    代码

    完整代码如下:

    #include 
    #include 
    #include 
    
    LRESULT CALLBACK WinProc(
    	HWND hwnd,
    	UINT msg,
    	WPARAM wParam,
    	LPARAM lParam
    	);
    
    int WINAPI WinMain(
    	HINSTANCE hInstance,
    	HINSTANCE hPreInstance,
    	LPSTR lpCmdLine,
    	int nCmdShow
    	)
    {
    	//设计
    	//操作系统为每个窗口类维护一个WNDCLASS结构
    	WNDCLASS wndcls;	
    
    	//水平垂直尺寸发生变化时重绘窗口
    	wndcls.style = CS_HREDRAW | CS_VREDRAW;	
    
    	//指定该窗口的窗口过程
    	wndcls.lpfnWndProc = WinProc;	
    
    	//类附加内存,一般用于存储类的附加信息,该窗口类的所有窗口共享该内存(理解成类的静态成员);0 表示不使用
    	wndcls.cbClsExtra = 0;
    
    	//窗口附加内存,Windows系统为每一个窗口(实例对象)管理一个内部数据结构,在注册一个窗口类时,应用程序能够指定一定字节数的附加内存空间,称为窗口附加内存,注意区别于类附加内存
    	//在创建该类窗口时,Windows系统就为窗口的结构(即Windows为窗口管理的内部数据结构)分配和追加指定书目的窗口附加内存空间,应用程序可用这部分内存存储窗口特有的数据。
    	//Windows系统把该部分初始化为0,表示不使用。如果应用程序用WNDCLASS结构注册对话框,则必须给DLGWINDOWEXTRA设置这个成员。
    	wndcls.cbWndExtra = 0;
    
    	//包含窗口过程的应用程序的实例句柄
    	wndcls.hInstance = hInstance;
    
    	//指定窗口类的图标句柄, 为NULL则为系统默认图标
    	//可使用LoadIcon函数来加载自定义图标资源, 原型如下:参数一为要加载的图标句柄,为NULL则加载标准图标;参数二为图标名称,LPCTSTR类型,可使用MAKEINTRESOURCE宏将ID转换成LPCTSTR
    	//HICON LoadIcon(HINSTANCE hInstance, LPCTSTR lpInconName);	
    	wndcls.hIcon = LoadIcon(NULL, IDI_INFORMATION); //IDI_INFORMATION   IDI_ERROR
    
    	//指定窗口类的光标句柄, 这个成员必须是一个关闭资源的句柄。如果为NULL,则无论何时鼠标进入应用程序窗口中,应用程序都必须明确地设置光标的形状
    	//可使用LoadCursor函数来加载一个光标资源.函数原型如下:参数一为要加载的光标句柄,为NULL则加载标准光标;参数二为光标名称,可使用MAKEINTRESOURCE宏将ID转换成LPCTSTR
    	//HCURSOR LoadCursor(HINSTANCE hInstance, LPCTSTR lpCursorName);
    	wndcls.hCursor = LoadCursor(NULL, IDC_CROSS);
    
    	//指定窗口类的背景画刷, 当窗口重绘时使用该画刷擦除窗口的背景
    	//可使用GetStockObject函数获取画刷句柄,该函数还可用来获取画笔、字体和调色板的句柄,原型如下:
    	//HGDIOBJ GetStockObject(int fnObject); 参数指定要获取的对象类型,例BLACK_BRUSH表示获取黑色的画刷; 还需注意GetStockObject可获取多种资源的句柄,因此返回值是一个通用的HGDIOBJ(void*),可强制类型转成所需类型
    	wndcls.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    
    	//指定窗口类的菜单资源, 菜单的ID一般都是UINT,可使用MAKEINTRESOURCE宏转换成LPCTSTR
    	wndcls.lpszMenuName = NULL;	//这里不指定菜单
    
    	//指定窗口类的名称
    	wndcls.lpszClassName = "WinPro";	//将自己设计的窗口类命名为WinPro
    
    	//注册, 使用RegisterClass注册窗口,原型如下:
    	//ATOM RegisterClass(CONST WNDCLASS* lpWndClass);	ATOM 为WORD, 即unsigned short
    	RegisterClass(&wndcls);
    
    	//创建,使用CreateWindow函数创建一个窗口示例
    	HWND hwnd = CreateWindow(_T("WinPro"), _T("这是一个标题"), WS_OVERLAPPEDWINDOW, 300, 200, 600, 400, NULL, NULL, hInstance, NULL);
    
    	//显示及刷新
    	ShowWindow(hwnd, SW_SHOWNORMAL);	//第一次显示窗口时应制定SW_SHOWNORMAL
    	UpdateWindow(hwnd);
    
    	//定义消息结构体,开始消息循环
    	MSG msg;
    	BOOL bRet = TRUE;
    
    	//可使用GetMessage函数从应用程序的消息队列中取出消息,函数原型如下:
    	//BOOL GetMessage(LPMSG lpMsg, HWND hwnd, UINT wMsgFilterMin, UINT wMsgFilterMax);	
    	//lpMsg保存从消息队列中取出的消息,hwnd制定接收哪个窗口的消息,为NULL则接收所有窗口的消息, wMsgFilterMin指定要获取消息的最小值, wMsgFilterMax指定要获取消息的最大值
    	//wMsgFilterMin和wMsgFilterMax均指定为0, 则接收所有消息
    	//GetMessage接收除WM_QUIT外的消息均返回非零值,对于WM_QUIT消息,返回0。如果出现了错误,则返回-1,例当hwnd为无效窗口句柄时或lpMsg为无效指针时,返回-1
    	while (bRet = GetMessage(&msg, NULL, 0, 0))
    	{
    		if (bRet == -1)
    		{
    			return -1;
    		}
    		else
    		{
    			//TranslateMessage用于将虚拟键消息转换成字符消息,然后再使用投递到msg.hwnd窗口的消息队列中。TranslateMessage并不会修改原有的消息,它只产生新的消息并投递到对应窗口的消息队列中
    			if (TranslateMessage(&msg))
    			{
    				OutputDebugString(_T("Translate"));
    			}
    
    			//DispatchMessage函数分派一个消息到窗口过程,DispatchMessage实际上是将消息回传给操作系统,由操作系统调用对应窗口过程进行处理(响应)
    			DispatchMessage(&msg);
    		}
    	}
    
    	return msg.wParam;
    }
    
    //窗口过程函数
    LRESULT CALLBACK WinProc(
    	HWND hwnd,
    	UINT msg,
    	WPARAM wParam,
    	LPARAM lParam
    	)
    {
    	switch (msg)
    	{
    	case WM_CHAR:
    		char szChar[20];
    		sprintf_s(szChar, sizeof(szChar), "char code is %d", wParam);
    		MessageBox(hwnd, szChar, "char", 0);
    		break;
    
    	case WM_LBUTTONDOWN:
    		MessageBox(hwnd, _T("mouse clicked"), _T("message"), 0);
    		HDC hdc;
    		hdc = GetDC(hwnd);	//不能在WM_PAINT的响应中调用GetDC函数,在WM_PAINT响应中必须且只能使用BeginPaint(HWND, PAINTSTRUCT*)
    		TextOut(hdc, 0, 50, _T("mouse clicked"), strlen("mouse clicked"));
    		ReleaseDC(hwnd, hdc);
    		break;
    
    	case WM_PAINT:
    		HDC hDC;
    		PAINTSTRUCT ps;		//用于接收绘制的信息
    		hDC = BeginPaint(hwnd, &ps);
    		TextOut( hDC, 0, 0, _T("WM_PAINT.."), _tcslen(_T("WM_PAINT..")) );
    		EndPaint(hwnd, &ps);
    		break;
    
    	case WM_CLOSE:
    		if (IDYES == MessageBox(hwnd, "是否关闭窗口?", "message", MB_YESNO))
    		{
    			DestroyWindow(hwnd);
    		}
    		break;
    
    	case WM_DESTROY:
    		PostQuitMessage(0);
    		break;
    
    	default:
    		return DefWindowProc(hwnd, msg, wParam, lParam);
    	}
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151

    总结

    通过该示例了解Windows应用程序的内部运行机制, 即:
    ①设计一个窗口类
    ②注册窗口类
    ③创建窗口
    ④显示/刷新窗口
    ⑤进入消息循环

    需要注意的是:
    ①设计窗口类是必须明确的指定光标
    ②必须注册已设计的窗口类, Windows系统会为每个窗口类维护一个WNDCLASS结构体
    ③必须创建已注册的窗口类
    ④第一次显示窗口时应该指定SW_NORMAL

    Windows系统会为每个应用程序创建一个消息队列, 因此需要一个消息循环不断的读取消息队列中的消息, 通过消息结构体MSG中的窗口句柄成员hwnd可以得知该消息来自哪个窗口, 然后分派给操作系统, 让其调用对应窗口的窗口过程.

  • 相关阅读:
    Java校园跑腿小程序校园代买帮忙外卖源码社区外卖源码
    最全面的Mybatis教程,从“开局”到“通关”,Ready Go!
    java项目-第92期基于ssm+shiro的物联及生产管理系统-java毕业设计
    Flutter 中数据存储的四种方式
    推特营销|推特群推你有无限可能
    基于Springboot的少儿编程管理系统(有报告)。Javaee项目,springboot项目。
    LabVIEW
    Servlet基础(1)
    异步编程-线程池实现异步编程
    React Native for Arcgis 地图开发 GraphicCtrl (十三)
  • 原文地址:https://blog.csdn.net/SNAKEpc12138/article/details/126734424