![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
GetMessage в отдельном потоке
msg © (2006-03-09 12:11:00)В отдельном потоке (TThread) создаю форму:
procedure TTest.Execute;
var
msg:TMsg;
wnd:HWND;
wc:WNDCLASS;
begin
wc.style:=0;
wc.cbClsExtra:=0;
wc.cbWndExtra:=0;
wc.hInstance:=hInstance;
wc.hIcon:=0;
wc.hCursor:=0;
wc.hbrBackground:=GetStockObject(LTGRAY_BRUSH);
wc.lpszMenuName:=nil;
wc.lpszClassName:='asdasd';
wc.lpfnWndProc:=@WndProc;
Windows.RegisterClass(wc);
wnd:=CreateWindow(
'asdasd',
'Hello world',
WS_CAPTION,
10,
10,
200,
80,
HWND_DESKTOP,
0,
hInstance,
nil
);
while GetMessage(Msg,wnd,0,0) do
begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
DestroyWindow(wnd);
Windows.UnRegisterClass('asdasd', hInstance);
end;
WndProc выглядит примерно так:
function WndProc(wnd:HWND; msg:UINT; w:UINT; l:UINT):UINT;stdcall;
var
begin
Result:=0;
case msg of
WM_QUIT: ;
WM_PAINT: ;
else
Result:=DefWindowProc(wnd,msg,w,l);
end;
end;
В основном потоке создаю отдельный поток:
var
T: TTest;
procedure TForm1.Button1Click(Sender: TObject);
begin
T := TTest.Create(true);
with T do
begin
FreeOnTerminate:=true;
Resume;
end;
end;
и посылаю сообщение:
procedure TForm1.Button2Click(Sender: TObject);
var
h:hWnd;
begin
h := FindWindow('asdasd', nil);
SendMessage(h, WM_QUIT, 0, 0);
end;
GetMessage ни в какую не желает принимать сообщение, даже если написать GetMessage(Msg,0,0,0), в то же время WndProc сообщение принимает. Я так понимаю, что посланные мной сообщения принимает обработчик главного потока. Так ли это? И нужен ли вообще, в таком случае, GetMessage в отдельном потоке?
msg (2006-03-10 12:45:00) [21]Ага, GetMessage действительно нужен, потому, что если
while GetMessage(Msg,wnd,0,0) do
begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
заменить на
while true do
sleep(1);
в оконную процедуру сообщение не передается, понял.
ранее SendMessage пытался заменить на PostMessage, но GetMessage управление всеравно не возвращала, щас возвращает... фиг с ним, спишем на собственную невнимательность.
С поведением GetMessage при PostMessage и SendMessage более менее разобрался.
Заметил еще одну интересную особенность, оконная процедура, при SendMessage(WM_QUIT) получает сообщение с кодом 18(WM_QUIT), а при PostMessage(WM_QUIT) оконная процедура получает два сообщения 2(WM_DESTROY) и 130(WM_NCDESTROY).
Теперь есть непонятки с PostQuitMessage, если ее вызываю в оконной процедуре в ответ на WM_DESTROY(PostMessage) или WM_QUIT(SendMessage), GetMessage возвращает управление, мы выходим из цикла...
Если же написать:
type
TTest=class(TThread)
...
public
Procedure ExitTest;
...
implementation
procedure TTest.ExitTest;
begin
PostQuitMessage(0);
end;
и из основного потока вызвать ExitTest:
T.ExitTest;
то мы вываливаемся из программы (т.е. завершает работу основной поток), почему? в справке написано:
Функция PostQuitMessage указывает Windows, что ПОТОК (в нашем случае не основной)послал запрос на завершение (выход).
Leonid Troyanovsky (2006-03-10 08:33:00) [22]
> Eraser © (09.03.06 22:50) [51]
> WaitMessage();
> if(PeekMessage(&msg,NULL,0,0,PM_REMOVE)
Скорее уж while(PeekMessage(&msg,NULL,0,0,PM_REMOVE)
--
Regards, LVT.
Игорь Шевченко (2006-03-09 23:31:00) [23]Eraser © (09.03.06 23:27) [53]
На самом деле интересно, так как дает представление о том, как реализуется поведение тех или иных функций Windows. Кроме того, если по механизмам ядра написана отличная книжка Руссиновича и Соломона, то по части оконной подсистемы довольно большое белое пятно. Даже по механизмам GDI написана книжка Фэня Юаня, а по USER и его ядерной части в Win32k.sys я ничего не видел.
Eraser (2006-03-09 23:27:00) [24]
> Игорь Шевченко © (09.03.06 23:05) [52]
выделю трафик.. скачаю.
интересно однако:)
Игорь Шевченко (2006-03-09 23:05:00) [25]Eraser © (09.03.06 22:50) [51]
Сначала проверка очереди, если пусто, то проверка, доставка и очистка Sent-сообщений, затем WaitMessage
> а исходники ReactOS доступны всем для скачивания?
> ссылочку можно? )
http://www.reactos.org
Eraser (2006-03-09 22:50:00) [26]
> Игорь Шевченко © (09.03.06 22:42) [50]
> Из их исходников видно, что GetMessage (NtUserGetMessage),
> если в очереди потока нет сообщений, вызывает внутреннюю
> функцию WaitMessage, которая и осуществляет диспетчеризацию
> Sent-сообщений - это к спору о WaitMessage :)
ну я подозоевал, что-то вроде
while(true)
{
WaitMessage();
if(PeekMessage(&msg,NULL,0,0,PM_REMOVE)
{
if(msg.message==WM_QUIT) break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
а исходники ReactOS доступны всемдля скачивания?
ссылочку можно? )
Игорь Шевченко (2006-03-09 22:42:00) [27]Eraser © (09.03.06 22:31) [49]
Они будут, согласно MSDN, отдиспетчированы - непосредственно переданы оконной процедуре нужного окна. Но при этом GetMessage не закончит своего выполнения как того ожидает автор. Поэтому ему в данном случае лучше выполнять PostMessage, а для конкретного примера в [0] и вовсе PostQuitMessage.
Я советую кроме MSDN еще исходниками пользоваться. Например, ReactOS. Поскольку они ставят своей целью совместимость с Windows, то их исходники можно рассматривать, как отправную точку для углубленных знаний.
Из их исходников видно, что GetMessage (NtUserGetMessage), если в очереди потока нет сообщений, вызывает внутреннюю функцию WaitMessage, которая и осуществляет диспетчеризацию Sent-сообщений - это к спору о WaitMessage :)
Eraser (2006-03-09 22:31:00) [28]
> Игорь Шевченко © (09.03.06 22:18) [48]
> GetMessage does not retrieve messages for windows that belong
> to other threads or applications"
странно.. не обращал внимание на этот ляп )
пользуюсь тоже MSDN.
> А про то, как обрабатываются сообщения на самом деле, и
> автор поста [1] и я знаем довольно неплохо :)
ну что ж... раз вы говорите, что тот товарисч вкурсе - приму на веру...
однако с формулировками по осторожнее быть надо.. а то тов. Crash Coredump раза 3 упамянул, что
> Сообщения по SendMessage никогда не будут выбраны вызовом
> GetMessage
...
Игорь Шевченко (2006-03-09 22:18:00) [29]Eraser © (09.03.06 22:03) [47]
> Сообщения по SendMessage никогда не будут выбраны вызовом
> GetMessage
Так, как ожидает автор (см. сабж) они выбраны не будут, разумеется. Автор (см. сабж), ожидает, что после того, как он послал окну сообщение, у него функция GetMessage возвратит результат.
А про то, как обрабатываются сообщения на самом деле, и автор поста [1] и я знаем довольно неплохо :)
Впрочем, окончательную точку ставит MSDN (VS 2005 beta 2)
"The GetMessage function retrieves a message from the calling thread's message queue. The function dispatches incoming sent messagesuntil a posted message is available for retrieval.
....
During this call, the system delivers pending messages that were sent to windows owned by the calling thread using the SendMessage, SendMessageCallback, SendMessageTimeout, or SendNotifyMessage function. The system may also process internal events. Messages are processed in the following order:
Sent messages
Posted messages
Input (hardware) messages and system internal events
Sent messages (again)
WM_PAINT messages
WM_TIMER messages "
в отличие от Win32.Hlp
"he GetMessage function retrieves a message from the calling thread's message queue and places it in the specified structure. This function can retrieve both messages associated with a specified window and thread messages posted via the PostThreadMessage function. The function retrieves messages that lie within a specified range of message values. GetMessage does not retrieve messages for windows that belong to other threads or applications"
Eraser (2006-03-09 22:03:00) [30]
> Игорь Шевченко © (09.03.06 21:55) [46]
в [1] автор прав насчёт RTFM - согласен - это никогда не лишнее )
PostThreadMessage однако он не упаминул.. ну да Бог с ним с PostThreadMessage.
Далее ведь тов. Crash Coredump начал доказывать, что Get/PeekMessage не нужна в доп. потоке.
Crash Coredump © (09.03.06 16:08) [5]
Не ту справку читаешь, почитай еще. Сообщения по SendMessage никогда не будут выбраны вызовом GetMessage
на что Сергей М. вполне резонно заметил
Бред сивой кобылы.
:-)
> Надо приводить код Windows.
а где его взять? )
вот по этой теме хорошая ветка
http://www.rsdn.ru/Forum/Message.aspx?mid=1410117
Игорь Шевченко (2006-03-09 21:55:00) [31]Eraser © (09.03.06 21:29) [44]
Eraser © (09.03.06 21:29) [45]
> весь код VNC приводить не буду
Надо приводить код Windows.
А этот код ты зачем привел ?
> по сабжу
А по сабжу все сказано в посте [1]
С потоком в данной ситуации надо обмениваться через Post[Thread]Message
Eraser (2006-03-09 21:29:00) [32]
// PROCESS THE MOUSE POINTER
// Some of the hard work is done in clients, some here
// This code fetches the desktop under the old pointer position
// but the client is responsible for actually encoding and sending
// it when required.
// This code also renders the pointer and saves the rendered position
// Clients include this when rendering updates.
// The code is complicated in this way because we wish to avoid
// rendering parts of the screen the mouse moved through between
// client updates, since in practice they will probably not have changed.
// Re-render the mouse's old location if it's moved
BOOL cursormoved = FALSE;
POINT cursorpos;
if (GetCursorPos(&cursorpos) &&
((cursorpos.x != oldcursorpos.x) ||
(cursorpos.y != oldcursorpos.y))) {
cursormoved = TRUE;
oldcursorpos = rfb::Point(cursorpos);
}
if (cursormoved) {
if (!m_desktop->m_cursorpos.is_empty())
rgncache = rgncache.union_(rfb::Region2D(m_desktop->m_cursorpos));
}
{
// Prevent any clients from accessing the Buffer
omni_mutex_lock l(m_desktop->m_update_lock);
// CHECK FOR COPYRECTS
// This actually just checks where the Foreground window is
m_desktop->CalcCopyRects(updates);
// GRAB THE DISPLAY
// Fetch data from the display to our display cache.
m_desktop->m_buffer.GrabRegion(rgncache);
// Render the mouse
m_desktop->m_buffer.GrabMouse();
if (cursormoved) {
// Inform clients that it has moved
m_desktop->m_server->UpdateMouse();
// Get the buffer to fetch the pointer bitmap
if (!m_desktop->m_cursorpos.is_empty())
rgncache = rgncache.union_(rfb::Region2D(m_desktop->m_cursorpos));
}
// SCAN THE CHANGED REGION FOR ACTUAL CHANGES
// The hooks return hints as to areas that may have changed.
// We check the suggested areas, and just send the ones that
// have actually changed.
// Note that we deliberately don't check the copyrect destination
// here, to reduce the overhead & the likelihood of corrupting the
// backbuffer contents.
rfb::Region2D checkrgn = rgncache.subtract(clipped_updates.get_copied_region());
rgncache = clipped_updates.get_copied_region();
rfb::Region2D changedrgn;
m_desktop->m_buffer.CheckRegion(changedrgn, checkrgn);
// FLUSH UPDATES TO CLIENTS
// Add the bits that have really changed to their update regions
// Note that the cursor is NOT included - they must do that
// themselves, for the reasons above.
// This call implicitly kicks clients to update themselves
updates.add_changed(changedrgn);
clipped_updates.get_update(m_server->GetUpdateTracker());
}
// Clear the update tracker and region cache
clipped_updates.clear();
}
// Now wait for more messages to be queued
if (!WaitMessage()) {
vnclog.Print(LL_INTERR, VNCLOG("WaitMessage() failed\n"));
break;
весь код VNC приводить не буду ... много там )
> И эта..."будьте вежливы".
я и так вежлив, особенно когда со мной спорят не ради спора, а по сабжу )
Eraser (2006-03-09 21:29:00) [33]
> Игорь Шевченко © (09.03.06 21:14) [43]
> Совет - спорь кодом, аргумент неубиенный.
vncDesktopThread::run_undetached(void *arg)
{
// Save the thread's "home" desktop, under NT (no effect under 9x)
HDESK home_desktop = GetThreadDesktop(GetCurrentThreadId());
// Attempt to initialise and return success or failure
if (!m_desktop->Startup())
{
vncService::SelectHDESK(home_desktop);
ReturnVal(FALSE);
return NULL;
}
// Grab the initial display contents
// *** m_desktop->m_buffer.GrabRegion(m_desktop->m_bmrect);
// *** m_desktop->m_buffer.Clear(m_desktop->m_bmrect);
// Succeeded to initialise ok
ReturnVal(TRUE);
// START PROCESSING DESKTOP MESSAGES
// We set a flag inside the desktop handler here, to indicate it's now safe
// to handle clipboard messages
m_desktop->SetClipboardActive(TRUE);
// All changes in the state of the display are stored in a local
// UpdateTracker object, and are flushed to the vncServer whenever
// client updates are about to be triggered
rfb::SimpleUpdateTracker clipped_updates;
rfb::ClippedUpdateTracker updates(clipped_updates, m_desktop->m_bmrect);
clipped_updates.enable_copyrect(true);
// Incoming update messages are collated into a single region cache
// The region cache areas are checked for changes before an update
// is triggered, and the changed areas are passed to the UpdateTracker
rfb::Region2D rgncache = m_desktop->m_bmrect;
// The previous cursor position is stored, to allow us to erase the
// old instance whenever it moves.
rfb::Point oldcursorpos;
// Set the hook thread to a high priority
// *** set_priority(omni_thread::PRIORITY_HIGH);
BOOL idle_skip = TRUE;
ULONG idle_skip_count = 0;
MSG msg;
while (TRUE)
{
if (!PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE)) {
//
// - MESSAGE QUEUE EMPTY
// Whenever the message queue becomes empty, we check to see whether
// there are updates to be passed to clients.
if (idle_skip) {
idle_skip = FALSE;
if (idle_skip_count++ < 4) {
Sleep(5);
continue;
}
}
idle_skip_count = 0;
// Clear the triggered flag
m_desktop->m_update_triggered = FALSE;
//
// CHECK SCREEN FORMAT
// First, we must check that the screen hasnt changed too much.
if (m_desktop->m_displaychanged || !vncService::InputDesktopSelected())
{
rfbServerInitMsg oldscrinfo = m_desktop->m_scrinfo;
m_desktop->m_displaychanged = FALSE;
// Attempt to close the old hooks
if (!m_desktop->Shutdown())
{
m_server->KillAuthClients();
break;
}
// Now attempt to re-install them!
if (!m_desktop->Startup())
{
m_server->KillAuthClients();
break;
}
// Check that the screen info hasn't changed
vnclog.Print(LL_INTINFO, VNCLOG("SCR: old screen format %dx%dx%d\n"),
oldscrinfo.framebufferWidth,
oldscrinfo.framebufferHeight,
oldscrinfo.format.bitsPerPixel);
vnclog.Print(LL_INTINFO, VNCLOG("SCR: new screen format %dx%dx%d\n"),
m_desktop->m_scrinfo.framebufferWidth,
m_desktop->m_scrinfo.framebufferHeight,
m_desktop->m_scrinfo.format.bitsPerPixel);
if ((m_desktop->m_scrinfo.framebufferWidth != oldscrinfo.framebufferWidth) ||
(m_desktop->m_scrinfo.framebufferHeight != oldscrinfo.framebufferHeight))
{
m_server->KillAuthClients();
break;
} else if (memcmp(&m_desktop->m_scrinfo.format, &oldscrinfo.format, sizeof(rfbPixelFormat)) != 0)
{
m_server->UpdateLocalFormat();
}
// Adjust the UpdateTracker clip region
updates.set_clip_region(m_desktop->m_bmrect);
// Add a full screen update to all the clients
rgncache = rgncache.union_(rfb::Region2D(m_desktop->m_bmrect));
m_server->UpdatePalette();
}
//
// CALCULATE CHANGES
//
if (m_desktop->m_server->UpdateWanted())
{
// POLL PROBLEM AREAS
// We add specific areas of the screen to the region cache,
// causing them to be fetched for processing.
if (m_desktop->m_server->PollFullScreen())
{
rfb::Rect rect = m_desktop->m_qtrscreen;
rect = rect.translate(rfb::Point(0, m_desktop->m_pollingcycle * m_desktop->m_qtrscreen.br.y));
rgncache = rgncache.union_(rfb::Region2D(rect));
m_desktop->m_pollingcycle = (m_desktop->m_pollingcycle + 1) % 4;
}
if (m_desktop->m_server->PollForeground())
{
// Get the window rectangle for the currently selected window
HWND hwnd = GetForegroundWindow();
if (hwnd != NULL)
PollWindow(rgncache, hwnd);
}
if (m_desktop->m_server->PollUnderCursor())
{
// Find the mouse position
POINT mousepos;
if (GetCursorPos(&mousepos))
{
// Find the window under the mouse
HWND hwnd = WindowFromPoint(mousepos);
if (hwnd != NULL)
PollWindow(rgncache, hwnd);
}
}
Игорь Шевченко (2006-03-09 21:14:00) [34]Eraser © (09.03.06 21:02) [42]
> обычно WaitMessage применяют балансировки нагрузки
Это как ? Расскажи, интересно.
Совет - спорь кодом, аргумент неубиенный. И эта..."будьте вежливы".
Eraser (2006-03-09 21:02:00) [35]
> Crash Coredump © (09.03.06 19:12) [40]
к чему ты вообще тут WaitMessage приплёл... сабж не про это.
> Leonid Troyanovsky © (09.03.06 19:28) [41]
именно. обычно WaitMessage применяют балансировки нагрузки. И то довольно редко... видел применение только один раз - в WinVNC.
Leonid Troyanovsky (2006-03-09 19:28:00) [36]
> Crash Coredump © (09.03.06 18:51) [34]
> потока. Вот честно, не проверял, будут ли обрабатываться
> сообщения, если поток будет ждать какого-нибудь объекта
> ядра функцией WaitForSingleObject, а что Рихтер на эту тему
> писал, не помню.
Я, конечно, тоже не помню, но работоспособный цикл
можно построить на основе MsgWaitForMultipleObjects + PeekMessage.
Или WaitMessages + PeekMessage.
--
Regards, LVT.
Crash Coredump (2006-03-09 19:12:00) [37]Eraser © (09.03.06 18:57) [38]
Мне не хочешь верить, может, Рихтеру поверишь, который Джеффри:
"When a worker thread sends a message to your window, the SendMessage function internally queues up the message for the window, and then the worker thread suspends itself. The next time the user-interface thread calls GetMessage, PeekMessage, or WaitMessage, it sees the queued sent message, pulls it from the queue, and processes the window's window procedure. "
http://www.microsoft.com/msj/1197/win321197.aspx
Crash Coredump (2006-03-09 19:01:00) [38]Eraser © (09.03.06 18:57) [38]
Это надо понимать, как отсутствие иных аргументов ?
Eraser (2006-03-09 18:57:00) [39]
> Crash Coredump © (09.03.06 18:56) [37]
проспись пойди.
Crash Coredump (2006-03-09 18:56:00) [40]Eraser © (09.03.06 18:52) [36]
> из любой.
Очередь сообщений, куда помещаются сообщения, посланные по SendMessage, WaitMessage тоже не обрабатывает, доставляя их оконной процедуре ?