为什么在组件创建或流式传输过程中不应使用句柄?

4
我想制作一个自定义的VCL控件,通过SDL_CreateWindowFrom函数包装一个SDL渲染表面。SDL_CreateWindowFrom需要一个现有的HWND句柄,并将高性能渲染上下文(它有几个后端可用,包括DirectX和OpenGL)放到该句柄上。
帮助文件中说:“在组件创建或流式传输期间不要引用Handle属性。”但它没有说明原因。它说第一次尝试访问Handle属性时,它会调用HandleNeeded以确保存在有效的句柄。
所以我有两个问题。1:为什么在组件创建期间不应引用Handle属性?2. 如果该控件的整个目的是包装需要初始化HWND的渲染表面,那么什么时候才能安全地执行理应在创建/流传期间进行的初始化呢?

关于http://stackoverflow.com/questions/578725/when-is-tgraphiccontrol-paint-called的问题:如果你想要调用SDL_CreateWindowFrom(),你应该从TCustomControl继承,而不是从TGraphicControl继承。 - mghie
是的。我在提问后注意到了这一点。“嘿!这甚至没有一个HWND句柄!” - Mason Wheeler
2个回答

15

本质上是性能问题。在流媒体过程中可能会发生其他“不良”副作用。事情正在“半途而废”,通常所期望的所有内容都可能不存在。

当您引用“Handle”属性时,这将启动句柄创建过程。因为读取 Handle 实际上调用了 GetHandle。如果在流媒体过程中过早地执行此操作,则最多会导致较慢的流媒体性能,在更糟糕的情况下,会出现部分配置不正确的“句柄”。

如果需要在属性 setter 中正确引用 Handle,则应通过检查 HandleAllocated 来检查该句柄是否已创建,然后再引用它。如果需要对句柄进行一些标志更改,例如调用 SetWindowLong(),则应在组件实例中“缓存”该状态,然后覆盖 CreateWnd 并在该点应用这些设置。另一个选择是在流媒体过程中推迟所有句柄访问(如果 ComponentState 中 csLoading 为真),直到调用 Loaded 虚拟方法。

最后,您需要意识到的一种情况是,您的句柄可能需要重新创建。如果周围的窗体或父组件的句柄经历重建过程,则可能会发生这种情况。在更早的 Windows 版本中,更改某些窗口标志的唯一方法是销毁句柄,并在 CreateWindowEx() 调用中使用新标志重新创建。仍有许多组件这样做。通过检查 (ControlState 中 csRecreating) 来了解是否处于重建情况。

因此,直接回答您的问题,最好的位置是覆盖 CreateWnd 并在其中完成工作。仅当句柄将要显示时,应向适当设计的组件获取一次 CreateWnd 调用。


3
回答您的第二个问题:假设您的控件是一个 TCustomControl,您应该重写 CreateWindowHandle()。这样做的好处是每次为控件创建新的窗口句柄时都会正确地重复所有初始化操作。这允许更改一些无法在不重新创建窗口的情况下设置或重置的窗口样式标志。它还可以通过在不需要句柄时释放它并稍后重新创建它来节省资源。

那么我想要重写CreateWnd和DestroyWnd...还有其他的吗? - Mason Wheeler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接