为什么在组件创建或流传输期间不应该使用句柄?
我想制作一个自定义 VCL 控件,通过 SDL_CreateWindowFrom 函数包装 SDL 渲染表面。 SDL_CreateWindowFrom 采用现有的 HWND 句柄并在其上放置高性能渲染上下文(它有多个可用后端,包括 DirectX 和 OpenGL)。
帮助文件显示“不要在组件创建或流式传输期间引用 Handle 属性”。 但它没有说为什么。 它表示,当您第一次尝试访问 Handle 属性时,它将调用 HandleNeeded 以确保存在有效的句柄。
所以我有两个问题。 1:创建组件时不应该引用Handle属性的原因是什么? 2. 如果控件的整个要点是包装需要初始化 HWND 的渲染表面,那么什么时候执行(理想情况下)应该在创建/流式传输期间进行的初始化是安全的?
I want to make a custom VCL control that wraps a SDL rendering surface via the SDL_CreateWindowFrom function. SDL_CreateWindowFrom takes an existing HWND handle and puts a high-performance rendering context (it has several backends available, including DirectX and OpenGL) onto it.
The helpfile says "Do not refer to the Handle property during component creation or streaming." But it doesn't say why. It says that the first time you try to access the Handle property, it'll call HandleNeeded to ensure that a valid handle exists.
So I have two questions. 1: What's the reason why you shouldn't reference the Handle property during component creation? 2. If the entire point of the control is to wrap a rendering surface that requires a HWND to be initialized, when is it safe to perform the initialization that (ideally) ought to be taking place during creation/streaming?
从本质上讲,这是一个性能问题。 在流传输过程中,还可能发生其他“不良”副作用。 事情正处于“建设中期”,通常预期的一切可能都没有。
当您引用“Handle”属性时,这将启动句柄创建过程。 这是因为读取Handle实际上调用了GetHandle。 在流处理过程中过早执行此操作,您最多可能会得到较慢的流性能,最糟糕的是,可能会得到部分配置的“句柄”。
如果您需要从属性设置器中正确引用句柄,则应通过检查 HandleAllocation 来检查句柄是否已创建,然后才引用它。 如果您需要对句柄进行一些标志更改,例如调用 SetWindowLong() 或其他内容,那么您应该在组件实例中“缓存”该状态,然后覆盖 CreateWnd 并在此时应用这些设置。 另一种选择是在流式传输时推迟所有句柄访问(如果是 ComponentState 中的 csLoading),直到调用 Loaded 虚拟方法。
最后,您需要了解可能需要重新创建句柄的情况。 如果周围的表单或父组件的句柄经历重新创建过程,则可能会发生这种情况。 直到更新的 Windows 版本为止,更改某些窗口标志的唯一方法是销毁句柄并在 CreateWindowEx() 调用中使用新标志重新创建。 有许多组件仍然执行此操作。 您可以通过检查(ControlState 中的 csRecreating)了解您是否处于重新创建情况。
因此,要直接回答您的问题,最好的地方是重写 CreateWnd 并在那里完成您的工作。 CreateWnd 仅在创建句柄时才会被调用。 正确设计的组件在显示之前应该只调用一次 CreateWnd。
At it's core, it's a performance thing. There are potentially other "bad" side-effects that can happen as well since during the streaming process. Things are in "mid-construction" and all that is normally expected to be there are probably not.
When you reference the "Handle" property, this will initiate the handle creation process. This is because reading Handle actually calls GetHandle. Do this too soon in the streaming process, and you may end up with, at best, slower streaming performance, at worse, a partially configured "handle."
If you need to refer to the Handle properly from within a property setter, you should check if the handle has been created by checking HandleAllocated, and only then do you reference it. If you needed to make some flag changes to the handle like calling SetWindowLong() or something, then you should "cache" that state in the component instance and then override CreateWnd and apply those settings at that point. Another option is to defer all handle access while streaming (if csLoading in ComponentState then) until the Loaded virtual method is called.
Finally, you need to be aware of cases where your handle may need to get recreated. This can happen if the surrounding form or the parent component's handle goes through a recreate process. Up until more recent releases of Windows, the only way to change some window flags was to destroy the handle and recreate with new flags in the CreateWindowEx() call. There are many components that still do this. You know if you're in a recreate situation by checking (csRecreating in ControlState).
So to directly answer your question, the best place is to override CreateWnd and do your work in there. CreateWnd will only be called when the handle gets created. A properly designed component should get only one call to CreateWnd right before it is going to be shown.