Win32 File-ID APIs
Windows Vista/2008からは、File IDによるファイル処理が標準で実装されている。これは、NTFSとかでGetFileInformationByHandleを使って取得できるBY_HANDLE_FILE_INFORMATION::nFileIndex*と同じもの。ファイルIDがわかっていれば、IDとドライブ識別のためのハンドルだけでファイルが開けるし、当然、ファイルIDからファイル名が取得できる。
そして、このAPIは、Windows XP/2003でも、Win32 FileID API Libraryでダウンロードできるfileextd.libをリンクすれば利用できる。
なので、このAPIはXP以降のOSならば使えると考えてよい。以下は、適当に書いたサンプル。コマンドラインの第1引数に与えられたファイルをCreateFileで開いた後に、ハンドルから元のファイル名を復元してみる。バッファのサイズは本来なら、ちゃんと確保しないといけないと思われるが手を抜いている。
興味深いのは、同時にファイルハンドルからファイル名を取得するAPIも提供されていること。今まで、これはやりたくてもなかなかできなかった。この機能を使うには、最初にkernel32からエクスポートされているGetFileInformationByHandleEx関数を取得してみて、それが取得できたら(Vistaなら取得できる)そっちを使って、そうでなければ、fileextd.libによって提供されている実装を利用する。でも、本当は、常にfileextd.libの実装を使うようにしても今のところは動く。また、fileextd.hは、Windows SDKの定義と衝突するので、あえてインクルードしない。
#include <windows.h> //#include <fileextd.h> #include <stdio.h> #include <locale.h> typedef BOOL (WINAPI *GetFileInformationByHandleExFunc)( HANDLE, FILE_INFO_BY_HANDLE_CLASS, LPVOID, DWORD); int wmain(int argc, wchar_t* argv[]) { setlocale(LC_ALL, ""); HANDLE hFile = CreateFileW( argv[1], GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); GetFileInformationByHandleExFunc gfibhx = (GetFileInformationByHandleExFunc)GetProcAddress( GetModuleHandle("kernel32"), "GetFileInformationByHandleEx"); if(!gfibhx) gfibhx = GetFileInformationByHandleEx; char buf[2048]; if(gfibhx(hFile, FileNameInfo, buf, sizeof(buf))) { FILE_NAME_INFO& info = *(FILE_NAME_INFO*)buf; info.FileName[info.FileNameLength / 2] = 0; wprintf(L"%s\n", info.FileName); } else { printf("err=%u\n", GetLastError()); } const DWORD PATHSIZE = 2047; WCHAR path[PATHSIZE + 1]; DWORD ret = GetFinalPathNameByHandleW(hFile, path, PATHSIZE, FILE_NAME_NORMALIZED); if(ret > PATHSIZE || ret == 0) { printf("err=%u\n", GetLastError()); } else { wprintf(L"%s\n", path); } CloseHandle(hFile); return 0; }
動かしてみた結果。
C:\Users\dummy\Desktop\test>test test.exe \Users\dummy\Desktop\test\test.exe \\?\C:\Users\dummy\Desktop\test\test.exe
取得されるファイル名にはドライブレターがないが、GetFinalPathNameByHandleだと、普通にドライブレター込みのパスを取得できる。これはどうやってドライブレターを取得すればいいのだろうか・・・。