インターネットからダウンロードしたファイルを実行しようとすると「これ、ネットからダウンロードしたファイルだけど、ホントに実行しちゃっていいの?ねぇ?」という確認メッセージが表示されます。いつも「うっせぇな、んなこたぁ知ってるよ!いいから実行しろよ!」みたいな感じで[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