GetFileInformationByHandleEx can provide some good information about a file from its handle, but it is only available for Window Vista and beyond. If you still need to support Window XP, then you can try using a static library called fileextd.lib that will provide this function.
The only problem is there seems to be some incompatibility problems as described in this post. One of the posters recommends creating a separate DLL as a way to get around the problem, but this seems like kind of a hassle.
I have created this function below called GetFileInfoByHandleEx that will work from Windows XP to Windows 7(and probably Windows 2000 as well). It does use some undocumented functions so I need to add a disclaimer that they could change or disappear some day, but I don’t think it is too likely.
#include <winternl.h>
// New FILE_INFO_BY_HANDLE_CLASS value for Windows 7
#ifndef FileRemoteProtocolInfo
#define FileRemoteProtocolInfo 13
#endif
typedef NTSTATUS (WINAPI *NTQUERYINFORMATIONFILE_FUNC)(HANDLE, PIO_STATUS_BLOCK,
PVOID, ULONG, FILE_INFORMATION_CLASS);
typedef NTSTATUS (WINAPI *NTQUERYDIRECTORYFILE_FUNC)(HANDLE, HANDLE, PIO_APC_ROUTINE,
PVOID, PIO_STATUS_BLOCK, PVOID , ULONG, FILE_INFORMATION_CLASS,
BOOLEAN, PUNICODE_STRING, BOOLEAN);
typedef ULONG (WINAPI *RTLNTSTATUSTODOSERROR_FUNC)(NTSTATUS);
typedef NTSTATUS (WINAPI *NTWAITFORSINGLEOBJECT_FUNC)(HANDLE, BOOLEAN, PLARGE_INTEGER);
//
// GetFileInformationByHandleEx
//
BOOL GetFileInfoByHandleEx(HANDLE hFile, FILE_INFO_BY_HANDLE_CLASS FileInformationClass,
LPVOID lpFileInformation, DWORD dwBufferSize)
{
IO_STATUS_BLOCK ioStatusBlock;
BOOL restartScan = FALSE;
int queryInformationClass = 0;
DWORD minimumSize = 8;
BOOL useDirectory = FALSE;
//
// Load ntdll.dll
//
HMODULE ntdllModule = LoadLibrary(_T("ntdll.dll"));
if (ntdllModule == NULL)
{
return FALSE;
}
//
// Get NTDLL Function Addresses
//
NTQUERYINFORMATIONFILE_FUNC pNtQueryInformationFile =
(NTQUERYINFORMATIONFILE_FUNC)GetProcAddress(ntdllModule, "NtQueryInformationFile");
NTQUERYDIRECTORYFILE_FUNC pNtQueryDirectoryFile =
(NTQUERYDIRECTORYFILE_FUNC)GetProcAddress(ntdllModule, "NtQueryDirectoryFile");
RTLNTSTATUSTODOSERROR_FUNC pRtlNtStatusToDosError =
(RTLNTSTATUSTODOSERROR_FUNC)GetProcAddress(ntdllModule, "RtlNtStatusToDosError");
NTWAITFORSINGLEOBJECT_FUNC pNtWaitForSingleObject =
(NTWAITFORSINGLEOBJECT_FUNC)GetProcAddress(ntdllModule, "NtWaitForSingleObject");
switch (FileInformationClass)
{
case FileAttributeTagInfo:
queryInformationClass = 35; // FileObjectIdInformation
break;
case FileIdBothDirectoryInfo:
case FileIdBothDirectoryRestartInfo:
queryInformationClass = 37; // FileOleDirectoryInformation
useDirectory = TRUE;
minimumSize = 0x70;
restartScan = 0;
break;
case FileRemoteProtocolInfo:
queryInformationClass = 55; // ???
minimumSize = 0x74; // sizeof(FILE_REMOTE_PROTOCOL_INFO)
break;
case FileCompressionInfo:
queryInformationClass = 28; // FileCompressionInformation
minimumSize = sizeof(FILE_COMPRESSION_INFO);
break;
case FileBasicInfo:
queryInformationClass = 4; // FileBasicInformation
minimumSize = sizeof(FILE_BASIC_INFO);
break;
case FileStandardInfo:
queryInformationClass = 5; // FileStandardInformation
minimumSize = 0x18;
break;
case FileNameInfo:
queryInformationClass = 9; // FileNameInformation
break;
case FileStreamInfo:
queryInformationClass = 22; // FileStreamInformation
minimumSize = 0x20;
break;
default:
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if (dwBufferSize < minimumSize)
{
SetLastError(ERROR_BAD_LENGTH);
return FALSE;
}
NTSTATUS status;
if (useDirectory)
{
status = pNtQueryDirectoryFile(hFile, NULL, NULL, NULL, &ioStatusBlock,
lpFileInformation, dwBufferSize,
(FILE_INFORMATION_CLASS)queryInformationClass, FALSE, NULL, restartScan);
if (status == STATUS_PENDING)
{
status = pNtWaitForSingleObject(hFile, FALSE, NULL);
if (FAILED(status))
{
SetLastError(pRtlNtStatusToDosError(status));
return FALSE;
}
status = ioStatusBlock.Status;
}
}
else
{
status = pNtQueryInformationFile(hFile, &ioStatusBlock, lpFileInformation,
dwBufferSize, (FILE_INFORMATION_CLASS)queryInformationClass);
}
if (FAILED(status))
{
SetLastError(pRtlNtStatusToDosError(status));
return 0;
}
if ((FileInformationClass == FileStreamInfo) && (restartScan == FALSE))
{
// STATUS_END_OF_FILE = 0xC0000011
SetLastError((pRtlNtStatusToDosError(0xC0000011)));
return FALSE;
}
return TRUE;
}
Hi there,
1. wouldn’t it be better to call the real GetFileInformationByHandleEx() function on systems where the function gets exported? Old systems are very unlikely to change the undocumented APIs, new ones would use the documented one.
2. LoadLibrary is unnecessary, because ntdll.dll and kernel32.dll are loaded into each and every Win32 program *before* the entry point gets executed. Thus GetModuleHandle does a fine job and relieves you of the FreeLibrary call that should normally follow each LoadLibrary call.
#1. Yes, you could call the documented GetFileInformationByHandleEx() on Vista and beyond to avoid the undocumented API issue.
#2. Yes, you can also call GetModuleHandle() as well.
Thanks for reading!