
So I was playing around with this last night and thought it would make a good demo, since while it seems like a single API would be straightforward it turns out this is fairly complex.
Alternate Data Streams are a hidden part of regular files that you can't normally see. They can be any length, but only the first one is reported in Windows size counts, so a 1KB file could actually be hiding 1GB in an alternate stream. The most common use of alternate streams is web browsers marking files downloaded from the internet, which is how Windows knows to ask you to confirm if you really want to run something you downloaded-- this is shown in the picture above, and getting/setting that was the subject of [VB6] Code Snippet: Get/set/del file zone identifier (Run file from internet? source). There's already code samples about these streams, notably Karl Peterson's, but I still wanted to post this since it's highly simplified and uses a different API- haven't seen any others that do it with GetFileInformationByHandleEx.
Code:
Option Explicit
Public Type FileStream
StrmName As String
StrmSize As Currency
StrmAllocSize As Currency
End Type
Public Declare Function GetFileInformationByHandleEx Lib "kernel32" (ByVal hFile As Long, ByVal FileInformationClass As FILE_INFO_BY_HANDLE_CLASS, ByVal lpFileInformation As Long, ByVal dwBufferSize As Long) As Long
Public Declare Function CreateFileW Lib "kernel32" (ByVal lpFileName As Long, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, ByVal lpSecurityAttributes As Any, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As Long
Public Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Public Const GENERIC_READ As Long = &H80000000
Public Const FILE_SHARE_READ = &H1&
Public Const OPEN_EXISTING = 3&
Public Const FILE_FLAG_BACKUP_SEMANTICS = &H2000000
Public Type LARGE_INTEGER
lowpart As Long
highpart As Long
End Type
Public Type FILE_STREAM_INFO
NextEntryOffset As Long
StreamNameLength As Long
StreamSize As LARGE_INTEGER
StreamAllocationSize As LARGE_INTEGER
StreamName(0) As Integer
End Type
Public Enum FILE_INFO_BY_HANDLE_CLASS
FileBasicInfo = 0
FileStandardInfo = 1
FileNameInfo = 2
FileRenameInfo = 3
FileDispositionInfo = 4
FileAllocationInfo = 5
FileEndOfFileInfo = 6
FileStreamInfo = 7
FileCompressionInfo = 8
FileAttributeTagInfo = 9
FileIdBothDirectoryInfo = 10 ' 0xA
FileIdBothDirectoryRestartInfo = 11 ' 0xB
FileIoPriorityHintInfo = 12 ' 0xC
FileRemoteProtocolInfo = 13 ' 0xD
FileFullDirectoryInfo = 14 ' 0xE
FileFullDirectoryRestartInfo = 15 ' 0xF
FileStorageInfo = 16 ' 0x10
FileAlignmentInfo = 17 ' 0x11
FileIdInfo = 18 ' 0x12
FileIdExtdDirectoryInfo = 19 ' 0x13
FileIdExtdDirectoryRestartInfo = 20 ' 0x14
MaximumFileInfoByHandlesClass = 2
End Enum
Public Declare Sub ZeroMemory Lib "NTDLL.DLL" Alias "RtlZeroMemory" (dest As Any, ByVal numBytes As Long)
Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
Public Const INVALID_HANDLE_VALUE = -1&
Public Function LargeIntToCurrency(li As LARGE_INTEGER) As Currency
CopyMemory LargeIntToCurrency, li, LenB(li)
LargeIntToCurrency = LargeIntToCurrency * 10000
End Function
Public Function GetFileStreams(sFile As String, tStreams() As FileStream) As Long
ReDim tStreams(0)
Dim hFile As Long
hFile = CreateFileW(StrPtr(sFile), GENERIC_READ, FILE_SHARE_READ, ByVal 0&, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0)
If hFile <> INVALID_HANDLE_VALUE Then
Dim tFSI As FILE_STREAM_INFO
Dim byBuf() As Byte
Dim byName() As Byte
Dim nErr2 As Long
Dim dwNameOffset As Long
Dim dwDirOffset As Long
Dim nEntryNum As Long
ReDim byBuf((LenB(tFSI) + CLng(260 * 2 - 3)) * CLng(&H10000))
ReDim byName(0)
If GetFileInformationByHandleEx(hFile, FileStreamInfo, VarPtr(byBuf(0)), UBound(byBuf) + 1) Then
' nErr2 = GetLastError()
' Debug.Print "lasterr=0x" & Hex$(nErr2)
dwDirOffset = 0
Do While 1
ReDim Preserve tStreams(nEntryNum)
ZeroMemory tFSI, LenB(tFSI)
CopyMemory tFSI, ByVal VarPtr(byBuf(dwDirOffset)), LenB(tFSI)
Erase byName
dwNameOffset = dwDirOffset + &H18
dwNameOffset = VarPtr(byBuf(dwNameOffset))
ReDim byName(tFSI.StreamNameLength - 1)
CopyMemory byName(0), ByVal dwNameOffset, tFSI.StreamNameLength
tStreams(nEntryNum).StrmSize = LargeIntToCurrency(tFSI.StreamSize)
tStreams(nEntryNum).StrmAllocSize = LargeIntToCurrency(tFSI.StreamAllocationSize)
tStreams(nEntryNum).StrmName = CStr(byName)
nEntryNum = nEntryNum + 1
If tFSI.NextEntryOffset = 0 Then Exit Do
dwDirOffset = dwDirOffset + tFSI.NextEntryOffset
Loop
GetFileStreams = nEntryNum
End If
clhn:
CloseHandle hFile
End If
End Function
Code:
Private Sub List1_Click()
If List1.ListIndex <> -1 Then
Text2.Text = LoadFile(Text1.Text & tStrm(List1.ListIndex).StrmName)
End If
End Sub
Public Function LoadFile(ByVal FileName As String) As String
Dim hFile As Long
On Error GoTo Hell
hFile = FreeFile
Open FileName For Binary As #hFile
LoadFile = Space$(LOF(hFile))
Get #hFile, , LoadFile
Close #hFile
Exit Function
Hell:
Debug.Print "LoadFile::" & Err.Description
End Function