我们知道Windows的窗口消息处理函数是C方式, 面向过程的, 所以窗口框架的基本任务就是将它转成面向对象的方式, 确切的说如何将消息处理函数第一参数HWND转成对象指针。

关于这个问题, 其实网上大家已经说滥了,  这里只是简单记录一下。

Map方式:MFC就是采用这种方式, 就是建立一张从HWND到CWindow*的映射表, 每次收到消息都从Map中根据HWND找到CWindow*, 再进行调用

UserData的方式:CreateWindow时将最后一个附加数据设置为对象CWindow* 指针, 当收到第一个消息WM_NCCREATE时, 取出传过来的附加数据指针, 将该指针设置成窗口的UserData,  SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast(pThis)), 后面收到任何消息就可以直接调用GetWindowLongPtr(hWnd, GWLP_USERDATA)取出窗口指针, 进行面向对象方式的调用。

Thunk方式:这是ATL采用的方式,通过汇编代码,直接将窗口消息处理函数的第一个参数HWND改写成CWindow*, 然后进行面向对象方式的调用, 原理可以见我以前写的 理解ATL中的一些汇编代码

这里也有一篇文章总结了这些封装方式: MFC、ATL窗口消息封装机制对比分析

最近工作中要写一些简单窗口相关的代码, 考虑用什么方式封装窗口过程:
MFC肯定不引入, map方式也不考虑。
UserData方式太低效 ,而且窗口的UserData让框架用了,我们其他地方可能还要用呢。
ATL的Thunk方式不错, 但是我们不想引入COM, 也不想用ATL的库和代码。
原始的 C API方式, 依赖性和效率都最佳, 可惜就是不是面向对象的。

各有优缺,怎样才能熊掌和鱼翅兼得?

最后决定把ATL中窗口Thunk相关的核心代码剥离出来, 做一个完全独立的最基本窗口框架。我们框架的基本目标是可以让我们方便的开发一些简单的窗口, 所以去掉了ATL窗口中一些不常用或是可替代的东西, 只留下必须和最有用的。简单说来,把ATL中的CWindow给去掉了,它只是窗口API的封装, 我们可以直接调用API来实现;把CWinTraits给去掉了,因为它只是窗口风格的封装; 把SubClass和SuperClass也去掉了, 我们的简单窗口用不到这个特性; 把Dialog, Container和COM相关的都去掉了, 这些都不是窗口的核心部分。最后只留下,窗口注册创建, thunk和消息映射相关的代码。

测试了下,这个窗口框架基本上只有2个核心文件,完全独立, 可以直接放到任何现有框架中使用(ATL/WTL中使用可能要改下内部一些类名, 但是用了ATL/WTL肯定就不用这个框架了)。

测试代码: CAltWinTest.rar
posted on 2013-09-08 14:47 Richard Wei 阅读(4276) 评论(11)  编辑 收藏 引用 所属分类: windows desktop

FeedBack:
# re: 关于Windows窗口框架[未登录]
2013-09-08 17:46 | avlee
做一个最基本窗口框架,可以完全不要使用消息映射,也就可以不需要使用thunk了。  回复  更多评论
  
# re: 关于Windows窗口框架
2013-09-08 19:16 | Richard Wei
@avlee
嗯,关键我们希望是面向对象的, 方便的支持多实例, 并且希望是线程安全的,这个框架都很好的满足了。消息处理是窗口程序的根本, 所以简单方便的消息映射也很重要。  回复  更多评论
  
# re: 关于Windows窗口框架
2013-09-08 22:27 | jilei
也可以用 SetProp 吧  回复  更多评论
  
# re: 关于Windows窗口框架
2013-09-09 08:52 | Richard Wei
@jilei
不错, UserData方式也可以用SetProp存储, 但是低效同样也是它的缺点。  回复  更多评论
  
# re: 关于Windows窗口框架
2013-09-09 14:52 | 聂晏冰
UserData方式太低效 只需要取一次,何来低效之说? 求解  回复  更多评论
  
# re: 关于Windows窗口框架
2013-09-09 18:34 | Richard Wei
@聂晏冰

怎么取一次?
每次收到消息都要转的, 大概代码如下:
LRESULT CALLBACK XWindow::WndProc(HWND hWnd, UINT uMsg,
WPARAM wParam, LPARAM lParam)
{
XWindow* pThis = NULL;
if (WM_NCCREATE == uMsg)
{
assert(!::IsBadReadPtr((void*)lParam, sizeof(CREATESTRUCT)));
LPCREATESTRUCT lpcs = reinterpret_cast(lParam);
pThis = static_cast(lpcs->lpCreateParams);
pThis->m_hWnd = hWnd;

assert(!::IsBadReadPtr(pThis, sizeof(XWindow)));
::SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast(pThis));
}
else
pThis = reinterpret_cast(::GetWindowLongPtr(hWnd, GWLP_USERDATA));

if (pThis)
return pThis->MsgProc(hWnd, uMsg, wParam, lParam);
else
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}  回复  更多评论
  
# re: 关于Windows窗口框架
2013-09-24 21:34 | 多多
现在很多界面框架采用DirectUI的方式,一个窗口中的所有控件都由自己绘制,而不是像使用API那样每个控件都对应一个窗口类,简单的说就是一个窗口和它内嵌的所有控件都只属于一个窗口类,对应一个HWND。窗口和控件的消息不依赖于API,完全由自己定义。

优点是灵活性非常大,窗口和控件的风格完全由自己决定,不受限于系统风格。
框架接口完全不依赖于系统API,使用者完全不用关系系统的事件和消息,只需要使用你提供的事件和消息就行。
缺点是工作量较大,每个控件都要自己重写,每个控件的事件和消息都要重新考虑。

QQ就是用的这种方式,另外很流行的界面框架Qt也是用的这种方式。  回复  更多评论
  
# re: 关于Windows窗口框架
2015-07-02 21:06 |
@多多 自己决定风格跟是不是directui没啥关系。
  回复  更多评论
  
# re: 关于Windows窗口框架
2015-07-02 21:11 | 多多
@龙 你没看懂我在说什么。  回复  更多评论
  
# re: 关于Windows窗口框架
2015-08-07 13:10 | 溪流
@多多
大概是你没看懂他在说什么吧  回复  更多评论
  
# re: 关于Windows窗口框架
2015-08-14 11:56 | 多多
@溪流
我是说用directui可以决定自己的风格,不是说决定自己的风格要用directui。这下懂了没?

也是强行秀自己语文……  回复  更多评论
  

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理