|
|
![]() |
|
||
|
|
|||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Visual Basic File API Routines GetFileVersionInfo: File Search and File Property Info |
|
| Posted: | Sunday October 3, 1999 |
| Updated: | Monday December 26, 2011 |
| Applies to: | VB4-32, VB5, VB6 |
| Developed with: | VB6, Windows NT4 |
| OS restrictions: | None |
| Author: | VBnet - Randy Birch |
|
Related: |
FindFirstFile: Recursive File Search for Single or Multiple File Types (minimal code) FindFirstFile: Recursive File Search Including/Excluding Single or Multiple File Types (minimal code) FindFirstFile: Recursive Search for Folders Using a Folder Mask (minimal code) GetLogicalDriveStrings: An API 'DriveExists' Routine FindFirstFile: An API 'FileExists' Routine FindFirstFile: An API 'FolderExists' Routine FindFirstFile: Comparison of FindFirstFile and SearchTreeForFile FindFirstFile: Save a Recursive Search of All Drives to Disk FindFirstFile: Save a Recursive Search of Specified Drives to Disk |
| Prerequisites |
| None. |
|
|
This page shows how several VBnet routines can be combined, virtually unchanged, to provide a custom the solution - to obtain specific file information typically displayed on a file's property page. Here the principles used in the GetIECypherVersion method from Obtaining Internet Explorer's Version, as well as the ODBCGetFileVersion function in Retrieving MS ODBC Driver Information, are combined with the basic FindFirstFile search routine methods listed above to perform the required task. The references mentioned detail the caveats and considerations for each, so please see these pages for additional info, and a discussion of the principles used. If you need a complete file property solution, take a look at Karl Peterson's site for FileInfo.zip, from which the two file information routines used here were based. Not shown in the demo are the returned file count and total file size tabulated in the recursive search routines. |
| BAS Module Code |
| None. |
|
|
| Form Code |
|
|
| Create a new project with a form containing a listview (ListView1), a text box (Text1), a combo box (Combo1), a check box (Check1) and a command button (Command1). Label as desired and add the following code: |
|
|
Option Explicit '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' Copyright ©1996-2011 VBnet/Randy Birch, All Rights Reserved. ' Some pages may also contain other copyrights by the author. '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' Distribution: You can freely use this code in your own ' applications, but you may not reproduce ' or publish this code on any web site, ' online service, or distribute as source ' on any media without express permission. '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' Private Type VS_FIXEDFILEINFO
dwSignature As Long
dwStrucVersion As Long 'e.g. 0x00000042 = "0.42"
dwFileVersionMS As Long 'e.g. 0x00030075 = "3.75"
dwFileVersionLS As Long 'e.g. 0x00000031 = "0.31"
dwProductVersionMS As Long 'e.g. 0x00030010 = "3.10"
dwProductVersionLS As Long 'e.g. 0x00000031 = "0.31"
dwFileFlagsMask As Long 'e.g. 0x3F for version "0.42"
dwFileFlags As Long 'e.g. VFF_DEBUG Or VFF_PRERELEASE
dwFileOS As Long 'e.g. VOS_DOS_WINDOWS16
dwFileType As Long 'e.g. VFT_DRIVER
dwFileSubtype As Long 'e.g. VFT2_DRV_KEYBOARD
dwFileDateMS As Long 'e.g. 0
dwFileDateLS As Long 'e.g. 0
End Type
Private Declare Function GetFileVersionInfoSize Lib "version.dll" _
Alias "GetFileVersionInfoSizeA" _
(ByVal lptstrFilename As String, _
lpdwHandle As Long) As Long
Private Declare Function GetFileVersionInfo Lib "version.dll" _
Alias "GetFileVersionInfoA" _
(ByVal lptstrFilename As String, _
ByVal dwHandle As Long, _
ByVal dwLen As Long, _
lpData As Any) As Long
Private Declare Function VerQueryValue Lib "version.dll" _
Alias "VerQueryValueA" _
(pBlock As Any, _
ByVal lpSubBlock As String, _
lplpBuffer As Any, nVerSize As Long) As Long
Private Declare Sub CopyMemory Lib "kernel32" _
Alias "RtlMoveMemory" _
(Destination As Any, _
Source As Any, _
ByVal Length As Long)
Private Const MAXDWORD As Long = &HFFFFFFFF
Private Const MAX_PATH As Long = 260
Private Const INVALID_HANDLE_VALUE As Long = -1
Private Const FILE_ATTRIBUTE_DIRECTORY As Long = &H10
Private Type FILETIME
dwLowDateTime As Long
dwHighDateTime As Long
End Type
Private Type WIN32_FIND_DATA
dwFileAttributes As Long
ftCreationTime As FILETIME
ftLastAccessTime As FILETIME
ftLastWriteTime As FILETIME
nFileSizeHigh As Long
nFileSizeLow As Long
dwReserved0 As Long
dwReserved1 As Long
cFileName As String * MAX_PATH
cAlternate As String * 14
End Type
Private Type FILE_PARAMS 'my custom type for passing info
bRecurse As Boolean 'var not used in this demo
bList As Boolean
bFound As Boolean 'var not used in this demo
sFileRoot As String
sFileNameExt As String
sResult As String 'var not used in this demo
nFileCount As Long 'var not used in this demo
nFileSize As Double 'var not used in this demo
End Type
Private Declare Function FindClose Lib "kernel32" _
(ByVal hFindFile As Long) As Long
Private Declare Function FindFirstFile Lib "kernel32" _
Alias "FindFirstFileA" _
(ByVal lpFileName As String, _
lpFindFileData As WIN32_FIND_DATA) As Long
Private Declare Function FindNextFile Lib "kernel32" _
Alias "FindNextFileA" _
(ByVal hFindFile As Long, _
lpFindFileData As WIN32_FIND_DATA) As Long
Private Declare Function lstrcpyA Lib "kernel32" _
(ByVal RetVal As String, ByVal Ptr As Long) As Long
Private Declare Function lstrlenA Lib "kernel32" _
(ByVal Ptr As Any) As Long
Private Sub Form_Load()
With Combo1
.AddItem "*.*"
.AddItem "*.dll"
.AddItem "*.exe"
.AddItem "*.ini"
.AddItem "*.ocx"
.AddItem "*.vxd"
.ListIndex = 2
End With
End Sub
Private Sub Command1_Click()
Dim FP As FILE_PARAMS
ListView1.ListItems.Clear
With FP
.sFileRoot = Text1.Text
.sFileNameExt = Combo1.Text
.bRecurse = Check1.Value = 1
.bList = True
End With
Call SearchForFiles(FP)
End Sub
Private Function SearchForFiles(FP As FILE_PARAMS) As Double
Dim WFD As WIN32_FIND_DATA
Dim hFile As Long
Dim nSize As Long
Dim sPath As String
Dim sRoot As String
Dim sTmp As String
sRoot = QualifyPath(FP.sFileRoot)
sPath = sRoot & "*.*"
'obtain handle to the first match
hFile = FindFirstFile(sPath, WFD)
'if valid ...
If hFile <> INVALID_HANDLE_VALUE Then
'This is where the method obtains the file
'list and data for the folder passed.
'
'GetFileInformation function returns the size,
'in bytes, of the files found matching the
'filespec in the passed folder, so its
'assigned to nSize. It is not directly assigned
'to FP.nFileSize because nSize is incremented
'below if a recursive search was specified.
nSize = GetFileInformation(FP)
FP.nFileSize = nSize
Do
'if the returned item is a folder...
If (WFD.dwFileAttributes And FILE_ATTRIBUTE_DIRECTORY) Then
'..and the Recurse flag was specified
If FP.bRecurse Then
'remove trailing nulls
sTmp = TrimNull(WFD.cFileName)
'and if the folder is not the default
'self and parent folders...
If sTmp <> "." And sTmp <> ".." Then
'..then the item is a real folder, which
'may contain other sub folders, so assign
'the new folder name to FP.sFileRoot and
'recursively call this function again with
'the amended information.
'
'Since nSize is a local variable whose value
'is both set above as well as returned as the
'function call value, nSize needs to be added
'to previous calls in order to maintain accuracy.
'
'However, because the nFileSize member of
'FILE_PARAMS is passed back and forth through
'the calls, nSize is simply assigned to it
'after the recursive call finishes.
FP.sFileRoot = sRoot & sTmp
nSize = nSize + SearchForFiles(FP)
FP.nFileSize = nSize
End If
End If
End If
'continue looping until FindNextFile returns
'0 (no more matches)
Loop While FindNextFile(hFile, WFD)
'close the find handle
hFile = FindClose(hFile)
End If
'because this routine is recursive, return
'the size of matching files
SearchForFiles = nSize
End Function
Private Function GetFileInformation(FP As FILE_PARAMS) As Long
Dim WFD As WIN32_FIND_DATA
Dim hFile As Long
Dim nSize As Long
Dim sPath As String
Dim sRoot As String
Dim sTmp As String
Dim itmx As ListItem
'FP.sFileRoot (assigned to sRoot) contains
'the path to search.
'
'FP.sFileNameExt (assigned to sPath) contains
'the full path and filespec.
sRoot = QualifyPath(FP.sFileRoot)
sPath = sRoot & FP.sFileNameExt
'obtain handle to the first filespec match
hFile = FindFirstFile(sPath, WFD)
'if valid ...
If hFile <> INVALID_HANDLE_VALUE Then
Do
'remove trailing nulls
sTmp = TrimNull(WFD.cFileName)
'Even though this routine uses filespecs,
'*.* is still valid and will cause the search
'to return folders as well as files, so a
'check against folders is still required.
If Not (WFD.dwFileAttributes And FILE_ATTRIBUTE_DIRECTORY) _
= FILE_ATTRIBUTE_DIRECTORY Then
'file found, so increase the file count
FP.nFileCount = FP.nFileCount + 1
'retrieve the size and assign to nSize to
'be returned at the end of this function call
nSize = nSize + (WFD.nFileSizeHigh * (MAXDWORD + 1)) + WFD.nFileSizeLow
'add to the list if the flag indicates
If FP.bList Then
'got the data, so add it to the listview
Set itmx = ListView1.ListItems.Add(, , LCase$(sTmp))
itmx.SubItems(1) = GetFileVersion(sRoot & sTmp)
itmx.SubItems(3) = GetFileSizeStr(WFD.nFileSizeHigh + WFD.nFileSizeLow)
itmx.SubItems(2) = GetFileDescription(sRoot & sTmp)
itmx.SubItems(4) = LCase$(sRoot)
End If
End If
Loop While FindNextFile(hFile, WFD)
'close the handle
hFile = FindClose(hFile)
End If
'return the size of files found
GetFileInformation = nSize
End Function
Private Function GetFileSizeStr(fsize As Long) As String
GetFileSizeStr = Format$((fsize), "###,###,###") '& " kb"
End Function
Private Function QualifyPath(sPath As String) As String
'assures that a passed path ends in a slash
If Right$(sPath, 1) <> "\" Then
QualifyPath = sPath & "\"
Else
QualifyPath = sPath
End If
End Function
Public Function TrimNull(startstr As String) As String
'returns the string up to the first
'null, if present, or the passed string
Dim pos As Integer
pos = InStr(startstr, Chr$(0))
If pos Then
TrimNull = Left$(startstr, pos - 1)
Exit Function
End If
TrimNull = startstr
End Function
Private Function HiWord(dw As Long) As Long
If dw And &H80000000 Then
HiWord = (dw \ 65535) - 1
Else
HiWord = dw \ 65535
End If
End Function
Private Function LoWord(dw As Long) As Long
If dw And &H8000& Then
LoWord = &H8000& Or (dw And &H7FFF&)
Else
LoWord = dw And &HFFFF&
End If
End Function
Private Function GetFileDescription(sSourceFile As String) As String
Dim FI As VS_FIXEDFILEINFO
Dim sBuffer() As Byte
Dim nBufferSize As Long
Dim lpBuffer As Long
Dim nVerSize As Long
Dim nUnused As Long
Dim tmpVer As String
Dim sBlock As String
If Len(sSourceFile) > 0 Then
'set file that has the encryption level
'info and call to get required size
nBufferSize = GetFileVersionInfoSize(sSourceFile, nUnused)
ReDim sBuffer(nBufferSize)
If nBufferSize > 0 Then
'get the version info
Call GetFileVersionInfo(sSourceFile, 0&, nBufferSize, sBuffer(0))
Call VerQueryValue(sBuffer(0), "\", lpBuffer, nVerSize)
Call CopyMemory(FI, ByVal lpBuffer, Len(FI))
If VerQueryValue(sBuffer(0), "\VarFileInfo\Translation", lpBuffer, nVerSize) Then
If nVerSize Then
tmpVer = GetPointerToString(lpBuffer, nVerSize)
tmpVer = Right("0" & Hex(Asc(Mid(tmpVer, 2, 1))), 2) & _
Right("0" & Hex(Asc(Mid(tmpVer, 1, 1))), 2) & _
Right("0" & Hex(Asc(Mid(tmpVer, 4, 1))), 2) & _
Right("0" & Hex(Asc(Mid(tmpVer, 3, 1))), 2)
sBlock = "\StringFileInfo\" & tmpVer & "\FileDescription"
'Get predefined version resources
If VerQueryValue(sBuffer(0), sBlock, lpBuffer, nVerSize) Then
If nVerSize Then
'get the file description
GetFileDescription = GetStrFromPtrA(lpBuffer)
End If 'If nVerSize
End If 'If VerQueryValue
End If 'If nVerSize
End If 'If VerQueryValue
End If 'If nBufferSize
End If 'If sSourcefile
End Function
Private Function GetStrFromPtrA(ByVal lpszA As Long) As String
GetStrFromPtrA = String$(lstrlenA(ByVal lpszA), 0)
Call lstrcpyA(ByVal GetStrFromPtrA, ByVal lpszA)
End Function
Private Function GetPointerToString(lpString As Long, nBytes As Long) As String
Dim Buffer As String
If nBytes Then
Buffer = Space$(nBytes)
CopyMemory ByVal Buffer, ByVal lpString, nBytes
GetPointerToString = Buffer
End If
End Function
Private Function GetFileVersion(sDriverFile As String) As String
Dim FI As VS_FIXEDFILEINFO
Dim sBuffer() As Byte
Dim nBufferSize As Long
Dim lpBuffer As Long
Dim nVerSize As Long
Dim nUnused As Long
Dim tmpVer As String
'GetFileVersionInfoSize determines whether the operating
'system can obtain version information about a specified
'file. If version information is available, it returns
'the size in bytes of that information. As with other
'file installation functions, GetFileVersionInfoSize
'works only with Win32 file images.
'
'A empty variable must be passed as the second
'parameter, which the call returns 0 in.
nBufferSize = GetFileVersionInfoSize(sDriverFile, nUnused)
If nBufferSize > 0 Then
'create a buffer to receive file-version
'(FI) information.
ReDim sBuffer(nBufferSize)
Call GetFileVersionInfo(sDriverFile, 0&, nBufferSize, sBuffer(0))
'VerQueryValue function returns selected version info
'from the specified version-information resource. Grab
'the file info and copy it into the VS_FIXEDFILEINFO structure.
Call VerQueryValue(sBuffer(0), "\", lpBuffer, nVerSize)
Call CopyMemory(FI, ByVal lpBuffer, Len(FI))
'extract the file version from the FI structure
tmpVer = Format$(HiWord(FI.dwFileVersionMS)) & "." & _
Format$(LoWord(FI.dwFileVersionMS), "00") & "."
If FI.dwFileVersionLS > 0 Then
tmpVer = tmpVer & Format$(HiWord(FI.dwFileVersionLS), "00") & "." & _
Format$(LoWord(FI.dwFileVersionLS), "00")
Else
tmpVer = tmpVer & Format$(FI.dwFileVersionLS, "0000")
End If
End If
GetFileVersion = tmpVer
End Function
|
| Comments |
| Before running,
correct hard-coded paths in the
Command1 event to reflects your system. Note: While it may be convenient to utilize VB's built-in constants in place of the FILE_ATTRIBUTE_* API values, care must be taken. There is a difference between related constant values that may cause unexpected performance at some point. For example, the constant 'vbNormal' is defined as having a value of 0, whereas the API FILE_ATTRIBUTE_NORMAL has a value of &H80 (decimal 128). |
|
|
|
|
|
|||||
|
|||||
|
|
|||||
|
Copyright ©1996-2011 VBnet and Randy Birch. All Rights Reserved. |
![]() |