Почему вы не должны использовать дескриптор во время создания компонента или потоковой передачи? - PullRequest
4 голосов
/ 24 февраля 2009

Я хочу создать пользовательский элемент управления VCL, который обертывает поверхность рендеринга SDL через функцию SDL_CreateWindowFrom. SDL_CreateWindowFrom берет существующий дескриптор HWND и помещает высокопроизводительный контекст рендеринга (он имеет несколько доступных бэкэндов, включая DirectX и OpenGL).

В справочном файле написано «Не ссылаться на свойство Handle во время создания или потоковой передачи компонента». Но это не говорит почему. В нем говорится, что при первой попытке доступа к свойству Handle он вызовет HandleNeeded, чтобы убедиться, что существует действительный дескриптор.

Итак, у меня два вопроса. 1: Почему вы не должны ссылаться на свойство Handle при создании компонента? 2. Если весь смысл элемента управления состоит в том, чтобы обернуть поверхность рендеринга, которая требует инициализации HWND, когда безопасно выполнить инициализацию, которая (в идеале) должна иметь место при создании / потоковой передаче?

Ответы [ 3 ]

14 голосов
/ 24 февраля 2009

По сути, это вопрос производительности. Потенциально возможны и другие «плохие» побочные эффекты, которые могут возникнуть и в процессе потоковой передачи. Вещи находятся в "середине конструкции", и все, что обычно ожидается, там, вероятно, нет.

Когда вы ссылаетесь на свойство «Дескриптор», это инициирует процесс создания дескриптора. Это потому, что чтение Handle на самом деле вызывает GetHandle. Сделайте это слишком рано в процессе потоковой передачи, и вы можете в лучшем случае снизить производительность потоковой передачи, а в худшем - частично сконфигурированный «дескриптор».

Если вам нужно правильно обратиться к дескриптору из установщика свойств, вам следует проверить, был ли создан дескриптор, путем проверки HandleAllocated, и только тогда вы ссылаетесь на него. Если вам нужно было внести некоторые изменения флага в дескриптор, например, вызвать SetWindowLong () или что-то еще, вы должны «кэшировать» это состояние в экземпляре компонента, а затем переопределить CreateWnd и применить эти настройки в этой точке. Другой вариант - отложить весь доступ к дескриптору во время потоковой передачи (если тогда csLoading в ComponentState) до вызова виртуального метода Loaded.

Наконец, вам нужно знать о случаях, когда ваш дескриптор может потребоваться воссоздать. Это может произойти, если окружающая форма или дескриптор родительского компонента проходит процесс воссоздания. Вплоть до более поздних выпусков Windows единственным способом изменения некоторых флагов окна было уничтожение дескриптора и воссоздание с новыми флагами в вызове CreateWindowEx (). Есть много компонентов, которые все еще делают это. Вы знаете, находитесь ли вы в ситуации воссоздания, проверяя (csRecreating в ControlState).

Так что, чтобы напрямую ответить на ваш вопрос, лучше всего переопределить CreateWnd и выполнить свою работу там. CreateWnd будет вызываться только тогда, когда будет создан дескриптор. Правильно спроектированный компонент должен получить только один вызов CreateWnd прямо перед его отображением.

3 голосов
/ 24 февраля 2009

Чтобы ответить на ваш второй вопрос: предполагая, что ваш элемент управления - TCustomControl , вам, вероятно, следует переопределить CreateWindowHandle () . Преимущество заключается в том, что вся инициализация правильно повторяется каждый раз, когда для элемента управления создается новый дескриптор окна. Это позволяет изменить некоторые флаги стиля окна, которые нельзя установить или сбросить без воссоздания окна. Это также позволяет экономить ресурсы, освобождая дескриптор, когда он не нужен, и восстанавливая его позже.

1 голос
/ 25 февраля 2009

См. Также этот вопрос в чем разница между createwnd-and-createwindowhandle и даже более подробные ответы о том, что делать и когда ...

...