Article View: borland.public.cppbuilder.nativeapi
Article #7223Re: IsIconic returns false when Application is minimised
From: JD
Date: Sun, 06 Mar 2005 19:52
Date: Sun, 06 Mar 2005 19:52
188 lines
8693 bytes
8693 bytes
"N Bentley" <nlb@ntl.com> wrote:
>
> This works fine except when the first Instance is minimised.
> When loading a second instance the code failes to restore
> the first instance because IsIconic always returns false.
That's correct. You need to apply the WS_EX_APPWINDOW flag
to the form's exStyle by overriding it's CreateParams method
for IsIconic to return the correct status. Be aware however,
this flag will cause the main form to have 2 icons on the
taskbar.
This class solves your problem and it can easily be wrapped
into a component. All you need to do to make it work is give
your main form an unique name - something like CoNameAppName
and add a line to the main form's constructor where you
allocate the class:
//-------------------------------------------------------------
#ifndef SingleInstanceH
#define SingleInstanceH
//-------------------------------------------------------------
#include <Classes.hpp>
//-------------------------------------------------------------
class TSingleInstance : public TComponent
{
private:
bool FHooked;
bool FMinimized;
HANDLE hMutex;
HHOOK WndReturnHook;
unsigned int SI_CUSTOM_MESSAGE;
TForm* FParentForm;
protected:
bool __fastcall AppWndProcHook( TMessage &Message );
static int __stdcall CallWndRetCallBackProc( int Code, WPARAM WParam, LPARAM LParam );
public:
__fastcall TSingleInstance(TComponent* Owner);
__fastcall TSingleInstance::~TSingleInstance();
__published:
};
//-------------------------------------------------------------
#endif
//-------------------------------------------------------------
#include <vcl.h>
#include <basepch.h>
//-------------------------------------------------------------
#pragma hdrstop
//-------------------------------------------------------------
#include "SingleInstance.h"
#pragma package(smart_init)
//-------------------------------------------------------------
TSingleInstance* ThisSingleInstance = NULL;
HWND hFirstInstance, hSecondInstance;
//-------------------------------------------------------------
BOOL CALLBACK EnumWindowsCallBack( HWND hWnd, LPARAM lParam )
{
char WindowClassName[256] = {0};
if( ::GetClassName(hWnd, WindowClassName, 255) )
{
if( stricmp(WindowClassName, (char*)lParam) == 0)
{
if( hFirstInstance )
{
hSecondInstance = hWnd;
return FALSE;
}
else hFirstInstance = hWnd;
}
}
return TRUE;
}
//-------------------------------------------------------------
__fastcall TSingleInstance::TSingleInstance(TComponent* Owner) : TComponent(Owner)
{
if( ThisSingleInstance ) throw Exception("TSingleInstance : Only one instance of TSingleInstance is allowed per application.");
if( !Owner ) throw Exception("TSingleInstance : You must assign an Owner.");
FParentForm = dynamic_cast<TForm*>(Owner);
if( !FParentForm ) throw Exception("TSingleInstance : The Owner must be a TForm.");
ThisSingleInstance = this;
FHooked = false;
hMutex = NULL;
WndReturnHook = NULL;
if( !ComponentState.Contains(csDesigning) )
{
char ThisClassName[ 256 ] = { 0 };
::GetClassName( FParentForm->Handle, ThisClassName, 255 );
SI_CUSTOM_MESSAGE = ::RegisterWindowMessage( ThisClassName );
hMutex = ::CreateMutex( NULL, TRUE, ThisClassName );
if( GetLastError() == ERROR_ALREADY_EXISTS )
{
Application->ShowMainForm = false;
hFirstInstance = hSecondInstance = NULL;
::EnumWindows( (WNDENUMPROC)EnumWindowsCallBack, (LPARAM)ThisClassName );
if( hFirstInstance == FParentForm->Handle ) hFirstInstance = hSecondInstance;
::SendMessage( hFirstInstance, SI_CUSTOM_MESSAGE, 0, 0 );
::SetForegroundWindow( hFirstInstance );
Application->Terminate();
}
else
{
Application->HookMainWindow( AppWndProcHook );
FHooked = true;
WndReturnHook = ::SetWindowsHookEx( WH_CALLWNDPROCRET, (int (__stdcall*)())CallWndRetCallBackProc, HInstance, ::GetCurrentThreadId() );
::PostMessage( Application->Handle, SI_CUSTOM_MESSAGE, 0, 0 );
}
}
}
//-------------------------------------------------------------
__fastcall TSingleInstance::~TSingleInstance()
{
if( FHooked ) Application->UnhookMainWindow( AppWndProcHook );
if( WndReturnHook ) ::UnhookWindowsHookEx( WndReturnHook );
if( hMutex ) ::CloseHandle( hMutex );
ThisSingleInstance = NULL;
}
//-------------------------------------------------------------
bool __fastcall TSingleInstance::AppWndProcHook( TMessage &Message )
{ /*
There is a problem with windows correctly recording the window state
when the form's exStyle does not include WS_EX_APPWINDOW. To be more
exact, without WS_EX_APPWINDOW, when the form is minimized, windows will
report the last state prior to minimizing so API's such as IsIconic will
fail to detect the correct window state. This is important because if
one blindly restores the first instance when it is already in a restored
state, it will exibit strange behavior when the user tries to minimize
the first instance.
The only way around this is to not manipulate the first instance's window
state directly and instead monitor the application's window state so that
the first instance can determine what it wants to do when it recieves a
custom restore message. */
if( Message.Msg == WM_SYSCOMMAND )
{
switch( Message.WParam )
{
case SC_RESTORE:
case SC_MAXIMIZE: FMinimized = false; break;
case SC_MINIMIZE: FMinimized = true;
}
}
else if( Message.Msg == SI_CUSTOM_MESSAGE )
{
// SI_CUSTOM_MESSAGE only reaches here once and is used to complete
// initialization. I could have used a seperate message but there was
// no need to register a seperate message.
if( FParentForm != Application->MainForm )
{
// The application is already running at this point. I had to wait
// to check if the object was owned by the main form and this is
// important because this hooks into the owner and it needs to be
// hooked into the main form.
MessageDlg("TSingleInstance : Object can only be placed on the Main Form.", mtError, TMsgDlgButtons() << mbOK, 0);
Application->Terminate();
}
else
{
if( Application->MainForm->WindowState == wsMinimized ) FMinimized = true;
else FMinimized = false;
}
}
return false;
}
//-------------------------------------------------------------
int __stdcall TSingleInstance::CallWndRetCallBackProc( int Code, WPARAM WParam, LPARAM LParam )
{
if( Code == HC_ACTION )
{
CWPRETSTRUCT* cwp = (CWPRETSTRUCT *) LParam;
if( cwp->message == ThisSingleInstance->SI_CUSTOM_MESSAGE )
{
if( ThisSingleInstance->FMinimized )
{
::SendMessage( Application->Handle, WM_SYSCOMMAND, SC_RESTORE, 0 );
}
}
}
return ::CallNextHookEx( ThisSingleInstance->WndReturnHook, Code, WParam, LParam );
}
//-------------------------------------------------------------
~ JD
Technical Details
Message-ID:
<422b5f7e$1@newsgroups.borland.com>Path:
arc1-ca.pugleaf.net!g2news1.google.com!news1.google.com!proxad.net!gatel-ffm!gatel-ffm!194.25.134.126.MISMATCH!newsfeed01.sul.t-online.de!t-online.de!newsgroups.borland.com!not-for-mailReferences:
<42299944@newsgroups.borland.com>Show Full Headers
Complete Article Headers
X-Google-Thread: 10cc67,e122cfc7c5c02b63 X-Google-Attributes: gid10cc67,public X-Google-Language: ENGLISH,ASCII-7-bit From: "JD" <nospam@nospam.com> Sender: "JD" <nospam@nospam.com> Reply-To: "JD" <nospam@nospam.com> Subject: Re: IsIconic returns false when Application is minimised Newsgroups: borland.public.cppbuilder.nativeapi X-User-Info: 172.150.226.235 References: <42299944@newsgroups.borland.com> NNTP-Posting-Host: 207.105.83.66 Message-ID: <422b5f7e$1@newsgroups.borland.com> Date: 6 Mar 2005 11:52:30 -0800 X-Trace: newsgroups.borland.com 1110138750 207.105.83.66 (6 Mar 2005 11:52:30 -0800) Lines: 189 Path: arc1-ca.pugleaf.net!g2news1.google.com!news1.google.com!proxad.net!gatel-ffm!gatel-ffm!194.25.134.126.MISMATCH!newsfeed01.sul.t-online.de!t-online.de!newsgroups.borland.com!not-for-mail