• dmk © (04.01.19 19:19) [0]
    Всем привет! Почему в такой конфигурации 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); //<- Размещаем в очереди WM_QUIT
       end;
     end;

     WM_NCHITTEST,
     WM_MOUSEMOVE:
     begin
       FWnd.MessageToChild(LMsg);
       FWnd.Process(LMsg);
     end;

     else FWnd.Process(LMsg);
     end;//case

     Result := DefWindowProc(Wnd, LMsg.Msg, LMsg.WParam, LMsg.LParam);
    end;


    После PostQuitMessage(0) выборка из очереди прекращается,
    т.е. WM_QUIT в очередь не добавляется. Почему так?
  • dmk © (04.01.19 19:24) [1]
    ЦИкл выборки сообщений такой:
    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;
  • Leonid Troyanovsky © (05.01.19 10:25) [2]

    > dmk ©   (04.01.19 19:24) [1]

    Непонятно, где ищется WM_QUIT, бо цикл заканчивается WM_CLOSE.
    И почему не WM_DESTROY?

    И в [1] очень подозрительная WndProc (class function?)
    Должно быть MakeObjectInstance and so on.

    --
    Regards, LVT.
  • dmk © (05.01.19 13:02) [3]
    >И почему не 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, а оно не размещается. Это и странно.
  • Styx © (05.01.19 20:55) [4]

    > bQuit := AMsg.message = (WM_QUIT or WM_CLOSE);

    А зачем тут WM_CLOSE?
  • dmk © (05.01.19 21:50) [5]
    >А зачем тут WM_CLOSE?
    WM_CLOSE генерится, когда закрываешь через крестик или меню.
    Причем параллельно генерится WM_SYSCOMMAND + SC_CLOSE.
  • dmk © (05.01.19 22:03) [6]
    В общем разобрался. Нужно 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;
  • Leonid Troyanovsky © (06.01.19 08:57) [7]

    > 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.
  • Leonid Troyanovsky © (06.01.19 09:14) [8]

    > 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 09:34) [9]

    > Leonid Troyanovsky ©   (06.01.19 08:57) [7]

    > Тебе уже двое намекнули на лог. ошибку с or WM_CLOSE.

    Хотя, это не та ошибка,  бо wm_quit or wm_close = wm_quit.
    Sorry.

    --
    Regards, LVT.
  • dmk © (06.01.19 10:34) [10]
    Дело в этой строчке:
    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.
    Не пытайте сильно.
  • Leonid Troyanovsky © (06.01.19 11:09) [11]

    > dmk ©   (06.01.19 10:34) [10]

    > MakeObjectInstance - это для VCL. У меня WinApi.

    Скопируй себе реализацию из classes, там от силы  несколько десятков строк.

    Не знаю, правда, поправили ли они FreeObjectInstance, там была утечка из-за
    отсутствия VirtualFree.

    > class var Wnds: TList<TWindow64

    Можно по-разному.
    Можно ссылку на объект хранить в пропертях  или юзердате окна.

    Но, аккуратней всего, IMHO,  MakeObjectInstance.

    --
    Regards, LVT.
  • dmk © (06.01.19 12:01) [12]
    >поправили ли они FreeObjectInstance
    Там адрес в список скидывается. Потом махом освобождается.
    Типа сборщика мусора (InstFreeList).

    Спасибо!
  • Leonid Troyanovsky © (06.01.19 12:28) [13]

    > 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.
Есть новые Нет новых   [118648   +58][b:0][p:0.001]