Zurueck zum Inhaltsverzeichnis
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:
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").
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:
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 |
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
);
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;
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.