erstellt von: Dr. Erhard Henkes (e.henkes@gmx.net) - C++ und MFC        Stand: 29.05.2002

Zurueck zum Inhaltsverzeichnis

zurück zum vorherigen Kapitel
 
 

Kapitel 3 - Dateien, Laufwerke und Systeminformationen

3.1 Dateien erzeugen, löschen, schreiben und lesen

In Kapitel 1 und 2 haben wir uns mit Dialogfenstern und den untergeordneten Elementen befaßt. Die angesprochenen Schnittstellen waren hierbei:

Für viele Anwendungen benötigen wir den Datentransfer mit dauerhaften Speichermedien (Band, DVD, CD, Festplatte, Diskette). Hierzu müssen wir folgendes beherrschen: Wir beginnen mit einem praktischen Beispiel zum prinzipiellen Umgang mit Dateien. Entwerfen Sie bitte eine dialogbasierende Anwendung analog Abb. 3.1:

IDD_DATEILESEN_DIALOG DIALOGEX 0, 0, 401, 269
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_APPWINDOW
CAPTION "Datei: Lesen und Schreiben"
FONT 8, "MS Sans Serif"
BEGIN
EDITTEXT IDC_EDIT1, 7, 25, 323, 68, ES_MULTILINE | ES_AUTOHSCROLL | ES_READONLY
PUSHBUTTON "Datei Lesen", IDC_BUTTON1, 7, 7, 57, 16
PUSHBUTTON "Datei Schreiben", IDC_BUTTON2, 7, 118, 58, 19
EDITTEXT IDC_EDIT2, 7, 140, 323, 68, ES_MULTILINE | ES_AUTOHSCROLL | ES_WANTRETURN
END

Abb. 3.1: oben: Dialog mit zwei Eingabefeldern und zwei Schaltflächen; unten: zugehörige Dialog-Ressource

Beide Eingabefelder ermöglichen mehrzeilige Eingaben (ES_MULTILINE). Das obere Eingabefeld ist zur Ausgabe schreibgeschützt (ES_READONLY), das untere erlaubt bei Eingaben die Verwendung von (einfachem) Return (ES_WANTRETURN). Strg + Return bewirkt im anderen Fall den Zeilenumbruch.

Bitte erzeugen Sie mit dem Klassen-Assistenten (Strg + W) folgende Member-Variablen:
 

Steuerlement-IDs: Variablen-Typ Element (Member-Variable)
IDC_EDIT1 CString m_strEdit1
IDC_EDIT2 CString m_strEdit2

Durch Doppelklick auf die Schaltflächen erzeugen Sie die beiden Member-Funktionen OnButton1() und OnButton2(). Geben Sie folgende Programmzeilen ein:

void CDateiLesenDlg::OnButton1() //Datei Lesen
{
TCHAR str[1000];
CFile datei( "C:\\demo.txt", CFile::modeRead );
datei.Read( str, sizeof( str ) );
datei.Close();
m_strEdit1 = str;
UpdateData( FALSE ); // Variablen ---> Felder
}

void CDateiLesenDlg::OnButton2() //Datei Schreiben
{
TCHAR str[1000];
UpdateData( TRUE ); // Felder ---> Variablen
_tcscpy( str, m_strEdit2 ); // kopiert m_strEdit2 in str
CFile datei( "C:\\demo.txt", CFile::modeCreate | CFile::modeWrite );
datei.Write( str, sizeof( str ) );
datei.Close();
}

Kern des Ganzen ist die MFC-Klasse CFile. Diese Klasse bietet entsprechende Member-Funktionen für den notwendige Transfer zwischen Programm und externen Dateien. Im obigen Beispiel lesen wir in OnButton1() einen Textstring aus einer Datei, den wir vorher in OnButton2() in die Datei "C:\demo.txt" geschrieben haben. Hier die wesentlichen Schritte:
 

Aufgabe Programmcode
Datei zum Schreiben öffnen: Cfile datei("C:\\demo.txt", CFile::modeCreate | CFile::modeWrite);
Daten aus str schreiben: datei.Write(str,sizeof(str));
Datei zum Lesen öffnen: CFile datei("C:\\demo.txt", CFile::modeRead);
Daten in str lesen: datei.Read(str,sizeof(str));
Datei schließen: datei.Close();

Allgemeine Syntax der verwendeten Member-Funktionen:

CFile( LPCTSTR lpszFileName, UINT nOpenFlags );
virtual UINT Read ( void* lpBuf , UINT nCount ); Rückgabewert: Zahl der gelesenen Bytes.

virtual void Write ( const void* lpBuf, UINT nCount );

virtual void Close ( );

Wenn Sie die Anwendung nun starten, könnte das etwa wie in Abb. 3.2 aussehen. Überzeugen Sie sich mit dem MS Explorer, daß die Datei auf dem Laufwerk C: angelegt wurde. Öffnen Sie die Datei zum Vergleich des Inhalts auch mit einem Texteditor (z.B. Notizblock).

Abb. 3.2: Dialog zum Schreiben und Lesen einer Datei

Der Parameter UINT nOpenFlags im Konstruktor regelt den Zugriffsmodus auf eine Datei: Sie können die verschiedenen Flags wie gewohnt mit dem bitweisen OR (|) kombinieren. Nachstehend nur die wichtigsten OpenFlags (weitere Flags siehe Hilfe unter "CFile::CFile").

Sie sehen, die grundsätzliche Handhabung von Dateien ist wahrlich keine Zauberei. Leicht unübersichtlich wurde das Programm durch die Notwendigkeit der Umwandlung der Typen CString und TCHAR (hier können Sie auch den klassischen 8-bit-Typ char verwenden).

TCHAR umfaßt sowohl wchar_t* (Unicode) als auch char* (ANSI). Während man die Zuweisung von TCHAR zu CString mit dem Zuweisungs-Operator erledigt, benötigt man für den umgekehrten Weg die String-Kopierfunktion _tcscpy( Zieladresse, Quelladresse ). Anstelle des Unicode-portablen _tcscpy kann man auch das klassische strcpy einsetzen. Man sollte sich jedoch immer auf die Zukunft ausrichten.

Die direkte Zuweisung versagt. Probieren Sie es aus. Schreiben Sie einfach:

str = m_strEdit2 und beobachten Sie die Fehlermeldung: "error C2440: '=' :

'class CString' kann nicht in 'char [1000]' konvertiert werden".

Das ist auch richtig, denn sonst könnten Sie einen mehr als 1000 Zeichen langen CString einfach über den "Rand" des char-Arrays [0 ... 1000] hinaus kopieren und damit Schaden anrichten. Es wacht jedoch auch niemand über den Programmierer, wenn er _tcscpy einsetzt. Hier ist man eigenverantwortlich. Ändern Sie die Funktion OnButton2() zum Experimentieren wie folgt ab:

void CDateiLesenDlg::OnButton2() //Datei Schreiben
{

TCHAR str[10];
UpdateData( TRUE ); // Felder ---> Variablen
_tcscpy( str, m_strEdit2 ); // kopiert m_strEdit2 in str
//str = m_strEdit2; // ist nicht zulässig!!!
CFile datei( "C:\\demo.txt", CFile::modeCreate | CFile::modeWrite );
datei.Write( str, sizeof( str ) );
datei.Close();
}

Mit TCHAR str[10] deklarieren Sie ein char-Array für 11 Zeichen (einschließlich dem einen String abschließenden Zeichen \0). Sie kopieren aber in jedem Fall den Inhalt der CString-Variable m_strEdit2 in dieses Array. Probieren Sie nach dem Starten der Anwendung folgendes aus, damit Sie die Vorzüge der MFC-Klasse CString schätzen lernen:

  1.  
  2. Geben Sie in das Eingabefeld 123456789 ein. Nach dem Schreiben und Lesen erhalten Sie die gleiche Zeichenfolge zurück.
  3. Geben Sie 1234567890 ein. Nach dem Schreiben und Lesen erhalten Sie evtl. diese Zeichenfolge: "1234567890ÌÌÌÌÌÌÌÌÌÌÌ (......) ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌäüd".
  4.  
  5. Geben Sie 12345678901 ein. Das Ergebnis ist identisch mit Versuch 2.
  6.  
  7. Geben Sie eine längere Zeichenkette ein, bis folgende Meldung das makabre Spiel beendet:

Abb. 3.3: Meldung, wenn man deutlich über das Ende eines char-Arrays Daten abspeichert.

Fazit: Das char-Array str[10] bietet nur Platz für 10 Zeichen. Das elfte Array-Element wird für das abschließende ‘\0’ benötigt. Sauber verwenden lassen sich in unserem konkreten Beispiel nur 9 Plätze. Wie auch immer: Beim Einlesen von Daten in ein Character-Array muß der Programmierer selbst prüfen, ob ausreichend Platz für die Daten zur Verfügung steht, mindestens sichtbare Stringlänge + 2. Diese Schikane bleibt Ihnen bei der MFC-Klasse CString erspart. Die Klasse CString stellt ausreichend Speicherplatz für alle Operationen sicher.

Nun ergänzen wir zur Übung in obigem Beispiel eine Routine zur automatischen Erzeugung von Zeichenfolgen im unteren Eingabefeld. Fügen Sie eine weitere Schaltfläche mit Beschriftung "Automatisch füllen" ein und erzeugen Sie mittels Doppelklick auf diese (bzw. mittels Klassen-Assistent) die Funktion OnButton3(). Geben Sie folgende Zeilen ein, die den ANSI-Code (ab 32 = ‘!’) ausgeben:

void CDateiLesenDlg::OnButton3() //Automatisch füllen
{

m_strEdit2 = "";
int c = 32;
CString zeichen;
for( c; c<256; c++ ) m_strEdit2 += zeichen = c;
UpdateData( FALSE );
}

Die erzeugte Zeichenkette ragt rechts (unsichtbar) aus dem Eingabefeld heraus. Sie können hier den horizontalen Scrolleffekt im Eingabefeld beobachten. Durch Eingabe einiger manueller Returns können Sie die Zeichenfolge sichtbar anordnen.

Die Zeile mit der for-Schleife wurde bewußt so verdichtet gestaltet, um Ihre C-Kenntnisse zu fördern:

c++ bedeutet (in diesem Fall) c=c+1 (gleichwertig: c+=1).

a+=b ist das gleiche wie a=a+b.

z=y=x bedeutet z=x.

Im nächsten Schritt werden wir uns damit befassen, wie man den im Programm bereits festgelegten Pfad- und Dateinamen "C:\demo.txt" flexibel handhaben kann. Für solche immer wiederkehrenden Aufgaben stellt uns Windows die folgenden Standarddialoge zur Verfügung:
 

Standardaufgabe MFC-Klasse
Dateiauswahl CFileDialog
Farbauswahl CColorDialog
Schriftauswahl CFontDialog
Suchen/Ersetzen CFindReplaceDialog
Einrichtung von Seiten zum Drucken  CPageSetupDialog
Drucken CPrintDialog

Solche Geschenke sollte man dankbar annehmen. Also binden wir CFileDialog sofort in unsere Schreibroutine ein. Ändern Sie den Code wie folgt ab:

void CDateiLesenDlg::OnButton2() //Datei Schreiben
{
TCHAR str[1000];
UpdateData( TRUE ); // Felder ---> Variablen
_tcscpy( str, m_strEdit2 ); // kopiert m_strEdit2 in str
//str = m_strEdit2; // ist nicht zulässig!!!
CFileDialog m_dlgFile( FALSE ); // TRUE = Datei öffnen,
// FALSE = Datei speichern
if( m_dlgFile.DoModal() == IDOK )
{
m_pathname = m_dlgFile.GetPathName(); // CString m_pathname deklarieren
}
CFile datei( m_pathname, CFile::modeCreate | CFile::modeWrite );
datei.Write( str, sizeof( str ) );
datei.Close();
}

Damit dieser Programmcode funktioniert, müssen wir noch die Member-Variable m_pathname vom Typ CString deklarieren (Rechtsklick auf unsere Dialogklasse). Als Zugriffsstatus wählen wir private.

Lassen Sie uns schrittweise analysieren, was hier genau vor sich geht. Wir erzeugen mit Hilfe von "CFileDialog m_dlgFile( FALSE );" ein Objekt der MFC-Klasse CFileDialog und übergeben dem Konstruktor den Parameter FALSE. Hierdurch wird der Dialog zum Speichern einer Datei erzeugt. "m_dlgFile.DoModal()" zeigt den Standarddialog in modaler Form an, das heißt der Dialog muß zunächst abgearbeitet werden, bevor man zu der ursprünglichen Anwendung zurückkehren kann. Sie können in modernen Multitasking-Betriebssystemen natürlich zu einer anderen Anwendung, z.B. MS Office, wechseln. Sobald Sie aber zum Ursprung zurückkehren, wartet wieder der modale Dialog auf seine Abarbeitung. Mit der if-Kontrollstruktur fragen wir den Rückgabewert dieser Funktion ab. Ist diese durch Drücken der OK-Schaltfläche gleich IDOK , dann übernehmen wir mit "m_pathname = m_dlgFile.GetPathName(); " den im Standarddialog ausgewählten Pfad- und Dateinamen in die Member-Variable m_pathname. Diesen String verwenden wir dann im Konstruktor von CFile anstelle der fixen Dateiangabe. Nach dem Kompilieren können Sie jetzt z.B. die Datei "C:\demo1.txt" erzeugen. Überzeugen Sie sich bitte mit einem Texteditor über den Erfolg.

Nun wollen wir diesen Weg auch beim Einlesen der Datei anwenden. Ergänzen Sie OnButton1() bitte wie folgt:

void CDateiLesenDlg::OnButton1() //Datei Lesen
{
TCHAR str[1000];
CFileDialog m_dlgFile( TRUE ); // TRUE = Datei öffnen,FALSE = Datei speichern
if( m_dlgFile.DoModal() == IDOK )
{
m_pathname = m_dlgFile.GetPathName();
}
CFile datei( m_pathname, CFile::modeRead );
datei.Read( str, sizeof( str ) );
datei.Close();
m_strEdit1 = str;
UpdateData( FALSE ); // Variablen ---> Felder
}

Hier läuft der analoge Vorgang ab. Wir verwenden hier jedoch für den Konstruktor von CFileDialog den Parameter TRUE, um den Standarddialog zum Öffnen von Dateien aufzurufen. Probieren Sie es aus. Sie haben nun einen kleinen Schreib-Lese-Texteditor erzeugt, der beliebige Windows-kompatible Dateinamen verwenden kann.

Da wir in unserem Dialog über zwei mehrzeilige Eingabefelder verfügen, werden wir diese dazu verwenden, den ausgelesenen Text in seiner Buchstabenabfolge umzukehren. Hierzu fügen Sie oben eine weitere Schaltfläche mit der Beschriftung "Textabfolge umkehren" hinzu, erzeugen durch Doppelklick darauf die Funktion OnButton4() und geben dort folgenden Programmcode ein:

void CDateiLesenDlg::OnButton4()
{
m_strEdit1.MakeReverse(); //kehrt die Zeichenfolge um
UpdateData( FALSE );
}

Abb. 3.4: Die Funktion CString::MakeReverse() kehrt die Textabfolge um.

Da für die Handhabung von Texten und Textdateien die Klasse CString wichtige Dienste leistet, werden an dieser Stelle einige Member-Funktionen der MFC-Klasse CString aufgelistet, damit Sie sich schnell an diese und möglichst wenig an die alten Methoden für character arrays gewöhnen:
 

CString als Array 
GetLength gibt Zahl der Zeichen zurück
IsEmpty prüft ob ein CString-Objekt leer ist
Empty setzt den String auf Länge null
GetAt liefert das Zeichen an einer Position
SetAt setzt ein Zeichen an einer Position.


Vergleich
Compare Vergleicht 2 Strings (Groß/Klein-sensitiv).
CompareNoCase Vergleicht 2 Strings (Groß/Klein-insensitiv).


Auswahl 
Mid schneidet in der Mitte aus; wie die Basic MID$ Funktion
Left schneidet von links her aus; wie die Basic LEFT$ Funktion
Right schneidet von rechts her aus; wie die Basic RIGHT$ Funktion
SpanIncluding extrahiert einen Substring, der nur bestimmte Zeichen enthält
SpanExcluding extrahiert einen Substring, der nur bestimmte Zeichen nicht enthält


Umwandlungen
MakeUpper wandelt alles in Großbuchstaben um
MakeLower wandelt alles in Kleinbuchstaben um
MakeReverse kehrt die Zeichenfolge im String um
Format Formatiert den String analog sprintf
TrimLeft entfernt führende Leerzeichen
TrimRight entfernt anhängende Leerzeichen


Suche 
Find sucht eine Zeichenfolge in einem String
ReverseFind sucht eine Zeichenfolge in einem String; startet von hinten
FindOneOf sucht das erste passende Zeichen mittels Vorgaben

Experimentieren Sie ruhig auf eigenen Faust analog dem Beispiel in OnButton4(). Beginnen Sie z.B. mit SetAt, MakeUpper, MakeLower, GetLength, TrimLeft, TrimRight und Find. Die genaue Syntax finden Sie in der Hilfe (starten Sie mit "CString Member Functions").
 
 

3.2 Informationen über die vorhandenen Laufwerke

Wenn Sie Dateien öffnen/lesen und schreiben/speichern, dann benötigen Sie in der Regel Informationen über die vorhandenen Laufwerke. Hierzu schreiben wir uns eine Anwendung namens DriveInfo:

Erzeugen Sie eine dialogbasierende Anwendung wie in Abb. 3.5 mit einer Schaltfläche, einem Textfeld, einem Eingabefeld und einem Listenfeld. Deklarieren Sie mittels Klassen-Assistent folgende Member-Variablen:
 

Steuerelemente-IDs Variablen-Typ Member-Variable (Element)
IDC_BUTTON1    
IDC_EDIT1 UINT m_uintGrenzwert
IDC_LIST1 CListBox m_ctlListBox

 

Abb. 3.5: Der Dialogfeld-Aufbau der Anwendung DriveInfo

Geben Sie folgenden Code in die der Schaltfläche zugeordneten Funktion ein:

void CDriveInfoDlg::OnLaufwerkInfo()
{
    DWORD limit = m_uintGrenzwert; // Grenzwert für min. freier Speicher
    UpdateData( TRUE ); // Eingaben ---> Variablen
    CClientDC dc( this );
    CString s;
    int Drive = 1; // Laufwerktyp (1-6)
    m_ctlListBox.ResetContent(); // Listenfeld leeren
    CString str[26]; // für A-Z

for( int zaehler = 65; zaehler<(65+26); zaehler++ )
{
  str[zaehler-65] = zaehler; // Großbuchstaben A bis Z erzeugen (Zeichen-Code 65 bis 90)
  int Drive = GetDriveType( str[zaehler-65] + ":" ); // Laufwerk-Typ abfragen

  //Spezialfall Programm befindet sich auf Festplatte
    char Buffer[260];
    GetCurrentDirectory(260,Buffer);
    if( str[zaehler-65] == Buffer[0] ) Drive = GetDriveType( NULL );
    // If lpRootPathName is NULL, the function uses the root of the current directory.
  //Spezialfall Programm befindet sich auf Festplatte

  s.Format( "Laufwerk %s: %i", str[zaehler-65], Drive );
  dc.TextOut( 0, (zaehler-65) * 23, s, strlen(s) );

  if( Drive == 3 || Drive == 4 ) // 3 Festplatte, 4 Netzlaufwerk
  {
    GetDiskFreeSpaceEx( str[zaehler-65] + ":", &m_FreeBytes, &m_TotalBytes, NULL ); //Freier Speicherplatz auf Laufwerk
    s.Format( "frei: %u MB", m_FreeBytes.QuadPart /(1024*1024) );
    dc.TextOut( 200, (zaehler-65) * 23, s, strlen( s ) );
    dc.TextOut( 350, (zaehler-65) * 23, " " );
 
    if( m_FreeBytes.QuadPart / (1024*1024) >= limit )
    {
      dc.TextOut(350, (zaehler-65) * 23, ">limit" );
      m_ctlListBox.AddString( str[zaehler-65] + ":" );
    }

  }
} // Ende der for-Schleife

CTime AktuelleZeit;
AktuelleZeit = CTime::GetCurrentTime();
dc.TextOut( 0, 620, AktuelleZeit.Format( "%H:%M:%S %A, %B %d, %Y" ) );

int x = 400;
int y = 480;
dc.TextOut( x, y, "1 = Nicht vorhanden" );
dc.TextOut( x, y+24, "2 = Disk-Laufwerk" );
dc.TextOut( x, y+48, "3 = Festplatte" );
dc.TextOut( x, y+72, "4 = Netzwerk" );
dc.TextOut( x, y+96, "5 = CD-ROM-Laufwerk" );
dc.TextOut( x, y+120, "6 = RAM-Disk" );
}

Folgende Variablen müssen in CDriveInfoDlg als "private" deklariert werden:
ULARGE_INTEGER m_FreeBytes;
ULARGE_INTEGER m_TotalBytes;

Abb. 3.6: DriveInfo informiert über die vorhandenen Laufwerke und deren Speicherplatz (im Bild in Bytes, im Programm in MB)

Da gibt es eine ganze Menge zu verdauen. Gehen wir Schritt für Schritt durch das Programm:

for( int zaehler = 65; zaehler<(65+26); zaehler++)
{
  str[zaehler-65] = zaehler; // Großbuchstaben A bis Z erzeugen (Zeichen-Code 65 bis 90)

Interessant wird es ab der for-Schleife. Wir starten die Variable zaehler nicht bei 0, sondern bei 65, dem Zeichen-Code für das (große) ‘A’. Der erste Schritt ist das Erzeugen der Großbuchstaben A-Z in Form eines CString-Arrays.
Dieses geht in der Schleife von str[0] für ‘A’ bis str[25] für ‘Z’.

int Drive = GetDriveType( str[zaehler-65] + ":" ); // Laufwerke auf Laufwerk-Typ abfragen

//Spezialfall Programm befindet sich auf Festplatte
    char Buffer[260];
    GetCurrentDirectory(260,Buffer);
    if( str[zaehler-65] == Buffer[0] ) Drive = GetDriveType( NULL );
    // If lpRootPathName is NULL, the function uses the root of the current directory.
  //Spezialfall Programm befindet sich auf Festplatte

s.Format( "Laufwerk %s: %i", str[zaehler-65], Drive );
dc.TextOut( 0, (zaehler-65) * 23, s, strlen(s) );

Die API-Funktion GetDriveType(Laufwerk ) gibt den Laufwerk-Typ zurück:
 

1 = Nicht vorhanden 2 = Disk-Laufwerk 3 = Festplatte
4 = Netzwerk 5 = CD-ROM 6 = RAM-Disk

Diesen Wert speichern wir in der Integer-Variablen Drive.

Es gibt hier ein Problem: Wenn ich das Programm z.B. von F:\Programme\... ausführe, findet es nicht den korrekten Typ des Laufwerks F:
Man erhält Drive = 1, obwohl dieses Laufwerk existiert. Daher verwenden wir hier einen Trick: Wir fragen mit GetCurrentDirectory das Laufwerk des Programms ab und vergleichen in der Schleife jeweils die Anfangsbuchstaben miteinander. Sind diese identisch (also z.B. F), dann erhalten wir wir den korrekten Laufwerk-Typ mit GetDriveType(NULL). Eine etwas seltsame Geschichte, aber sie funktioniert.

s.Format(...) formatiert den String s, so daß er den Laufwerk-Buchstaben und den Laufwerk.Typ enthält. Der String wird umgehend mittels TextOut(...) ausgegeben. Zur Berechnung des y-Wertes der Textausgabe benützen wir die Variable zaehler. Als vertikalen Abstand wählen wir hier z.B. 23 Dialogfeldeinheiten.

if(Drive==3 || Drive == 4)
{
  GetDiskFreeSpaceEx( str[zaehler-65]+":", &m_FreeBytes, &m_TotalBytes, NULL );
  s.Format( "frei: %u MB", FreeBytes.QuadPart /(1024*1024) );
  dc.TextOut( 200, (zaehler-65) * 23, s, strlen( s ) );
  dc.TextOut( 350, (zaehler-65) * 23, " " );

Wenn es sich um eine Festplatte oder ein Netzlaufwerk handelt wird die if-Kontrollstruktur durchlaufen. Hier wirkt zunächst die Funktion GetDiskFreeSpaceEx, die als ersten Parameter das Laufwerk, als zweiten einen Zeiger auf eine Variable zur Ablage der freien Bytes und als dritten einen Zeiger auf eine Variable zur Ablage der gesamten Bytes benötigt. Der vierte Parameter wird auf den Zeiger NULL gesetzt. Die erweiterte Funktion GetDiskFreeSpaceEx funktioniert ab Windows 95, OEM Service Release 2. GetDiskFreeSpace liefert falsche Daten, wenn das Laufwerk größer 2 GigaByte ist. Daher muß man heutzutage GetDiskFreeSpaceEx einsetzen. Die genaue Syntax lautet:

BOOL GetDiskFreeSpaceEx(
LPCTSTR lpDirectoryName,
PULARGE_INTEGER lpFreeBytesAvailableToCaller ,
PULARGE_INTEGER lpTotalNumberOfBytes ,
PULARGE_INTEGER lpTotalNumberOfFreeBytes
);

lpFreeBytesAvailableToCaller
Pointer to a variable to receive the total number of free bytes on the disk that are available to the user associated with the calling thread.
lpTotalNumberOfBytes
Pointer to a variable to receive the total number of bytes on the disk that are available to the user associated with the calling thread.
 
lpTotalNumberOfFreeBytes
Pointer to a variable to receive the total number of free bytes on the disk.
This parameter can be NULL.
Nur der letzte Parameter darf NULL sein. Daher haben wir die Variable m_TotalBytes deklariert, obwohl wir diese nicht verwenden.

PULARGE_INTEGER: Was ist das?

PULARGE_INTEGER bedeutet Pointer (Zeiger) auf ULARGE_INTEGER. Hierbei handelt es um eine Union , die aus zwei 32-bit-unsigned-DWORD besteht, um einen 64-bit-unsigned-integer-Wert darzustellen:

typedef union _ULARGE_INTEGER 

    struct 
        {
                        DWORD LowPart; 
                        DWORD HighPart; 
        };
    ULONGLONG QuadPart;
} ULARGE_INTEGER;
LowPart: low-order 32 bits. HighPart:high-order 32 bits.
QuadPart: 64-bit unsigned integer.

The ULARGE_INTEGER structure is actually a union. If your compiler has built-in support for 64-bit integers, use the QuadPart member to store the 64-bit integer. Otherwise, use the LowPart and HighPart members to store the 64-bit integer.

Durch die immer größer werdenden Festplatten gerät man mit 32-bit-Zahlen in Probleme: 2 hoch 32 = 4.294.967.296

if( m_FreeBytes.QuadPart/(1024*1024) >= limit )
{
  dc.TextOut( 350, (zaehler-65) * 23, ">limit" );
  m_ctlListBox.AddString( str[zaehler-65] + ":" );
}

Wir sprechen die Union als "QuadPart" (64-bit unsigned integer) an. Vielleicht haben Sie auf Ihrem Rechner die Festplatten fast immer randvoll und können diese Probleme nicht nachvollziehen, aber aus eigener Erfahrung mit neuen Rechnern kann ich nur zu diesem Vorgehen raten.
Nachdem wir es geschafft haben, den freien Speicherplatz mit einem von uns im Eingabefeld gewählten Limit zu vergleichen, fügen wir das Laufwerk dem Listenfeld zu, wenn es die Vorgabe einhält.

CTime AktuelleZeit;
AktuelleZeit = CTime::GetCurrentTime();
dc.TextOut( 0, 620, AktuelleZeit.Format( "%H:%M:%S %A, %B %d, %Y") );

Als kleine Beigabe erhalten Sie obenstehend einen Dreizeiler, mit denen Sie in Ihren Anwendungen Uhrzeit und Datum einblenden können. So etwas kann man immer brauchen.
 

Weiter zum Nächsten Kapitel

Zurueck zum Inhaltsverzeichnis