Nur eine Instanz

Zurück Zum Inhaltsverzeichnis Weiter

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.

Zurück Zum Inhaltsverzeichnis Weiter


Copyright © 2004

Senden Sie Emails mit Fragen oder Kommentaren zu dieser Website an: mailto:info@cpp-tutor.de
 Wolfgang Schröder, Lerchenweg 23, D-72805 Lichtenstein. Tel: +49 7129 6470