中年人学C语言Windows程序设计,2 第一个windows窗口
要想创建窗口,首先要注册一个窗口类,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;
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.
如果移动或大小调整更改客户端区域的高度,重新绘制整个窗口。
窗口回调函数的原型为:
LRESULT CALLBACK WindowProc(
_In_ HWND hwnd,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
hwnd是消息响应的窗口句柄
uMsg是消息
wParam和lParam是消息附加值
额外 的 字节 分配给 窗口类 结构 数目 。默认 为 零 字节 。
额外 分配 的 字节 后 窗口 实例 数 。 系统 初始化 为 零 字节 。 如果 应用程序 使用 WNDCLASS 注册 对话框 中 的 资源 文件 中 使用 类 指令 创建 的 它 必须 将 此 成员 设置 为 DLGWINDOWEXTRA 。(就是说普通的窗口为0即可)
WinMain的第一个参数,也可以用GetModuleHandle()动态获取
设置窗口图标,LoadIcon(NULL,IDI_APPLICATION);是默认图标,也可以从自己的资源里面加载,那样的话LoadIcon的第一个参数用实例句柄。(我将会在下一篇的RC资源简单使用中详细说)
默认的箭头是:LoadCursor(NULL, IDC_ARROW);
=======================================================
IDC_APPSTARTING 标准的箭头和小沙漏
IDC_ARROW 标准的箭头
IDC_CROSS 十字光标
IDC_HAND Windows 98/Me, Windows 2000/XP: Hand
IDC_HELP 标准的箭头和问号
IDC_IBEAM 工字光标
IDC_ICON Obsolete for applications marked version 4.0 or later.
IDC_NO 禁止圈
IDC_SIZE Obsolete for applications marked version 4.0 or later. Use IDC_SIZEALL.
IDC_SIZEALL 四向箭头指向东、西、南、北
IDC_SIZENESW 双箭头指向东北和西南
IDC_SIZENS 双箭头指向南北
IDC_SIZENWSE 双箭头指向西北和东南
IDC_SIZEWE 双箭头指向东西
IDC_UPARROW 垂直箭头
IDC_WAIT 沙漏,Windows7系统下会显示为选择的圆圈表示等待
同理,也可以用自己资源里的光标指针,那样的话LoadCursor的第一个参数用实例句柄。(与使用自定义图标相同)
这个可以是一个画刷句柄,也可以用已下预定值:COLOR_ACTIVEBORDER,COLOR_ACTIVECAPTION,COLOR_APPWORKSPACE,COLOR_BACKGROUND,COLOR_BTNFACE,COLOR_BTNSHADOW,COLOR_BTNTEXT,COLOR_CAPTIONTEXT,COLOR_GRAYTEXT,COLOR_HIGHLIGHT,COLOR_HIGHLIGHTTEXT,COLOR_INACTIVEBORDER,COLOR_INACTIVECAPTION,COLOR_MENU,COLOR_MENUTEXT,COLOR_SCROLLBAR,COLOR_WINDOW,COLOR_WINDOWFRAME,COLOR_WINDOWTEXT
当 此 成员 为 NULL 时 , 应用程序 必须 绘制 自己 的 背景 , 每当 它 请求 时 , 在 其 客户 区 绘制 。 若要 确定 是否 必须 绘制 背景 , 应用程序 可以 处理 WM_ERASEBKGND 消息 或 测试 由 BeginPaint 函数 的 PAINTSTRUCT 结构 的 成员 。
系统窗口默认颜色使用:(HBRUSH)(COLOR_WINDOW);
使用喜欢的颜色可以用CreateSolidBrush创建一个画刷,例如黑色:CreateSolidBrush(0x00FFFFFF+1); 注意颜色值需要加1,0x00FFFFFF也可以用RGB宏
使用资源里的菜单,不要菜单使用NULL即可
如果 lpszClassName 是 一个 字符串 , 它 指定 窗口 类 的 名称 。 类 名称 可以 是 任何 注册 与 RegisterClass 或 RegisterClassEx , 或 任何 预定义 的 控件类 名称 的 名称 。
LpszClassName 的 最大 长度 为 256 。 如果 lpszClassName 是 大于 最 大 长度 , RegisterClass 函数 将 失败 。
下面演示一下如何注册一个窗口类:
注册窗口类,就是使用RegisterClass或RegisterClassEx函数,我习惯使用RegisterClass,此API函数的原型:
ATOM WINAPI RegisterClass(
_In_ const WNDCLASS *lpWndClass
只有一个参数,就是一个WNDCLASS结构的指针。
我喜欢使用CreateWindowEx函数,当然你也可以使用CreateWindow函数。
CreateWindowEx函数原型如下:
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
);
CreateWindowEx函数返回我们创建的窗口的窗口句柄,失败返回NULL,调用GetLastError获取错误码。
dwExStyle 窗口扩展风格
WS_EX_ACCEPTFILES
窗口 接受 拖放 文件 。
WS_EX_APPWINDOW
把顶层的并且可见的窗口放到任务栏上。
WS_EX_CLIENTEDGE
该 窗口 有 一个 边界 与 凹下 的 边缘 。
WS_EX_COMPOSITED
涂料 中 使用 双缓冲 的 底部到顶部 绘画 顺序 的 窗口 的 所有 后代 。 有关 详细 信息 , 请参见 备注 。 如果 窗口 类 样式 的 CS_OWNDC 或 CS_CLASSDC , 这 不 能 使用 。
Windows 2000 : 这 种 风格 是 不 受支持 。
WS_EX_CONTEXTHELP
窗口 的 标题 栏 包含 一个 问号 。 当 用户 单击 问号 , 问号 指针 光标将 发生变化 。 如果 用户 然后 点击 一个 子 窗口 , 孩子 接受 WM_HELP 消息 。 子 窗口 应 将 消息 传递 给 父 窗口 过程 , 应 调用 WinHelp 函数 使用 HELP_WM_HELP 命令 。 帮助 应用程序 将显示 一个 弹出 窗口 , 通常 包含 的 子 窗口 的 帮助 。
WS_EX_CONTROLPARENT
允许用户使用Tab键在窗口的子窗口间搜索。
WS_EX_DLGMODALFRAME
该 窗口 有 双 边框 ; 可以 (可选) 创建 窗口 标题 栏 与 通过 捕获 参数 中 指定 的 WS_CAPTION 样式 。
WS_EX_LAYERED
窗口是一个分层的窗口。如果该窗口有CS_OWNDC或CS_CLASSDC 的类样式,不能使用这种风格。
Windows 8: WS_EX_LAYERED 样式 支持 的 顶级 窗口 和 子 窗口 。 以前 的 Windows 版本 支持 WS_EX_LAYERED 仅 用于 顶级 窗口 。
WS_EX_LAYOUTRTL
如果 shell 语言 是 希伯来语 、 阿拉伯语 或 支持 阅读 顺序 排列 , 窗口 的 水平 起源 是 右 边缘 上 的 另一种 语言 。 增加 水平 值 前进 到 左边 。
WS_EX_LEFT
该 窗口 有 泛型 左对齐 属性 。 这 是 默认设置 。
WS_EX_LEFTSCROLLBAR
如果 shell 语言 是 希伯来语 , 阿拉伯语 或 另一种 语言 , 支持 阅读 顺序 排列 , 垂直 滚动 条 (如果 存在) 是 左边 的 客户端 区域 。 对于 其他 语言 , 该 样式 将 被 忽略 。
WS_EX_LTRREADING
使用 从左到右 的 阅读顺序 属性 显示 窗口 文本 。 这 是 默认设置 。
WS_EX_MDICHILD
窗口 是 一个 MDI 子 窗口 。(MDI是一个窗口中的一个窗口容器,就像VC6那种IDE里有很多小窗口)
WS_EX_NOACTIVATE
用 这 种 方式 创建 一个 顶级 窗口 不 成为 前台 窗口 ,当 用户 单击 它 时 。 该 系统 并 不 到 前台 带来 此 窗口 , 当 用户 最小化 或 关闭 前台 窗口 。(让一个窗口不能激活。)
要 激活 的 窗口 , 使用 SetActiveWindow。
窗口 在 不 在 默认情况下 会 出现 在 任务栏 上 。 若要 强制 窗口 在 任务栏 上 显示 , 请 使用 WS_EX_APPWINDOW 风格 。
WS_EX_NOINHERITLAYOUT
窗口 没有 传递 给 其子 窗口 的 窗口 布局 。
WS_EX_NOPARENTNOTIFY
用 这 种 方式 创建 的 子 窗口当它创建或销毁 不向 它 的 父 窗口 发送 WM_PARENTNOTIFY 消息。
WS_EX_NOREDIRECTIONBITMAP
窗口 不 呈现 到 重定向 的 曲面 。 这 是 为 windows 没有 可见 的 内容 , 或 使用 其他 表面 机制 来 提供 他们 的 视觉 。
WS_EX_OVERLAPPEDWINDOW(WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE)
该 窗口 是 重叠 的 窗口 。
WS_EX_PALETTEWINDOW(WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST)
窗口 为 组件面板 窗口 , 是 一个 非模态 的 对话框 , 提出了 一 系列 的 命令 。
WS_EX_RIGHT
该 窗口 有 泛型 “右对齐” 属性 。 这 取决 于 窗口 类 。 这 种 风格 有 影响 , 只有 当 壳 语言 是 希伯来语 , 阿拉伯语 或 另一种 语言 , 它 支持 阅读顺序 排列 ; 否则 , 将忽略 该 样式 。
使用 静态 WS_EX_RIGHT 样式 或 编辑 控件 已 分别 在 使用 SS_RIGHT 或 ES_RIGHT 的 风格 , 相同 的 效果 。 使用 这 种 风格 与 按钮 控件 已 使用 BS_RIGHT 和 BS_RIGHTBUTTON 样式 相同 的 效果 。
WS_EX_RIGHTSCROLLBAR
垂直 滚动 条 (如果 存在) 是 右侧 的 工作 区 。 这 是 默认设置 。
WS_EX_RTLREADING
如果 shell 语言 希伯来语 、 阿拉伯语 或 支持 阅读顺序 排列 的 另一种 语言 , 使用 从 右到左 阅读顺序 属性 显示 窗口 文本 。 对于 其他 语言 , 该 样式 将 被 忽略 。
WS_EX_STATICEDGE0x00020000L
该 窗口 有 打算 三维 边框 样式
WS_EX_TOOLWINDOW
窗口被打算用于作为一个浮动的工具栏。工具窗口具有小于正常的标题栏,标题栏和使用较小的字体绘制窗口标题。工具窗口不会出现在任务栏中或在当用户按下ALT + TAB时出现的对话框中。如果一个工具窗口的系统菜单,其图标不会显示在标题栏上。但是,你可以显示系统菜单,通过右键单击或键入ALT + 空格。
WS_EX_TOPMOST
窗口应放置高于一切非最顶层的窗口,并应该呆在上面,甚至当窗口被停用。要添加或删除此样式,请使用SetWindowPos函数。(让窗口始终处于Z序顶端,说白了就是总在最前面,对Win8 Metro界面和应用无效,据说需要uiAccess和组策略许可(正在研究中,这只是我的一些猜测))
WS_EX_TRANSPARENT
窗口对于消息是透明的,比方说,鼠标点击此窗口时鼠标消息会穿透到下一个窗口。
WS_EX_WINDOWEDGE
窗口 有 一个 凸起 的 边缘 带有 边框 。
加红色的是我经常用的,其他的我一般用不着
lpClassName 窗口类名
就是我们创建窗口类时的类名
lpWindowName 窗口标题
dwStyle 窗口风格
WS_BORDER
该 窗口 有 一个 细线 边框 。
WS_CAPTION
该 窗口 有 标题 栏 (包括 WS_BORDER 样式)。
WS_CHILD
窗口 是 一个 子 窗口 。 使用 这 种 样式 的 窗口 不能 有 一个 菜单 栏 。 这 种 风格 不能 用 的 WS_POPUP 样式 。
WS_CHILDWINDOW
WS_CHILD 样式 相同 。
WS_CLIPCHILDREN
不包括 在 父 窗口 中 绘图 时 , 子 窗口 所 占据 的 区域 。 创建 父 窗口 时 ,将 使用 这 种 风格 。
WS_CLIPSIBLINGS
剪辑 子 窗口 相对于 彼此 ; 那 就 是 , 当 一个 特定 的 子 窗口 收到 WM_PAINT 消息 时 , WS_CLIPSIBLINGS 风格 剪辑 出 子 窗口 要 更新 该 地区 所有 其他 重叠 的 子 窗口 。 如果 未 指定 WS_CLIPSIBLINGS 和 子 窗口 重叠 , 它 是 可能 的 绘制 一个 子 窗口 , 绘制 一个 相邻 的 子 窗口 客户端 区域 内 客户端 区域 内 时 。
WS_DISABLED
最初 禁用 窗口 。 禁用 的 窗口 不能 接收 用户 的 输入 。 要 更改 此 设置在 创建 窗口 后 , 请 使用 EnableWindow 函数 。
WS_DLGFRAME
该 窗口 有 边框 的 样式 通常 用于 对话框 。 使用 这 种 样式 的 窗口 不能 有 一个 标题 栏 。
WS_GROUP
窗口 为 一 组 控件 的 第一个 控件 。 该 小组 包括 这 第一个 控件 和 在 它 以后 , 到 下一个 控件 与 WS_GROUP 样式 定义 的 所有 控件 。 每个 组 中 的 第一个 控件 通常 具有 WS_TABSTOP 风格 以便 用户 可以 移动 到 组 。 用户 随后 可以 更改 键盘 焦点 从 一个 控件 组 到 组 中 的 下一个 控件 的 使用 方向 下, 键 。
你可以打开这种风格和关闭更改对话框框导航。要在创建窗口后,请更改此样式,请使用可以函数。
WS_HSCROLL
该 窗口 有 一个 水平 滚动 条 。
WS_ICONIC
窗口 是 最初 最小化 。 WS_MINIMIZE 样式 相同 。
WS_MAXIMIZE
该 窗口 最初 最大化 。
WS_MAXIMIZEBOX
该 窗口 有 最大化 按钮 。 不 能 与 WS_EX_CONTEXTHELP 样式 组合 。 此外 必须 指定 WS_SYSMENU 风格 。
WS_MINIMIZE
窗口 是 最初 最小化 。 WS_ICONIC 样式 相同 。
WS_MINIMIZEBOX
该 窗口 有 最小化 按钮 。 不 能 与 WS_EX_CONTEXTHELP 样式 组合 。 此外 必须 指定 WS_SYSMENU 风格 。
WS_OVERLAPPED
该 窗口 是 重叠 的 窗口 。 重叠 的 窗口 具有 标题 栏 和 边框 。 WS_TILED 样式 相同 。
WS_OVERLAPPEDWINDOW(WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | 带有最小 | WS_MAXIMIZEBOX)
该 窗口 是 重叠 的 窗口 。 WS_TILEDWINDOW 样式 相同 。
WS_POPUP
Windows 是 一个 弹出式 窗口 。 这 种 风格 不能 用 WS_CHILD 风格 。
WS_POPUPWINDOW(WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX)
窗口 是 一个 弹出式 窗口 。 WS_CAPTION 和 WS_POPUPWINDOW 样式 必须 结合起来 , 使 窗口 菜单中 可见 。
WS_SIZEBOX
该 窗口 有 大小调整 边框 。 WS_THICKFRAME 样式 相同 。
WS_SYSMENU
窗口 具有 其 标题 栏 上 的 窗口 菜单 。 此外 必须 指定 WS_CAPTION 风格 。
WS_TABSTOP
窗口 是 一个 控件 , 当 用户 按 TAB 键 可以 接收 键盘 焦点 。 按 TAB 键 将 键盘 焦点 更改 到 下一个 控件 具有 WS_TABSTOP 风格 。
你可以打开这种风格和关闭更改对话框框导航。要在创建窗口后,请更改此样式,请使用可以函数。为用户创建windows和非模态对话框选项卡与工作停止,改变消息循环调用IsDialogMessage函数。
WS_THICKFRAME
该 窗口 有 大小调整 边框 。 WS_SIZEBOX 样式 相同 。
WS_TILED
该 窗口 是 重叠 的 窗口 。 重叠 的 窗口 具有 标题 栏 和 边框 。 WS_OVERLAPPED 样式 相同 。
WS_TILEDWINDOW(WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | 带有最小 | WS_MAXIMIZEBOX)
该 窗口 是 重叠 的 窗口 。 WS_OVERLAPPEDWINDOW 样式 相同 。
WS_VISIBLE
窗口 是 最初 可见 。 这 种 风格 可以 打开 和 关闭 利用 橱窗 或 SetWindowPos 函数 。
WS_VSCROLL
该 窗口 有 一个 垂直 滚动 条 。
注意不是所有的风格组合都可以直接在CreateWindowEx里面使用的,有的风格即使用了也相当于没用,用Spy++看就根本没有这些风格,只能用SetWindowLong手动设置。
我比较常用的风格:
WS_OVERLAPPED:产生一个层叠的窗口,一个层叠的窗口有一个标题栏和一个边框。
WS_CAPTION:创建一个有标题栏的窗口。
WS_SYSMENU:创建一个在标题栏上带有系统菜单的窗口,要和WS_CAPTION类型一起使用。
WS_THICKFRAME:创建一个具有可调边框的窗口。
WS_MINIMIZEBOX:创建一个具有最小化按钮的窗口,必须同时设定WS_ SYSMENU类型。
WS_MAXIMIZEBOX:创建一个具有最大化按钮的窗口,必须同时设定WS_ SYSMENU类型。
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即可。
当调用完 CreateWindow 函数的时候,应用程序实例以及相应的消息队列已经诞生了。
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(正常显示)等等
MSDN:https://msdn.microsoft.com/en-us/library/dd145167(v=vs.85).aspx
BOOL UpdateWindow(
_In_ HWND hWnd
);
让窗口立即重画无效区域,一般创建完窗口都调用这个让窗口立即显示。
用户的任何操作都被看作一个事件,操作系统会自动将该事件转换为相应的消息并投入该应用程序的消息队列等待处理
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
GetMessage 函数从应用程序的队列里取出一个消息,如果这个消息是 WM_QUIT(程序退出消息)就返回 0,结束 while 循环。如果不是退出消息,那么就执行 TranslateMessage 翻译消息,这个操作主要是对一些消息进行转换,例如把键盘的虚拟键消息转换为字符消息。
接着调用 DispatchMessage 将消息分派给相应的窗口过程。
注意:他不是直接通过 DispatchMessage 调用我们的窗口过程 WndProc,这里其实 DispatchMessage 是带着消息去找操作系统,然后再由操作系统调用 WndProc 窗口过程。看起来有点纠结哈,不过这就是消息机制的一个真实面目,操作系统为了绝对的控制权,时时刻刻都监控着应用程序的运行。
在窗口过程中,我们对感兴趣的消息进行监控并部署相应的代码,对不感兴趣的消息我们都扔给DefWindowProc,让操作系统以默认的方式来处理消息。
如果我们直接这样创建窗口,那么就会看到窗口一闪即逝,因为我们创建完窗口后,WinMain函数直接返回,程序退出,因此,我们需要守护进程,使用消息将主线程驻留内存。因此,我们需要加入一下代码,这些代码可以从消息队列中取出消息,并发送到回调函数(队列消息)
但是,此时窗口没有处理任何消息,所以你会发现即使关掉窗口程序也不会退出!!因为我们用消息循环让进程驻留内存了。如果想让窗口关闭时退出程序,就要在回调函数里做文章了。
效果图:
MSDN: https://msdn.microsoft.com/en-us/library/windows/desktop/ms633573(v=vs.85).aspx
函数原型
LRESULT CALLBACK WindowProc(
_In_ HWND hwnd,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
CALLBACK,WINAPI,APIENTRY等宏都是__stdcall标准调用约定,
hwnd是窗口句柄,uMsg是消息,wParam和lParam是消息附加值。
常用的消息:
WM_PAINT:窗口绘制消息(这篇文章不包含关于绘制窗口的内容,以后我会发布GdiplusFlat(不是Gdiplus哦!)连载,将于那时候详细说明)
WM_DESTROY:窗口销毁后(调用DestroyWindow()后),消息队列得到的消息。
WM_CLOSE:用户点击关闭按钮后收到的消息。
WM_LBUTTONDOWN:在窗口客户区域点击鼠标左键的时候发送。
WM_RBUTTONDOWN:在窗口客户区域点击鼠标右键的时候发送。
WM_ERASEBKGND:当窗口背景必须被擦除时发送。
WM_SETFOCUS:获取焦点后产生的消息。
WM_KILLFOCUS:失去焦点后产生的消息。
WM_CTLCOLORSTATIC:静态控件设置背景和文字颜色。
WM_CREATE:窗口创建完毕。
WM_SIZE:当主窗口的客户区部分大小改变时发送。
WM_MOVE:窗口被移动。
WM_MOUSEMOVE:鼠标移动时被发送至已获焦点的窗口。
WM_SETFONT:设置字体。
WM_SYSCOMMAND;让窗口的客户区拖动窗口(其中的一种办法):在 WM_LBUTTONDOWN 里 PostMessage(hwnd, WM_SYSCOMMAND, 61458, 0);
WM_MOUSELEAVE:鼠标离开窗口时发出的消息。我随后就写一篇博文详细说这个消息(大概一两章之后)
在VS2019中创建第一个windows窗口,在窗口的外观为Windows默认模式,窗口中显示"大家好,这是我的第一个Windows窗口程序!"
完整的窗口代码如下:
/* -------------------------------------------------------------------
中年人学C语言Windows程序设计
--------------------------------------------------------------------*/
#include
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
//主函数
// 四个参数:
//hInstance : 程序当前实例的句柄(handle to current instance),以后随时可以用GetModuleHandle(0)来获得
//hPrevInstance : 前一个实例的句柄(handle to previous instance),在Win32中,每一个进程都有一个独立的4G地址空间,从0到2G属于进程私有,对其他进程来说是不可见的。所以,在Win32中,hPrevInstance总是为NULL。
//szCmdLine : 指向以 / 0结尾的命令行,不包括EXE本身的文件名(pointer to command line),以后随时可以用GetCommandLine()来获取完整的命令行。
//iCmdShow : 指明应该以什么方式显示主窗口(show state of window)。
{
static TCHAR szAppName[] = TEXT("MyWindows");
HWND hwnd;//句柄
MSG msg;//消息
WNDCLASS wndclass;//定义窗口类结构
HBRUSH hRedBrush = CreateSolidBrush(RGB(200, 200, 200));//设定背景颜色
//结构成员:
wndclass.style = CS_HREDRAW | CS_VREDRAW; //窗口类型
wndclass.lpfnWndProc = WndProc; //窗口过程(必须是回调函数)
wndclass.cbClsExtra = 0;//预留的额外空间,一般为0
wndclass.cbWndExtra = 0;//预留的额外空间,一般为0
wndclass.hInstance = hInstance;//应用程序的实例句柄
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);//为所有基于该窗口类的窗口设定一个图标
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);//为所有基于该窗口类的窗口设定一个鼠标指针
//wndclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);//指定窗口背景色
wndclass.hbrBackground = hRedBrush;//指定窗口背景色
wndclass.lpszMenuName = NULL; //指定窗口菜单
wndclass.lpszClassName = szAppName; //指定窗口类名
//注册窗口类
if (!RegisterClass(&wndclass))
/* RegisterClass:注册在随后调用CreateWindow函数和CreateWindowEx函数中使用的窗口类。
参数为lpWndClass,指向一个WNDCLASS结构的指针
返回值:ATOM的宏定义
typedef WORD ATOM;
typedef unsigned short WORD;
关于RegisterClassEx:参数lpwcx指向一个WNDCLASSEX结构的指针如果函数成功,返回这个窗口类型的标识号;
如果函数失败,返回值为0。若想获得更多错误信息,请调用GetLastError函数。*/
{
MessageBox(NULL, TEXT("这个程序需要在Windows系统执行!"), szAppName, MB_ICONERROR);
return 0;
}
//实例化创建窗口
hwnd = CreateWindow(szAppName,// 窗口类名称
TEXT("中年人学C语言Windows程序设计"),// 窗口标题
WS_OVERLAPPEDWINDOW,// 窗口风格,或称窗口格式
CW_USEDEFAULT,// 初始 x 坐标
CW_USEDEFAULT, // 初始 y 坐标
CW_USEDEFAULT,// 初始 x 方向尺寸
CW_USEDEFAULT,// 初始 y 方向尺寸
NULL,// 父窗口句柄
NULL,// 窗口菜单句柄
hInstance,// 程序实例句柄
NULL);// 创建参数
/*_In_说明该参数是输入的,_opt_说明该参数是可选参数
函数成功返回窗口句柄,否则返回NULL*/
//显示窗口
ShowWindow(hwnd, iCmdShow);
/* 第一次调用时应使用WinMain的参数nCmdShow作为参数
如果窗口之前可见,则返回非0否则返回0*/
//更新窗口
UpdateWindow(hwnd);
/* 绕过消息队列直接向窗口过程发送WM_PAINT消息
函数调用成功返回非0*/
//消息循环
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
/*作用:从当前线程的消息队列里取出一个消息并放入MSG结构中,不能获得其他线程的消息
若消息队列为空,函数会一直等待到有消息到来才有返回值
返回值:
函数出现错误则返回 - 1,
获得WM_QUIT消息返回0
否则返回非0*/
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
//回调函数
//参数:
//hwnd : 窗口句柄
//message : 消息ID
//wParam和lParam:消息参数
{
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
switch (message)
{
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &rect);
DrawText(hdc, TEXT("大家好,这是我的第一个Windows窗口程序!"), -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);
}