Applikationsklasse

Sehen wir uns nochmals die vom Assistenten generierte Applikationsklasse in Form des Klassenbaums an:

Applikationsklasse

Wie Sie sehen erstellt der Assistent drei Methoden: den Konstruktor CDVBasisApp(...), die bereits bekannte Methode InitInstance(...) und die Methode OnAppAbout(...). Die zuletzt genannte Methode werden wir zunächst nicht weiter betrachten. Sie ist, wie erwähnt, dafür verantwortlich den vom Assistenten automatisch generierten About-Dialog darzustellen.

Den Konstruktor des Anwendungsobjektes können wir ebenfalls getrost bei der Beschreibung überspringen, er ist standardmäßig leer. Die zentrale Methode ist die Methode InitInstance(...). Wie Sie bereits wissen wird innerhalb dieser Methode die Anwendung initialisiert.

/////////////////////////////////////////////////////////////////////////////
//
CDVBasisApp Initialisierung

BOOL CDVBasisApp::InitInstance()
{
    AfxEnableControlContainer();

    // 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.


#ifdef _AFXDLL
    Enable3dControls();          
// Bei MFC in gemeinsam genutzten DLLs aufrufen
#else
    Enable3dControlsStatic();   
// Bei statischen MFC-Anbindungen aufrufen
#endif

    // Ändern des Registrierungsschlüssels, unter dem unsere Einstellungen
    // gespeichert sind.
    // ZU ERLEDIGEN: Sie sollten dieser Zeichenfolge einen geeigneten Inhalt geben
    // wie z.B. den Namen Ihrer Firma oder Organisation.

    SetRegistryKey(_T("Local AppWizard-Generated Applications"));

    LoadStdProfileSettings(); // Standard INI-Dateioptionen laden (einschließlich MRU)

    // Dokumentvorlagen der Anwendung registrieren. Dokumentvorlagen
    // dienen als Verbindung zwischen Dokumenten, Rahmenfenstern und Ansichten.


    CSingleDocTemplate* pDocTemplate;
    pDocTemplate = new CSingleDocTemplate(
        IDR_MAINFRAME,
        RUNTIME_CLASS(CDVBasisDoc),
        RUNTIME_CLASS(CMainFrame), // Haupt-SDI-Rahmenfenster
        RUNTIME_CLASS(CDVBasisView));
    AddDocTemplate(pDocTemplate);

    // Befehlszeile parsen, um zu prüfen auf Standard-Umgebungsbefehle DDE, Datei offen
    CCommandLineInfo cmdInfo;
    ParseCommandLine(cmdInfo);

    // Verteilung der in der Befehlszeile angegebenen Befehle
    if (!ProcessShellCommand(cmdInfo))
        return FALSE;

    // Das einzige Fenster ist initialisiert und kann jetzt angezeigt und
    // aktualisiert werden.
    m_pMainWnd->ShowWindow(SW_SHOW);
    m_pMainWnd->UpdateWindow();

    return TRUE;
}

Gleich am Anfang der Methode steht ein Kommentar den Sie sich unbedingt durchlesen sollten. Der Assistent hat Ihnen eine InitInstance(...) Methode erzeugt die alle für ihn erdenklichen Initialisierungsroutinen berücksichtigen. Doch welche Initialisierungsroutinen sind unbedingt erforderlich und welche nicht? Sehen wir uns dazu den Ablauf der Methode an.

Die erste auszuführende Initialisierungsroutine Enable3dControls(...) bzw. Enable3dControlsStatic(...) wird vom Assistenten eingefügt wenn Sie beim Erstellen des Projektes angegeben haben dass Sie 3D-Controls verwenden wollen. Durch den Aufruf dieser Methoden wird die DLL CTL3D32.DLL geladen. Je nach dem, ob Sie die MFC als DLL oder statisch verwenden, wir die eine oder andere Methode ausgeführt.

So, als nächstes wird es etwas 'unangenehm'. Durch den Aufruf der Methode SetRegistryKey(...), die später bei der Behandlung der Registrierungsdatenbank noch ausführlich beschrieben wird, werden durch die Anwendung Einträge in der Registrierungsdatenbank vorgenommen. Der Assistent erzeugt standardmäßig den als Parameter anzugebenden Schlüssel Local AppWizard-Generated Applications. Der vollständige Pfad, unter dem die Werte schließlich in der Datenbank abgelegt werden, lautet HKEY_CURRENT_USER\Software\<key>\<application name>, wobei <key> der an SetRegistryKey(...) übergebene String ist und <application name> der Name der Anwendung. Vielleicht sagen Sie sich nun: ich speichere doch in meiner Anwendung gar nichts in der Registrierungsdatenbank ab. Und doch werden indirekt Einträge in der Datenbank vorgenommen. Eine vom Assistenten generierte Anwendung verwendet die Datenbank unter anderem zur Ablage der MRU Liste (MRU = most recently used = Liste mit den zuletzt geöffneten Dateien).

Der oben genannte Registrierungsdatenbank-Eintrag wird eventuell auch unter HKEY_USERS dupliziert.
Falls Sie das Beispiel nicht schon einmal gestartet haben, starten Sie es jetzt. Gehen Sie dann in das Datei-Menü und öffnen eine beliebige Datei. Sie werden natürlich im dargestellten Fenster noch nichts sehen da wir ja noch keine Ausgaben vornehmen können. In der Titelzeile des Fensters sollte jedoch der Name der geöffneten Datei erscheinen. Schließen Sie die Anwendung und starten Sie dann den Registrierungsdatenbank-Editor REGEDIT. Wenn alles richtig funktioniert hat müssten unter dem Pfad  HKEY_CURRENT_USER\Software\Local AppWizard-Generated Applications\DVBasis die nachfolgenden Einträge vorhanden sein:

Eintrag in Registrierungs-DB

Der Eintrag File1 im rechten Teil enthält den Namen der vorher geöffneten Datei, hier im Bild die Datei D:\TMP\CDoc.htm.

Beachten Sie bitte, dass jedes vom Assistenten erstellte Programm ohne Modifikation des Codes diese Einträge in der Registrierungsdatenbank vornimmt. Außer der Speicherung der zuletzt benutzten Dateien erfolgen teilweise noch weitere Einträge in der Datenbank. So wird z.B. auch die bei der Erstellung des Programms angegeben Dateierweiterung automatisch über die Datenbank mit der Anwendung verknüpft.
Löschen Sie mittels des Programms REGEDIT den oben angegebenen Eintrag DVBasis aus der Registrierungsdatenbank.

Anschließend kommentieren Sie den Aufruf der Methode SetRegistryKey(...) einmal aus, übersetzen das Programm wieder und starten es erneut. Öffnen Sie das Datei-Menü. Es sollten jetzt keine zuletzt geöffneten Dateien mehr angezeigt werden. Öffnen Sie als nächstes eine beliebige Datei und beenden dann das Programm sofort wieder. Wenn Sie jetzt die Registrierungsdatenbank erneut öffnen werden Sie den oben angegeben Eintrag nicht mehr vorfinden. Die Anwendung hat also jetzt keine Einträge mehr in der Datenbank vorgenommen. Starten Sie jetzt erneut das Programm und öffnen dann das Datei-Menü.

Hoppla?! Im Datei-Menü ist wieder die zuletzt geöffnete Datei vorhanden. Doch woher kommt diese Information jetzt? Nun, anstelle eine Eintrags in der Datenbank hat die Anwendung jetzt im WINDOWS-Verzeichnis die INI-Datei DVBASIS.INI erstellt und dort die Informationen abgelegt. Was letztendlich besser ist, ein Eintrag in der Datenbank oder eine INI-Datei im WINDOWS-Verzeichnis , das bleibt jedem selber überlassen. Später, bei der Behandlung der Registrierungsdatenbank und der INI-Dateien, werden wir auf dieses Thema nochmals zurückkommen. Dort erfahren Sie auch, wie Sie die INI-Datei im gleichen Verzeichnis wie die Anwendung anlegen können.

Löschen Sie nun aus dem WINDOWS-Verzeichnis die Datei DVBASIS.INI und fügen den Aufruf von SetRegistryKey(...) wieder zur Anwendung hinzu.

Fahren wir mit der Analyse der InitInstance(...) Methode fort. Nach dem Aufruf der Methode SetRegistryKey(...) wird die Methode LoadStdProfileSettings(...) aufgerufen. Diese Methode ist, wie auch im Kommentar beschrieben, für die Ausgabe der MRU-Liste zuständig. Wenn Sie keine MRU-Liste verwenden, und auch keine sonstigen Einstellungen abspeichern wollen, können Sie die beiden Aufrufe von SetRegistryKey(...) und LoadStdProfileSettings(...) aus dem Programm entfernen.

Als nächstes folgt die Definition der Dokumentenverwaltung. Die Dokumentverwaltung wird später, nach der Behandlung der Dokumenten- und Ansichtsklasse, erläutert. Diese Initialisierung dürfen Sie auf keinen Fall entfernen!

Die Aufrufe der beiden nachfolgenden Methoden EnableShellOpen(...) und RegisterShellFileTypes(...) sind für die DDE (dynamic data exchange) Initialisierung zuständig. Wenn Sie weder DDE noch OLE (object linking and embedding) Funktionalität in der Anwendung benötigen können Sie diese Aufrufe getrost entfernen. Die Aufruf der Methode RegisterShellFileTypes(...) erstellt zudem ebenfalls Einträge in der Registrierungsdatenbank, durch die die bei der Projekterstellung angegeben Dateierweiterung mit der Anwendung verknüpft wird.

Die nachfolgenden Anweisungen, die laut Kommentar im obigen Listing nur mit Auswertung der Kommandozeile zu tun haben, dürfen Sie aber auf keinen Fall entfernen. Hinter den dort aufgerufenen Methoden ParseCommandLine(...) und ProcessShellCommand(...) steckt weit mehr als nur die Verarbeitung der Kommandozeile!

Kommentieren Sie im Beispiel einmal die Zeilen für die (angebliche) Kommandozeilen-Verarbeitung wie folgt aus.
    // Befehlszeile parsen, um zu prüfen auf Standard-Umgebungsbefehle DDE, Datei offen
//    CCommandLineInfo cmdInfo;
//    ParseCommandLine(cmdInfo);

    // Verteilung der in der Befehlszeile angegebenen Befehle
//    if (!ProcessShellCommand(cmdInfo))
//        return FALSE;

    // Das einzige Fenster ist initialisiert und kann jetzt angezeigt und....

Übersetzen und starten Sie das Programm. Kurz nach dem Programmstart werden Sie etwa folgende Meldung erhalten:

Eine Exception!

Wenn Sie dann den OK-Button anklicken unterbricht der Debugger die Anwendung und zeigt Ihnen aus der Datei winocc.cpp die nachfolgende Methode an:

Die Exception-Ursache

Der Debugger sollte dabei auf der ASSERT-Zeile stehen. Nun, was ist hier passiert? Sehen Sie sich einmal den Namen der Methode an, in der der Fehler aufgetreten ist! Bemühen wir den Debugger weiter. Öffnen Sie das Aufrufliste-Fenster (oder anders ausgedrückt, den Stack-Trace) entweder über das Symbol Stack-Trace, das Ansicht-Menü oder durch Drücken der Taste <ALT>-<F7>.

Der Aufruf-Stack

Ganz oben steht die Methode (einschließlich der Parameter) in der der Fehler aufgetreten ist, hier also die Methode ShowWindow(....). Dieser Methode wurde durch die darunter stehende Methode InitInstance(...) des Beispiels aufgerufen. Sie können jetzt im Aufrufliste-Fenster einen Doppelklick auf die InitInstance(...) Methode ausführen und gelangen dann zur Aufrufstelle von ShowWindow(...) in dieser Methode. Stellen Sie nun den Cursor über die Variable m_pMainWnd und sehen sich deren Wert im Tooltipp an: der Zeiger auf das Hauptfenster m_pMainWnd enthält den Wert 0, d.h. die Anwendung besitzt kein Hauptfenster!

MERKE: Bei einer durch den Assistenten generierten Anwendung wird das Hauptfenster indirekt durch die Methode ProcessShellCommand(...) erstellt!

Entfernen Sie jetzt die vorher einfügten Kommentare und übersetzen und starten das Programm wieder. Es sollte danach wieder korrekt laufen.

Die beiden letzten Aufruf von ShowWindow(...) und UpdateWindow(...) in InitInstance(...) benötigen in der Zwischenzeit wohl keine weiteren Erklärungen. Sie zeigen das Rahmenfenster an und veranlassen eine Aktualisierung des Fensterinhalts.

Hiermit beenden wir die Behandlung der Applikationsklasse und wenden uns als nächstes der Dokumentenklasse zu. Die Beschreibung der Fensterklasse für das Rahmenfenster sparen wir uns. Diese Klasse enthält wirklich nichts neues.



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