Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Общие вопросы > Не могу обработать сообщение WM_USER


Автор: Gnus 11.11.2011, 13:40
Доброго времени суток.
Суть проблемы такова: есть объект, который порождает нить. Нить без освобождения при завершении. Завершение нити может произойти при разрушении родителя, тогда нить тоже разрушается. Но возможны ситуации, когда завершение нити произошло в ходе ее исполнения. Необходимо проинформировать родителя о завершении нити и инициировать его (родителя) разрушение.

Попытка решения с помощью сообщений: перед завершением нити вызывается метод родителя, в котором вызывается метод PostMessage(0, WM_USER, 0, 0), которая возвращает истину. У родителя описан следующий метод обработки сообщения WM_USER:

Код

procedure WMUser(var Msg:TMessage); message WM_USER;


в реализации WMUser пока что только контрольная печать в файл.

Вобщем, не вызывается этот WMUser. Уже и не знаю, что делать.

Автор: kami 11.11.2011, 20:44
Цитата(Gnus @  11.11.2011,  13:40 Найти цитируемый пост)
PostMessage(0, WM_USER, 0, 0)

Первый параметр функции, конечно, просто так придуман...
Это намек. Остальное скажут F1 и MSDN

Автор: Gnus 11.11.2011, 22:33
Цитата(kami @  11.11.2011,  20:44 Найти цитируемый пост)
Первый параметр функции, конечно, просто так придуман...
Это намек. Остальное скажут F1 и MSDN 

Экий толстый намёк.
Упомянутый F1 не запрещает вызывать PostMessage, передавая первым параметром ноль. В этом случае вызов PostMessage аналогичен вызову PostThreadMessage с передачей идентификатора текущего потока.


Автор: kami 12.11.2011, 09:40
Цитата(Gnus @  11.11.2011,  22:33 Найти цитируемый пост)
 F1 не запрещает вызывать PostMessage

Попробовал бы он запретить smile
Только не надо забывать про 
Цитата(Gnus @  11.11.2011,  22:33 Найти цитируемый пост)
с передачей идентификатора текущего потока

Метод, содержащий PostMessage, вызывается в контексте порожденной нити (судя по описанию). То, что этот метод содержится не в самом коде потока, а в коде родителя. ничего не меняет. Куда отправится сообщение? Ждут ли его там, куда оно в действительности улетело?

Автор: Gnus 12.11.2011, 09:47
Цитата(kami @  12.11.2011,  09:40 Найти цитируемый пост)
Куда отправится сообщение? Ждут ли его там, куда оно в действительности улетело? 

но разве эта процедура в коде родителя 
Цитата(Gnus @  11.11.2011,  13:40 Найти цитируемый пост)
procedure WMUser(var Msg:TMessage); message WM_USER;

не является обработчиком события?

Попробовал перекрыть DefaultHandler родителя - не выполняется.
Событие OnTerminate у потока тоже не выполняется.
Какой-то замкнутый круг.
Повторюсь: Родитель (наследник TObject) порождает нить (наследник TThread). При завершении нити необходимо разрушить родителя. Что посоветуете?

Автор: kami 12.11.2011, 11:33
Еще раз повторяю:
PostMessage будет вызвано в контексте созданного потока. Это значит, что сообщение (при параметре Handle = 0) будет отправлено именно этому созданному потоку. А там оно не нужно. Потому что в этом случае сообщение нужно отправлять не потоку, а требуемому окну. И скорее всего у этого потока даже выборка сообщений не реализована.


Цитата(Gnus @  12.11.2011,  09:47 Найти цитируемый пост)
не является обработчиком события?

Я не знаю, что это за компонент-родитель. Может, и не является, если родитель - не оконный. В любом случае, сообщение до него не доходит.

Автор: Gnus 12.11.2011, 11:48
Т.е., насколько я понял, использовать механизм сообщений нельзя. Никакого "требуемого окна" у меня нет.
Как же организовать отложенное уведомление родителя о завершении потока?

Автор: kami 12.11.2011, 11:55
Цитата(Gnus @  12.11.2011,  11:48 Найти цитируемый пост)
использовать механизм сообщений нельзя

Я этого не говорил.
Цитата(Gnus @  12.11.2011,  11:48 Найти цитируемый пост)
Никакого "требуемого окна" у меня нет.

Если  нет - не проблема создать.
Что представляет из себя родитель? Что за класс (если собственный - наследник какого базового)?

Автор: Gnus 12.11.2011, 12:02
Цитата(kami @  12.11.2011,  11:55 Найти цитируемый пост)
Что представляет из себя родитель? Что за класс (если собственный - наследник какого базового)?


Цитата(Gnus @  12.11.2011,  09:47 Найти цитируемый пост)
Повторюсь: Родитель (наследник TObject) порождает нить (наследник TThread). При завершении нити необходимо разрушить родителя.


Автор: kami 12.11.2011, 12:41
Цитата(Gnus @  12.11.2011,  12:02 Найти цитируемый пост)
Родитель (наследник TObject)

Это не страшно.
Принцип работы следующий (код будет отрывками, только концепция, возможно несколько ошибок, т.к. пишу прямо в браузере):
1. Объявляем приватную переменную myHandle:THandle и оконную процедуру myMsgHandler(var Message: TMessage)
2. В конструкторе объекта (надеюсь, этот объект создается в главном потоке?) пишем:
  
Код

myHandle:=AllocateHWND(myMsgHandler)

3. Создаваемому потоку передаем этот myHandle. Где он будет храниться - определите сами.
4. в процедуре myMsgHandler пишем следующее:
Код

  if Message.msg = WM_USER then
    begin
      // здесь - код, обрабатывающий уничтожение или_что_там_еще вторичного потока.
    end
  else
    Message.Result:=DefaultWindowProc(myHandle, Message.Msg, Message.WParam, Message.LParam); // может, название процедуры написал неверно.
    // возможно, она называется DefWindowProc или как-нибудь так.

5. При необходимости передачи сообщения в методе вторичного потока пишем PostMessage(ParentHandle, WM_User.....). И не нужно вызывать методы родителя, прямо в коде вторичного потока, он же из п.2 знает хендл нужного окна.

Добавлено через 1 минуту и 57 секунд
Обратите внимание на 
Цитата(kami @  12.11.2011,  12:41 Найти цитируемый пост)
надеюсь, этот объект создается в главном потоке?

В противном случае манипуляции могут резко поменяться или дополниться.

Автор: Gnus 12.11.2011, 13:30
kami, огромное спасибо. Признаюсь, не знал, что можно привязать к обычному объекту окно с функцией обработки сообщений. До сегодняшнего дня не задумывался о том, как работает тот же TTimer например. Так что еще раз спасибо за AllocateHWND.

Кстати, в деструкторе родителя приходится делать DeallocateHWND.

Если под главным потоком Вы имели ввиду поток VCL, то тогда ответ отрицательный. Объект-родитель создается другими объектами при вызове главным потоком функции подключения из dll. Соответственно, в списке потоков я вижу их один после запуска приложения, три после вызова функции подключения из dll. После неудачи подключения теперь остается только один поток, как я и хотел.


Автор: kami 12.11.2011, 14:02
Цитата(Gnus @  12.11.2011,  13:30 Найти цитируемый пост)
стати, в деструкторе родителя приходится делать DeallocateHWND.

Я рад, что Вы не ограничились только воспроизведением моего совета.  smile 

Цитата(Gnus @  12.11.2011,  13:30 Найти цитируемый пост)
Объект-родитель создается другими объектами при вызове главным потоком функции подключения из dll. Соответственно, в списке потоков я вижу их один после запуска приложения, три после вызова функции подключения из dll.

Это (дальше - 2 варианта):
а) ничего не значит, потому что несмотря на создание других потоков, объект создается в контексте основного (да, я говорил именно проVCL-поток). В этом случае всё отлично, потому что vcl поток имеет обработчик сообщений.
б) если объект создается в дополнительном потоке и всё работает, значит этот дополнительный поток создал и имеет очередь сообщений, ну и обрабатывает их, само собой. Если бы поток не имел цикл обработки сообщений, то ни одно окно, созданное в его контексте (в т.ч. и созданное AllocateHWND) ничего бы не получало.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)