Search code examples
delphiapimethodstrayflashwindowex

Flash ShowMessage(Pos) or any other predefined window in Dialogs unit in Delphi for Post-WInXP OS


This is approach I found for Tray ... :

http://www.programmersheaven.com/mb/delphikylix/257563/257563/how-can-i-make-a-system-tray-flash/

Does the same technique works for Dialogs ( as they are forms with addition params, in fact )? Or I can do it with way faster methods like getting handle / address / interface and overload or overdrive the function with FlashWindow(Ex) method?

I mean - can I make, for example ShowMessage(), window / dialog flash using FlashWindowEx() method and if I can, can it be done using the example in link given above?

Please, point to best direction or clarify my doubts ..

Thanks.

Sorry for bad formulation of question.


Solution

  • There are many ways a modal form or dialog (both VCL or native from the system) can be shown from a Delphi program, so you need to somehow hook into message processing and catch messages that are sent when a modal form or dialog is shown.

    For that a message hook can be set using the SetWindowsHookEx() API function. Since you need this only while the application is inactive you could set it in the handler of the OnDeactivate application event, and reset it in the handler for the OnActivate application event:

    var
      gNextHook: HHOOK;
    
    procedure TForm1.AppActivate(Sender: TObject);
    begin
      if gNextHook <> 0 then
        UnhookWindowsHookEx(gNextHook);
      gNextHook := 0;
    end;
    
    procedure TForm1.AppDeactivate(Sender: TObject);
    begin
      gNextHook := SetWindowsHookEx(WH_CALLWNDPROC, @WndProcHook, 0,
        GetCurrentThreadId);
    end;
    

    The hook function would watch for messages that are sent when a modal dialog or form is shown, and call FlashWindowEx() with the correct parameters:

    function WndProcHook(nCode: integer; AWParam: WPARAM; ALParam: LPARAM): LRESULT; stdcall;
    var
      DataPtr: PCWPStruct;
      Fwi: TFlashWInfo;
    begin
      DataPtr := PCWPStruct(ALParam);
      if (DataPtr^.message = WM_INITDIALOG)
        or ((DataPtr^.message = CM_ACTIVATE) and (DataPtr^.lParam = 0) and (DataPtr^.wParam = 0))
      then begin
        Fwi.cbSize := SizeOf(TFlashWInfo);
        // flash caption of new modal window
        Fwi.hwnd := DataPtr^.hwnd;
        Fwi.dwFlags := FLASHW_ALL or FLASHW_TIMERNOFG;
        Fwi.uCount := 0;
        Fwi.dwTimeout := 0;
        FlashWindowEx(Fwi);
        // uncomment this to flash task bar button as well
    (*
        Fwi.hwnd := Application.MainForm.Handle;
        Fwi.dwFlags := FLASHW_TRAY or FLASHW_TIMERNOFG;
        FlashWindowEx(Fwi);
    *)
      end;
      Result := CallNextHookEx(gNextHook, nCode, AWParam, ALParam);
    end;
    

    I chose WM_INITDIALOG which is sent for native dialogs like the open or save dialogs, and CM_ACTIVATE which is sent when a VCL form is shown modally. There may be more such messages that need to be caught. Above code works for the MessageDlg() function, the Application.MessageBox() function and TOpenDialog at least.

    Since these dialogs don't have their own taskbar button I added (commented out) code to flash the taskbar button of the main form as well. This isn't optimal, as they flash out of sync.

    Tested with Delphi 2009 on Windows XP, all error handling omitted, use it as a starting point only.