Board index » cppbuilder » Re: Catching the Application->Minimize() ..event from TaskBar Buttons

Re: Catching the Application->Minimize() ..event from TaskBar Buttons


2005-11-11 12:16:52 AM
cppbuilder4
Simon Guertin < XXXX@XXXXX.COM >wrote:
Quote

I am using the TTrayIcon [...]
Do not use TTrayIcon. It's very buggy and it has 2 fatal bugs
that can cause your application to crash or be orphananed. I
would suggest you look here instead:
tinyurl.com/bvoy6
~ JD
 
 

Re:Re: Catching the Application->Minimize() ..event from TaskBar Buttons

Good morning all!
I am using the TTrayIcon and there is one odd behavior that is happening
in my application.
When I have a TForm open and I use the TaskBar button to toggle between
Minimized and The restore state, it does not behave correctly.
If the Window is already open, (In maximized state or normal) and I
click on the Taskbar button, it makes the window disapear.
But if I click on the Taskbar after I intentionaly minimized the window
by using the Minimize Button, it restores the state of the window.
I was trying to catch WM_Message that could come from the task bar to
fix this behavior but I don't know where to trap it.
Or do I need to Extend and Modify TTrayIcon to alterate the behavior
when the minize/restore message is received from the OS?
thanks
Simon
 

Re:Re: Catching the Application->Minimize() ..event from TaskBar Buttons

Simon Guertin < XXXX@XXXXX.COM >wrote:
Quote

[...] I now have another problem, I need to convince my boss
to do so also
Should be easy considering that you have the complete source
for it *and* no restrictions on using it within your
applications.
~ JD
 

{smallsort}

Re:Re: Catching the Application->Minimize() ..event from TaskBar Buttons

JD wrote:
Quote
Simon Guertin < XXXX@XXXXX.COM >wrote:

>I am using the TTrayIcon [...]


Do not use TTrayIcon. It's very buggy and it has 2 fatal bugs
that can cause your application to crash or be orphananed. I
would suggest you look here instead:

tinyurl.com/bvoy6

~ JD

Thank you, I now have another problem, I need to convince my boss to do
so also
:)
have a good day!
 

Re:Re: Catching the Application->Minimize() ..event from TaskBar Buttons

Quote
Thank you, I now have another problem, I need to
convince my boss to do so also
The code JD has shown you is definitely the most
complete tray code you can find for BCB on the net...
You have the whole source, so you are the master.
No need to worry.
--
Best regards,
Vladimir Stefanovic
 

Re:Re: Catching the Application->Minimize() ..event from TaskBar Buttons

Vladimir Stefanovic wrote:
Quote
>Thank you, I now have another problem, I need to
>convince my boss to do so also


The code JD has shown you is definitely the most
complete tray code you can find for BCB on the net...

You have the whole source, so you are the master.
No need to worry.



I know I know, I don't really worry about that
 

Re:Re: Catching the Application->Minimize() ..event from TaskBar Buttons

I am using the TrayIcon from your link and I modified it so the
application does not minimize to the tray icon when I push the task bar
button. It now works fine but there is one little problem remaining.
When I minimize the Window to the task bar, instead of just clicking the
task bar button, I right click on the task bar button and then I choose
restore.
The application restores properly but after this, clicking on the task
bar button does not toggle between the Minimized and Restore state. It
does not even trigger the minimize and restore events. But if I simply
right click again (I don't have to choose Restore or Minimize) on the
task bar button, the behavior is normal again.
I notice I am still receiving other events but not WM_SYSCOMMAND types
event. I also modified the Tray to use a "Fake Main Form" because my
main form is special and remains hiddden forever.
Thank you
Simon
 

Re:Re: Catching the Application->Minimize() ..event from TaskBar Buttons

Never mind this, I believe it is a normal Windows Behavior, it seems to
do the same in all the other applications (but it still seems like a bug )
Simon Guertin wrote:
Quote
I am using the TrayIcon from your link and I modified it so the
application does not minimize to the tray icon when I push the task bar
button. It now works fine but there is one little problem remaining.
When I minimize the Window to the task bar, instead of just clicking the
task bar button, I right click on the task bar button and then I choose
restore.

The application restores properly but after this, clicking on the task
bar button does not toggle between the Minimized and Restore state. It
does not even trigger the minimize and restore events. But if I simply
right click again (I don't have to choose Restore or Minimize) on the
task bar button, the behavior is normal again.

I notice I am still receiving other events but not WM_SYSCOMMAND types
event. I also modified the Tray to use a "Fake Main Form" because my
main form is special and remains hiddden forever.

Thank you

Simon
 

Re:Re: Catching the Application->Minimize() ..event from TaskBar Buttons

Simon Guertin < XXXX@XXXXX.COM >wrote:
Quote

[...] I modified it so the application does not minimize to
the tray icon [...]
In the Minimize method, instead of minimizing the application,
the application is hidden and then marked at minimized for
Windows using the win32 API SetWindowLong. I did it this way
because the application would animate to the taskbar and then
disappear from the taskbar. The result is that the application
animates to the system tray instead.
Exactly how did you modify the Minimize and Restore methods?
Quote
[...] but after this, clicking on the task bar button does
not toggle between the Minimized and Restore state. It does
not even trigger the minimize and restore events. But if I
simply right click again (I don't have to choose Restore or
Minimize) on the task bar button, the behavior is normal
again.
It is possible to confuse Windows and get a window's state
mixed up and Windows will get it right eventually but only by
right clicking the program's icon.
I've seen it before when one restores a window that is already
restored. The Minimize border icon is disabled and clicking
the tray icon does nothing unless it's right clicked but then
that clears the problem.
Quote
I am still receiving other events but not WM_SYSCOMMAND
types event.
That's because the component is looking for those messages to
come through the main form and you changed the design.
Quote
I also modified the Tray to use a "Fake Main Form" because
my main form is special and remains hiddden forever.
That's the reason that you're not getting the other messages
and explains why/how the window state is getting mixed up.
To do what you are trying to do would require rewritting
several blocks of code because Windows doesn't report the
window state correctly 100% of the time and you need to know
the correct state before you restore.
The component deals with this by hooking into the main form's
WndProc method and manually tracking the application's state.
Since you changed the main form from being the 'main form',
you need to hook into the other form instead.
Why are you hidding the main form and displaying another? ISTM
that if all it is being used for is a class container, that
that code could go into a seperate unit and be allocated in
the main form's constructor.
I also read your other post and to be clear, it is not normal
Windows behavior. It's your code that's doing it.
~ JD
 

Re:Re: Catching the Application->Minimize() ..event from TaskBar Buttons

Good morning!
Quote
I also read your other post and to be clear, it is not normal
Windows behavior. It's your code that's doing it.

First, about the Windows Behavior. I am 200% positive that the behavior
is window's fault or implementation Or they themselfs badly impolemented
their own things wrong. I have just reproduced the behavior and here are
the exacts steps.
1. Reboot Windows. (To make sure nothing is left from my TrayIcon code
just in case)
2. I opened Internet Explorer (6.0)
3. I maximized IE using the top right Maximize button.
4. I then minimized IE using the Top right minimize button
5. Then I right click on IE's Taskbar button at the bottom of my desktop.
6. I then choose Restore (The window gets restore in the maximized state)
7. I Left click many times on the IE's task bar button (The button get
toggled in the push state and unclicked state) but nothing happens anymore.
8. I simply right click on IE's task bar button to make the menu show up
but I don't choose anything.
9. I click somewhere else in the screen to make this menu disapear.
10. I left click on IE's Taskbar button and the behavior if finally
restored. (It makes the window toggles between minimize and maximize
state.)
Quote
Why are you hidding the main form and displaying another? ISTM
that if all it is being used for is a class container, that
that code could go into a seperate unit and be allocated in
the main form's constructor.
We need a fake "Main form" different then the Real MainForm because we
cannot control which form is the MainForm of the application.
Let me explain why. Our application is very tricky and we don't create
all our forms in the project's main .cpp file.
Also, the way Borland assigns the Main form is not the way it is said in
the Documentation. It does not assign the Main form to the Main form
specified in the Project's Option main form or to the first form that is
created in the project's main .cpp file (normally the Main Form is the
first one Application->CreateForm(__classid(TForm1), &Form1);)
It really allocates the Main form to the first form that has completely
return from creation.)
Ok so in my case, there are some objects that are getting parse during
the creation of a form and indirectly creates Other forms but they
remain invisible. In the end, the first form that finish creating
becomes the application's main form. We have no way to control this
order, so when we would close that form, the application would shutdown.
So I created the real MainForm that does not get parse and That I am
sure is created first. This form is like our dummy form that is always
the Application's Application->MainForm.
And so we delegated the Applciation's Main form role to another Window.
A Normal window that the user interacts with.
Quote

Exactly how did you modify the Minimize and Restore methods?
I modified the complete object to either point to my Fake Main form.
Sometime I kept the real Application->MainForm or sometimes I called the
new field FMainForm ... But this object is still instanciated in the
Real Application->MainForm.
bool __fastcall TAnimatedTrayIcon::Minimize(bool IsSystemMinimize)
{
FMinimized = true;
if( FUseTray && FActive )
{
if( !FAnimating || !FLoopSound ) ::PlaySound(
(LPCSTR)MinimizeWave, NULL, SND_MEMORY | SND_ASYNC | SND_NODEFAULT |
SND_NOWAIT );
BoundsRect = FMainForm->BoundsRect;
//if(!IsSystemMinimize)::DrawAnimatedRects(FMainForm->Handle,
IDANI_CAPTION, &BoundsRect, &TrayRect );
if( FMainForm->FormStyle != fsMDIForm )
{
for( int x = 0; x < Screen->FormCount; ++x )
{
if( Screen->Forms[x]->Visible ) vForms.push_back(
Screen->Forms[x] );
}
HWND hWnd = ::GetTopWindow( FMainForm->Handle );
while( hWnd )
{
vZOrder.push_back( hWnd );
hWnd = ::GetNextWindow( hWnd, GW_HWNDNEXT );
}
for( unsigned int x = 0; x < vForms.size(); ++x )
vForms[x]->Visible = false;
}
else FMainForm->Visible = false;
Application->ShowMainForm = false;
if(IsSystemMinimize)
::ShowWindow( Application->Handle, SW_MINIMIZE );
else
::ShowWindow( Application->Handle, SW_HIDE );
::SetWindowLong( Application->Handle, GWL_STYLE,
::GetWindowLong(Application->Handle, GWL_STYLE) | WS_MINIMIZE );
return true;
}
return false;
}
//--------------------------------------------------------------
bool __fastcall TAnimatedTrayIcon::Restore(bool IsSystemRestore)
{
bool Result = false;
if( FUseTray && FActive && FMinimized )
{
if( !FAnimating || !FLoopSound ) ::PlaySound(
(LPCSTR)RestoreWave, NULL, SND_MEMORY | SND_ASYNC | SND_NODEFAULT |
SND_NOWAIT );
//if(!IsSystemRestore) ::DrawAnimatedRects(FMainForm->Handle,
IDANI_CAPTION, &TrayRect, &BoundsRect );
if( FMainForm->FormStyle != fsMDIForm )
{
if(FMainForm->Visible == false && !IsSystemRestore)
FMainForm->Visible = true;
for( int x = vForms.size() - 1; x>-1; --x )
vForms[x]->Visible = true;
for( int x = vZOrder.size() - 1; x>-1; --x )
::ShowWindow( vZOrder[x], SW_SHOW );
vZOrder.clear();
vForms.clear();
}
else
{
FMainForm->Visible = true;
FMainForm->ArrangeIcons();
}
::ShowWindow( Application->Handle, SW_SHOW );
// ::ShowWindow( FMainForm->Handle, SW_SHOW );
Application->Restore();
Application->BringToFront();
Result = true;
}
FMinimized = false;
return Result;
}
 

Re:Re: Catching the Application->Minimize() ..event from TaskBar Buttons

Simon Guertin < XXXX@XXXXX.COM >wrote:
Quote

>[...] it is not normal Windows behavior.
[...] the behavior is window's fault [...] the exacts steps.
1. Reboot Windows. [...]
You and I were talking about 2 different animals. Even though,
I was unable to reproduce it on an XP machine.
Quote
2. I opened Internet Explorer (6.0)
Were you connected? Not that it mattered with XP.
Quote
>Why are you hidding the main form [...]
[...] because we cannot control which form is the MainForm
Sure you can.
Quote
[...] there are some objects that are getting parse during
the creation of a form and indirectly creates Other forms
[...] the first form that finish creating becomes the
application's main form. We have no way to control this
order,
Yes, you do and the solution is quite simple. All you have to
do is delay creating the other forms until the main form is
fully created. In fact, my TrayIcon source demonstrates doing
this. In it's constructor, it uses the win32 API PostMessage
to complete initialization because the final step has to wait
until the TApplication::MainForm is valid.
The TrayIcon code would need a slight modification because the
TForm already has a WndProc method so instead of creating one
like the TrayIcon, you need to override it instead. For
example:
//--- in the header -------------------------------------------
protected: // User declarations
virtual void __fastcall WndProc( TMessage &Message );
private:
#define UWM_INITIALIZE (WM_USER + 100)
typedef TForm inherited;
//--- in the unit ---------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner)
{
// Do all of your initialization except the stuff
// related to creating other forms.
// As your last command:
::PostMessage( Handle, UWM_INITIALIZE, 0, 0 );
}
//-------------------------------------------------------------
void __fastcall TForm1::WndProc( TMessage &Message )
{
if( Message.Msg == UWM_INITIALIZE )
{
// move your problem code from the ctor to here
}
inherited::WndProc( Message );
}
//-------------------------------------------------------------
Another way to accomplish the exact same thing is to use a
message map instead. For example:
//--- in the header -------------------------------------------
private:
#define UWM_INITIALIZE (WM_USER + 100)
MESSAGE void __fastcall UWMInitialize( TMessage &Message );
public:
BEGIN_MESSAGE_MAP
VCL_MESSAGE_HANDLER( UWM_INITIALIZE, TMessage, UWMInitialized )
END_MESSAGE_MAP( TForm )
//--- in the unit ---------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner)
{
// Do all of your initialization except the stuff
// related to creating other forms.
// As your last command:
::PostMessage( Handle, UWM_INITIALIZE, 0, 0 );
}
//-------------------------------------------------------------
MESSAGE void __fastcall TForm1::UWMInitialize(TMessage &Message)
{
// move your problem code to here
}
//-------------------------------------------------------------
Should take you 5 minutes to cut and paste - tops.
Quote
>Exactly how did you modify the Minimize and Restore methods?

I modified the complete object to either point to my Fake Main form.
Sometime I kept the real Application->MainForm or sometimes I called the
new field FMainForm
To get the WM_SYSCOMMAND(s), you'll need (if you don't
change the main form) to move this code from the tray's
AppHook method to the fake main form's WndProc method
that is demonstrated above:
if( Message.Msg == WM_SYSCOMMAND )
{
switch( Message.WParam )
{
case SC_RESTORE:
case SC_MAXIMIZE: return Restore();
case SC_MINIMIZE: return Minimize();
}
}
but it's not as simple as that. The Restore and Minimize
methods would no longer return a value so they would be
defined as void instead of bool. How you deal with actually
minimizing and restoring should be different as well because
now Windows will actually minimize and restore the fake main.
I don't see any obviouse problems with your Minimize and
Restore methods but I'm also not inclined to build a test
bed to check it.
That said, I would strongly suggest that you reconsider using
the fake main form. ISTM that it would clear up several issues
for you and most likely prevent several more in the time to
come.
~ JD
 

Re:Re: Catching the Application->Minimize() ..event from TaskBar Buttons

Thanks , I will look into your suggestions and let you know!
JD wrote:
Quote
Simon Guertin < XXXX@XXXXX.COM >wrote:

>>[...] it is not normal Windows behavior.


>[...] the behavior is window's fault [...] the exacts steps.
>1. Reboot Windows. [...]


You and I were talking about 2 different animals. Even though,
I was unable to reproduce it on an XP machine.


>2. I opened Internet Explorer (6.0)


Were you connected? Not that it mattered with XP.


>>Why are you hidding the main form [...]


>[...] because we cannot control which form is the MainForm


Sure you can.


>[...] there are some objects that are getting parse during
>the creation of a form and indirectly creates Other forms
>[...] the first form that finish creating becomes the
>application's main form. We have no way to control this
>order,


Yes, you do and the solution is quite simple. All you have to
do is delay creating the other forms until the main form is
fully created. In fact, my TrayIcon source demonstrates doing
this. In it's constructor, it uses the win32 API PostMessage
to complete initialization because the final step has to wait
until the TApplication::MainForm is valid.

The TrayIcon code would need a slight modification because the
TForm already has a WndProc method so instead of creating one
like the TrayIcon, you need to override it instead. For
example:

//--- in the header -------------------------------------------
protected: // User declarations
virtual void __fastcall WndProc( TMessage &Message );
private:
#define UWM_INITIALIZE (WM_USER + 100)
typedef TForm inherited;

//--- in the unit ---------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner)
{
// Do all of your initialization except the stuff
// related to creating other forms.
// As your last command:
::PostMessage( Handle, UWM_INITIALIZE, 0, 0 );
}
//-------------------------------------------------------------
void __fastcall TForm1::WndProc( TMessage &Message )
{
if( Message.Msg == UWM_INITIALIZE )
{
// move your problem code from the ctor to here
}
inherited::WndProc( Message );
}
//-------------------------------------------------------------

Another way to accomplish the exact same thing is to use a
message map instead. For example:

//--- in the header -------------------------------------------
private:
#define UWM_INITIALIZE (WM_USER + 100)
MESSAGE void __fastcall UWMInitialize( TMessage &Message );
public:
BEGIN_MESSAGE_MAP
VCL_MESSAGE_HANDLER( UWM_INITIALIZE, TMessage, UWMInitialized )
END_MESSAGE_MAP( TForm )

//--- in the unit ---------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner)
{
// Do all of your initialization except the stuff
// related to creating other forms.
// As your last command:
::PostMessage( Handle, UWM_INITIALIZE, 0, 0 );
}
//-------------------------------------------------------------
MESSAGE void __fastcall TForm1::UWMInitialize(TMessage &Message)
{
// move your problem code to here
}
//-------------------------------------------------------------

Should take you 5 minutes to cut and paste - tops.


>>Exactly how did you modify the Minimize and Restore methods?
>
>I modified the complete object to either point to my Fake Main form.
>Sometime I kept the real Application->MainForm or sometimes I called the
>new field FMainForm


To get the WM_SYSCOMMAND(s), you'll need (if you don't
change the main form) to move this code from the tray's
AppHook method to the fake main form's WndProc method
that is demonstrated above:

if( Message.Msg == WM_SYSCOMMAND )
{
switch( Message.WParam )
{
case SC_RESTORE:
case SC_MAXIMIZE: return Restore();
case SC_MINIMIZE: return Minimize();
}
}

but it's not as simple as that. The Restore and Minimize
methods would no longer return a value so they would be
defined as void instead of bool. How you deal with actually
minimizing and restoring should be different as well because
now Windows will actually minimize and restore the fake main.

I don't see any obviouse problems with your Minimize and
Restore methods but I'm also not inclined to build a test
bed to check it.

That said, I would strongly suggest that you reconsider using
the fake main form. ISTM that it would clear up several issues
for you and most likely prevent several more in the time to
come.

~ JD

 

Re:Re: Catching the Application->Minimize() ..event from TaskBar Buttons

Hello again! I am encountering a problem when I tried to display a modal
form directly from the tray icon and without having a Fake Main form or
Real Main form open.
The first time around I would right click on the tray to open a form
(Form->ShowModal()) and it would work. I would close the form using
Right click ->Close on the task bar button. I would then reopen the
same Modal form using the tray. It would then crash, saying cannot
display modal form. I fixed this bu calling Application->Restore before
showing the Modal form. It seems to work. I suspect that my problem
could be at two places.
First I am thinking it is the modified right click of the popup. Do you
think setting the foreground window to the fake main form if ok or I
really need to set the foreground window the the real Application->MainForm.
Or secondly, I should check if there is a parent window before calling a
showmodal and if not I just need to call Show() on my form?
//Modified popup right click
if( FPopupMenu )
{
::SetForegroundWindow( FMainForm->Handle );
//or ::SetForegroundWindow( Application->MainForm->Handle );
FPopupMenu->PopupComponent = this;
FPopupMenu->Popup( P.x, P.y );
::PostMessage( Application->MainForm->Handle, WM_NULL, 0, 0 );
//why this post message that does nothing? should it be send to my fake
//main form instead?
}
thanks
Simon
 

Re:Re: Catching the Application->Minimize() ..event from TaskBar Buttons

Simon Guertin < XXXX@XXXXX.COM >wrote:
Quote

[...] I would then reopen the same Modal form using the
tray. It would then crash, saying cannot display modal form.
I don't see why you need it to be modal and that could be the
the only problem. However, since you also said that calling
Application->Restore fixed the problem, there's also a
possibility that the window state is getting mixed up which
would be a result of using a fake main.
Typically, when a tray icon displays a secondary form, it's
non-modal and gets it's own taskbar icon. The seperate taskbar
icon is achieved by overriding the form's CreateParams method
and applying the WS_EX_APPWINDOW to it's ExStyle.
Quote
[...] I suspect that my problem could be at two places.
Without having the code to trace through, I'd guess it has to
do with blocking message queue's. By now, you've modified the
tray code so that it's a different animal and it's only going
to get more complicated. How close are you? Have you tried
getting rid of the fake main?
Quote
::PostMessage( Application->MainForm->Handle, WM_NULL, 0, 0 );
//why this post message that does nothing?
It's a work-around for a bug.
Quote
should it be send to my fake
//main form instead?
It needs to fo to the HWND that is processing clicks from the
TrayIcon.
~ JD