用相同的窗口类多次调用RegisterWindow的副作用?
我目前正在开发一个小的测试应用程序,并且有多个窗口对象在浮动,它们每个都使用相同的WNDCLASSEX结构调用RegisterWindowEx(主要是因为它们都是同一类的实例)。
第一个注册正确,然后多个失败,表示类已按预期注册。
我的问题是这不好吗?我在考虑使用哈希表存储ATOM结果,以便在调用RegisterWindow之前进行查找,但是Windows似乎已经这样做了?
解决方案
我们只需要调用一次RegisterWindowEx,然后在创建窗口时使用生成的ATOM。在几个窗口中重复使用此ATOM不会有任何问题。
我们可以测试以前是否已调用GetClassInfoEx来注册窗口类。
If the function finds a matching class and successfully copies the data, the return value is nonzero.
http://msdn.microsoft.com/zh-CN/library/ms633579(VS.85).aspx
这样,我们可以根据GetClassInfoEx的返回条件有条件地注册窗口类。
如果窗口类是在DLL中定义的
也许我们应该在DllMain的PROCESS_ATTACH部分中调用RegisterClass()
,并在DllMain的PROCESS_DETACH部分中调用UnregisterClass()
如果窗口类在可执行文件中定义
也许我们应该在消息循环之前在主体中调用RegisterClass()
,在消息循环之后在主体中调用UnregisterClass()
。
在对象构造函数中注册将是一个错误
因为我们可以通过反射在析构函数中对其进行清理。如果一个窗口被销毁,则将调用析构函数,然后...如果我们还有其他窗口在浮动...
而且,使用全局数据来计算活动注册的数量将需要适当的同步,以确保代码是线程友好的(即使不是线程安全的)。
为什么在main / DllMain中?
因为我们正在注册某种全局对象(至少对于进程而言)。因此,以"全局方式"(在主体中或者在DllMain中)对其进行初始化是有意义的。
为什么它不是那么邪恶?
因为Windows不会仅仅因为我们多次注册而失败。
一个干净的代码会使用GetClassInfo()来询问该类是否已经注册。
但是同样,Windows不会崩溃(至少由于这个原因)。
我们甚至可以避免取消注册窗口类,因为Windows将在进程结束时清除它们。我在有关该主题的MSDN博客上看到了相互矛盾的信息(两年前...不要再要求我找到该信息了)。
无论如何为什么它是邪恶的?
我个人的观点是,我们应该彻底处理资源,即分配一次,然后重新分配一次。仅仅因为Win32将清除泄漏的内存,并不应该阻止我们释放动态分配的内存。窗口类也是如此。
好吧,我们也许可以避免调用内核RegisterClass似乎需要停下来,但是窗口类是按进程和按模块进行的,因此,多次注册一个类不会对我们造成任何伤害。
鉴于通常没有那么多的类,因此发现它实际上是作为链接列表实现的,我不会感到惊讶。通过在哈希表中查找它可能会有所收获,但是最好将其作为一个简单的布尔值来做。
我最近有这个问题。为了解决这个问题,并将所有代码保留在相关的类中,而不是将其散布在程序中,我在调用RegisterClass()之前增加了一个类静态引用计数。然后,我忽略了ERROR_CLASS_ALREADY_EXISTS的返回值,并且当引用计数为0时,仅在dtor中调用了UnregisterClass()。
这不需要任何锁定(使用InterlockedIncrement()和InterlockedDecrement()来管理引用计数),这意味着该类的使用不需要知道或者关心该类在内部使用隐藏窗口。