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