关于窗口子类化需要注意的地方

关于窗口子类化需要注意的地方

作者:BlogUpdater |  时间:2020-07-14 |  浏览:1536 |  评论已关闭 条评论

看看有啥问题
Windows的窗口子类化(Subclassing)是一个比你想象的复杂的一种技术。我们先看看下面的代码有啥问题没有:

我想大部分人应该可以看着里面的问题来。

如果在”…do stuff…”阶段,其他人又一次进行了窗口子类化,将会发生什么呢?
当我们恢复窗口过程的时候,我们会重复执行两次解除子类化(Unsubclassing)操作。第一次是我们新设置的窗口过程,第二次是其他人设置的版本。如果第二个子类化中分配了内存(这是很常见的),则这个内存将会发生泄露,另外原本希望进行的窗口子类化也不会像预期那样工作。

请不要觉得窗口的子类化的添加或者移除像栈那样工作。如果你希望解除窗口子类化的时候发现你设置的窗口过程不是在设置链的顶部,则你将不能安全的执行这个操作。
你不得不需要等待一个合适的时间点来完成这项操作,在这个时间点之前,你的窗口过程会一直挂接在调用链上,并且你还需要不断地将收到的消息转发给之前的窗口过程。

还是要按照规则来
这看起来是一个比较复杂的事情,所以Windows外壳(Shell)开发团队实现了一些帮助函数来帮助你完成这些繁杂的事物。
SetWindowSubclass这个API会帮你干所有这些脏活:安装一个子类化的窗口过程,保存安装之前的窗口过程指针,并将引用数据传递到你提供的子类化窗口过程。

另外,你还可以使用DefSubclassProc这个API,当你在你自己的版本的窗口过程中处理完一个消息后,可以使用这个API可以将消息转发给调用链之前的一个窗口过程。

如果你希望将窗口过程从调用链中移除,则可以试试RemoveWindowSubclass这个API。当你的窗口过程不处于调用链的顶部时,这个函数也能按照预期的规则完成所有的底层工作。

还有一个在开发文档中没有解释的很清楚的地方是,在一个被子类化的窗口销毁之前,你必须移除串窗口的子类化。
通常可以使用如下两种方法来确保完成这个操作:
1) 一旦不再需要子类化了,则移除它。
2) 如果你正在永久性的子类化一个窗口,可以在新版本的窗口过程中添加一条对RemoveWindowSubclass的调用,如下图所示:

有人担心说,一个消息可能在SubclassWindow调用和前一个窗口过程的保存操作之间。通常,这是一个安全的操作,因为你的代码执行在一个拥有被子类化的窗口的线程中。
请注意一下,消息的发送只会在线程处于”接收消息”的状态,例如线程代码中调用了GetMessage或者PeekMessage。
如果有人在一个不处于接收状态的线程中尝试发送消息,则这个消息会一直等到线程调用GetMessage才会被投递。因为我们没有在SubclassWindow调用和保存旧的窗口过程(OldWndProc)之间调用任何和消息接收有关的API,所以不需要担心消息会在保存旧的窗口过程之前到来。

总结
子类化还是比较复杂的技术,大家可以先从MSDN看起。
今儿就先这么着。

评论已关闭。