· Начало · Отвђтить · Статистика · Поиск · FAQ · Правила · Установки · Язык · Выход · WASM.RU · Noir.Ru ·

 WASM Phorum —› WASM.WIN32 —› Помогите разобраться с работой API-функции MessageBox.

<< . 1 . 2 . 3 . >>

Посл.отвђт Сообщенiе


Дата: Сен 6, 2004 23:26:18

„пока енто сообщение не обработалося не приходит НОВОЕ!“

Если быть точнее , то поток "спит" (крутиться в ядре) пока очередь сообщений пуста .

Давайте попорядку .
1. SetTimer - говорим ядру период и адрес процедуры обработки WM_TIMER .
2. GetMessage - говорим ядру адрес структуры MSG и уходим в ожидание (поток спит этот период) .
3. Когда приходит время (для любого сообщения) ЯДРО пишет в структуру MSG номер сообщения и передаёт управление за GetMessage .
4. Теперь DispatchMessage смотрит в структуру MSG и анализирует тип сообщения . Если это WM_TIMER (113h) , то передаём управление на MSG.lParam , а там уже как раз лежит (вписанный ядром) адрес нашей процедуры .

Всё !! А теперь представьте , что у вас х-ва туча сообщений , а MSG.message это всего один дворд , ядро туда может и пишет все WM_TIMER , но DispatchMessage ведь не может создавать потоки на каждое .

Можете поэкспериментировать на одном WM_TIMER .
http://wasm.ru/forum/files/_934647330__timeproc.zip


Дата: Сен 7, 2004 00:13:12 · Поправил: rsrc

bogrus
Немного не так! :)

Вот этот код дает возможность жить окну(ам):
BOOL bRet;
MSG msg;
while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
{ 
    if (bRet == -1)
    {
        // handle the error and possibly exit
    }
    else
    {
        TranslateMessage(&msg); 
        DispatchMessage(&msg); 
    }
}


Примерно тоже самое есть и у MessageBox (это же окно!) где-то :))).
GetMessage() проверяет очередь сообщений потока на WM_TIMER в самую последнюю очередь (это сообщение низкоприоритетное!) и если находит его, то тогда тогда сбрасывает таймер и возвращает управление.

Таймер отправляет сообщение твоему окну и ждет когда его сбросят, чтобы опять посчитать и отправить WM_TIMER, но Sleep() это (сбросить таймер) сделать не может! А вот когда ты вызываешь MessageBox(), то вот тогда (см. код выше!) его GetMessage() и сбрасывает таймер и он опять считает и дает WM_TIMER, а ты в это время зыришь свой MessageBox() и :) неожиданно для тебя выскакивает еще один….

Oleg_SK, и вот теперь ты уже все знаешь!

З.Ы. Дядька Рихтер толковую книжку написал.


Дата: Сен 7, 2004 00:56:43

rsrc Скоро мы доберёмся до истины ! :)

Это я привёл случай , когда процесс вообще без окон , но с обработкой WM_TIMER . Т.е. у нас крутиться свой цикл .

Теперь , если мы добавим в процедуру Sleep , то после очередного её вызова GetMessage пойдёт уже с этой задержкой .

Если же мы добавим MessageBox , то доставть наши сообщения (а поток у нас один) будет НЕ наш GetMessage !

„Примерно тоже самое есть и у MessageBox (это же окно!) где-то :))). “

Это называеться DefDlgProc . Вот он и будет доставать теперь наши сообщения . Т.е. этот "чужой" GetMessage указывает ядру свою MSG (на стеке) , НО ЯДРО ПОМНИТ адрес нашей процедуры (WM_TIMER) и записывает его в "чужой" MSG , и 113h туда записывает . Ну а DefDlgProc тоже имеет DispatchMessage (см. пункт 4) , вот он и передаёт управление снова в нашу процедуру , а там MessageBox готовиться создать ещё одно окно (цикл они имеют все один - в DefDlgProc) .

з.ы. Дядька Олли тоже гуд :)


Дата: Сен 7, 2004 01:22:22 · Поправил: rsrc

bogrus
1. Непонятно тогда как поток без окна может получить WM_TIMER? У него раз нет окон, то тогда нет и очереди сообщений!


The SetTimer function creates a timer with the specified time-out value.

UINT_PTR SetTimer(
HWND hWnd, // handle to window
UINT_PTR nIDEvent, // timer identifier
UINT uElapse, // time-out value
TIMERPROC lpTimerFunc // timer procedure
);



2. Не совсем DefDlgProc(), она конечно для каждого диалога вызывается, но надо же еще кое-какие мессаги обработать MessageBox'у раньше "старого" окна, MessageBox же модальное окно! У MessageBox'a должен быть еще свой цикл выборки сообщений, ведь "старый" GetMessage()-DispatchMessage() ему никак не найти!!!


Дата: Сен 7, 2004 01:28:48

bogrus, вот придет Four-F и нас рассудит!


Дата: Сен 7, 2004 01:39:54

„1. Непонятно тогда как поток без окна может получить WM_TIMER? “

Усё там понятно :) , посмотри исходник по ссылке .

„HWND hWnd, // handle to window “
If this parameter is NULL, no window is associated with the timer and the nIDEvent parameter is ignored.


„надо же еще кое-какие мессаги обработать MessageBox'у раньше "старого" окна“

Ну вот и обрабатывает мессаги потока , и в "старое" передаёт (можешь spyxx-ом посмотреть) . А на счёт можальности (в чём она выражаеться под дебаггером) надо будет ещё посмотреть .

„вот придет Four-F и нас рассудит!“

Тю ! Так мы ж с тобой не спорим , просто делимся впечатлениями :) Four-F , скорее по ядрам , а тут всё в юзермоде происходит .


Дата: Сен 7, 2004 02:27:50 · Поправил: rsrc

bogrus

1. Ответ нашел у Рихтера: как только поток обратиться к той или иной GUI-функции (например, для проверки очереди сообщений или создания окна), система автоматом выделит ему ресурсы (THREADINFO, очередь сообщений и т.д.) и вот поэтому твой код работает без окон, но с GetMessage()!

И потом я имел ввиду оконный обработчик WM_TIMER, а не callback!

2. А по этому пункту я прав однозначно! %)

P.S. bogrus, и все же я прав по всем пунктам! Ну, а то что я раньше думал, что если у потока нет окон, то и нет очереди сообщений, то это ни-че-го и к делу не относится :)


Дата: Сен 7, 2004 12:09:22 · Поправил: bogrus

Ну с дядькой мне спорить тяжко , а с тобой попробую :)

„1. Ответ нашел у Рихтера: как только поток обратиться“

Т.е. ты (вы с дядькой ?) хотите сказать , что пока поток не обращаеться „к той или иной GUI-функции “ , то у него нет очереди ?

Брехня ! А давай сделаем эксперимент - пошлем чужому потоку любое сообщение . Следовательно , если он никуда не обращался к той или иной GUI , то сообщение пропадёт .

А вот и нет , оно не пропадёт , а будет храниться в его очереди - значит она у него есть (см. аттач) , и заметь - никаких окон .

„2. А по этому пункту я прав однозначно“

У MessageBox'a тот же цикл , что и у диалога . А "старый" это значит свой (тот что сами делаем в своём модуле , например при создании окна "ручками") . Тут ещё надо определиться , что ты хочешь доказать , я не вижу конкретного утверждения .


Дата: Сен 7, 2004 12:28:06

Вот аттач , в первом была маленькая ошибочка

1258456590__Msg.zip


Дата: Сен 7, 2004 12:29:05

1. щас буду смотреть и думать.
2. тоже самое и я говорил, но токо другими словами... :)


Дата: Сен 7, 2004 13:56:11 · Поправил: rsrc

Привожу полностью слова Рихтера:

Создавая какой-либо поток, система предполагает, что он не будет иметь отношения к поддержке пользовательского интерфейса. Это позволяет уменьшить объем выделяемых ему системных ресурсов. Но, как только поток обратиться к той или иной GUI-функции (например, для проверки очереди сообщений или создания окна), система автоматически выделит ему дополнительные ресурсы, необходимые для выполнения задач, связанных с пользовательским интерфейсом. А если конкретнее, то система создает структуру THREADINFO (недокументированная!) и сопоставляет ее с этим потоком. Когда с потоком связывается структура THREADINFO, он получает свой набор очередей сообщений.


И из этого можно сделать вывод – у любого потока есть очередь сообщений, а когда эта очередь сообщений для него создается нам должно быть пох…й :)

bogrus, я был не прав!


Дата: Сен 7, 2004 14:39:01

„И из этого можно сделать вывод – у любого потока есть очередь сообщений, а когда эта очередь сообщений для него создается нам должно быть пох…й :) “

Я понял , что дядька имел ввиду . Указатель на THREADINFO лежит в [fs:40] . Теперь простыми экспериментами видно , что для консольной проги он пустой , а для гуёвой заполнен . Т.е. он хотел сказать о Subsystem в PE , если GUI , то загрузчик должен создать THREADINFO .


Дата: Сен 7, 2004 15:38:02

[ Oleg_SK: но к сожалению кроме этой статьи я больше ни чего ни где не нашел по этой теме ]

Есть, по крайней мере, ещё одна супер-статья по SEH - "A Crash Course in Structured Exception Handling" дядьки Питрека. http://www.microsoft.com/msj/0197/Exception/Exception.htm


[ Oleg_SK: В случае же использования функции MessageBox все летит кувырком, т.к. выполнение потока возобновляется в то время, когда процедура обработчика исключений еще не отработала:( ]

В этом случае выполнение потока вообще не прекращается. Тебе просто кажется, что пока MessageBox не вернется поток спит. Если бы это было так, то любая прога вызвавшая MessageBox не смогла бы себя перерисовывать. Запусти тот же notepad и выбери там любой диалог из меню. Сверни все окна и разверни. Основное окно notepad перерисуется. А Sleep суспедит поток, т.е. она явно исключает его из планирования. Если её влепить куда-нить в процедуру окна, то такое окно не сможет себя перерисовывать, его нельзя будет двигать и т.д. и т.п.


[ bogrus: Брехня! А давай сделаем эксперимент - пошлем чужому потоку любое сообщение. Следовательно, если он никуда не обращался к той или иной GUI, то сообщение пропадёт. ]

Точнее не будет послано. Тут Рихтер и rsrc правы :) bogrus, ты неверно ставищь эксперимент. Дело в том, что первичный поток процесса уже автоматом является GUI-потоком. Т.е. очередь сообщений у него появляется ещё до того, как система вызовет точку входа. Нужно создать рабочий поток и уже ему слать мессагу, но только до того как он вызовет GetMessage. Ибо тогда он сразу будет конвертирован в GUI-поток и у него появится очередь сообщений. Если вызвать PostThreadMessage до этого, то она обломится и GetLastError вернет ERROR_INVALID_THREAD_ID.


[ rsrc: И из этого можно сделать вывод – у любого потока есть очередь сообщений... ]

Нет. Создай поток через CreateThread и не будет у него никакой очереди сообщений и нельзя ему будет ничего послать, до тех пор пока он не вызовет какую-нить функцию требующую перевода его в GUI поток.


[ bogrus: Т.е. он хотел сказать о Subsystem в PE, если GUI, то загрузчик должен создать THREADINFO. ]

Не знаю, что хотел сказать Рихтер, но GUI поток или не GUI однозначно можно определить по полю KTHREAD.ServiceTable. Смещения 0DCh, 0E0h, 124h для w2k, xp и Server 2003, соответственно. Повторяю, первичный поток процесса автоматом является GUI-потоком. Даже для КОНСОЛЬНОГО приложения. Для НЕ GUI потоков в KTHREAD.ServiceTable будет указатель на KeServiceDescriptorTable, а для GUI потоков - указатель на KeServiceDescriptorTableShadow. Указатели на KTHREAD можно получить по команде thread (для текущего потока можно посмотреть разделительную зеленую полоску в айсе). Указатель на KeServiceDescriptorTable можно получить набрав exp keser


Дата: Сен 7, 2004 15:52:19

Four-F Чесно говоря , я пока ставлю эксперименты только в юзер моде . О ядре я очень мало знаю , не интересуюсь . Наверное из-за сайса , уже три раза его ставил и потом три раза сносил , не могу вытерпеть его интерфейс . Чтобы научиться ним пользоваться , нужно читать мануал , а я не очень люблю :)

Если THREADINFO о которой говорил Рихтер лежит в [fs:40] , то он заполняеться в момент инициализации gdi32.dll , вот таким кодом (w2ksp4) :
====================GDI32.GdiDllInitialize=======================
77F4224D    B8 D4100000          MOV     EAX, 10D4
77F42252    8D5424 04            LEA     EDX, DWORD PTR SS:[ESP+4]
77F42256    CD 2E                INT     2E
77F42258    C3                   RETN
==================================================================

Т.е. я правильно понимаю , что после этого вызова ядра мы имеем очередь сообщений , а если в нашем процессе нету gdi32.dll , то нет и очереди ?


Дата: Сен 7, 2004 16:37:56

bogrus, скорее всего нет!
gdi32.dll загружается (отображается) в процесс, который может содержать GUI-потоки и НЕ GUI-потоки, ты же можешь юзать gdi32.dll!TextOutW() в НЕ GUI-потоке, а там очередь сообщений тебе не нужна, например, рисовать в окне Рабочего стола TextOutW() и тогда нет смысла системе из-за этого делать GUI-поток.

через прерывание int 2Eh вызываются сервисы ядра.

<< . 1 . 2 . 3 . >>


Powered by miniBB 1.6 © 2001-2002
Время загрузки страницы (сек.): 0.061