Disk Full ?

Leserbewertung(3):bewerten...
kommentieren...

Hans Brender                                                                           Freespace.zip

Auch im Zeitalter der Gigabyte großen Festplatten ist es immer noch ein Thema: Wie viel Platz ist noch auf der Festplatte frei ? Nicht nur die Angabe der eigenen Festplatte ist für Entwickler interessant, im Zuge von Client Server Applikationen sind auch die Angaben der Serverfestplatte immer wichtiger, denn...

Die Geschichte wird von vielen Programmierern gerne außer Acht gelassen, gibt es doch seit den Tagen von Quick-Basic Fehlermeldungen in der Programmiersprache, die uns beim Schreiben einer Datei bei zu geringer Kapazität reagieren lassen können, doch Hand aufs Herz, wer überprüft seinen Code auf solche Eventualitäten, und im Zeitalter von Datenbanken, was bringt uns eine solche Fehlermeldung, wenn ein neuer Datensatz hinzugefügt werden soll...

Zu spät ist es allemal, man sollte eigentlich schon im Vorfeld über einen geeigneten Mechanismus prüfen, ob das Ziellaufwerk noch genügend Restkapazität verfügt und den Anwender darüber informieren, dass es demnächst eng wird.

Bei 32Bit-Applikationen und der, auch in kleinen Büros zu findenden Netzwerken, werden Dateien oder Datenbanken immer häufiger auf dem Server abgelegt und immer mehr Entwickler setzen UNC-Dateinamen in ihren Applikationen ein, um den Benutzer von weiteren Laufwerksbuchstaben, die im Explorer eine Verknüpfung zu diesen Festplatten repräsentieren, zu verschonen. Ein UNC-Dateiname (Universal Naming Convention) beruht auf der Tatsache, dass jeder PC in einem Netzwerk, auch der Server einen eindeutigen Namen haben muss. Des weiteren kann jede Festplatte oder aber jedes beliebige Verzeichnis für andere Benutzer freigegeben werden, und über der PC-Namen und dem Freigabenamen baut sich der UNC-Dateiname auf. Gilt der Doppelpunkt als zweites Zeichen als Merkmal eines "herkömmlichen" Laufwerks so wird bei der UNC-Konvention dem eindeutigen Rechnernamen zwei "\\" vorangestellt. Nach dem Rechnernamen folgt ein weiterer "\" und danach der Freigabenamen, gefolgt von möglichen Verzeichnissen, wie üblich durch ein "\" getrennt, und schließlich der Dateiname mit einer möglichen Endung.

Als Beispiel nehmen wir einen Server mit dem Namen "Server", auf dem es eine Verzeichnisstruktur \Programme\Visual Basic\HBSOFT gibt. Das Verzeichnis "Visual Basic" ist auf dem Server mit dem Freigabenamen "VB" für alle Benutzer des Netzwerkes freigegeben. Nehmen wir als weiteres an, im Verzeichnis HBSOFT liegt eine Datenbank mit dem Namen Produktion.mdb. Der Zugriff auf diese Datenbank per UNC von Ihrem Client-Rechner lautet dann: \\Server\VB\HBSOFT\Produktion.mdb . Es wird also kein weiterer Laufwerksbuchstabe gebraucht, um Zugriff auf diese Datenbank zu erhalten. Wenn Sie die Freigabenamen des Zielrechners nicht kennen, genügt die Eingabe "\\Rechnername" im Startmenüpunkt Ausführen, und ein neues Explorer-Fenster mit allen Freigabenamen wird angezeigt.

Um nun die freie Kapazität eines Festplattenlaufwerkes zu ermitteln, greifen wir auf die im Kernel32.dll enthaltene Funktion GetDiskFreeSpaceA" zurück, deren erster Parameter ein Pfadname verlangt. Die restlichen vier Parameter sind als Long definiert und werden von der Funktion mit Werten gefüllt, wenn im ersten Parameter korrekte Werte eingetragen sind. Dies gilt zum einen für vordefinierte Laufwerke, bestehend aus Laufwerksbuchstaben, dem Doppelpunkt und einem Backslash oder aber für einen UNC-Dateinamen. Wie viele Programmierer habe ich mehrere Betriebssystem auf meinem Entwicklungsrechner, und als ich diese Funktion zum ersten Male ausprobierte, war Windows 95 gestartet. Der Zugriff über einen Laufwerksbuchstaben erfolgte korrekt, nur beim Einsatz eines korrekten UNC-Dateinamens wurde mir immer ein Fehlerwert zurückgegeben. Während der Suche in der MSDN Knowledgebase stieß ich dann irgendwann auf einen Artikel, indem Microsoft feststellt, daß der Zugriff über einen UNC-Dateinamen innerhalb von Windows 95 nicht möglich ist, also schlichtweg ein Fehler des Betriebssystems Windows 95 ist. Nebenbei gesagt, unter Windows NT funktioniert der UNC-Zugriff einwandfrei. Um dennoch unter Windows 95 mit UNC-Dateinamen die Festplattenkapazität eines Rechners im Netz ermitteln zu können, bedarf es eines einfachen Tricks, der aber den Einsatz von drei weiteren API-Funktionen erfordert:

GetDriveTypeA ebenfalls in der kernel32.dll ermittelt den Typ eines Laufwerkes

WNetAddConnectionA in der mpr.dll zum Verbinden mit einem Netzwerklaufwerk

WnetCancelConnectionA in der mpr.dll zum Lösen einer Netzwerkverknüpfung

Zum Ermitteln der Kapazität habe ich die Funktion FGetDiskFreeSpace geschrieben. Im ersten Parameter übergeben wir entweder einen Laufwerksbuchstaben oder ein gültigen UNC-Dateinamen. Der zweite Parameter dient als Rückgabewert der ermittelten Restkapazität der ausgewählten Festplatte in Bytes.

In der Routine wird über den eindeutigen Doppelpunkt an Position 2 ermittelt, ob der Zugriff über den klassischen Laufwerksbuchstaben oder aber über einen UNC-Dateinamen erfolgen soll. Wie schon erwähnt, wird bei Windows 95 als Betriebssystem und der Verwendung von UNC-dateinamen ein Fehler ausgegeben. Wir müssen also einen temporären Netzwerkzugriff erzeugen. Da jeder Rechner über mehr oder weniger feste Netzwerkverbindungen oder reservierte Laufwerksbuchstaben innehat, müssen wir die Funktion FFirstFreeDrive bemühen, die uns den ersten freien Laufwerksbuchstaben zurück gibt, den wir für unsere temporäre Netzwerkverbindung benötigen.

Diesen ermittelten Laufwerksbuchstaben, den UNC-Dateinamen und ein leerer Paßwort-String übergeben wir der API-Funktion WnetAddConnection. Beachten Sie jedoch, dass Sie nur eine Verbindung zu diesem Netzwerklaufwerk erhalten, wenn Sie dort über die entsprechenden Rechte verfügen. Bei durch ein zusätzliches Passwort geschützten Freigabenamen müssen Sie Funktion FGetDiskFreeSpace durch ein weiteren Übergabeparameter, der das Passwort erhält, erweitern. Ist der Netzwerkzugriff erfolgreich, kann jetzt mit dem Laufwerksbuchstaben die Kapazität ermittelt werden. Nach erfolgreichem Zugriff müssen wir das temporär erzeugte Netzwerklaufwerk wieder freigeben. Die Berechnung der tatsächlichen freien Kapazität ist eine einfache Multiplikation der Rückgabewerte Sektoren per Cluster, Bytes per Sektor und FreeClusters.

Für eine visuelle Ausgabe ist eine Angabe in Bytes nicht immer interessant. In der Statusleiste des Explorers finden Sie immer nur zusammengefaßte Werte, die für den Anwender wesentlich einfacher auszuwerten sind. Die Funktion FFileSize2Display bildet genau diese Zusammenfassung nach. Sie übergeben die exakte Anzahl der ermittelten Bytes und erhalten einen formatierten String zurück

Im Source-Code finden Sie zwei weitere Funktionen FConnectDrive und FDisconnectDrive, welche eine Verbindung zu einem Netzwerk-Pfad über die Eingabe eines UNC-Dateinamens und deren Löschung beinhaltet.
VERSION 4.00
Begin VB.Form frmFreeSpace

Caption = "Netzwerk-Festplattencheck "
ClientHeight = 2295
ClientLeft = 2460
ClientTop = 2025
ClientWidth = 5190
Height = 2700
Left = 2400
LinkTopic = "Form1"
ScaleHeight = 2295
ScaleWidth = 5190
Top = 1680
Width = 5310
Begin VB.CommandButton cmdAction
Caption = "&Ermitteln"
Height = 405
Left = 3390
TabIndex = 3
Top = 1680
Width = 1575
End
Begin VB.Frame Frame1
Caption = "Kapazität"
Height = 945
Left = 120
TabIndex = 2
Top = 600
Width = 4875
Begin VB.Label lblKap
Height = 495
Left = 180
TabIndex = 4
Top = 240
Width = 3765
End
End
Begin VB.TextBox txtDrive
Height = 315
Left = 2010
TabIndex = 1
Text = "C:\"
Top = 180
Width = 2925
End
Begin VB.Label lblDrive
Caption = "Laufwerksbuchstaben oder UNC-Laufwerk"
Height = 495
Left = 120
TabIndex = 0
Top = 150
Width = 1785
End
End


Private Sub cmdAction_Click()
Dim lFreeSpace As Long
If FGetDiskFreeSpace(txtDrive.Text, lFreeSpace) Then
lblKap.Caption = "Das ausgewählte Laufwerk hat eine freie Kapazität von " & _
FFileSize2Display(lFreeSpace) & " (oder genau: " & CStr(lFreeSpace) & " Bytes )"
Else
lblKap = "Fehler aufgetreten"
End If
End Sub


Attribute VB_Name = "basUtil"

Option Explicit
'Zur ermittlung von freiem Speicherplatz; Funktion FGetDiskFreeSpace
Private Declare Function GetDiskFreeSpace Lib "kernel32" Alias "GetDiskFreeSpaceA" _
(ByVal lpRootPathName As String, _
lpSectorsPerCluster As Long, lpBytesPerSector As Long, _
lpNumberOfFreeClusters As Long, _
lpTotalNumberOfClusters As Long) As Long

Private Const WN_SUCCESS As Integer = &H0 'Function was successful.

Private Declare Function WNetAddConnection Lib "mpr.dll" Alias "WNetAddConnectionA" _
(ByVal lpszNetPath As String, _
ByVal lpszPassword As String, _
ByVal lpszLocalName As String) As Long
Private Declare Function WNetCancelConnection Lib "mpr.dll" Alias "WNetCancelConnectionA" _
(ByVal lpszName As String, _
ByVal bForce As Long) As Long

Private Declare Function GetDriveType Lib "kernel32" Alias "GetDriveTypeA" (ByVal nDrive As String) As Long
Private Const BS As String = "\"

'**************************************************
'**
'** Description:FConnectDrive
'** etabliert eine Netzwerkverbindung unter Zuhilfename eines
'** UNC-Dateinamens und gibt den Zugriff "klassisch" zurück
'**
'** \\Server\Freigabe\Dateiname --> G:\Datiname
'**
'** Input:
'** stUNCName - UNC-Dateinamen
'**
'** Output:
'**
'**
'** Returnvalues:
'** Klassischer Dateiname
'**
'** Create Date/Who:
'** 19.05.97 / HB
'**
'** Change Date/Who/Why:
'**
'**************************************************
Public Function FConnectDrive(ByVal stUncName As String) As String
Dim stPath As String
Dim stPasswordText As String
Dim stDriveletter As String
Dim lSuccess As Long
Dim iPos As Integer
Dim iCount As Integer

On Error GoTo Default_Errorhandler

stUncName = UCase$(stUncName)
stPasswordText = ""
'ermittle den nächsten freien Laufwerksbuchstaben
stDriveletter = FFirstFreeDrive
'zum Netzwerkrechner verbinden ohne "\"
If Right$(stUncName, 1) = BS Then
stUncName = Left$(stUncName, Len(stUncName) - 1)
End If
'splitt off UNCName: look for 4th "\" --> \\Server\FName\...
iCount = 0
iPos = 1
Do
If Mid$(stUncName, iPos, 1) = BS Then
iCount = iCount + 1
End If
iPos = iPos + 1
Loop Until iCount = 4
stPath = Left$(stUncName, iPos - 2)

'zum Netzwerk verbinden... Temporär
lSuccess = WNetAddConnection(stPath, stPasswordText, stDriveletter)
If lSuccess = WN_SUCCESS Then
FConnectDrive = stDriveletter & BS & Mid$(stUncName, iPos)
End If
Exit Function

Default_Errorhandler:
Resume Next

End Function


'***********************************************
'**
'** Description:FDisConnectDrive
'** löscht den mit FConnectDrive etablierten Netzwerkzugriff
'**
'** Input:
'** stPath
'**
'** Output:
'**
'**
'** Returnvalues:
'** True on Success
'**
'** Create Date/Who:
'** 01.06.97 / HB
'**
'** Change Date/Who/Why:
'**
'*************************************************
Public Function FDisconnectDrive(ByVal stPath) As Boolean
Dim lSuccess As Long
Dim stDriveletter As String

On Error GoTo Default_Errorhandler

stDriveletter = Left$(stPath, 2)
lSuccess = WNetCancelConnection(stDriveletter, 1)
If lSuccess = WN_SUCCESS Then
FDisconnectDrive = True
End If
Exit Function

Default_Errorhandler:
Resume Next

End Function


'****************************************************
'**
'** Description:FGetDiskFreeSpace
'** ermittelt die freie Kapazität eines Laufwerkes.
'** Dabei kann sowohl ein Laufwerk ("C:\") oder ein
'** UNC-Pfadname übergeben werden ("\\Server\Share-Name\")
'**
'** Durch einen Bug in Win95 (laesst keinen UNC-Namen zu!)
'** wird als Workaraound ein neuer Laufwerksbuchstaben ermittelt
'** eine Net-Connection durchgeführt, die Kapazität ermittelt
'** und anschließend eine Deconnection durchgeführt.
'**
'** Input:
'** stUNCName - Laufwerk oder UNC-Name
'**
'** Output:
'** lFreeSpace - ermittelte freie Kapazität in Bytes
'**
'** Returnvalues:
'** TRUE on Success
'**
'** Create Date/Who:
'** 19.05.97 / HB
'**
'** Change Date/Who/Why:
'**
'*************************************************
Public Function FGetDiskFreeSpace(ByVal stUncName As String, lFreeSpace As Long) As Boolean

Dim numSectorsPerCluster As Long
Dim numBytesPerSector As Long
Dim numFreeClusters As Long
Dim numTotalClusters As Long
Dim lSuccess As Long
Dim stPasswordText As String
Dim stDriveletter As String
Dim bError As Boolean

On Error GoTo Default_Errorhandler

'wenn ein Laufwerk mit einem Pfad übergeben wurde..
If Mid$(stUncName, 2, 1) = ":" Then
stUncName = Left$(stUncName, 3)
If Right$(stUncName, 1) <> BS Then
stUncName = stUncName & BS
End If
End If

'ermittle freie Kapazität
lSuccess = GetDiskFreeSpace(stUncName, numSectorsPerCluster, numBytesPerSector, numFreeClusters, numTotalClusters)
'wenn false, (Error reportet by W95 )
If lSuccess = False Then
'Es muss ein Laufwerksbuchstabe erzeugt werden
stUncName = UCase$(stUncName)
stPasswordText = ""
'ermittle den nächsten freien Laufwerksbuchstaben
stDriveletter = FFirstFreeDrive
'zum Netzwerkrechner verbinden ohne "\"
If Right$(stUncName, 1) = BS Then
stUncName = Left$(stUncName, Len(stUncName) - 1)
End If
'zum Netzwerk verbinden... Temporär
lSuccess = WNetAddConnection(stUncName, stPasswordText, stDriveletter)
'wenn erfolgreich,
If lSuccess = WN_SUCCESS Then
lSuccess = GetDiskFreeSpace(stDriveletter & BS, numSectorsPerCluster, numBytesPerSector, numFreeClusters,numTotalClusters)
If lSuccess = False Then 'Fehler
FGetDiskFreeSpace = False
Else 'GetDiskFreeSpace ok
lFreeSpace = numSectorsPerCluster * numBytesPerSector * numFreeClusters
'und abmelden
lSuccess = WNetCancelConnection(stDriveletter, 1)
FGetDiskFreeSpace = Not bError
End If
End If
Else 'GetDiskFreeSpace ok
lFreeSpace = numSectorsPerCluster * numBytesPerSector * numFreeClusters
FGetDiskFreeSpace = Not bError
End If
Exit Function

Default_Errorhandler:
bError = True
Resume Next

End Function

'******************************************************
'**
'** Description:FFirstFreeDrive
'** ermittelt das nächste freie Laufwerk und gibt
'** den Laufwerksbuchstaben mit ":" zurück
'**
'** Input:
'**
'**
'** Output:
'**
'**
'** Returnvalues:
'** nächster freier Laufwerksbuchstabe
'**
'** Create Date/Who:
'** 19.05.97 / HB
'**
'** Change Date/Who/Why:
'**
'*********************************************************
Private Function FFirstFreeDrive() As String
Dim lDriveNum As Long
Dim stFirstFreeDrive As String
Dim lFirstDrive As Long

On Error GoTo Default_Errorhandler

'Laufwerk A und B sind Diskettenlaufwerke
lDriveNum = 2
Do
lDriveNum = lDriveNum + 1
stFirstFreeDrive = Chr$(lDriveNum + 65) + ":\"
lFirstDrive = GetDriveType(stFirstFreeDrive)
'wenn frei, wird lFistDrive=1
Loop Until lFirstDrive = 1
'\ absplitten
FFirstFreeDrive = Left(stFirstFreeDrive, 2)

FreeDrive_End:
Exit Function

Default_Errorhandler:
Resume FreeDrive_End

End Function


'*******************************************************
'**
'** Description:FFileSize2Display
'** Die übergebenen BYTES werden nach Exploerer-Art formatiert
'** und dann als String zurück gegeben
'**
'** Input: lUeber - BYTES
'**
'**
'** Output:
'**
'**
'** Returnvalues:
'** formatierter String
'**
'** Create Date/Who:
'** 19.05.97 / HB
'**
'** Change Date/Who/Why:
'**
'****************************************************
Public Function FFileSize2Display(lUeber As Long) As String

Const FS_GIGA = 1073741824 '1024*1024*1024
Const FS_MEGA = 1048576 '1024*1024
Const FS_KILO = 1024
Const FS_10P3 = 1000
Const FS_10P2 = 100
Const FS_256 = 256
Const FS_10P1 = 10
Const BYTES = " BYTES"
Const KB = " KB"
Const MB = " MB"
Const GB = " GB"
Const stFMT1 = "0.00"
Const stFMT2 = "##0"
Const stFMT3 = "#0.0"

Dim lWert As Double

On Error GoTo Default_Errorhandler

Select Case lUeber
Case Is < FS_10P3
FFileSize2Display = Format$(lUeber, stFMT2) & BYTES
Case Is < FS_KILO
lWert = lUeber
Select Case lWert
Case Is > FS_10P3: FFileSize2Display = Format$(lWert, stFMT1) & KB
Case Is > FS_10P2: FFileSize2Display = Format$(lWert, stFMT2) & BYTES
Case Is > FS_10P1: FFileSize2Display = Format$(lWert, stFMT3) & BYTES
Case Else: FFileSize2Display = Format$(lWert, stFMT2) & BYTES
End Select
Case Is < FS_MEGA
lWert = lUeber / FS_KILO
Select Case lWert
Case Is > FS_10P3: FFileSize2Display = Format$(lWert, stFMT1) & MB
Case Is > FS_10P2: FFileSize2Display = Format$(lWert, stFMT2) & KB
Case Is > FS_10P1: FFileSize2Display = Format$(lWert, stFMT3) & KB
Case Else: FFileSize2Display = Format$(lWert, stFMT1) & KB
End Select
Case Is < FS_GIGA
lWert = lUeber / FS_MEGA
Select Case lWert
Case Is > FS_10P3: FFileSize2Display = Format$(lWert, stFMT1) & GB
Case Is > FS_10P2: FFileSize2Display = Format$(lWert, stFMT2) & MB
Case Is > FS_10P1: FFileSize2Display = Format$(lWert, stFMT3) & MB
Case Else: FFileSize2Display = Format$(lWert, stFMT1) & MB
End Select
Case Is > FS_GIGA
lWert = lUeber / FS_GIGA
FFileSize2Display = Format$(lWert, "# ### ##0") & GB
End Select
Exit Function

Default_Errorhandler:
Resume Next

End Function