-
Всем привет! Почему в такой конфигурации PostQuitMessage не срабатывает?
class function TRenderForm.WndProc(Wnd: HWND; Msg: NativeInt; WParam: WParam; LParam: LParam): LResult;
var
LMsg: TMessage;
CanClose: Boolean;
begin
LMsg.Msg := Msg;
LMsg.WParam := WParam;
LMsg.LParam := LParam;
LMsg.Result := 0;
case LMsg.Msg of
WM_CLOSE:
begin
FWnd.DoCloseQuery(FWnd, CanClose);
if CanClose then
begin
FWnd.DoClose(FWnd);
PostQuitMessage(0); end;
end;
WM_NCHITTEST,
WM_MOUSEMOVE:
begin
FWnd.MessageToChild(LMsg);
FWnd.Process(LMsg);
end;
else FWnd.Process(LMsg);
end;
Result := DefWindowProc(Wnd, LMsg.Msg, LMsg.WParam, LMsg.LParam);
end; После PostQuitMessage(0) выборка из очереди прекращается, т.е. WM_QUIT в очередь не добавляется. Почему так?
-
ЦИкл выборки сообщений такой:
procedure ProcessMessages(AWnd: TRenderForm); var AMsg: tagMSG; bRet, bQuit: Boolean;
begin if (AWnd <> nil) then begin repeat bRet := PeekMessage(AMsg, AWnd.Handle, 0, 0, PM_REMOVE); bQuit := AMsg.message = (WM_QUIT or WM_CLOSE);
if bRet then begin TranslateMessage(AMsg); //<- Преобразует коды в символьное представление. Например WM_TEXT DispatchMessage(AMsg); end else //OnIdle begin AWnd.Render; end;
until bQuit; end; end;
-
> dmk © (04.01.19 19:24) [1]
Непонятно, где ищется WM_QUIT, бо цикл заканчивается WM_CLOSE. И почему не WM_DESTROY?
И в [1] очень подозрительная WndProc (class function?) Должно быть MakeObjectInstance and so on.
-- Regards, LVT.
-
>И почему не WM_DESTROY? Нигде не используется. Пока не нужно. Только WM_QUIT и WM_CLOSE. >class function? class function WndProc(Wnd: HWND; Msg: NativeInt; WParam: WParam; LParam: LParam): LResult; static; stdcall;
Она существует для всех экземпляров объекта. У меня форма на чистом WinApi. Внутри класса все class-переменные доступны как обычно. Без созданного экземпляра класса class-переменные и class-функции доступны для применения. WndProc в TWinControl тоже является не частью класса. Еще переделать на новые «фичи» не успели :) >И в [1] очень подозрительная WndProc class-методы можно делать статичными. Как бы внутри класса и вроде как вне класса. Видимо для удобства сделали. http://docwiki.embarcadero.com/RADStudio/Tokyo/en/Class_Methods#How_Class_Methods_Work_in_Delphi>Должно быть MakeObjectInstance and so on. Окно через RegisterClassEx/CreateWindowEx создается. WndProc тоже через RegisterClassEx назначается. DispatchMessage(AMsg) вызывает WndProc. Если WM_CLOSE, то хочется разместить в очереди сообщение WM_QUIT, а оно не размещается. Это и странно.
-
> bQuit := AMsg.message = (WM_QUIT or WM_CLOSE);
А зачем тут WM_CLOSE?
-
>А зачем тут WM_CLOSE? WM_CLOSE генерится, когда закрываешь через крестик или меню. Причем параллельно генерится WM_SYSCOMMAND + SC_CLOSE.
-
В общем разобрался. Нужно 2 цикла. Очередь же постоянно пустеет, поэтому выход из цикла означает состояние OnIdle. Вот так нормально работает и PostQuitMessage(0) тоже работает.
procedure ProcessMessages(AWnd: TRenderForm); var AMessage: tagMSG; bMessage, bAppDone: Boolean;
begin if (AWnd <> nil) then begin bAppDone := False;
while (not bAppDone) do begin repeat bMessage := PeekMessage(AMessage, AWnd.Handle, 0, 0, PM_REMOVE);
if bMessage then begin TranslateMessage(AMessage); DispatchMessage(AMessage); end;
bAppDone := (AMessage.message = WM_QUIT);
until (not bMessage) or bAppDone;
//--------------------------------------------- // OnIdle //---------------------------------------------
if (not bAppDone) then begin AWnd.Render; end; end; end; end;
-
> dmk © (05.01.19 13:02) [3]
> У меня форма на чистом WinApi.
Да хоть на грязном хаке. Оконная процедура из метода класса _должна_ создаваться путем MakeObjectInstance и точка. RTFM.
> функции доступны для применения. WndProc в TWinControl тоже > является не частью класса. Еще переделать на новые «фичи» > не успели :) class function существует с начала дельфи, но использовать ее в качестве оконной никто никогда не планировал. Задумайся, что будет если объект/окно не единственный. С глобальной-то fwnd.
> вызывает WndProc. Если WM_CLOSE, то хочется разместить в > очереди сообщение WM_QUIT, а оно не размещается. Это и странно.
Тебе уже двое намекнули на лог. ошибку с or WM_CLOSE.
-- Regards, LVT.
-
> dmk © (05.01.19 22:03) [6]
> В общем разобрался. Нужно 2 цикла. Очередь же постоянно > пустеет, поэтому выход из цикла означает состояние OnIdle. > Вот так нормально работает и PostQuitMessage(0) тоже работает.
WM_QUIT и не должен попадать в оконную процедуру, это же сигнал о выходе из цикла обработки сообщений.
while GetMessage(msg, 0, 0, 0) do begin TranslateMessage(msg); DispatchMessage(msg); end;
В WM_DESTROY, ну или WM_CLOSE делаем PQM и всё.
А отрисовку обычно делают в WM_PAINT. Для него уже предусмотрена низкоприоритетная обработка, безо всяких велосипедов.
-- Regards, LVT.
-
> Leonid Troyanovsky © (06.01.19 08:57) [7]
> Тебе уже двое намекнули на лог. ошибку с or WM_CLOSE.
Хотя, это не та ошибка, бо wm_quit or wm_close = wm_quit. Sorry.
-- Regards, LVT.
-
Дело в этой строчке: bAppDone := (AMessage.message = WM_QUIT); Если проверку перед Dispatch поставить, то WM_QUIT не ловится, а если после Dispatch поставить, то ловится. Я же писал выше, что разобрался. PostQuitMessage работает. MakeObjectInstance - это для VCL. У меня WinApi. >Задумайся, что будет если объект/окно не единственный. С глобальной-то fwnd. Можно так: class var Wnds: TList<TWindow64>; Тут с NoUser'ом разбирались: http://pda.delphimaster.net/?id=1490153872&n=5Я свою миссию выполнил. От VCL оторвался. Леонид, я всего 2 года в программировании. Много не знаю. До этого были простые студенческие поделки аля AppConsole. Не пытайте сильно.
-
> dmk © (06.01.19 10:34) [10]
> MakeObjectInstance - это для VCL. У меня WinApi.
Скопируй себе реализацию из classes, там от силы несколько десятков строк.
Не знаю, правда, поправили ли они FreeObjectInstance, там была утечка из-за отсутствия VirtualFree.
> class var Wnds: TList<TWindow64
Можно по-разному. Можно ссылку на объект хранить в пропертях или юзердате окна.
Но, аккуратней всего, IMHO, MakeObjectInstance.
-- Regards, LVT.
-
>поправили ли они FreeObjectInstance Там адрес в список скидывается. Потом махом освобождается. Типа сборщика мусора (InstFreeList).
Спасибо!
-
> dmk © (06.01.19 12:01) [12]
> Типа сборщика мусора (InstFreeList).
InstFreeList там был такой указатель:
procedure FreeObjectInstance(ObjectInstance: Pointer);
begin
if ObjectInstance <> nil then
begin
PObjectInstance(ObjectInstance)^.Next := InstFreeList;
InstFreeList := ObjectInstance;
end;
end;
А VirtualFree для того, что было выделено Block := VirtualAlloc(..) - нет. Ну, для экзешника или для статической линковки длл оно некритично. -- Regards, LVT.
|