MFC функции GetMessage и PeekMessage, чем они отличаются?

Рубрика: MFC, Дата: 18 August, 2015, Автор:

GetMessageЗдорова!

Сегодня разберем такие функции как GetMessage и PeekMessage и чем они отличаются.

GetMessage немножно теории

Функция GetMessage применяется для отлова сообщений вот в такой конструкции

MSG message;
if(GetMessage(&message,NULL,0,0))
{
	::TranslateMessage(&message);
	::DispatchMessage(&message);
}

Эта функция работает следующим образом, если есть в очереди сообщения она возвращает true и входит в if, если нету сообщений то эта функция походу не будет возвращать false и будет ждать пока сообщения появятся, в общем функция которая ожидает сообщения. Если мы заменим if на while, то эта функция никогда не выйдет из цикла while, то есть будет бесконечный цикл, потому что GetMessage всегда будет возвращать true либо переходить в режим ожидания если нету сообщения.

PeekMessage немножко теории

теперь переходим к PeekMessage, функция также применяется для отлова сообщений, от например эта конструкция

while(::PeekMessage(&message,NULL,0,0,PM_REMOVE))
{
	::TranslateMessage(&message);
	::DispatchMessage(&message);
}

видите мы здесь применили оператор while потому что эта функция, если нету в очереди сообщений возвращает false и не ожидает пока появятся сообщения в очереди. Так же можно использовать и if чтобы ускорится. Пятый параметр PM_REMOVE вроде как удаляет сообщение из очереди, а есть еще и PM_NOREMOVE это вроде не удаляет сообщения из очереди.

Пример использования функций GetMessage и PeekMessage

Создадим диалоговое приложение, на диалог накинем кнопку “start” и поле редактирование как на рисунке

MFC GetMessage

Добавим сообщение WM_TIMER и обработчик кнопки “start” и в них добавим код

Ctest_GetMessageDlg::OnBnClickedButton1 »

void Ctest_GetMessageDlg::OnBnClickedButton1()
{
	// TODO: добавьте свой код обработчика уведомлений
	int nTimer(0);
	nTimer=SetTimer(1,500,NULL);//установка таймера
	ASSERT(nTimer!=0);
/*	::PostMessage(GetSafeHwnd(),WM_TIMER,0,0);
	::PostMessage(GetSafeHwnd(),WM_TIMER,0,0);
	::PostMessage(GetSafeHwnd(),WM_TIMER,0,0);
	::PostMessage(GetSafeHwnd(),WM_TIMER,0,0);
	::PostMessage(GetSafeHwnd(),WM_TIMER,0,0);*/

	MSG message;
	for(int i=0;i<100000;i++)
	{
		UpdateData(TRUE);
		m_iEdit=i;
		UpdateData(FALSE);
		TRACE("i=%dn",i);
//		while(::GetMessage(&message,NULL,0,0))
		while(::PeekMessage(&message,NULL,0,0,PM_REMOVE))
		{
			::TranslateMessage(&message);
			::DispatchMessage(&message);
		}
	}
}

Ctest_GetMessageDlg::OnTimer »

 

void Ctest_GetMessageDlg::OnTimer(UINT_PTR nIDEvent)
{
	// TODO: добавьте свой код обработчика сообщений или вызов стандартного
	for(int i=0;i<100;i++)
		TRACE("*********************8OnTimern");
	
	CDialogEx::OnTimer(nIDEvent);
}

У нас все нормально работает, диалог наш не блокируется, сообщения WM_TIMER обрабатываются, да у нас все сообщения которые в очереди обрабатываются и потом когда их нету только тогда PeekMessage вернет false, GetMessage такого бы не сделала, она вместо возврата false ждала бы появления сообщения, можете проверить поменяв на GetMessage.

С данным кодом у меня возникли проблемы я изначально делал через if(::GetMessage(&message,NULL,0,0)) и у меня там выше стоят UpdateData и из за этого не получалось WM_TIMER выловить, потому что они видимо были после тех сообщений которые посылал UpdateData. Так что как то так, нужно ставить несколько if(::GetMessage(&message,NULL,0,0)) например один в начале, а другой в конце.

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

И так я сделал несколько выводом, например мы можем зациклить данную программу, от например мы вызываем сообщение WM_LBUTTONDOWN в обработчике делаем бесконечный цикл и все у нас приложение виснет, кнопочки недоступны, потому что цикл все поглащает и не идет обработка сообщений, чтобы этого небыло нам нужно и в самой этой функции обработчике отлавливать сообщения, например также в цикл добавляем

if(::PeekMessage(&message,NULL,0,0,PM_REMOVE))
{
	::TranslateMessage(&message);
	::DispatchMessage(&message);
}

Теперь немножко о том что произойдет если мы вызовем обработчик WM_LBUTTONDOWN и затем из него еще раз пошлем это же сообщение, как вы думаете что будет? Просто из обработчика вызовется еще одна функция обработчик. То есть допустим мы кликаем левой кнопкой мышки, вызывается обработчик левой кнопки мышки в котором идет отлов сообщений в цикле от 1 допустим и до 1000 и у нас допустим на итерации цикла 100 мы вызвали снова WM_LBUTTONDOWN  и отловили его, то у нас вызывается новая функция, она выведет в цикле от 1 до 1000 в отладчик допустим и затем продолжится вывод первого обработчика со 101 и до 1000, ну с того места где он отловил WM_LBUTTONDOWN.

Из всего этого напрашивается вывод что это как бы один поток, просто из функций идет вызов функций которые что то делают. От например мы вызвали функцию обработчик кнопки и в ней цикл, все она положит всю программу, цикл все поглотит. Для того чтобы такого не было мы как бы эмитируем параллельное выполнение, мы просто проверяем есть ли в очереди “функции на вызов”, если есть то их вызываем и так каждый раз. Тогда у нас получается как бы программа не виснет. Это мои выводы, я не уверен что я прав, просто оно как бы так само собой напрашивается, да и тем более зная что это один поток, а в одном потоке все идет попорядку, да и из теста в котором одна функция ложит всю программу становится понятным что сообщения работают последовательно.

Немножко терминов своими словами чтобы не забыть 🙂

Основной поток – это вроде называют саму программу. Это окошко с элементами управления.

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

 

Комментарии:


Оставить комментарий

Your email address will not be published. Required fields are marked *