Версия для печати темы

Нажмите сюда для просмотра этой темы в обычном формате

Форум «Всё о Паскале» _ Делфи _ Что делает PeekMessage

Автор: TarasBer 4.08.2010 15:15

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

if (@IdleProc <> nil) and not PeekMessage(Message, 0, 0, 0, pm_NoRemove) then IdleProc

По моему замыслу, она должна работать так: если процедура ожидания есть и нету сообщений в очереди, то выполнить процедуру ожидания. Однако после долгой отладки выяснилось, что PeekMessage зачем-то ещё и пытается обработать сообщение! Ну и внутри обработчика деактивации IdleProc ставился на nil, чтобы свёрнутая программа проц не ела, а потом этот IdleProc пытался вызваться.
Так вот, что делать, чтобы просто проверить наличие сообщений? Я не хочу, чтобы она их ещё и обрабатывала.
Я САМ РЕШУ, когда мне надо обработать сообщение, а когда нет!

Автор: volvo 4.08.2010 21:18

Нужно больше кода, чтобы понять, что именно происходит в программе. В частности - как именно работает IdleProc, что она делает? Где именно IdleProc устанавливается в Nil? Какие именно сообщения обрабатываются программой? У PeekMessage слишком много подводных камней, чтобы можно было "вслепую" что-то советовать.

Автор: TarasBer 5.08.2010 13:00

> В частности - как именно работает IdleProc, что она делает?

Считает что-то, рисует, не суть важно.

> Где именно IdleProc устанавливается в Nil? Какие именно сообщения обрабатываются программой?

Она ставится в ноль в ответ на сообщение деактивации внутри оконной процедуры.

Автор: volvo 5.08.2010 14:08

Цитата
Она ставится в ноль в ответ на сообщение деактивации внутри оконной процедуры.
Так ты что, используя PeekMessage ждешь WM_ACTIVATE? Напрасно... Это так называемое non-queued сообщение, оно поступает сразу в оконную процедуру, а не в очередь. Я ж говорю - мало информации. Сделай тестовый проект, иллюстрирующий проблему, тогда можно будет думать, как обойти эту проблему. А пока - это все переливания из пустого в порожнее.

Автор: TarasBer 5.08.2010 14:51

> Это так называемое non-queued сообщение, оно поступает сразу в оконную процедуру, а не в очередь.

Не понял, оно может взять и прервать выполнение программы в любом месте, а потом снова вернуть? Или как?

Автор: volvo 5.08.2010 15:17

Цитата
Типы сообщений
синхронные сообщения (queued) - сообщения, которые Windows помещает в очередь сообщений программы, и которые извлекаются и диспетчеризуются в цикле обработки сообщений.
Отправление: с помощью функции PostMessage – ставится в очередь и немедленно возвращает управление.
Получение: результат пользовательского ввода путем нажатия клавиш (например, WM_KEYDOWN и WM_KEYUP), это символы, введенные с клавиатуры (WM_CHAR), результат движения мыши (WM_MOUSEMOVE) и щелчков кнопки мыши (WM_LBOTTONDOWN). Кроме этого синхронные сообщения включают в себя сообщение от таймера (WM_TIMER), сообщение о необходимости плановой перерисовки (WM_PAINT) и сообщение о выходе из программы (WM_QUIT)

асинхронные сообщения (nonqueued) – сообщения, передающиеся непосредственно окну, когда Windows вызывает оконную процедуру.
Отправление: с помощью функции SendMessage – не ставится в очередь, оконная процедура вызывается непосредственно, а возврат управления только после обработки сообщения оконной процедурой.
Получение: во всех остальных случаях, не указанных в получении синхронных сообщений, часто являются результатом синхронных сообщений.
Ну, или здесь: http://msdn.microsoft.com/en-us/library/ms644927%28VS.85%29.aspx#routing (англ.)

Понимаешь разницу, да? PeekMessage что делает?
Цитата
PeekMessage Function
Dispatches incoming sent messages, checks the thread message queue for a posted message, and retrieves the message (if any exist).
А WM_ACTIVATE в очередь не поступает, а обрабатывается сразу после получения в WndProc (в функции окна). А потом твоя программа опять ждет появления событий (синхронных!!!) в очереди.

Автор: TarasBer 5.08.2010 15:31

То есть код
if @IdleProc <> nil then IdleProc
может обратиться к нулевому адресу, если между проверкой на ноль и вызовом произошла обработка сообщения, которые выставило IdleProc в nil?
PeekMessage мне сам нужен не для обработчки сообщения, а просто чтобы узнать, если ли что обрабатывать.
По идее, мне нужен универсальный главцикл, который для @IdleProc=nil просто ждёт сообщение, не гоняя проц по циклу, а для <>nil выполняет этот IdleProc, елси никаких сообщений в очереди нет.
Пока что он у меня такой:


procedure GetMessages;
var
Message: TMsg;
begin
repeat
try
if not PeekMessage(Message, 0, 0, 0, 0) and (@IdleProc <> nil) then IdleProc
else begin
if not GetMessage(Message, 0, 0, 0) then Break;
if TranslateAccelerator(FormStack.Handle, FormStack.Accels, Message) = 0 then begin
TranslateMessage(Message);
DispatchMessage(Message);
end;
end;
except
MessageBox(MainForm, PChar('Runtime error ' + IntToHex(ExceptionCode)), 'ERROR', mb_OK or mb_IconError);
end;
until False;
end;


Автор: volvo 5.08.2010 16:06

Цитата
То есть код
if @IdleProc <> nil then IdleProc
может обратиться к нулевому адресу, если между проверкой на ноль и вызовом произошла обработка сообщения, которые выставило IdleProc в nil?
Именно.

Цитата
По идее, мне нужен универсальный главцикл
Хм... Ну, никто ж не мешает сделать так:

// определяем свои сообщения I_AM_ACTIVATED / I_AM_DEACTIVATED

// и в функции окна при получении:
WM_ACTIVATE:
case(wParam) of
WA_ACTIVE: PostMessage(hwnd, I_AM_ACTIVATED, 0, 0);
WA_INACTIVE: PostMessage(hwnd, I_AM_DEACTIVATED, 0, 0);
end; { case }

, а уже IdleProc обнулять или нет при получении своих, синхронных сообщений. Тогда все будет, как положено быть, даже при первом варианте цикла. Попробуй.