Nur eine Instanz
In manchen Fällen kann es erforderlich sein, dass von einer Anwendung nur ein
Instanz zu einer Zeit läuft. Wird versucht die Anwendung ein zweites Mal zu starten, so
wird statt dessen die erste Instanz wieder aktiviert. Um dies zu erreichen muss irgendwie
überprüft werden, ob schon eine Instanz der Anwendung bereits läuft. Zu WINDOWS 3.xx
Zeiten war dies relativ leicht möglich, erhielt doch die Funktion WinMain(...)
im Parameter hPrevInstance das Instanzen-Handle der vorhergehende Instanz. War
dieses Handle 0, so wurde noch keine Instanz der Anwendung vorher gestartet. Ab WINDOWS95
ist der Parameter hPrevInstance aber immer 0. Also muss hier ein anderer Weg gefunden
werden. Nun stellt WINDOWS die API-Funktion FindWindow(...)
zur Verfügung, mit deren Hilfe nach einem bestimmten Fenster im gesamten System gesucht
werden kann. Als Parameter müssen Sie dieser Funktion entweder den Fenstertitel und/oder
den Fensterklassen-Name des gesuchten Fensters übergeben. Existiert das gesuchte Fenster
nicht, so liefert die Funktion den Wert 0 zurück und ansonsten das Fenster-Handle des
gefunden Fensters.
Soweit, so gut. Wie Sie bestimmt in der Zwischenzeit bemerkt haben, haben die über den
Anwendungs-Assistenten erstellten Anwendungen immer zwei Fenster: das Rahmenfenster und
das Fenster für den Ausgabebereich der Anwendung. Doch nach welchem Fenster sollten Sie
suchen? Suchen Sie immer nach dem Rahmenfenster! Den nur wenn Sie das Rahmenfenster gefunden
haben können Sie die erste Anwendung aktivieren.
Da FindWindow(...) die Angabe eines Fenstertitels und/oder eines
Fensterklassen-Namens zulässt taucht gleich noch eine weitere Frage auf: soll nach dem
Fenstertitel oder nach dem Fensterklassen-Namen gesucht werden? Ich rate Ihnen: suchen Sie
immer nach dem Fensterklassen-Namen. Die meisten MFC-Programme zeigen nämlich in der
Titelzeile zusätzlich zum Programmnamen noch den Namen der gerade geöffneten Datei an.
Und damit müssten Sie wissen, welche Datei eine eventuell schon gestartet Anwendung
gerade geöffnet hat.
Sehen wir uns jetzt an, wie Sie für das Rahmenfenster eine eigene WINDOWS
Fensterklasse registrieren können. Zunächst benötigen Sie einmal eine static
Methode in der Klasse des Rahmenfenster um den Namen der WINDOWS Fensterklasse zu
spezifizieren.
const char* CMainFrame::GetClassName()
{
return "ONLYONECLASS";
} |
Warum diese eine static Methode sein muss werden wir gleich noch sehen. Anschließend
müssen Sie in der PreCreateWindow(...) Methode des Rahmenfensters die WINDOWS
Fensterklasse erstellen und diese Ihrem Rahmenfenster zuweisen.
BOOL
CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if( !CFrameWnd::PreCreateWindow(cs) )
return FALSE;
// ZU ERLEDIGEN: Ändern Sie hier die Fensterklasse oder das
Erscheinungsbild,
// indem Sie CREATESTRUCT cs modifizieren.
// Extra WINDOWS Fensterklasse erstellen
WNDCLASS strWndClass;
memset(&strWndClass,0,sizeof(strWndClass));
strWndClass.hIcon = AfxGetApp()->LoadStandardIcon(IDI_HAND);
strWndClass.style = CS_DBLCLKS|CS_HREDRAW|CS_VREDRAW;
// Fensterklassen-Name ueber static
Methode auslesen da
// auch das Anwendungsobjekt den Namen in seiner Methode
// InitInstance() benoetigt
strWndClass.lpszClassName =
GetClassName();
// Adresse der MFC-Fensterprozedur holen
strWndClass.lpfnWndProc = AfxWndProc;
// WINDOWS Fensterklasse jetzt
registrieren
AfxRegisterClass(&strWndClass);
// Nicht vergessen Fensterklassen-Name
ueberschreiben!
cs.lpszClass = GetClassName();
cs.dwExStyle &= ~WS_EX_CLIENTEDGE;
return TRUE;
} |
Beachten Sie bitte, dass die neu erstellte WINDOWS Fensterklasse als Fensterprozedur
die MFC-Fensterprozedur verwenden muss!
So, damit haben wir für das Rahmenfenster eine eigene Fensterklasse geschaffen. Bleibt
nun nur noch die Abfrage übrig: wo in der Anwendung sollte die Abfrage erfolgen ob dies
die erste Instanz der Anwendung ist oder nicht? Da bei bereits laufender Instanz der
Anwendung auch kein weiteres Hauptfenster angezeigt werden soll muss die Abfrage noch vor
der Erstellung des Hauptfensters erfolgen, und dies ist innerhalb der CWinApp
Methode InitInstance(...). InitInstance(...) hat zudem noch dem Vorteil,
dass durch Rückgabe des Returnwertes 'FALSE' die Anwendung sofort nach verlassen der
Methode beendet werden kann. Damit könnten wir in einem ersten Schritt die Abfrage auf
eine weitere Instanz wie folgt in die InitInstance(...) Methode einfügen:
BOOL COnlyOneApp::InitInstance()
{
// Standardinitialisierung
// Wenn Sie diese Funktionen nicht nutzen und die Größe Ihrer
fertigen
// ausführbaren Datei reduzieren wollen, sollten Sie die nachfolgenden
// spezifischen Initialisierungsroutinen, die Sie nicht benötigen,
entfernen.
// Fensterklassen-Name vom Fenster holen
und dann
// nach dem entsprechenden Fenster suchen
HWND hFirstWnd =
::FindWindow(CMainFrame::GetClassName(),NULL);
// Falls Fenster mit der Fensterklasse
existiert laueft schon
// die Anwendung schon einmal!
if (hFirstWnd != 0)
{
::MessageBox(NULL,"Nur eine Instanz
erlaubt!\nAktiviere erste Instanz.",
"HINWEIS",MB_OK|MB_ICONEXCLAMATION);
// Anwendung zum
Schluss noch beenden!
return FALSE;
}
....
} |
Die Funktion FindWindow(...) holt sich über die static Methode des
Rahmenfensters dessen WINDOWS Fensterklassen-Name und sucht nach einem solchen Fenster.
Wird ein Fenster dieser WINDOWS Fensterklasse gefunden, wird eine Meldung ausgegeben und
durch Rückgabe des Wertes FALSE die Anwendung dann beendet.
Doch damit sind wir noch nicht ganz fertig. Wir wollen zusätzlich noch das Fenster der
ersten Instanz als Vollbild aktivieren. Dazu muss zunächst die API-Funktion ShowWindow(...)
mit dem Anzeigeflag SW_SHOWMAXIMIZED aufgerufen werden. Wohl gemerkt, es handelt sich hier
um die API-Funktion und nicht um die gleichnamige CWnd-Methode! Wenn Sie den
Aufruf von ShowWindow(...) in das obige Listing alleine einfügen, wird zwar das
Fenster der ersten Instanz als Vollbild dargestellt werden, jedoch nicht als aktives
Fenster. Um das Fenster auch zum aktiven Fenster zu machen muss zusätzlich noch die
API-Funktion SetForegroundWindow(...) aufgerufen werden. Und damit ergibt sich
folgender endgültiger IF-Zweig:
BOOL COnlyOneApp::InitInstance()
{
....
if (hFirstWnd != 0)
{
::MessageBox(NULL,"Nur eine Instanz
erlaubt!\nAktiviere erste Instanz.",
"HINWEIS",MB_OK|MB_ICONEXCLAMATION);
// Fenster
der ersten Anwendung als Vollbild anzeigen
::ShowWindow
(hFirstWnd,SW_SHOWMAXIMIZED);
// und in den
Vordergrund bringen
::SetForegroundWindow(hFirstWnd);
// Anwendung
zum Schluss noch beenden!
return FALSE;
}
....
} |
Das fertige Bespiel finden Sie unter 03EPMMFC\OnlyOne.
|