System Delphi

Notifying the user that attention to your program is needed by flashing the taskbar button, the window, or both.
--------------------------------------------------------------------------------
If the application you are building is targeted at Windows 98 or above, you can use the API call FlashWindowEx and pass it a FlashWInfo structure. If you are targeting Windows 95 your options are more limited, and you have to use a timer to achieve the desired result.
FlashWindowEx
A TFlashWInfo structure is needed to hold the information Windows needs to do its job when you make the API call. Luckily all the work has been done for you, and Delphi already knows about the structure. It is declared in Delphi like this:
type
TFlashWInfo = record
cbSize : LongInt;
hWnd : LongInt;
dwFlags : LongInt;
uCount : LongInt;
dwTimeout : LongInt;
end;
All you need to do is to populate the record and call the API function. For this example I have placed both in the OnClick event of a button, but you can of course locate them wherever suits you:
procedure TForm1.Button1Click(Sender: TObject);
var
FWinfo: TFlashWInfo;
begin
FWinfo.cbSize := 20;
FWinfo.hwnd := Application.Handle; // Handle of Window to flash
FWinfo.dwflags := FLASHW_ALL;
FWinfo.ucount := 10; // number of times to flash
FWinfo.dwtimeout := 0; // speed in ms, 0 default blink cursor rate
FlashWindowEx(FWinfo); // make it flash!
end;
Note that the flag shown against the dwflags property determines what the call does with the flashing, and the following constants are defined:
FLASHW_STOP = 0 // Stop flashing
FLASHW_CAPTION = 1 // Flash the window caption
FLASHW_TRAY = 2 // Flash the taskbar button
FLASHW_ALL = 3 // Flash both the window caption and taskbar button
FLASHW_TIMER = 4 // Flash continuously, until the FLASHW_STOP flag is set
FLASHW_TIMERNOFG = 5 // Flash continuously until the window
// comes to the foreground
This should work fine with Windows 98 and above. If you want to work with Windows 95, however, you will need to take a different approach:
FlashWindow
Under Windows 95 (and Delphi versions 3 and below which do not include the appropriate API wrapper) a different approach is needed. With this operating system the FlashWindowEx API call does not exist, and you need to use FlashWindow instead. (The FlashWindow call will work under later operating systems as well.)
The problem with FlashWindow is that it only works once, so to achieve the required flashing button it is necessary to use a system timer. This, of course, uses up valuable resources - but it does achieve the desired effect.
For the purposes of this example you need to create a form and drop a Timer and a Button onto it. Then select the Timer and double click the OnTimer event to create the event handler. Then add the following code:
procedure TForm1.Timer1Timer(Sender: TObject);
begin
FlashWindow(Application.Handle, True);
end;
Next, you need to start the flashing. To do this simply use the button OnClick event to set the Enabled property of the Timer to True. In this example I have used the button event to toggle the flashing on or off:
procedure TForm1.Button1Click(Sender: TObject);
begin
Timer1.Enabled := not Timer1.Enabled;
end;
So there you have it. Two different ways to achieve the same thing.
© Chris Bray /Vertical Software 2002
Update
Within minutes of posting this example Simon Clayton got in touch to suggest a way of causing the flashing ONLY if the application is not the currently active one. Over to Simon:
I've done it like this:
procedure TForm1.Timer1Timer(Sender: TObject);
begin
if (GetForeGroundWindow()<>Form1.Handle) then
begin
FWinfo.cbSize := 20;
FWinfo.hwnd := Application.Handle;
FWinfo.dwflags := FLASHW_ALL;
FWinfo.ucount := 5;
FWinfo.dwtimeout := 0;
Flashing := True;
FlashWindowEx(FWinfo);
end
else if (Flashing) then
begin
FWinfo.cbSize := 20;
FWinfo.hwnd := Application.Handle;
FWinfo.dwflags := FLASHW_STOP;
FWinfo.ucount := 0;
FWinfo.dwtimeout := 0;
FlashWindowEx(FWinfo);
Flashing := false;
end;
end;
I have also put some code in the Form's onPaint event to stop the flashing:
procedure TForm1.FormPaint(Sender: TObject);
begin
if (Flashing) then
begin
FWinfo.cbSize := 20;
FWinfo.hwnd := Application.Handle;
FWinfo.dwflags := FLASHW_STOP;
FWinfo.ucount := 0;
FWinfo.dwtimeout := 0;
FlashWindowEx(FWinfo);
end;
end;
The only problem I have now to solve is that if I am using an app on my second monitor and the form is on top on my first monitor then the onPaint event doesn't seem to get called when I switch back to the application which means that the flashing carries on - something I've noticed is a problem in MS Instant Messenger sometimes anyway.
Well done Simon - great tip!