Узнайте, почему сообщения потоков приложений пропадают с помощью TSaveDialog

Разработка с помощью RAD Studio (Delphi) v10.2.1 (Tokyo, выпуск 1) в Windows 10 «Creators Update», 64-разрядная версия, но 32-разрядная разработка.

Приложение представляет собой VCL с несколькими фоновыми потоками, каждый из которых использует Indy TidHTTP для получения сетевых ресурсов. Синхронизация между основным потоком и фоновыми потоками реализована с помощью очередей сообщений (вызовов PostThreadMessage). Это настолько сложно, что предлагать здесь прямой код было бы затруднительно и запутанно, поэтому я начну со словесного описания.

Что должно произойти: Откройте файл со ссылками на внешние ресурсы, это сгенерирует HTTP-запросы и передаст их в фоновую обработку, а затем дождется входящих сообщений в очереди сообщений приложения, чтобы сказать, что ресурсы были загружены. Сообщения приложения сопоставляются в коде события, назначенном TApplication.OnMessage (в этом, я подозреваю, и заключается моя проблема).

Это работает. Все чаще всего проходит гладко. Но если я открою TSaveDialog — даже если я отменю диалоговое окно, а не сделаю что-либо, — тогда сообщения пропадут из очереди сообщений приложения.

В процессе написания сообщений журнала (отладка невозможна напрямую, потому что это нарушает время, необходимое для возникновения проблемы) я выяснил, что фоновые потоки действительно публикуют сообщения (и получают положительный ответ от PostThreadMessage), но они никогда не появляется в моем коде события TApplication.OnMessage.

Я видел, что какой-то хитрый код в различных библиотеках устанавливает свои собственные циклы PeekMessage/TranslateMessage/DispatchMessage, но не все из них помнят о проверке наличия события TApplication.OnMessage. Но я только что просмотрел код VCL и дюжину или около того сторонних библиотек, которые я использую, и не нашел ни одного экземпляра этого, который мог бы попасть в этот случай (насколько я могу судить).

Примечание. Я использую madExcept, Indy, FastReport, AddictSpell, SynEdit, VclStyleUtils (среди нескольких других менее известных библиотек).

Примечание 2: мне интересно, может ли это быть связано с обновлением Delphi 10.2.1 или Windows 10 Creator, поскольку я также наблюдаю какое-то другое странное поведение (длительные задержки с первым исключением или первым TOpenDialog — но только в некоторых приложениях), которые определенно не произошло с 10.1 (я не использовал 10.2.0). ... Но это может быть (вероятно) что-то другое.

Итак, мой вопрос: что я могу сделать по этому поводу?

Любые предложения о том, как найти/проверить, что есть какой-то другой код, крадущий сообщения приложения? Что еще я должен искать, кроме PeekMessage?

Есть ли другой способ перехватить сообщения очереди сообщений приложения, которые могли бы позволить мне избежать проблемы?

Если лучшие варианты не появятся, мне, возможно, придется отказаться от использования сообщений потока приложений и внедрить свою собственную систему обмена сообщениями/синхронизации, которую я бы предпочел не делать после того, как эта другая работает очень хорошо в остальное время.


person GeoffW    schedule 31.08.2017    source источник
comment
Вы можете использовать SetWindowsHookEx() для установки хука WH_GETMESSAGE для конкретного потока в потоке пользовательского интерфейса.   -  person Remy Lebeau    schedule 31.08.2017
comment
@ Реми, спасибо за это. Такой хук даст мне возможность централизованно обмениваться сообщениями вокруг идентификатора потока, что может быть более удобным, чем необходимость вводить дескрипторы окна.   -  person GeoffW    schedule 01.09.2017


Ответы (1)


Вы упомянули PostThreadMessage. Не смотрите дальше. Вы не можете использовать это, если не контролируете все циклы сообщений, которые могут получать сообщения потока, а вы этого не делаете. Цикл диалоговых сообщений модального файла находится вне вашего контроля. Он будет получать сообщения потока, предназначенные для другого цикла сообщений, и не знает, что с ними делать.

Решение достаточно простое. Отправляйте сообщения в окно, а не в поток. Таким образом, все разумные циклы сообщений (цикл сообщений диалогового окна модального файла является нормальным) будут отправлять сообщения в предполагаемое окно получателя.

В терминах Delphi это предполагает использование AllocateHWnd или аналогичного для создания скрытого окна для получения сообщений.

Раймонд Чен рассмотрел эту тему здесь: Почему сообщения, отправленные PostThreadMessage, исчезают?

person David Heffernan    schedule 31.08.2017
comment
Смелая часть вопроса, кажется, предполагает, что сообщения не работают даже после отмены диалога. А может я неправильно понимаю.. - person Sertac Akyuz; 31.08.2017
comment
@SertacAkyuz Я понял, что диалог был открыт, а затем отменен, и все же некоторые события пропали. В любом случае, PTM - это проблема, без сомнения. - person David Heffernan; 31.08.2017
comment
Сертак, из-за характера проблемы трудно точно сказать, когда сообщения снова начинают поступать, но они определенно начинаются снова после того, как диалог исчезает. Объяснение Дэвида подходит. - person GeoffW; 31.08.2017
comment
Дэвид, твое объяснение имеет смысл. Наверное, я всегда думал, что VCL обеспечит вызов OnMessage. Теперь я знаю лучше. Это немного грустно, все, что мне нужно было раньше, это идентификатор потока, не имело значения, кто отправлял или получал, теперь мне нужно изменить код, чтобы отличить основной поток от других потоков. Таково программирование. Большое спасибо. - person GeoffW; 31.08.2017
comment
@Geoff Джефф, ты вышел из VCL, когда вызываешь диалоговое окно API. ;) - person Sertac Akyuz; 31.08.2017
comment
@Sertac, да, я действительно должен был подумать об этом - я не скажу вам, сколько времени мне потребовалось, чтобы понять, что происходит, не говоря уже о том, почему это происходит. - person GeoffW; 31.08.2017