Windows API functions for finding and copying files.
The FileInfo class encapsulates the WIN32_FIND_INFO structure and
includes handy functions for decoding timestamps and filesize as well as some attributes
such as readonly and directory.
The FileManagement class includes the standard calls such as FindFile and CopyFile,
and also more interesting ones such as CopyFileEX (aliased here to CopyFileWithProgress)
which allows for the showing of a progress indicator when copying very large files.
Inside FileManagement you'll find...
- RecursiveCopy: Copies the contents of a directory and all subdirectories
to another path.
- FindAllFiles: Returns an arraylist containing all files and subdirectories
that were found in a directory. Requires a parameter (EXT) representing the file and
extension to look for. The wildcard "*.*" returns all files and subdirectories.
- FileExists: Returns true if a file and path exists, otherwise returns false.
- GetDirectoryInfo: Fills a FileInfo.WIN32_FIND_DATA class with information
about a specified directory.
- GetFileInfo: Fills a FileInfo.WIN32_FIND_DATA class with information about the
specified file.
- CopyFileWithProgress: Copies a file and periodically raises FileCopyProgress events.
A FileCopyComplete event is raised when the copy is complete.
- FindAllDirectories: Finds all directories in a given path and returns the results
as an arraylist.
Here's the VB.NET source for the two classes:
Option Explicit On
Imports System.Runtime.InteropServices
Imports System.Xml
Namespace jlion
'''
''' Encapsulates information about a file, on either a remote or local location.
'''
'''
Public Class FileInfo
Private msRoot As String
Private msFullName As String
Private msName As String
Private mbIsDirectory As Boolean
Private mbIsReadonly As Boolean
Private moCreateTime As Object
Private moLastWriteTime As Object
Private moLastAccessTime As Object
Private mlFileSize As Int64
Private msParentDirectory As String = ""
Private msErrorMessage As String
#Region "APIs"
_
Private Shared Function FileTimeToSystemTime( _
<[In]()> ByVal lpFileTime As FILETIME, _
ByRef lpSystemTime As SYSTEMTIME) _
As Boolean
End Function
Public Const FILE_ATTRIBUTE_READONLY = &H1
Public Const FILE_ATTRIBUTE_HIDDEN = &H2
Public Const FILE_ATTRIBUTE_SYSTEM = &H4
Public Const FILE_ATTRIBUTE_DIRECTORY = &H10
Public Const FILE_ATTRIBUTE_ARCHIVE = &H20
Public Const FILE_ATTRIBUTE_NORMAL = &H80
Public Const FILE_ATTRIBUTE_TEMPORARY = &H100
Public Const FILE_ATTRIBUTE_COMPRESSED = &H800
Public Const FILE_ATTRIBUTE_OFFLINE = &H1000
'''
''' Structure used by windows to store security attributes
'''
'''
_
Public Class SECURITY_ATTRIBUTES
Public nLength As Long
Public lpSecurityDescriptor As Long
Public bInheritHandle As Long
End Class
'''
''' Structure used by windows to store file times. Referenced by WIN32_FIND_DATA structure
'''
'''
_
Public Class FILETIME
Public dwHighDateTime As Int32
Public dwLowDateTime As Int32
End Class
'''
''' Information about a remote file, returned by the FTPFindFirstFile and InternetFindNextFile API calls.
'''
'''
_
Public Class WIN32_FIND_DATA
Public dwFileAttributes As System.IO.FileAttributes
Public ftCreationTime As FILETIME
Public ftLastAccessTime As FILETIME
Public ftLastWriteTime As FILETIME
Public nFileSizeHigh As Integer
Public nFileSizeLow As Integer
Public dwReserved0 As Integer
Public dwReserved1 As Integer
_
Public cFileName As String
_
Public cAlternate As String
End Class
_
Private Structure SYSTEMTIME
Public Year As Short
Public Month As Short
Public DayOfWeek As Short
Public Day As Short
Public Hour As Short
Public Minute As Short
Public Second As Short
Public Milliseconds As Short
End Structure
#End Region
Public Sub New(ByVal oFile As WIN32_FIND_DATA)
msName = oFile.cFileName
mbIsDirectory = oFile.dwFileAttributes And FileInfo.FILE_ATTRIBUTE_DIRECTORY
mbIsReadonly = oFile.dwFileAttributes And FileInfo.FILE_ATTRIBUTE_READONLY
mlFileSize = CalculateFileSize(oFile.nFileSizeHigh, oFile.nFileSizeLow)
moLastWriteTime = CalculateFileTime(oFile.ftLastWriteTime)
moLastAccessTime = CalculateFileTime(oFile.ftLastAccessTime)
End Sub
Public Sub New( _
ByVal oFile As WIN32_FIND_DATA, _
ByVal sRoot As String, _
ByVal sBaseDir As String, _
ByVal sParentDir As String, _
ByVal bIsDir As Boolean)
msRoot = sRoot
msName = oFile.cFileName
If sParentDir > "" Then
msFullName = sBaseDir & "\" & sParentDir & "\" & oFile.cFileName
Else
msFullName = sBaseDir & "\" & oFile.cFileName
End If
msParentDirectory = sParentDir
moCreateTime = CalculateFileTime(oFile.ftCreationTime)
moLastWriteTime = CalculateFileTime(oFile.ftLastWriteTime)
moLastAccessTime = CalculateFileTime(oFile.ftLastAccessTime)
If Not bIsDir Then
NewFile(oFile)
Else
NewDir(oFile)
End If
End Sub
Private Sub NewFile(ByVal oFile As WIN32_FIND_DATA)
mbIsDirectory = oFile.dwFileAttributes And FileInfo.FILE_ATTRIBUTE_DIRECTORY
mbIsReadonly = oFile.dwFileAttributes And FileInfo.FILE_ATTRIBUTE_READONLY
mlFileSize = CalculateFileSize(oFile.nFileSizeHigh, oFile.nFileSizeLow)
End Sub
Private Sub NewDir(ByVal oFile As WIN32_FIND_DATA)
mbIsDirectory = True
mbIsReadonly = False
mlFileSize = 0
End Sub
Public ReadOnly Property Root() As String
Get
Return msRoot
End Get
End Property
Public ReadOnly Property FullName() As String
Get
Return msFullName
End Get
End Property
Public ReadOnly Property Name() As String
Get
Return msName
End Get
End Property
Public ReadOnly Property Extension() As String
Get
Return ""
End Get
End Property
Public ReadOnly Property ParentDirectory() As String
Get
Return msParentDirectory
End Get
End Property
Public ReadOnly Property IsDirectory() As Boolean
Get
Return mbIsDirectory
End Get
End Property
Public ReadOnly Property IsReadonly() As Boolean
Get
Return mbIsReadonly
End Get
End Property
Public ReadOnly Property FileSize() As Int64
Get
Return mlFileSize
End Get
End Property
Public ReadOnly Property CreateTime() As Object
Get
Return moCreateTime
End Get
End Property
Public ReadOnly Property LastWriteTime() As Object
Get
Return moLastWriteTime
End Get
End Property
Public ReadOnly Property LastAccessTime() As Object
Get
Return moLastAccessTime
End Get
End Property
Private Function CalculateFileSize(ByVal lFileSizeHigh As Long, ByVal lFileSizeLow As Long) As Int64
Dim lFileSize64 As Int64 = lFileSizeLow
If lFileSizeLow < 0 Then
lFileSize64 = lFileSize64 + 4294967296@
End If
If lFileSizeLow > 0 Then
lFileSize64 = lFileSize64 + (lFileSizeHigh * 4294967296@)
End If
Return lFileSize64
End Function
Private Function CalculateFileTime(ByVal oFileTime As FILETIME) As Object
Dim oSystemTime As New SYSTEMTIME
Dim bSuccess As Boolean = FileTimeToSystemTime(oFileTime, oSystemTime)
If bSuccess = True Then
Dim oTime As New DateTime( _
oSystemTime.Year, _
oSystemTime.Month, _
oSystemTime.Day, _
oSystemTime.Hour, _
oSystemTime.Minute, _
oSystemTime.Second, _
oSystemTime.Milliseconds)
Return oTime
Else
Return Nothing
End If
End Function
Public Function NotTheSameAs(ByVal oFileInfo As FileInfo) As Boolean
Dim mbNotTheSame As Boolean = False
If oFileInfo.IsDirectory = True _
AndAlso oFileInfo.Name <> msName Then
mbNotTheSame = True
Else
With oFileInfo
If .Name <> msName Then
mbNotTheSame = True
End If
If mbNotTheSame = False _
AndAlso .LastWriteTime <> moLastWriteTime Then
If IsDate(.LastWriteTime) AndAlso IsDate(moLastWriteTime) _
AndAlso Math.Abs(DateDiff(DateInterval.Minute, CDate(.LastWriteTime), CDate(moLastWriteTime))) < 1 Then
'---they are the same
Else
mbNotTheSame = True
End If
End If
If mbNotTheSame = False _
AndAlso .FileSize <> mlFileSize Then
mbNotTheSame = True
End If
End With
End If
Return mbNotTheSame
End Function
Private Function FileNameFromFullName(ByVal sFullName As String) As String
Dim sResult As String = sFullName
Dim iLastSlash As Integer = sFullName.LastIndexOf("\")
If iLastSlash > -1 Then
sResult = sFullName.Substring(iLastSlash, sFullName.Length - iLastSlash - 1)
End If
Return sResult
End Function
End Class
Public Class FileManagement
'By Joe Lynds, 2008
'http://www.jlion.com
#Region "FindFile APIs"
Private Const INVALID_HANDLE_VALUE As Long = -1
'''
''' FindFirstFile API. Necessary because system.io functions can't access shadow volume.
''' http://msdn2.microsoft.com/en-us/library/aa364418.aspx
'''
'''
'''
'''
'''
_
Private Shared Function FindFirstFile( _
<[In]()> ByVal lpszSearchFile As String, _
<[In](), Out()> ByVal lpFindFileData As FileInfo.WIN32_FIND_DATA) _
As IntPtr
End Function
'''
''' FindNextFile API. Necessary because system.io functions can't access shadow volume.
''' http://msdn2.microsoft.com/en-us/library/aa364428.aspx
'''
'''
'''
'''
'''
_
Private Shared Function FindNextFile( _
<[In]()> ByVal hFind As IntPtr, _
<[In](), Out()> ByVal lpFindFileData As FileInfo.WIN32_FIND_DATA) _
As Boolean
End Function
'''
''' FindClose API. Necessary because system.io functions can't access shadow volume.
''' http://msdn2.microsoft.com/en-us/library/aa364413.aspx
'''
'''
'''
'''
_
Private Shared Function FindClose( _
<[In]()> ByVal hFind As IntPtr) _
As Boolean
End Function
#End Region
#Region "CopyFile APIs"
Public Event FileCopyProgress(ByVal lTotalSize As Long, ByVal lTotalTransferred As Long)
Public Event FileCopyComplete(ByVal sSourceFile As String, ByVal sTargetFile As String)
'''
''' Copy File API. Necessary because system.io functions can't access shadow volume.
''' http://msdn2.microsoft.com/en-us/library/aa363851.aspx
'''
'''
'''
'''
'''
'''
_
Private Shared Function CopyFile( _
<[In]()> ByVal lpExistingFileName As String, _
<[In]()> ByVal lpNewFileName As String, _
<[In]()> ByVal bFailIfExists As Byte) _
As Boolean
End Function
Private Enum CopyProgressResult As UInteger
PROGRESS_CONTINUE = 0
PROGRESS_CANCEL = 1
PROGRESS_STOP = 2
PROGRESS_QUIET = 3
End Enum
Private Enum CopyProgressCallbackReason As UInteger
CALLBACK_CHUNK_FINISHED = 0
CALLBACK_STREAM_SWITCH = 1
End Enum
Private Enum CopyFileFlags As UInteger
COPY_FILE_FAIL_IF_EXISTS = 1
COPY_FILE_RESTARTABLE = 2
COPY_FILE_OPEN_SOURCE_FOR_WRITE = 4
COPY_FILE_ALLOW_DECRYPTED_DESTINATION = 8
End Enum
Public Delegate Function CopyProgressRoutine( _
ByVal TotalFileSize As Int64, _
ByVal TotalBytesTransferred As Int64, _
ByVal StreamSize As Int64, _
ByVal StreamBytesTransferred As Int64, _
ByVal dwStreamNumber As Int32, _
ByVal dwCallbackReason As Int32, _
ByVal hSourceFile As Int32, _
ByVal hDestinationFile As Int32, _
ByVal lpData As Int32 _
) As Int32
Declare Auto Function CopyFileEx Lib "kernel32.dll" ( _
ByVal lpExistingFileName As String, _
ByVal lpNewFileName As String, _
ByVal lpProgressRoutine As CopyProgressRoutine, _
ByVal lpData As Int32, _
ByVal lpBool As Int32, _
ByVal dwCopyFlags As Int32 _
) As Int32
Public Function myCopyProgressRoutine( _
ByVal TotalFileSize As Int64, _
ByVal TotalBytesTransfered As Int64, _
ByVal StreamSize As Int64, _
ByVal StreamBytesTransferred As Int64, _
ByVal dwStreamNumber As Int32, _
ByVal dqCallbackReason As Int32, _
ByVal hSourceFile As Int32, _
ByVal hDestinationFile As Int32, _
ByVal lpData As Int32 _
) As Integer
RaiseEvent FileCopyProgress(TotalFileSize, TotalBytesTransfered)
End Function
Public Function CopyFileWithProgress( _
ByVal sSource As String, _
ByVal sTarget As String, _
ByVal bFailIfExists As Boolean) As Boolean
Dim dwFlags As Int32 = 0
If bFailIfExists = True Then
dwFlags = CopyFileFlags.COPY_FILE_FAIL_IF_EXISTS
End If
Dim cb As CopyProgressRoutine
cb = AddressOf Me.myCopyProgressRoutine
Dim iRet As Integer = CopyFileEx(lpExistingFileName:=sSource, lpNewFileName:=sTarget, lpProgressRoutine:=cb, lpData:=0, lpBool:=0, dwCopyFlags:=dwFlags)
RaiseEvent FileCopyComplete(sSource, sTarget)
If iRet = 0 Then
'---function failed
Return False
Else
Return True
End If
End Function
#End Region
Public Sub RecursiveCopy( _
ByVal sSourcePath As String, _
ByVal sTargetPath As String, _
ByVal sExt As String)
If Not sSourcePath.EndsWith("\") Then sSourcePath = sSourcePath & "\"
If Not sTargetPath.EndsWith("\") Then sTargetPath = sTargetPath & "\"
Console.Write(sSourcePath)
'---Get a list of subdirectories
Dim oDirList As ArrayList = FindAllDirectories(sSourcePath & "*.*")
'---Create each subdirectory in target path if it doesn't already exist.
For Each sDirName As String In oDirList
If Not System.IO.Directory.Exists(sTargetPath & sDirName) Then
System.IO.Directory.CreateDirectory(sTargetPath & sDirName)
End If
Next
'---Get a list of files
Dim oFileList As ArrayList = FindAllFiles(sSourcePath, "*.*")
For Each sFile As String In oFileList
Console.Write(".")
CopySpecificFile(sSourcePath & sFile, sTargetPath & sFile)
Next
Console.WriteLine()
'---Recurse subdirectories
For Each sDirName As String In oDirList
RecursiveCopy(sSourcePath & sDirName, sTargetPath & sDirName, sExt)
Next
End Sub
Public Function FindAllFiles( _
ByVal sPath As String, _
ByVal sExt As String) As ArrayList
Dim wfd As New FileInfo.WIN32_FIND_DATA
Dim hFile As Long
Dim oList As New ArrayList
Dim sSearchPath As String = sPath
If sSearchPath.EndsWith("\") Then
sSearchPath = sSearchPath & sExt
Else
sSearchPath = sSearchPath & "\" & sExt
End If
'Start searching for files in
'sSource by obtaining a file
'handle to the first file matching
'the filespec passed
hFile = FindFirstFile(sSearchPath, wfd)
If hFile <> INVALID_HANDLE_VALUE Then
'must have at least one, so ...
Do
If (wfd.dwFileAttributes And IO.FileAttributes.Directory) = 0 Then
Dim oWFD As New FileInfo.WIN32_FIND_DATA
CopyStructure(From:=wfd, To:=oWFD)
oList.Add(oWFD)
End If
Loop Until FindNextFile(hFile, wfd) = 0
End If
'Close the search handle
Call FindClose(hFile)
Return oList
End Function
Public Function FindAllDirectories( _
ByVal sPath As String) As ArrayList
Dim sSearchPath As String = sPath
If sSearchPath.EndsWith("\") Then
sSearchPath = sSearchPath & "*.*"
Else
sSearchPath = sSearchPath & "\*.*"
End If
Dim wfd As New FileInfo.WIN32_FIND_DATA
Dim hFile As Long
Dim oList As New ArrayList
'Start searching for files in
'sSource by obtaining a file
'handle to the first file matching
'the filespec passed
hFile = FindFirstFile(sSearchPath, wfd)
If hFile <> INVALID_HANDLE_VALUE Then
'must have at least one, so ...
Do
If (wfd.dwFileAttributes And IO.FileAttributes.Directory) > 0 _
AndAlso wfd.cFileName <> "." _
AndAlso wfd.cFileName <> ".." Then
Dim oWFD As New FileInfo.WIN32_FIND_DATA
CopyStructure(From:=wfd, To:=oWFD)
oList.Add(oWFD)
End If
Loop Until FindNextFile(hFile, wfd) = 0
End If
'Close the search handle
Call FindClose(hFile)
Return oList
End Function
Private Sub CopyStructure( _
ByVal [From] As FileInfo.WIN32_FIND_DATA, _
ByRef [To] As FileInfo.WIN32_FIND_DATA)
With [To]
.cAlternate = [From].cAlternate
.cFileName = [From].cFileName
.dwFileAttributes = [From].dwFileAttributes
.dwReserved0 = [From].dwReserved0
.dwReserved1 = [From].dwReserved1
.ftCreationTime = [From].ftCreationTime
.ftLastAccessTime = [From].ftLastAccessTime
.ftLastWriteTime = [From].ftLastWriteTime
.nFileSizeHigh = [From].nFileSizeHigh
.nFileSizeLow = [From].nFileSizeLow
End With
End Sub
Public Sub CopySpecificFile(ByVal sSource As String, ByVal sTarget As String)
Dim bsuccess As Boolean = CopyFileWithProgress(sSource, sTarget, bFailIfExists:=False)
'Dim bSuccess As Boolean = CopyFile(sSource, sTarget, 0)
If Not bSuccess Then
Dim iErr As Integer = Err.LastDllError
Dim sError As String = New System.ComponentModel.Win32Exception(iErr).Message
Throw New util.FileCopyException("Unable to copy file " & sSource & ". Error Text: " & sError)
End If
End Sub
Public Function FileExists(ByVal sFullName As String) As Boolean
Dim bFileFound As Boolean = False
Dim wfd As New FileInfo.WIN32_FIND_DATA
Dim hFile As Long
Dim oList As New ArrayList
'Start searching for files in
'sSource by obtaining a file
'handle to the first file matching
'the filespec passed
hFile = FindFirstFile(sFullName, wfd)
If hFile <> INVALID_HANDLE_VALUE Then
bFileFound = True
End If
'Close the search handle
Call FindClose(hFile)
Return bFileFound
End Function
Public Function GetDirectoryInfo( _
ByVal sDirName As String, _
ByRef oDirInfo As FileInfo.WIN32_FIND_DATA) As Boolean
Dim bFileFound As Boolean = False
Dim wfd As New FileInfo.WIN32_FIND_DATA
Dim hFile As Long
Dim oList As New ArrayList
'Start searching for files in
'sSource by obtaining a file
'handle to the first file matching
'the filespec passed
hFile = FindFirstFile(sDirName, wfd)
If hFile <> INVALID_HANDLE_VALUE Then
bFileFound = True
CopyStructure(wfd, oDirInfo)
End If
'Close the search handle
Call FindClose(hFile)
Return bFileFound
End Function
Public Function GetFileInfo( _
ByVal sFileName As String, _
ByRef oFileInfo As FileInfo.WIN32_FIND_DATA) As Boolean
Dim bFileFound As Boolean = False
Dim wfd As New FileInfo.WIN32_FIND_DATA
Dim hFile As Long
Dim oList As New ArrayList
'Start searching for files in
'sSource by obtaining a file
'handle to the first file matching
'the filespec passed
hFile = FindFirstFile(sFileName, wfd)
If hFile <> INVALID_HANDLE_VALUE Then
bFileFound = True
CopyStructure(wfd, oFileInfo)
End If
'Close the search handle
Call FindClose(hFile)
Return bFileFound
End Function
End Class
End Namespace