这篇文章包括一下几个部分:
1。窗口类详解
2。窗口样式详解
3。窗口显示更新详解
4。窗口回调函数和常用消息详解
5。窗口常用API函数和常用控制消息详解
(这篇文章不包含关于绘制窗口的内容,以后我会发布GdiplusFlat(不是Gdiplus哦!)连载,将于那时候详细说明)
要想创建窗口,首先要注册一个窗口类,SDK为我们提供了一个结构“WNDCLASS”,先来看看这个结构的原型
typedef struct tagWNDCLASS { UINT style; WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HINSTANCE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrBackground; LPCTSTR lpszMenuName; LPCTSTR lpszClassName; } WNDCLASS, *PWNDCLASS;
MSDN:https://msdn.microsoft.com/en-us/library/windows/desktop/ms633576(v=vs.85).aspx
style:窗口类风格(0为缺省风格)
类样式定义窗口类的其他元素。两个或多个样式可以使用位或运算。
我一般都用0,有特殊需要时用一下风格:
CS_BYTEALIGNCLIENT
Aligns the window's client area on a byte boundary (in the x direction). This style affects the width of the window and its horizontal placement on the display.
将窗口的客户区 (在 x 方向) 的字节边界上对齐。这种风格影响的窗口和其水平位置上显示的宽度。
CS_BYTEALIGNWINDOW
Aligns the window on a byte boundary (in the x direction). This style affects the width of the window and its horizontal placement on the display.
将窗口 (在 x 方向) 的字节边界上对齐。这种风格影响的窗口和其水平位置上显示的宽度。
CS_CLASSDC
Allocates one device context to be shared by all windows in the class. Because window classes are process specific, it is possible for multiple threads of an application to create a window of the same class. It is also possible for the threads to attempt to use the device context simultaneously. When this happens, the system allows only one thread to successfully finish its drawing operation.
分配一个设备上下文类中的所有窗口共享。因为窗口类是具体的过程,有可能为多个线程的应用程序创建一个窗口在同一类。它也是可能的线程试图同时使用的设备上下文。当发生这种情况时,系统只允许一个线程能顺利完成其绘图操作。
CS_DBLCLKS
Sends a double-click message to the window procedure when the user double-clicks the mouse while the cursor is within a window belonging to the class.
当用户双击鼠标光标位于内属于类的窗口时,将双击消息发送到窗口过程。
CS_DROPSHADOW
Enables the drop shadow effect on a window. The effect is turned on and off through SPI_SETDROPSHADOW. Typically, this is enabled for small, short-lived windows such as menus to emphasize their Z-order relationship to other windows. Windows created from a class with this style must be top-level windows; they may not be child windows.
使窗口上有阴影效果。效果是通过 SPI_SETDROPSHADOW 开启和关闭。通常情况下,这是为启用小而短寿的窗口,例如菜单强调他们到其他窗口的 Z 顺序关系。从具有这种风格的类创建的窗口必须是顶级窗口;他们可能不是子窗口。
CS_GLOBALCLASS
Indicates that the window class is an application global class. For more information, see the "Application Global Classes" section of About Window Classes.
指示窗口类是应用程序的全局类。更多的信息,请参阅关于窗口类的"应用程序全局类"节(MSDN:https://msdn.microsoft.com/en-us/library/windows/desktop/ms633574(v=vs.85).aspx)。
CS_HREDRAW
Redraws the entire window if a movement or size adjustment changes the width of the client area.
如果移动或大小调整更改客户端区域的宽度将重新绘制整个窗口。
CS_NOCLOSE
Disables Close on the window menu.
在窗口菜单中禁用关闭
CS_OWNDC
Allocates a unique device context for each window in the class.
每个窗口类中分配一个唯一的设备上下文。
CS_PARENTDC
Sets the clipping rectangle of the child window to that of the parent window so that the child can draw on the parent. A window with the CS_PARENTDC style bit receives a regular device context from the system's cache of device contexts. It does not give the child the parent's device context or device context settings. Specifying CS_PARENTDC enhances an application's performance.
这样的子窗口可以画在父到父窗口设置子窗口的裁剪的矩形。一个带有 CS_PARENTDC 样式位窗口接收常规设备上下文从系统的缓存中的设备上下文。它不给孩子父母的设备上下文或设备上下文设置。指定 CS_PARENTDC 增强应用程序的性能。
CS_SAVEBITS
Saves, as a bitmap, the portion of the screen image obscured by a window of this class. When the window is removed, the system uses the saved bitmap to restore the screen image, including other windows that were obscured. Therefore, the system does not send WM_PAINT messages to windows that were obscured if the memory used by the bitmap has not been discarded and if other screen actions have not invalidated the stored image.
This style is useful for small windows (for example, menus or dialog boxes) that are displayed briefly and then removed before other screen activity takes place. This style increases the time required to display the window, because the system must first allocate memory to store the bitmap.
作为一个位图保存屏幕图像被此类窗口遮盖的部分。窗口删除时,系统将使用保存的位图来还原屏幕图像,包括其他窗口遮挡。因此,系统并不发送 WM_PAINT 消息如果位图使用的内存不被丢弃,并且其他屏幕操作都不会失效所存储的图像就被遮住的窗口。
这种风格是有用的小窗口 (例如,菜单或对话框中),并简要显示然后删除其他屏幕活动发生之前。这种风格会增加因为系统必须首先分配的内存来存储位图来显示窗口,所需的时间。
CS_VREDRAW
Redraws the entire window if a movement or size adjustment changes the height of the client area.
如果移动或大小调整更改客户端区域的高度,重新绘制整个窗口。
(参考资料:https://msdn.microsoft.com/en-us/library/windows/desktop/ff729176(v=vs.85).aspx)
lpfnWndProc 窗口过程(窗口回调函数指针)
窗口回调函数的原型为:
LRESULT CALLBACK WindowProc( _In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam );
hwnd是消息响应的窗口句柄
uMsg是消息
WinMain的第一个参数,也可以用GetModuleHandle()动态获取
hIcon 图标句柄
设置窗口图标,LoadIcon(NULL,IDI_APPLICATION);是默认图标,也可以从自己的资源里面加载,那样的话LoadIcon的第一个参数用实例句柄。(我将会在下一篇的RC资源简单使用中详细说)
hCursor 光标指针句柄
默认的箭头是:LoadCursor(NULL, IDC_ARROW);
来自百度百科:http://baike.baidu.com/link?url=-GIckBl735_HY4ykNJlcEJAgpRNGBGErrTN1B1eYffn0u-sqgjQFp2_rwQYbg0umkh74yU-KN_rxX8zUa0X6fK
=======================================================
ATOM WINAPI RegisterClass( _In_ const WNDCLASS *lpWndClass );
#include "stdafx.h" #include <windows.h> #pragma comment(lib,"user32.lib") #pragma comment(lib,"gdi32.lib") LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); const TCHAR* AppName = TEXT("MyWindowClass"); int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPTSTR lpCmdLine, _In_ int nCmdShow) { WNDCLASS wc; //这里是在构建窗口类结构 wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc;//窗口回调函数指针 wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance;//实例句柄 wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);//默认图标 wc.hCursor = LoadCursor(NULL, IDC_ARROW);//默认指针 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW);//默认背景颜色 wc.lpszMenuName = NULL; wc.lpszClassName = AppName;//窗口类名 //注册窗口类 if (!RegisterClass(&wc)) { MessageBox(NULL, TEXT("注册窗口类失败!"), TEXT("错误"), MB_ICONERROR); return 0; } } //这是窗口回调函数,暂时什么也不做 LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { return DefWindowProc(hwnd, message, wParam, lParam); }
HWND WINAPI CreateWindowEx( _In_ DWORD dwExStyle, _In_opt_ LPCTSTR lpClassName, _In_opt_ LPCTSTR lpWindowName, _In_ DWORD dwStyle, _In_ int x, _In_ int y, _In_ int nWidth, _In_ int nHeight, _In_opt_ HWND hWndParent, _In_opt_ HMENU hMenu, _In_opt_ HINSTANCE hInstance, _In_opt_ LPVOID lpParam );
lpWindowName 窗口标题
dwStyle 窗口风格
你可以打开这种风格和关闭更改对话框框导航。要在创建窗口后,请更改此样式,请使用可以函数。
你可以打开这种风格和关闭更改对话框框导航。要在创建窗口后,请更改此样式,请使用可以函数。为用户创建windows和非模态对话框选项卡与工作停止,改变消息循环调用IsDialogMessage函数。
WS_OVERLAPPEDWINDOW=WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_THICKFRAME WS_MINIMIZEBOX|WS_MAXIMIZEBOX
创建无边框窗口:
SetWindowLong(hwnd,GWL_STYLE,WS_OVERLAPPED|WS_VISIBLE|WS_CLIPCHILDREN|WS_CLIPSIBLINGS);
x,y,nWidth,nHeight 窗口位置
x::相对于屏幕左上角的横坐标
y:相对于屏幕左上角的纵坐标
nWidth:宽度
nHeight:高度
hWndParent 父窗口句柄
hMenu 菜单句柄,一般为NULL
hInstance 实例句柄
lpParam
(MSDN解释:Pointer to a value to be passed to the window through the CREATESTRUCT structure (lpCreateParams member) pointed to by the lParam param of the WM_CREATE message. This message is sent to the created window by this function before it returns.If an application calls CreateWindow to create a MDI client window, lpParam should point to a CLIENTCREATESTRUCT structure. If an MDI client window calls CreateWindow to create an MDI child window, lpParam should point to a MDICREATESTRUCT structure. lpParam may be NULL if no additional data is needed.)
一般为NULL即可。
显示窗口(ShowWindow)
MSDN:https://msdn.microsoft.com/en-us/library/ms633548(VS.85).aspx
BOOL WINAPI ShowWindow( _In_ HWND hWnd, _In_ int nCmdShow );
第一个参数:窗口句柄
第二个参数:显示窗口的方式
WinMain函数传来的nCmdShow参数是系统希望窗口显示方式,比如我们使用ShellExcute运行一个exe时预设的显示方式,因此我们可以直接使用
ShowWindow(hwnd, nCmdShow);
但是我们可以使用我们自己喜欢的方法显示窗口,比如 SW_HIDE(隐藏窗口) SW_MAXIMIZE(最大化) SW_MINIMIZE(最小化) SW_SHOWMAXIMIZED(激活窗口并将其最大化) SW_SHOWMINIMIZED(激活窗口并将其最小化)SW_SHOW(正常显示)等等
更新窗口(UpdateWindow)
MSDN:https://msdn.microsoft.com/en-us/library/dd145167(v=vs.85).aspx
BOOL UpdateWindow( _In_ HWND hWnd );
让窗口立即重画无效区域,一般创建完窗口都调用这个让窗口立即显示。
消息循环
如果我们直接这样创建窗口,那么就会看到窗口一闪即逝,因为我们创建完窗口后,WinMain函数直接返回,程序退出,因此,我们需要守护进程,使用消息将主线程驻留内存。因此,我们需要加入一下代码,这些代码可以从消息队列中取出消息,并发送到回调函数(队列消息)
MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam;
创建窗口 代码:
#include "stdafx.h" #include <windows.h> #pragma comment(lib,"user32.lib") #pragma comment(lib,"gdi32.lib") LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); const TCHAR* AppName = TEXT("MyWindowClass"); int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPTSTR lpCmdLine, _In_ int nCmdShow) { WNDCLASS wc; HWND hwnd; //这里是在构建窗口类结构 wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc;//窗口回调函数指针 wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance;//实例句柄 wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);//默认图标 wc.hCursor = LoadCursor(NULL, IDC_ARROW);//默认指针 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW);//默认背景颜色 wc.lpszMenuName = NULL; wc.lpszClassName = AppName;//窗口类名 //注册窗口类 if (!RegisterClass(&wc)) { MessageBox(NULL, TEXT("注册窗口类失败!"), TEXT("错误"), MB_ICONERROR); return 0; } //创建窗口 int style = WS_OVERLAPPEDWINDOW; hwnd = CreateWindowEx(NULL,AppName,TEXT("窗口标题"),style,50,50,500,500,0,0,hInstance,0); if (hwnd==NULL) { MessageBox(NULL, TEXT("创建窗口失败!"), TEXT("错误"), MB_ICONERROR); return 0; } //无边框窗口 SetWindowLong(hwnd, GWL_STYLE, WS_OVERLAPPED | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS); //显示、更新窗口 ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); //消息循环 MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } //这是窗口回调函数,暂时什么也不做 LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { return DefWindowProc(hwnd, message, wParam, lParam); }
LRESULT CALLBACK WindowProc( _In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam );CALLBACK,WINAPI,APIENTRY等宏都是__stdcall标准调用约定,在我的第一篇文章中就已经提到这一点了( http://blog.csdn.net/zuishikonghuan/article/details/46327725)
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { int xPos, yPos; switch (uMsg) { case WM_CLOSE://询问窗口是否关闭 if (MessageBox(hwnd, TEXT("要关闭窗口吗?"), TEXT("请确认"), MB_OKCANCEL | MB_ICONQUESTION) == IDCANCEL) { return 0;//表示处理了此消息,系统不再处理 } break; case WM_DESTROY: PostQuitMessage(0);//退出消息循环,结束应用程序 return 0; break; case WM_LBUTTONDOWN: //让无边框窗口能够拖动(在窗口客户区拖动),下面两个任选其一 //PostMessage(hwnd, WM_SYSCOMMAND, 61458, 0); //PostMessage(hwnd, WM_NCLBUTTONDOWN, HTCAPTION, 0); break; case WM_MOUSEMOVE://鼠标移动 xPos = GET_X_LPARAM(lParam);//鼠标位置X坐标 yPos = GET_Y_LPARAM(lParam);//鼠标位置Y坐标 //不要用LOWORD和HIWORD获取坐标,因为坐标有可能是负的 break; default: break; } return DefWindowProc(hwnd, uMsg, wParam, lParam);//其他消息交给系统处理 }
#include "stdafx.h" #include <windows.h> #include <windowsx.h> #pragma comment(lib,"user32.lib") #pragma comment(lib,"gdi32.lib") LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); const TCHAR* AppName = TEXT("MyWindowClass"); int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPTSTR lpCmdLine, _In_ int nCmdShow) { WNDCLASS wc; HWND hwnd; //这里是在构建窗口类结构 wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc;//窗口回调函数指针 wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance;//实例句柄 wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);//默认图标 wc.hCursor = LoadCursor(NULL, IDC_ARROW);//默认指针 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW);//默认背景颜色 wc.lpszMenuName = NULL; wc.lpszClassName = AppName;//窗口类名 //注册窗口类 if (!RegisterClass(&wc)) { MessageBox(NULL, TEXT("注册窗口类失败!"), TEXT("错误"), MB_ICONERROR); return 0; } //创建窗口 int style = WS_OVERLAPPEDWINDOW; hwnd = CreateWindowEx(NULL,AppName,TEXT("窗口标题"),style,50,50,500,500,0,0,hInstance,0); if (hwnd==NULL) { MessageBox(NULL, TEXT("创建窗口失败!"), TEXT("错误"), MB_ICONERROR); return 0; } //无边框窗口 SetWindowLong(hwnd, GWL_STYLE, WS_OVERLAPPED | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS); //显示、更新窗口 ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); //消息循环 MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } //这是窗口回调函数 LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { int xPos, yPos; switch (uMsg) { case WM_CLOSE://询问窗口是否关闭 if (MessageBox(hwnd, TEXT("要关闭窗口吗?"), TEXT("请确认"), MB_OKCANCEL | MB_ICONQUESTION) == IDCANCEL) { return 0;//表示处理了此消息,系统不再处理 } break; case WM_DESTROY: PostQuitMessage(0);//退出消息循环,结束应用程序 return 0; break; case WM_LBUTTONDOWN: //让无边框窗口能够拖动(在窗口客户区拖动),下面两个任选其一 //PostMessage(hwnd, WM_SYSCOMMAND, 61458, 0); //PostMessage(hwnd, WM_NCLBUTTONDOWN, HTCAPTION, 0); break; case WM_MOUSEMOVE://鼠标移动 xPos = GET_X_LPARAM(lParam);//鼠标位置X坐标 yPos = GET_Y_LPARAM(lParam);//鼠标位置Y坐标 //不要用LOWORD和HIWORD获取坐标,因为坐标有可能是负的 break; default: break; } return DefWindowProc(hwnd, uMsg, wParam, lParam);//其他消息交给系统处理 }