用相同的窗口类多次调用RegisterWindow的副作用?

时间:2020-03-06 14:53:54  来源:igfitidea点击:

我目前正在开发一个小的测试应用程序,并且有多个窗口对象在浮动,它们每个都使用相同的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()来管理引用计数),这意味着该类的使用不需要知道或者关心该类在内部使用隐藏窗口。