Page 1 of 2
Blink the titlebar of the target window
Posted: Wed Oct 06, 2010 6:16 am
by Mistrel
Windows already has a function FlashWindow and FlashWindowEx. However, these functions don't work on windows outside of the current process. So I wrote a replacement to mimic this behavior.
Thanks to netmaestro for this high performance timer code I snagged from elsewhere on the forum.
By capturing the #WM_MOUSEACTIVATE message, you can imitate the effect of a modal window by calling MessageBeep_(#MB_OK) along with FlashWindow(hWnd), which was my intent. It works great when you want to halt your main process until another one you just launched exits.
Code: Select all
;/ The default blink rate for Windows is four blinks with a 60 ms delay
Procedure FlashWindow(hWnd, Count=4, Timeout=60)
Protected Timer.TIMECAPS
Protected i
Protected OldStyle
;/ Set the window to a non-popup child so that it imitates a modal
;/ window and does not blink any borders around the currently
;/ highlighted control or other selection
OldStyle=GetWindowLongPtr_(hWnd,#GWL_STYLE)
SetWindowLongPtr_(hWnd,#GWL_STYLE,OldStyle&(~#WS_POPUP)|#WS_CHILD)
;/ Get timer capabilities
timeGetDevCaps_(@Timer,SizeOf(TIMECAPS))
;/ Reset timer resolution to lowest possible (usually 1)
timeBeginPeriod_(Timer\wPeriodMin)
;/ Blink the window by changing the foreground window between the
;/ target hWnd and the desktop
SetForegroundWindow_(GetDesktopWindow_())
For i=1 To Count
SetForegroundWindow_(hWnd)
Sleep_(Timeout)
SetForegroundWindow_(GetDesktopWindow_())
Sleep_(Timeout)
Next i
;/ Restore the old window style
SetWindowLongPtr_(hWnd,#GWL_STYLE,OldStyle)
;/ Reset timer resolution to normal
timeEndPeriod_(Timer\wPeriodMin)
EndProcedure
Re: Blink the titlebar of the target window
Posted: Wed Oct 06, 2010 7:05 am
by Mistrel
This new version uses threads so you can click a bunch of times and have the blinking blink faster. Also, if you want to emit a message beep as well, you can beep-beep as quick as you can click your mouse instead of having to wait for the blinking to stop.
The atomic counter is so that a non-modal window won't have its non-modal status reinstated until the last thread has ended.
Code: Select all
Global Glob_FlashWindow_ThreadCount
Structure FlashInfo
hWnd.i
Count.l
Timeout.l
IsModal.l
EndStructure
Procedure FlashWindowThread(*FlashInfo.FlashInfo)
Protected Timer.TIMECAPS
Protected i
Protected OldStyle
Debug *FlashInfo\hWnd
Debug *FlashInfo\Count
Debug *FlashInfo\Timeout
;/ Set the window to a non-popup child so that it imitates a modal
;/ window and does not blink any borders around the currently
;/ highlighted control or other selection
If Not *FlashInfo\IsModal
Style=GetWindowLongPtr_(*FlashInfo\hWnd,#GWL_STYLE)
SetWindowLongPtr_(*FlashInfo\hWnd,#GWL_STYLE,Style&(~#WS_POPUP)|#WS_CHILD)
EndIf
;/ Blink the window by changing the foreground window between the
;/ target hWnd and the desktop
SetForegroundWindow_(GetDesktopWindow_())
For i=1 To *FlashInfo\Count
SetForegroundWindow_(*FlashInfo\hWnd)
Sleep_(*FlashInfo\Timeout)
SetForegroundWindow_(GetDesktopWindow_())
Sleep_(*FlashInfo\Timeout)
Next i
;/ Decrement the total thread count
ThreadCount=InterlockedDecrement_(@Glob_FlashWindow_ThreadCount)
;/ Restore the old window style only if the thread count is zero
If Not *FlashInfo\IsModal And Not ThreadCount
SetWindowLongPtr_(*FlashInfo\hWnd,#GWL_STYLE,Style&(~#WS_CHILD)|#WS_POPUP)
EndIf
FreeMemory(*FlashInfo)
EndProcedure
;/ The default blink rate for Windows is four blinks with a 60 ms delay
Procedure FlashWindow(hWnd, Count=4, Timeout=60, IsModal=#False)
Protected *FlashInfo.FlashInfo
;/ Increment the total thread count
InterlockedIncrement_(@Glob_FlashWindow_ThreadCount)
*FlashInfo=AllocateMemory(SizeOf(FlashInfo))
*FlashInfo\hWnd=hWnd
*FlashInfo\Count=Count
*FlashInfo\Timeout=Timeout
*FlashInfo\IsModal=IsModal
CreateThread(@FlashWindowThread(),*FlashInfo)
EndProcedure
Re: Blink the titlebar of the target window
Posted: Wed Oct 06, 2010 10:01 am
by PB
> these functions don't work on windows outside of the current process
What do you mean? I can use the following code to flash the Calculator window:
Code: Select all
c=FindWindow_(0,"Calculator")
For n=1 To 4
FlashWindow_(c,1)
Sleep_(250)
FlashWindow_(c,0)
Sleep_(250)
Next
> So I wrote a replacement to mimic this behavior
Hmm, it's not an exact replacement though, because it doesn't flash the button
on the Taskbar of the app in another color, which FlashWindow does. So, can
you give an example of where FlashWindow is failing to flash a window of another
process? It works for me on XP.
Re: Blink the titlebar of the target window
Posted: Wed Oct 06, 2010 10:21 am
by Mistrel
Using your code to try and flash a window four times with a 60 ms delay doesn't work. I get on average between 1 and 2 flashes:
Code: Select all
hWnd=FindWindow_(0,"Calculator")
For i=1 To 4
FlashWindow_(hWnd,1)
Sleep_(60)
FlashWindow_(hWnd,0)
Next
End
FlashWindowEx doesn't work at all:
Code: Select all
Structure FLASHWINFO
cbSize.l
hwnd.i
dwFlags.l
uCount.l
dwTimeout.l
EndStructure
#FLASHW_ALL=$00000003
#FLASHW_CAPTION=$00000001
#FLASHW_STOP=0
#FLASHW_TIMER=$00000004
#FLASHW_TIMERNOFG=$0000000C
#FLASHW_TRAY=$00000002
FlashWindow.FLASHWINFO
FlashWindow\cbSize=SizeOf(FLASHWINFO)
FlashWindow\dwFlags=#FLASHW_CAPTION
FlashWindow\dwTimeout=60
FlashWindow\uCount=4
hWnd=FindWindow_(0,"Calculator")
FlashWindow\hwnd=hWnd
If hWnd
FlashWindowEx_(@FlashWindow)
EndIf
Repeat
Delay(1)
ForEver
FlashWindowEx works fine if the window is from the same process:
Code: Select all
Structure FLASHWINFO
cbSize.l
hwnd.i
dwFlags.l
uCount.l
dwTimeout.l
EndStructure
#FLASHW_ALL=$00000003
#FLASHW_CAPTION=$00000001
#FLASHW_STOP=0
#FLASHW_TIMER=$00000004
#FLASHW_TIMERNOFG=$0000000C
#FLASHW_TRAY=$00000002
FlashWindow.FLASHWINFO
FlashWindow\cbSize=SizeOf(FLASHWINFO)
FlashWindow\dwFlags=#FLASHW_CAPTION
FlashWindow\dwTimeout=60
FlashWindow\uCount=4
OpenWindow(0,0,0,240,240,"Grue")
Procedure FlashThread(*FlashWindow.FLASHWINFO)
Delay(1000)
hWnd=FindWindow_(0,"Grue")
*FlashWindow\hwnd=hWnd
If hWnd
Debug "Attempting to flash Grue"
FlashWindowEx_(*FlashWindow)
Else
Debug "Can't find Grue"
EndIf
EndProcedure
CreateThread(@FlashThread(),@FlashWindow)
Repeat
Until WaitWindowEvent()=#WM_CLOSE
But if you try to open window "Grue" from another process you will see that it cannot be flashed.
PB wrote:it's not an exact replacement though, because it doesn't flash the button on the Taskbar of the app in another color, which FlashWindow does.
The code for flashing the title bar works great. If you want it to flash something else then it will be a cinch to modify. I only needed the titlebar to flash.
Re: Blink the titlebar of the target window
Posted: Wed Oct 06, 2010 10:41 am
by cas
Mistrel wrote:FlashWindowEx doesn't work at all:
Code: Select all
Structure FLASHWINFO
cbSize.l
hwnd.i
dwFlags.l
uCount.l
dwTimeout.l
EndStructure
#FLASHW_ALL=$00000003
#FLASHW_CAPTION=$00000001
#FLASHW_STOP=0
#FLASHW_TIMER=$00000004
#FLASHW_TIMERNOFG=$0000000C
#FLASHW_TRAY=$00000002
FlashWindow.FLASHWINFO
FlashWindow\cbSize=SizeOf(FLASHWINFO)
FlashWindow\dwFlags=#FLASHW_TIMERNOFG
FlashWindow\dwTimeout=60
FlashWindow\uCount=4
hWnd=FindWindow_(0,"Calculator")
If hWnd
FlashWindowEx_(@FlashWindow)
EndIf
Repeat
Delay(1)
ForEver
You forgot to store hWnd to structure. It works fine when you do it right.
Re: Blink the titlebar of the target window
Posted: Wed Oct 06, 2010 10:45 am
by Mistrel
Sorry about that. However, if you test it, it still does not work:
Code: Select all
Structure FLASHWINFO
cbSize.l
hwnd.i
dwFlags.l
uCount.l
dwTimeout.l
EndStructure
#FLASHW_ALL=$00000003
#FLASHW_CAPTION=$00000001
#FLASHW_STOP=0
#FLASHW_TIMER=$00000004
#FLASHW_TIMERNOFG=$0000000C
#FLASHW_TRAY=$00000002
FlashWindow.FLASHWINFO
FlashWindow\cbSize=SizeOf(FLASHWINFO)
FlashWindow\dwFlags=#FLASHW_CAPTION
FlashWindow\dwTimeout=60
FlashWindow\uCount=4
hWnd=FindWindow_(0,"Calculator")
FlashWindow\hwnd=hWnd
If hWnd
FlashWindowEx_(@FlashWindow)
EndIf
Repeat
Delay(1)
ForEver
Re: Blink the titlebar of the target window
Posted: Wed Oct 06, 2010 10:50 am
by cas
It looks like 60ms is not good a good value because it fades out after every blink for more than 100-200ms. This looks fine here:
Code: Select all
FlashWindow\dwFlags=#FLASHW_ALL
FlashWindow\dwTimeout=700
FlashWindow\uCount=4
Re: Blink the titlebar of the target window
Posted: Wed Oct 06, 2010 10:58 am
by Mistrel
cas wrote:It looks like 60ms is not good a good value because it fades out after every blink for more than 100-200ms.
And on my system is does nothing. Also, I want 4 blinks every 60 ms, like a modal window. And as I said before, this works fine for windows of the same process but not for those of another. Hence why I wrote the substitute.
If you can illustrate 4 blinks with a delay of 60 ms using either FlashWindow or FlashWindowEx on another process like "Calculator", then you will have a counter argument. Getting the window to kinda-sorta-flash or just obtain focus doesn't count.
Re: Blink the titlebar of the target window
Posted: Wed Oct 06, 2010 11:10 am
by cas
Your code form 1st and 2nd post ignores Count parameter on Windows 7 and it flashes 7 times every time. Maybe we need Select OsVersion() : EndSelect. #FLASHW_ALL and then set #FLASHW_STOP when window is active works fine and it doesn't ignore Count parameter on Windows 7.
Re: Blink the titlebar of the target window
Posted: Wed Oct 06, 2010 11:21 am
by Mistrel
Would you post example code? The project I implemented both instances (post 1 and 2) both blink the expected number of times.
When I count blinks I count on/off as a single blink. Are you sure you're not counting each separately?
I'm running Windows XP 64-bit and compiling with PureBasic 4.51 x86. I did not test my code for x64 compatibility.
Re: Blink the titlebar of the target window
Posted: Wed Oct 06, 2010 12:02 pm
by cas
Yes, i'm sure. No matter what Count parameter is, it flashes 7 times with this code:
Code: Select all
FlashWindow(FindWindow_(0,"Calculator"),4,60)
OS is Windows 7 x64 and PB 4.51 x86.
And there is a registry key in
HKEY_CURRENT_USER\ControlPanel\Desktop called
ForegroundFlashCount which is set to 7 by default.
Re: Blink the titlebar of the target window
Posted: Wed Oct 06, 2010 1:04 pm
by PB
> try and flash a window four times with a 60 ms delay doesn't work
That's why I used 250. Where did you get 60 from? The MSDN docs
for FlashWindowEx says the flash rate is the default cursor blink rate,
which you can get with GetCaretBlinkTime_(). This isn't 60 by default.
> Sorry about that. However, if you test it, it still does not work
Well, it works fine here on XP with a value of 60 or 250 for the rate.
Re: Blink the titlebar of the target window
Posted: Wed Oct 06, 2010 9:13 pm
by Mistrel
PB wrote:> Well, it works fine here on XP with a value of 60 or 250 for the rate.
Would you post your code? Are you using it on the same process or on Calculator?
Here is the behavior I'm imitating. Click on the main window and the message box will flash 4 times with a 60 ms delay (at least it does on XP):
Code: Select all
OpenWindow(0,0,0,320,240,"")
MessageRequester("","Hello!")
Repeat
Until WaitWindowEvent()=#WM_CLOSE
Re: Blink the titlebar of the target window
Posted: Wed Oct 06, 2010 9:52 pm
by PB
> Would you post your code? Are you using it on the same process or on Calculator?
I was using your own code example on Calculator. But, read on!
> the message box will flash 4 times with a 60 ms delay
We are talking about 2 different window flashes here, which is the issue.

You're talking about a modal window flash, and I'm talking about the window
flash when a non-focussed window wants to get your attention by flashing
itself in the taskbar. That's what FlashWindow is intended for (from MSDN:
"a window is flashed to inform the user that the window requires attention
but that it does not currently have the keyboard focus"). That is what I
assumed you were trying to do because you said you were mimicking the
FlashWindow API. But now, from your comment above, I can see you're
trying to flash in a modal way instead. No wonder we're getting different
results!

Re: Blink the titlebar of the target window
Posted: Wed Oct 06, 2010 10:03 pm
by Mistrel
Yes, that would make sense. I did mention the FlashWindowEx but I didn't mention that I used non-default parameters.
Basically, I wanted to launch an application with RunProgram and have the main application alert you to the other one as if it was part of the application. I did this by duplicating the behavior of a blinking message box.
The blinking may be different on Windows 7. I haven't tried it there yet. But on XP it seems to flash 4 times with a 60 ms interval. I "can" duplicate this with FlashWindowEx but "only" if the window is in the same process. Hence why I wrote a replacement.
And, yes. You are correct. It doesn't duplicate the default behavior of FlashWindow or FlashWindowEx.
The root of the problem was that I didn't provide any example code for a frame of reference. But I had no idea this would turn into such an event!