WinAPIを介した代替データストリームの操作

前の記事で、代替ストリームとは何か、どのように使用できるかについて説明しました。 コマンドラインを使用してそれらを操作する例がありましたが、標準のWinAPIツールを使用してソフトウェアで同じことを行うことができます。

以下は、この問題に関連する記事の一部の無料翻訳です。



タイプチェックFS



最初のステップは、ファイルシステムが代替ストリームをサポートしているかどうかを確認することです。それ以外の場合、それらを操作しようとする意味はありません。

char szVolName[MAX_PATH], szFSName[MAX_PATH];

DWORD dwSN, dwMaxLen, dwVolFlags;

::GetVolumeInformation("C:\\", szVolName, MAX_PATH, &dwSN,

&dwMaxLen, &dwVolFlags, szFSName, MAX_PATH);



if (dwVolFlags & FILE_NAMED_STREAMS)

{

// File system supports named streams

}

else

{

// Named streams are not supported

}




* This source code was highlighted with Source Code Highlighter .






代替ストリームの作成



通常のファイルと同様に、CreateFile関数を使用して代替ストリームを作成できます。

HANDLE hFile = ::CreateFile( "file.dat:alt" , ...





ファイルが存在しなかった場合、メインストリームの指定された名前を持つ長さゼロのファイルが作成されます。



代替ストリームの削除



代替ストリームを削除することは、通常のファイルを削除することほど難しくありません。 ストリームを完全にサポートするDeleteFile関数は、これに適しています。

::DeleteFile( "file.dat:alt" );





標準ストリームを削除すると、その代替ストリームもすべて削除されます。



代替ストリームをコピー



代替ストリームをコピーするには、Win32 APIのCopyFile / CopyFileEx関数を使用できます。 ただし、これらの関数は予期しない結果を生じる可能性があります。 コピーには2つのオプションがあります。



標準ストリームから標準ストリームへ:通常のファイル操作。ファイルとともにすべての名前付きストリームがコピーされます。 ファイルが存在する場合は、置き換えられます。



名前付きストリームから標準ストリームへ:この場合、選択されたストリームのみがコピーされます。 宛先ファイルが存在する場合、すべてのストリーム(存在する場合)とともに削除され、新しいファイルはコピーが行われる標準ストリームのみで作成されます。



単純な読み取り/書き込みサイクルでコピーすると、期待される結果が得られます。

HANDLE hInFile = ::CreateFile(szFromStream, GENERIC_READ, FILE_SHARE_READ, NULL,

OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);

HANDLE hOutFile = ::CreateFile(szToStream, GENERIC_WRITE, FILE_SHARE_READ, NULL,

CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);



BYTE buf[64*1024];

DWORD dwBytesRead, dwBytesWritten;



do

{

::ReadFile(hInFile, buf, sizeof (buf), &dwBytesRead, NULL);

if (dwBytesRead) ::WriteFile(hOutFile, buf, dwBytesRead, &dwBytesWritten, NULL);

}

while (dwBytesRead == sizeof (buf));



::CloseHandle(hInFile);

::CloseHandle(hOutFile);




* This source code was highlighted with Source Code Highlighter .






スレッドのリスト



ネイティブAPIのいくつかの機能を使用して、スレッドのリストを取得できます。 以下は、ファイルからストリームのリストを取得するためのコードです。

// Open a file and obtain stream information



BYTE InfoBlock[64 * 1024]; // Buffer must be large enough

PFILE_STREAM_INFORMATION pStreamInfo = (PFILE_STREAM_INFORMATION)InfoBlock;

IO_STATUS_BLOCK ioStatus;



HANDLE hFile = ::CreateFile(szPath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE,

NULL, OPEN_EXISTING, 0, NULL);

NtQueryInformationFile(hFile, &ioStatus, InfoBlock,

sizeof (InfoBlock), FileStreamInformation);

::CloseHandle(hFile);




* This source code was highlighted with Source Code Highlighter .






ディレクトリフローのリストを取得する状況はより複雑です。

// Open a directory and obtain stream information



// Obtain backup privilege in case we don't have it

HANDLE hToken;

TOKEN_PRIVILEGES tp;

::OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken);

::LookupPrivilegeValue(NULL, SE_BACKUP_NAME, &tp.Privileges[0].Luid);

tp.PrivilegeCount = 1;

tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

::AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof (TOKEN_PRIVILEGES), NULL, NULL);

::CloseHandle(hToken);



HANDLE hFile = ::CreateFile(szPath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE,

NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);



BYTE InfoBlock[64 * 1024]; // Buffer must be large enough

PFILE_STREAM_INFORMATION pStreamInfo = (PFILE_STREAM_INFORMATION)InfoBlock;

IO_STATUS_BLOCK ioStatus;



pStreamInfo->StreamNameLength = 0; // Zero in this field means empty info block

NtQueryInformationFile(hFile, &ioStatus, InfoBlock,

sizeof (InfoBlock), FileStreamInformation);

::CloseHandle(hFile);




* This source code was highlighted with Source Code Highlighter .






これで、ファイル/ディレクトリに関する情報を受け取ったので、リスト自体を表示できます。

WCHAR wszStreamName[MAX_PATH];



for (;;)

{

// Check if stream info block is empty (directory may have no stream)

if (pStreamInfo->StreamNameLength == 0) break ; // No stream found



// Get null-terminated stream name

memcpy(wszStreamName, pStreamInfo->StreamName, pStreamInfo->StreamNameLength);

wszStreamName[pStreamInfo->StreamNameLength / sizeof (WCHAR)] = L '\0' ;



print( "%S" , wszStreamName);



if (pStreamInfo->NextEntryOffset == 0) break ; // No more stream records

pStreamInfo = (PFILE_STREAM_INFORMATION)

((LPBYTE)pStreamInfo + pStreamInfo->NextEntryOffset); // Next stream record

}




* This source code was highlighted with Source Code Highlighter .








VC ++コンソールの例




All Articles