Programmieren unter MS-Windows

Kapitel 12: Die Microsoft Foundation Class

Letzte Änderung: 2.11.97 von B. Tritsch

Überblick

Zurück zum Index "PC- und MS-Windows-Support"

Zurück zum Inhalt


Die MFC ist eine Klassenbibliothek, die das gesamte Windows-API kapselt. Sie besitzt daher Klassen für folgende Windows-Bestandteile:

Die "Mutter" (fast) aller Klassen in der MFC ist CObject.Sie verzweigt grob gesehen in die folgende Klassenhierarchie:

Applikations-Architektur (CWinApp)
  • Applikationen und Threads
  • Document Templates
  • Document-Klassen
  • Document Items
  • OLE-Klassen
Fensterunterstützung (CWnd)
  • Frame Window
  • View Window
  • Dialoge
  • Controls
Graphikunterstützung
  • GDI-Objekte
  • Device Contexts
Systemunterstützung
  • Exceptions
  • File-Objekte
  • Synchronisation
  • Windows Sockets
  • ODBC, DAO

Klassen, die nicht von CObject abgeleitet werden:

Die Klassenhierarchie läßt sich jedoch zu einer alternativen Betrachtungsweise in acht Gruppen unterteilen:

  1. Die Anwendungsarchitektur-Klassen bilden die Grundlage für die Botschaftsverarbeitung (Message-Mapping), für die MFC-Dokumentenklassen zur Dateiverwaltung sowie für die Klasse CWinApp, die für den Haupt-Thread der Anwendung verantwortlich ist.
  2. Zu den allgemein nützlichen Klassen gehört die Klasse CObject mit ihren abgeleiteten CFile- und CException-Klassen (Dateizugriff, Fehlerbehandlung). Die allgemein nützlichen Klassen umfassen weiterhin so vielfältige Klassen wie CString, CTime, CRect, CPoint, CRunTime, CMemoryState und CFileStatus.
  3. Die Visual Object-Klassen betreffen praktisch alle Anwendungselemente, die auf dem Bildschirm erscheinen. Darunter fallen Zeichenwerkzeuge, Fenster, menüs, Dialogsteuerelemente und die RecordView-Klasse.
  4. Die Collection-Klassen verwalten Daten in Form vom von Vektoren, Listen und Hashing-Tabellen.
  5. Die OLE2-Klassen bieten die Möglichkeit zur Objektkommunikation, so daß voneinander unabhängige Anwendungen Operationen und Daten austauschen können.
  6. Die Datenbank-Klassen bieten funktionalen Zugriff auf und die Unterstützung für eine Vielzahl von Datenbankquellen.
  7. Die Klassen für die Standard-Steuerelemente beinhalten eine Reihe von visuellen Steuerelementen.
  8. Die Socket-Klassen ermöglichen es den MFC-Klassen, Windows-Sockets für den Netzzugang zu verwenden.

Im Detail läßt sich Klassenhierarchie folgendermaßen anzeigen:

Klassen für die Anwendungsarchitektur

CObject

Allgemein nützliche Klassen

CObject

Visual Object-Klassen

CObject

CObject - CCmdTarget

Die Collection-Klassen

CObject

Die OLE2-Klassen

CObject

Datenbank-Klassen

CObject

Standard-Steuerelemente

CObject

Die Socket-Klassen

CObject

 

Die zentralen Klassen

Ein MFC-Programm besteht mindestens aus einer aus der Anwendungsklasse CWinApp abgeleiteten Klasse. Sie besorgt die Programmabarbeitung, jedoch nicht die Fenstererstellung. Im Programm-Code wird die CWinApp-Methode InitInstance() mit den zur Fenstererzeugung und -anzeige nötigen Befehlen überladen. Es gibt daher auch keine WinMain()-Funktion mehr!

Die Klasse CWnd ist die Basisklasse aller MFC-Fenster. Von ihr leiten sich alle weiteren wichtigen Fensterklassen ab:

In der Klasse CWnd setzt der Mechanismus zur Abarbeitung der Nachrichten in einer Fensterfunktion an. Er wird mittels sogenannter Message Maps und zugehöriger Makros realisiert.

Die Member Functions der CWnd-Klasse unterteilen sich in die folgenden Kathegorien:

Die Klasse CFrameWnd (abgeleitet von CWnd) besitzt alle Eigenschaften eines normalen Popup-Windows. Es kann somit als Hauptfenster einer Anwendung dienen. Es sind darin allerdings keine weiteren Funktionalitäten realisiert!

Weitere von CWnd abgeleitete Klassen machen es auf einfache Art möglich weitere graphische Gestaltungselemente in ein Fenster einzuführen:

Die Klasse zur Erstellung und Verwaltung von Menüs ist CMenu. Der Zugriff auf die Systemmenüs erfolgt auch durch eine Methode dieser Klasse und wird nicht über eine eigens eingeführte Klasse realisiert.

Zur Erzeugung und Verwaltung von Dialogen wird die Klasse CDialog genutzt. Natürlich wird auch bei diesen Dialogen zwischen modal und nicht-modal unterschieden.

Von CDialog ausgehend sind weitere Sonderklassen definiert, die den Dialogen entsprechen, die in der Datei COMMDLG.DLL für alle Windows-Programmierer vorgegeben sind:

Die Ausgabe von Text und Zeichnungen erfolgt über Gerätekontexte. In der MFC stammen diese von der Klasse CDC ab. Abgeleitete Kontexte hiervon sind:

Zur Ausgabe von Graphiken wurden die Klassen der wichtigsten Werkzeuge definiert:

Die MFC stellt auch viele Funktionen und Klassen zur Verfügung, die die Handhabung von Documents und Views vereinfachen sollen. Damit können Dokumente durch mehrere von ihnen abhängige Ansichten dargestellt werden. Die beteiligten Klassen sind:

Ein MFC-Beispiel

Das folgende MFC-Programm stellt ein Grundgerüst dar. Klickt man mit der linken Maustaste auf die Client Area, wird eine Message Box ausgegeben.

       #include <afxwin.h>
/* 2*/
/* 3*/ class CAnwendung : public CWinApp
/* 4*/  {
/* 5*/   public:
/* 6*/   BOOL InitInstance();
/* 7*/  };
/* 8*/
/*11*/ class CHauptfenster : public CFrameWnd
/*12*/  {
/*13*/   public:
/*14*/     CHauptfenster();
/*15*/     afx_msg void OnLButtonDown(UINT, CPoint);
/*16*/     DECLARE_MESSAGE_MAP();
/*17*/  };
/*18*/
/*19*/ BEGIN_MESSAGE_MAP(CHauptfenster, CFrameWnd)
/*20*/   ON_WM_LBUTTONDOWN()
/*21*/ END_MESSAGE_MAP()
/*22*/
/*23*/ void CHauptfenster::OnLButtonDown(UINT, CPoint)
/*24*/  {
/*25*/   MessageBox("Da bin ich", "Achtung", MB_OK);
/*26*/  }
/*27*/
/*28*/ static CAnwendung Anwendung;	
/*29*/
/*30*/ BOOL CAnwendung::InitInstance()
/*31*/  {
/*32*/   m_pMainWnd = new CHauptfenster;
/*33*/   m_pMainWnd->ShowWindow(m_nCmdShow);
/*34*/   return TRUE;
/*35*/  }               
/*36*/
/*37*/ CHauptfenster::CHauptfenster()
/*38*/  {
/*39*/   Create(NULL, "Erste MFC-Anwendung");
/*40*/  }

Zeile 1:
In der Header-Datei AFXWIN.H sind alle Deklarationen und Konstanten enthalten der MFC enthalten oder über andere Includes referenziert (inkl. WINDOWS.H).

Zeile 3 - 17:
Die Applikations-Anwendungsklasse und -Fensterklasse werden von den entsprechenden MFC-Klassen abgeleitet.

Zeile 19 - 21:
Deklaration der Message Map. Die Macros dienen der Nachrichtenverteilung aus der Nachrichtenschleife der Apllikation. Die zugehörigen Member-Funktionen dirigieren die Nachrichten an ihr Zielfenster, wie es auch Nicht-MFC-Applikationen tun würden. Daher ist der primäre Empfänger immer ein Fenster. Der Message Handler in einem Objekt, das in der Lage ist Nachrichten zu empfangen, wertet Nachrichten aus oder leitet sie weiter. Dies geschieht in einer bestimmten Reihenfolge:

  1. Weiterleitung zu einem aktiven Kindobjekt
  2. Auswertung durch das Objekt selbst
  3. Weiterleitung an ein anderes Objekt, das Nachrichten auswerten kann

Zeile 23 - 26:
Programm-Code zur Reaktion auf die Mauseingabe

Zeile 28:
Erzeugt das Anwendung-Objekt und resultiert im Aufruf der InitInstance()-Funktion

Zeile 30 - 35:
Erzeugt ein neues CMainWindow-Objekt. Dies resultiert im Aufruf des CMainWindow-Konstruktors

Zeile 37 - 40:
Überladener Konstruktor, ruft die Create()-Funktion auf. Sie erzeugt das Programmfenster.

Graphische Ausgabe und Interaktion

Soll das Programm graphische Elemente ausgeben und auf Benutzerinteraktion reagieren, müssen sowohl die Message Map als auch die entsprechenden Funktionsaufrufe erweitert werden:

/*19 */ BEGIN_MESSAGE_MAP(CHauptfenster, CFrameWnd)
/*20 */   ON_WM_LBUTTONDOWN()
/*20A*/   ON_WM_PAINT()
/*20B*/   ON_COMMAND(IDM_SHOW, OnShowText)
/*21 */ END_MESSAGE_MAP()

Zeile 20A - 20B:
Die erste Zeile sorgt für das "Repaint", die zweite für die initiale Darstellung einer Graphik als Reaktion auf eine Benutzereingabe (z.B. über einen Menüpunkt). Die ausführenden Funktionen sind von zwei entsprechenden Klassen abgeleitet (CPaintDC, CClientDC).

void CHauptfenster::OnPaint()
 {
  CString string = "Repaint";
  CPaintDC dc(this);
  dc.TextOut(30, 30, string, string.GetLength));
 }


void CHauptfenster::OnShowText()
 {
  CString string = "Initial";
  CClientDC dc(this);
  dc.TextOut(30, 30, string, string.GetLength));
 } 

Multiple Document Interface - MDI

Durch das Multiple Document Interface (MDI) wird es dem Anwender im Gegensatz zum Standard Sigle Document Interface (SDI) ermöglicht, in mehreren Kindfenstern unterschiedliche Objekte eines Typs oder aber ein Objekt in mehreren Darstellungsformen zu bearbeiten. Beispiele hierfür sind die "guten alten" Windows Datei- und Programm-Manager.

Eine MDI-Anwendung setzt sich aus drei Komponenten zusammen:

MDI-Applikationen sind stärker objektorientiert, was die Programmiertechniken etwas verändert. Aus diesem Grund ist ihre Unterstützung in der MFC besonders ausgeprägt.

MDI ist für professionelle Programmentwicklung äußerst wichtig, obwohl die Erstellung einer MDI-Applikation eine sehr komplexe Aufgabe ist (Beispiel: Word für Windows, etc.)

Eine MFC-Rahmenapplikation innerhalb des Developer Studios

Die bisher vorgestellten MFC-Programme wurden "von Hand" editiert. Mit Hilfe des Microsoft Developer Studios, das integraler Bestandteil der Visual C++-Entwicklungsumgebung ist, sieht ein typisches Rahmenprogramm etwas aufwendiger aus. Dies liegt daran, daß das Developer Studio interaktiv Programmcharakteristika erfragt und mit diesen Informationen als Code-Generator fungiert. Dies nimmt dem Entwickler sehr viel Routinearbeit ab.

Erzeugt man innerhalb des Developer Studios mit dem AppWizzard ein neues SDI-Projekt mit dem Namen YAH, so werden eine ganze Reihe von Dateien erstellt:

YAH.h Dies ist das Haupt-Header-File der Applikation. Es inkludiert andere projektspezifische Header-Dateien und deklariert die CYAHApp-Klasse
YAH.cpp Dies ist die zentrale Quelldatei, die die Applikationsklasse CYAHApp beinhaltet
YAH.rc Die primäre Ressourcendatei, die das Programm verwendet. Sie beinhaltet Verweise auf die inkludierten Icons, Bitmaps und Cursors, die im RES-Unterverzeichnis abgelegt sind. Diese Datei wird direkt vom Developer Studio editiert
MainFrm.h
MainFrm.cpp
Diese Dateien beinhalten die Frame-Klasse CMainFrame, die von CFrameWnd abgeleitet ist und die SDI-Rahmen-Features kontrolliert
YAHDco.h
YAHDoc.cpp
Diese Dateien beinhalten die CYAHDoc-Dokumenten-Klasse. Sie müssen editiert werden, um spezielle Dokumentendaten hinzuzufügen und um das Datei laden bzw. Datei speichern zu implementieren
YAHView.h
YAHView.cpp
Diese Dateien beinhalten die CYAHView-View-Klasse. CYAHView-Objekte werden verwendet, um CYAHDoc-Objekte anzuzeigen
StdAfx.h
StdAfx.cpp
Diese Dateien werden verwendet, um eine vorkompilierten Header (PCH-File) mit dem Namen YAH.pch und eine vorkompilierte Typendatei mit dem Namen StdAfx.obj zu erzeugen
Resource.h Dies ist die Standard-Headerdatei, in der neue Ressource IDs definiert werden. Das Developer Studio liest und beschreibt diese Datei

Sieht man sich die Deklaration der CYAHApp-Klasse (abgeleitet von CWinApp) in YAH.h an, so findet man drei Member-Funktionen: Einen Constructor, eine überschriebene virtuelle Funktion InitInstance und eine Member-Funktion CAppAbout.

// YAH.h : main header file for the YAH application
//

#ifndef __AFXWIN_H__
	#error include 'stdafx.h' before including this file for PCH
#endif

#include "resource.h"       // main symbols

/////////////////////////////////////////////////////////////////////////////
// CYAHApp:
// See YAH.cpp for the implementation of this class
//

class CYAHApp : public CWinApp
{
public:
	CYAHApp();

// Overrides
	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CYAHApp)
	public:
	virtual BOOL InitInstance();
	//}}AFX_VIRTUAL

// Implementation

	//{{AFX_MSG(CYAHApp)
	afx_msg void OnAppAbout();
		// NOTE - the ClassWizard will add and remove member functions here.
		//    DO NOT EDIT what you see in these blocks of generated code !
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()
};

In der Datei YAH.cpp folgt dann die Implemetierung der CYAHApp-Klasse und der CAboutDlg-Klasse. Zweitere ist für die Realisierung einer About-Dialogbox zuständig und soll uns hier nicht weiter interessieren. Viel zentraler ist die Member-Funktion InitInstance der CYAHApp-Klasse.

#include "stdafx.h"
#include "YAH.h"

#include "MainFrm.h"
#include "YAHDoc.h"
#include "YAHView.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CYAHApp

BEGIN_MESSAGE_MAP(CYAHApp, CWinApp)
	//{{AFX_MSG_MAP(CYAHApp)
	ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
		// NOTE - the ClassWizard will add and remove mapping macros here.
		//    DO NOT EDIT what you see in these blocks of generated code!
	//}}AFX_MSG_MAP
	// Standard file based document commands
	ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
	ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
	// Standard print setup command
	ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CYAHApp construction

CYAHApp::CYAHApp()
{
	// TODO: add construction code here,
	// Place all significant initialization in InitInstance
}

/////////////////////////////////////////////////////////////////////////////
// The one and only CYAHApp object

CYAHApp theApp;

/////////////////////////////////////////////////////////////////////////////
// CYAHApp initialization

BOOL CYAHApp::InitInstance()
{
	// Standard initialization
	// If you are not using these features and wish to reduce the size
	//  of your final executable, you should remove from the following
	//  the specific initialization routines you do not need.

#ifdef _AFXDLL
	Enable3dControls();        // Call this when using MFC in a shared DLL
#else
	Enable3dControlsStatic();  // Call this when linking to MFC statically
#endif

	LoadStdProfileSettings();  // Load standard INI file options (including MRU)

	// Register the application's document templates.  Document templates
	//  serve as the connection between documents, frame windows and views.

	CSingleDocTemplate* pDocTemplate;
	pDocTemplate = new CSingleDocTemplate(
		IDR_MAINFRAME,
		RUNTIME_CLASS(CYAHDoc),
		RUNTIME_CLASS(CMainFrame),       // main SDI frame window
		RUNTIME_CLASS(CYAHView));
	AddDocTemplate(pDocTemplate);

	// Enable DDE Execute open
	EnableShellOpen();
	RegisterShellFileTypes(TRUE);

	// Parse command line for standard shell commands, DDE, file open
	CCommandLineInfo cmdInfo;
	ParseCommandLine(cmdInfo);

	// Dispatch commands specified on the command line
	if (!ProcessShellCommand(cmdInfo))
		return FALSE;

	// Enable drag/drop open
	m_pMainWnd->DragAcceptFiles();

	return TRUE;
}

In YAH.cpp wird ein globales Objekt des Typs CYAHApp mit dem Namen theApp deklariert. Mit ihm beginnt das Leben des Programms, da es zum Anfang der Laufzeit konstruiert wird. Dies impliziert, daß sein Konstruktor aufgerufen wird.

In der Member-Funktion InitInstance des CYAHApp-Objekts finden eine Reihe von Initialisierungen statt, z.B. das 3D-Aussehen der Applikation. Der wahrscheinlich wichtigste Initialisierungsschritt ist jedoch die Erzeugung eines Document Templates. Daher wird ein Objekt des Typs CSingleDocTemplate kreiert und zu den Document Templates der Applikation mit Hilfe der Funktion AddDocTemplate hinzugefügt. Die Information, die im Document Template gespeichert ist, wird bei der Verwendung des "New"-Kommandos aus dem Anwendungsmenü benötigt. Die Standard-Implementation dieses Kommandos ist die Funktion CWinApp::OnFileNew. Die Funktion nutzt die Template-Information um zu entscheiden was für ein Objekt sie erzeugen muß um ein neues Dokumentenobjekt und eine entsprechende Sichtweise (View) zu repräsentieren.

Soll mehr als ein Dokumententyp verwendet werden, muß die Anwendung als MDI-Applikation konzipiert werden!

The Frame, the Document, and the View

In einer einfachen Nicht-MFC-Applikation würde man typischerweise ein Fenster erzeugen und seine Client Area zur Darstellung aller Ausgaben verwenden. MFC-Applikationen auf der anderen Seite verwenden mindestens zwei Fenster: Das Frame Windows und das View Window.

Das Frame Window verwaltet die Applikationsmenüs, Werkzeugleisten und all die anderen Komponenten der Benutzerschnittstelle. Das View Window ist vorgesehen, um Daten aus dem Applikationsdokument zu präsentieren. Hierbei ist das Dokumentenobjekt nicht sichtbar, es repräsentiert die Applikationsdaten oder korrespondiert mit dem Inhalt einer Datei. Das Dokumentenobjekt interagiert mit dem View Windows für die Darstellung der Daten und für die Benutzerinteraktion.

Das Frame Window einer Applikation wird durch die Klasse CMainFrame unterstützt. Die Dokumentenklasse einer Applikation wird von CDocument abgeleitet. Das View Fenster benötigt eine entsprechende Klasse abgeleitet von der CView-Klasse.

Die Klasse CView liefert die grundlegende Funktionalität, um benutzerdefinerte View-Klassen zu erzeugen. Dabei handelt es sich um Views, die mit Dokumenten verbunden sind und als Übersetzer oder Mittler zwischen dem Dokument und dem Benutzer fungieren, indem sie ein Abbild des Dokuments auf dem Bildschirm ausgeben und die Benutzereingaben als Operation für das Dokument interpretieren.

Auch wenn einem View nur ein Dokument beigeordnet sein kann, kann ein Dokument ohne weiteres mehrere Views haben, die jeweils andere Dokumentansichten zeigen. Ein View ist jedoch nicht dafür geschaffen die Daten zu speichern. Es liegt gänzlich im Verantwortungsbereich des Dokuments, den View mit den nötigen Informationen zu versorgen. Dies kann erreicht werden, indem es dem View ermöglicht wird, direkt oder über Member-Funktionen auf die Datenelemente zuzugreifen.

Verschiedene spezielle Klassen

CString

Die CString-Klasse dient dazu, die alten C-Zeichenarrays zu ersetzen und bietet mächtige und flexible String-Operationen. Die CString-Klasse stellt Zeichenstrings variabler Länge und Operatoren zum Verketten (Konkatenation) und Vergleich bereit. Sie bietet darüber hinaus auch vereinfachte Speicherverwaltungsfunktionen und -operationen, die denen von Basic ähnlich sind.

Die Klasse CString basiert auf dem Datentyp TCHAR. Ist das Symbol als _UNICODE definiert, wird TCHAR als Typ wchar_t (ein 16-Bit Zeichentyp) definiert, ansonsten als normaler 8-Bit-Wert.

CString-Objekte weisen folgende Eigenschaften auf:

Ein kleines Beispiel zum Erzeugen eines CString-Objekts, der Anbindung eines zweiten Strings und der Ausgabe über einen Device Context mit dem Namen dc wird im folgenden aufgezeigt. Beim Device Context wird davon ausgegangen, daß er vorher über die Klasse CPaintDC oder CClientDC erzeugt wurde.

#include <strstrea>  // Header-File für Strings/Strams
CString AString;     // Erzeugt ein CString-Objekt
AString = "Der vollstaendige String ";
AString += "ist so lang.";
dc.TextOut(10, 10, AString, AString.GetLength());

Die CString-Klasse hat eine Reihe von Member-Funktionen. Einige sollen hier aufgeführt werden:

Der Übergang zwischen einem CString-Objekt und einem normalen Zeiger auf einen Zeichenpuffer ist ebenfalls recht einfach möglich. Damit lassen sich auch die String-Funktionen aus dem ANSI- und dem Windows-API-Sprachumfang leicht parallel verwenden.

char * pCharBuffer = (const char *) AString;

CFile

Die MFC-Bibliothek stellt die CFile-Klasse für Dateizugriffe zur Verfügung. Die Funktionen sehen denen aus DOS recht ähnlich. Die am meisten verwendeten Member-Funktionen werden hier aufgeführt:

Weiterhin gibt es Funktionen zur Manipulation der Datei-Modi, z.B. zum Datei erzeugen und zum Setzen von Read-only, Read/Write bzw. Write-only Flags.

CTime

Die CTime-Klassenobejekte geben das absolute Datum und die Uhrzeit an. Hierbei kapseln sie den ANSI-Datentyp time_t und seine verwandten Laufzeitfunktionen, einschließlich der Fähigkeit, in die Gregorianische Zeitrechnung sowie 24-Stunden-Anzeigen umzurechnen. CTime-Werte basieren auf UCT (Universal Coordinate Time oder Greenwich Mean Time), wobei die lokale Zeitzone durch die TZ-Umgebungsvariable identifiziert wird.

Da die CTime-Klasse die strftime-Funktion benutzt, die nicht von den Windows-DLLs unterstützt wird, kann die CTime-Klasse nicht innerhalb einer DLL eingesetzt werden!

Zum nächsten Kapitel