你的第一Windows程序——管理应用程序状态
管理应用程序状态
- WM_NCCREATE
- WM_CREATE
// 定义一个结构保存一些状态信息
struct StateInfo {
// ... (结构成员没有显示)
};
当你调用CreateWindowsEx函数,将结构指针传递给最后的void*参数。
StateInfo *pState = new (std::nothrow) StateInfo;
if (pState == NULL)
{
return 0;
}
// 在这里初始化结构成员 (示例没有显示).
HWND hwnd = CreateWindowEx(
0, // Optional window styles.
CLASS_NAME, // Window class
L"Learn to Program Windows", // Window text
WS_OVERLAPPEDWINDOW, // Window style
// Size and position
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, // Parent window
NULL, // Menu
hInstance, // Instance handle
pState // 额外的数据信息
);
当你收到WM_NCCREATE和WM_CREATE消息,每个消息的 lParam的参数指向CREATESTRUCT结构的指针。而后,CREATESTRUCT结构包含的指针传入CreateWindowEx。
CREATESTRUCT *pCreate = reinterpret_cast<CREATESTRUCT*>(lParam);
结构
中
的
lpCreateParams
成员
是
在
CreateWindowEx
中
指定
的
原始
void
指针
。计算lpCreateParams获取数据结构的指针。
pState = reinterpret_cast<StateInfo*>(pCreate->lpCreateParams);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pState);
最后一个函数的调用的目的是将StateInfo指针储存在窗口的实例数据中。一旦你这样做,你总是可以从窗口调用
GetWindowLongPtr得到指针。
LONG_PTR ptr = GetWindowLongPtr(hwnd, GWLP_USERDATA);
StateInfo *pState = reinterpret_cast<StateInfo*>(ptr);
每个窗口都有自己的实例数据,所以你可以创建多个窗口,每个窗口都有自己的数据结构的实例。这种方法是特别有用的,如果你定义一个窗口类和创建该类的多个窗口。比如你创建了一个自定义控件类。GetWindowsLongPtr调用可以很方便的封装在一个小的辅助函数。
inline StateInfo* GetAppState(HWND hwnd)
{
LONG_PTR ptr = GetWindowLongPtr(hwnd, GWLP_USERDATA);
StateInfo *pState = reinterpret_cast<StateInfo*>(ptr);
return pState;
}
现在你可以写你的窗口过程如下:
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
StateInfo *pState;
if (uMsg == WM_CREATE)
{
CREATESTRUCT *pCreate = reinterpret_cast<CREATESTRUCT*>(lParam);
pState = reinterpret_cast<StateInfo*>(pCreate->lpCreateParams);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pState);
}
else
{
pState = GetAppState(hwnd);
} switch (uMsg)
{ // Remainder of the window procedure not shown ... }
return TRUE;
}
面向对象的方法
// 伪代码 LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
StateInfo *pState; /* Get pState from the HWND. */ switch (uMsg)
{
case WM_SIZE:
HandleResize(pState, ...);
break; case WM_PAINT:
HandlePaint(pState, ...);
break; // And so forth.
}
}
// 伪代码 LRESULT MyWindow::WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_SIZE:
this->HandleResize(...);
break; case WM_PAINT:
this->HandlePaint(...);
break;
}
}
唯一的问题是用什么方法连接MyWindow::WindowProc。RegisterClass函数需要的窗口过程是一个函数指针,在此上下文中,你不能将指针传递给(非静态)成员函数。然而,你可以将指针传递给一个静态成员函数,然后委托给成员函数。以下是一个类模板,显示这种方法:
template <class DERIVED_TYPE>
class BaseWindow
{
public:
static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
DERIVED_TYPE *pThis = NULL; if (uMsg == WM_NCCREATE)
{
CREATESTRUCT* pCreate = (CREATESTRUCT*)lParam;
pThis = (DERIVED_TYPE*)pCreate->lpCreateParams;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pThis); pThis->m_hwnd = hwnd;
}
else
{
pThis = (DERIVED_TYPE*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
}
if (pThis)
{
return pThis->HandleMessage(uMsg, wParam, lParam);
}
else
{
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
} BaseWindow() : m_hwnd(NULL) { } BOOL Create(
PCWSTR lpWindowName,
DWORD dwStyle,
DWORD dwExStyle = 0,
int x = CW_USEDEFAULT,
int y = CW_USEDEFAULT,
int nWidth = CW_USEDEFAULT,
int nHeight = CW_USEDEFAULT,
HWND hWndParent = 0,
HMENU hMenu = 0
)
{
WNDCLASS wc = {0}; wc.lpfnWndProc = DERIVED_TYPE::WindowProc;
wc.hInstance = GetModuleHandle(NULL);
wc.lpszClassName = ClassName(); RegisterClass(&wc); m_hwnd = CreateWindowEx(
dwExStyle, ClassName(), lpWindowName, dwStyle, x, y,
nWidth, nHeight, hWndParent, hMenu, GetModuleHandle(NULL), this
); return (m_hwnd ? TRUE : FALSE);
} HWND Window() const { return m_hwnd; } protected: virtual PCWSTR ClassName() const = 0;
virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) = 0; HWND m_hwnd;
};
BaseWindow是一个抽象类,从特定的窗口了派生。例如,以下是一个简单的派生类的basewindow的声明:
class MainWindow : public BaseWindow<MainWindow>
{
public:
PCWSTR ClassName() const { return L"Sample Window Class"; }
LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
};
要创建一个窗口,调用BaseWindow::Create:
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow)
{
MainWindow win; if (!win.Create(L"Learn to Program Windows", WS_OVERLAPPEDWINDOW))
{
return 0;
} ShowWindow(win.Window(), nCmdShow); // Run the message loop. MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
} return 0;
}
纯虚拟的 BaseWindow::HandleMessage方法用于执行窗口过程。下面的执行是相当于开始显示窗口过程:
LRESULT MainWindow::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0; case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(m_hwnd, &ps);
FillRect(hdc, &ps.rcPaint, (HBRUSH) (COLOR_WINDOW+1));
EndPaint(m_hwnd, &ps);
}
return 0; default:
return DefWindowProc(m_hwnd, uMsg, wParam, lParam);
}
return TRUE;
}
注意,窗口句柄存储在成员变量(m_hwnd)所以我们不需要将它作为一个参数传递给HandleMessage。
许多现有的Windows编程框架,如Microsoft基础类(MFC)和活动模板库(ATL)使用的基本上类似于此处所示的方法。当然,一个完全广义的框架(如MFC)比这种相对简单的例子更复杂。
你的第一Windows程序——管理应用程序状态的更多相关文章
- 0x02 译文:Windows桌面应用Win32第一个程序
本节课我们将用C++ 写一个最简单的Windows 程序. 目录: 创建一个窗口 窗口消息 编写窗口过程 绘制窗口 关闭窗口 管理应用程序状态 代码如下: #ifndef UNICODE #defin ...
- SharePoint 创建列表并使用Windows Presentation Foundation应用程序管理列表
SharePoint创建列表并使用程序管理列表 列表是SharePoint开发者输入数据的方式之中的一个.使用Web界面创建一个列表并加入一些数据.过程例如以下: 1. 打开站点. 2 ...
- Windows程序内部运行机制 转自http://www.cnblogs.com/zhili/p/WinMain.html
一.引言 要想熟练掌握Windows应用程序的开发,首先需要理解Windows平台下程序运行的内部机制,然而在.NET平台下,创建一个Windows桌面程序,只需要简单地选择Windows窗体应用程序 ...
- 深入浅出话VC++(1)——Windows程序内部运行机制
一.引言 要想熟练掌握Windows应用程序的开发,首先需要理解Windows平台下程序运行的内部机制,然而在.NET平台下,创建一个Windows桌面程序,只需要简单地选择Windows窗体应用程序 ...
- CentOS学习笔记--程序管理
程序管理 一个程序被加载到内存当中运行,那么在内存内的那个数据就被称为程序(process).程序是操作系统上非常重要的概念, 所有系统上面跑的数据都会以程序的型态存在.那么系统的程序有哪些状态?不同 ...
- Windows内存管理[转]
本文主要内容:1.基本概念:物理内存.虚拟内存:物理地址.虚拟地址.逻辑地址:页目录,页表2.Windows内存管理3.CPU段式内存管理4.CPU页式内存管理 一.基本概念1. 两个内存概念物理内存 ...
- windows程序员进阶系列:《软件调试》之堆 (一)
windows程序员进阶系列:<软件调试>之堆 (一) 堆是软件在运行时动态申请内存空间的主要途径.从堆上申请来的空间需要程序员自己申请和释放,且申请和释放操作必须绝对匹配.忘记释放或者多 ...
- 第十七章、程序管理与 SELinux 初探
---恢复内容开始--- 什么是程序 (process) 在 Linux 底下所有的命令与你能够进行的动作都与权限有关, 而系统依据UID/GID以及文件的属性相关性判定你的权限!在 Linux 系统 ...
- windows虚拟内存管理
内存管理是操作系统非常重要的部分,处理器每一次的升级都会给内存管理方式带来巨大的变化,向早期的8086cpu的分段式管理,到后来的80x86 系列的32位cpu推出的保护模式和段页式管理.在应用程序中 ...
随机推荐
- Html.Action、html.ActionLink与Url.Action的区别
1.html.ActionLink返回的指向指定controller.指定action的超链接标签<a>标签.如果没有指定controller,则默认为本页面对应的Controller. ...
- Entity Framework 新增实体,新增抽象实体
抽象实体不能new 抽象类:人,实体类:学生 人 p_人= new 学生(); 添加数据,学生和人都添加 抽象类可以提供一个抽象的方法,但是并没有实现,类似接口,但又不同于接口.子类继承父类时必须 ...
- 用CAGradientLayer实现渐变色动画
效果图: github:https://github.com/RinpeChen/CAGradientLayerBasicDemo
- Zend Framework
参考:http://www.php100.com/manual/ZendFramework/index.html 1.1. 概述 Zend Framework (ZF) 是一个开放源代码的 PHP5 ...
- 关于操作DC时的资源泄露
首先应明确一个概念 句柄, 关于句柄的详细介绍请见这里 对于句柄的使用小结:借来的要归还,创建的要释放,选出的要选入[尤其是针对GDI的一些句柄而言,如HPEN,HBRUSH等] 1. 使用GetDC ...
- nodejs远程获取图片
if(require("http")) { var http = require("http"); http.g ...
- 总结一下apply和call的异同点
call, apply都属于Function.prototype的一个方法,它是JavaScript引擎内在实现的,因为属于Function.prototype,所以每个Function对象实例,也就 ...
- JS函数定义与匿名函数的调用
一.函数声明.函数表达式.匿名函数 函数声明:function fnName () {…};使用function关键字 声明一个函数,再指定一个函数名,叫函数声明. 函数表达式 var fnName ...
- Solr 单机配置
一. 准备软件 提前安装好Java1.8和Tomcat9 下载Solr6.1,官网位置:http://mirrors.tuna.tsinghua.edu.cn/apache/lucene/solr/6 ...
- 装饰者模式 (decorator pattern)
参考 : Head First 设计模式(中文版) 这篇只作为个人温习! 用意 : 动态地给一个对象添加|扩展一些行为.Decorator 强调用对象组合而非继承来实现扩展,这显得较为灵活. 角色: ...