インターネットからダウンロードしたファイルを実行しようとすると「これ、ネットからダウンロードしたファイルだけど、ホントに実行しちゃっていいの?ねぇ?」という確認メッセージが表示されます。いつも「うっせぇな、んなこたぁ知ってるよ!いいから実行しろよ!」みたいな感じで[OK]ボタンをクリックするのですが、はて、Windowsはどうしてそのファイルがダウンロードされたものと知っているのでしょうか。
そのうち、Windows系のMVPさんにでも聞いてみようかと思っていましたが、思わぬことからその答がわかりました。秘密はNTFSのストリームだったんですね。実に興味深い技術です。いろいろな使い道が考えられますね。とりあえず、今わかっている事だけ書いておきましょう。以下はVBAのコードですが、たぶんVB6でも動くでしょう。
Sub Sample1()
Dim FSO
Set FSO = CreateObject("Scripting.FileSystemObject")
FSO.CreateTextFile "E:\Sample.txt:myData"
With FSO.GetFile("E:\Sample.txt:myData").OpenAsTextStream(8)
.Write "このファイルは田中が作りました" & vbCrLf
.Write Now()
.Close
End With
Set FSO = Nothing
End Sub
Sub Sample2()
Dim FSO, buf As String
Set FSO = CreateObject("Scripting.FileSystemObject")
With FSO.GetFile("E:\Sample.txt:myData").OpenAsTextStream
buf = .ReadAll
MsgBox buf
.Close
End With
Set FSO = Nothing
End Sub
テキストファイルに任意の情報を保存できるのですから驚きです。もちろん、このテキストファイルをエディタで開いてもストリームの文字列は表示されません。ブック(xls)のストリームに何らかの情報を書き込んでおく…という使い方もありそうです。
Public Const MAX_PATH = 260
Public Type WIN32_STREAM_ID
dwStreamID As Long
dwStreamAttributes As Long
dwStreamSizeLow As Long
dwStreamSizeHigh As Long
dwStreamNameSize As Long
End Type
Public Const GENERIC_READ = &H80000000
Public Const INVALID_HANDLE_VALUE = -1
Public Const FILE_SHARE_READ = &H1
Public Const OPEN_EXISTING = 3
Public Const FILE_FLAG_BACKUP_SEMANTICS = &H2000000
Public Declare Function CreateFile Lib "kernel32" Alias _
"CreateFileA" (ByVal lpFileName As String, _
ByVal dwDesiredAccess As Long, _
ByVal dwShareMode As Long, _
ByVal lpSecurityAttributes As Long, _
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 Declare Function BackupRead Lib "kernel32" (ByVal hFile As Long, _
lpBuffer As Any, _
ByVal nNumberOfBytesToRead As Long, _
lpNumberOfBytesRead As Long, _
ByVal bAbort As Long, _
ByVal bProcessSecurity As Long, _
lpContext As Any) As Long
Public Declare Function BackupSeek Lib "kernel32" (ByVal hFile As Long, _
ByVal dwLowBytesToSeek As Long, _
ByVal dwHighBytesToSeek As Long, _
lpdwLowByteSeeked As Long, _
lpdwHighByteSeeked As Long, _
lpContext As Long) As Long
Public Declare Function DeleteFile Lib "kernel32" Alias _
"DeleteFileA" (ByVal lpFileName As String) As Long
Sub Sample3()
MsgBox EnumStreams("E:\Sample.txt")
End Sub
Public Function EnumStreams(szFile) As String
Dim arr() As String
Dim ind As Long: ind = 0
Dim hFile As Long
hFile = CreateFile(szFile, GENERIC_READ, FILE_SHARE_READ, 0, _
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0)
If hFile = INVALID_HANDLE_VALUE Then
Exit Function
End If
Dim lpContext As Long: lpContext = 0
Dim sid As WIN32_STREAM_ID
Dim dwRead As Long
Dim wszStreamName(1 To MAX_PATH * 2) As Byte
Dim bContinue As Boolean: bContinue = True
Do While (bContinue)
sid.dwStreamID = 0
sid.dwStreamAttributes = 0
sid.dwStreamSizeLow = 0
sid.dwStreamSizeHigh = 0
sid.dwStreamNameSize = 0
Dim dwStreamHeaderSize As Long
dwStreamHeaderSize = LenB(sid) + sid.dwStreamNameSize
Dim k
For k = 1 To MAX_PATH * 2
wszStreamName(k) = 0
Next
bContinue = BackupRead(hFile, sid, dwStreamHeaderSize, dwRead, _
False, False, lpContext)
If dwRead = 0 Then Exit Do
If dwRead <> dwStreamHeaderSize Then Exit Do
Call BackupRead(hFile, wszStreamName(1), sid.dwStreamNameSize, _
dwRead, False, False, lpContext)
If dwRead <> sid.dwStreamNameSize Then Exit Do
If dwRead > 0 Then
Dim StreamName As String
StreamName = Left(wszStreamName, InStr(wszStreamName, vbNullChar) - 1)
ReDim Preserve arr(ind)
arr(ind) = StreamName
ind = ind + 1
End If
Dim dw1 As Long, dw2 As Long
Call BackupSeek(hFile, sid.dwStreamSizeLow, sid.dwStreamSizeHigh, _
dw1, dw2, lpContext)
Loop
Call CloseHandle(hFile)
EnumStreams = Join(arr, vbCrLf)
End Function
上記のコードは、吉岡 照雄さんの「EnumStreams.XLS」(EXCEL VBA経由でNTFSストリームを列挙/削除するVBScript)を参考(というか流用)させていただきました。素晴らしいです。ありがとうございます。勝手に使ってすいません。
吉岡 照雄さんの作品ページはこちら→http://www.vector.co.jp/vpack/browse/person/an010222.html