Ответ Реми Лебо содержит объяснение того, что я сделал не так. Я включил это обновление, чтобы вы могли увидеть хитрые детали конкретного случая, который показывает, насколько тонкой может быть ошибка, сохраняющая ссылку на элемент управления VCL UI в фоновом потоке. Надеюсь, эта информация поможет вам отладить ваш собственный код.
Часть моего приложения включает созданный мною компонент VCL, который происходит от TCustomControl, который, в свою очередь, происходит от TWinControl. Он объединяет сокет, и этот сокет создает фоновый поток для приема видео с внешнего устройства.
При возникновении ошибки этот фоновый поток отправляет сообщение в элемент управления TMemo для целей аудита с использованием PostMessage (). Именно здесь я допустил ошибку, потому что дескриптор окна (HWND), который я использую с PostMessage (), принадлежит элементу управления TMemo. Элемент управления TMemo находится в той же форме, что и мой компонент.
Когда теряется видеосоединение, сокет, обслуживающий его, закрывается и уничтожается, но оказывается, что фоновый поток, обслуживающий его, еще не завершился. Теперь, когда сокет пытается выполнить операцию на несуществующем сокете, на который он ссылается, это приводит к ошибке сокета # 10038 (операция не для сокета). Вот тут и начинается беда.
Когда он вызывает PostMessage () с дескриптором TMemo, TMemo находится в состоянии, в котором он должен воссоздать дескриптор по требованию , явление коварной проблемы, которое описывает Реми. Это означает, что WndProc () в воссозданном окне TMemo теперь выполняется в контексте фонового потока .
Это соответствует всем доказательствам. Мало того, что я получаю предупреждение фонового потока в моем переопределенном WndProc (), как упомянуто выше, но все, что делается в окне TMemo с помощью мыши, вызывает поток сообщений об ошибках # 10038 в TMemo. Это происходит потому, что теперь существует слабосвязанное циклическое условие между TMemo, переопределенным компонентом WndProc () и фоновым потоком, поскольку этот поток имеет цикл GetMessage в своем методе Execute ().
Каждый раз, когда сообщение окна отправляется в элемент управления TMemo, например, от движений мыши и т. Д., Оно попадает в очередь сообщений фонового потока, поскольку в настоящее время ему принадлежит окно за TMemo. Так как фоновый поток пытается выйти и пытается закрыть сокет при выходе, каждая попытка закрытия генерирует другое сообщение # 10038, которое будет отправлено в TMemo, сохраняя цикл, потому что каждый PostMessage () по сути теперь является самопубликацией. .
С тех пор я добавил метод уведомления к объекту, который управляет фоновым потоком, который сокет вызывает в своем деструкторе, позволяя потоку узнать о его исчезновении и что ссылка недействительна. Я никогда не думал делать это раньше, потому что сокет закрывает фоновый поток во время уничтожения, однако я не жду события завершения из фонового потока. Конечно, альтернативным решением было бы дождаться завершения фонового потока. Обратите внимание, что если бы я применил этот подход, этот сценарий оказался бы в тупике, а не привел бы к странному поведению с элементом управления TMemo.
[ПРИМЕЧАНИЕ для редактора Stack Overflow - я добавляю эту деталь в качестве ответа, а не изменяю исходное сообщение, чтобы не толкать ответ Реми, содержащий решение, далеко вниз по странице.]