c - 如何获取与打开的 HANDLE 关联的名称

标签 c windows winapi

在 Win32 中获取与打开的 HANDLE 关联的文件名的最简单方法是什么?

最佳答案

我尝试了 Mehrdad 在这里发布的代码。它有效,但有局限性:

  1. 它不应该用于网络共享,因为 MountPointManager 可能会挂起很长时间。
  2. 它使用未记录的 API (IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH) 我不太喜欢那样
  3. 它不支持创建虚拟 COM 端口的 USB 设备(我的项目需要它)

我还研究了其他方法,如 GetFileInformationByHandleEx()GetFinalPathNameByHandle(),但这些方法没有用,因为它们仅返回路径 + 文件名,但没有驱动器。此外 GetFinalPathNameByHandle() 也有挂起的错误。

MSDN 中的 GetMappedFileName() 方法(由 Max 在此处发布)也非常有限:

  1. 它只适用于真实文件
  2. 文件大小不能为零字节
  3. 不支持目录、网络和 COM 端口
  4. 代码笨拙

所以我写了自己的代码。我在 Win XP 和 Win 7、8 和 10 上对其进行了测试。它运行良好。

注意:您不需要任何额外的 LIB 文件来编译此代码!

CPP 文件:

t_NtQueryObject NtQueryObject()
{
    static t_NtQueryObject f_NtQueryObject = NULL;
    if (!f_NtQueryObject)
    {
        HMODULE h_NtDll = GetModuleHandle(L"Ntdll.dll"); // Ntdll is loaded into EVERY process!
        f_NtQueryObject = (t_NtQueryObject)GetProcAddress(h_NtDll, "NtQueryObject");
    }
    return f_NtQueryObject;
}


// returns
// "\Device\HarddiskVolume3"                                (Harddisk Drive)
// "\Device\HarddiskVolume3\Temp"                           (Harddisk Directory)
// "\Device\HarddiskVolume3\Temp\transparent.jpeg"          (Harddisk File)
// "\Device\Harddisk1\DP(1)0-0+6\foto.jpg"                  (USB stick)
// "\Device\TrueCryptVolumeP\Data\Passwords.txt"            (Truecrypt Volume)
// "\Device\Floppy0\Autoexec.bat"                           (Floppy disk)
// "\Device\CdRom1\VIDEO_TS\VTS_01_0.VOB"                   (DVD drive)
// "\Device\Serial1"                                        (real COM port)
// "\Device\USBSER000"                                      (virtual COM port)
// "\Device\Mup\ComputerName\C$\Boot.ini"                   (network drive share,  Windows 7)
// "\Device\LanmanRedirector\ComputerName\C$\Boot.ini"      (network drive share,  Windwos XP)
// "\Device\LanmanRedirector\ComputerName\Shares\Dance.m3u" (network folder share, Windwos XP)
// "\Device\Afd"                                            (internet socket)
// "\Device\Console000F"                                    (unique name for any Console handle)
// "\Device\NamedPipe\Pipename"                             (named pipe)
// "\BaseNamedObjects\Objectname"                           (named mutex, named event, named semaphore)
// "\REGISTRY\MACHINE\SOFTWARE\Classes\.txt"                (HKEY_CLASSES_ROOT\.txt)
DWORD GetNtPathFromHandle(HANDLE h_File, CString* ps_NTPath)
{
    if (h_File == 0 || h_File == INVALID_HANDLE_VALUE)
        return ERROR_INVALID_HANDLE;

    // NtQueryObject() returns STATUS_INVALID_HANDLE for Console handles
    if (IsConsoleHandle(h_File))
    {
        ps_NTPath->Format(L"\\Device\\Console%04X", (DWORD)(DWORD_PTR)h_File);
        return 0;
    }

    BYTE  u8_Buffer[2000];
    DWORD u32_ReqLength = 0;

    UNICODE_STRING* pk_Info = &((OBJECT_NAME_INFORMATION*)u8_Buffer)->Name;
    pk_Info->Buffer = 0;
    pk_Info->Length = 0;

    // IMPORTANT: The return value from NtQueryObject is bullshit! (driver bug?)
    // - The function may return STATUS_NOT_SUPPORTED although it has successfully written to the buffer.
    // - The function returns STATUS_SUCCESS although h_File == 0xFFFFFFFF
    NtQueryObject()(h_File, ObjectNameInformation, u8_Buffer, sizeof(u8_Buffer), &u32_ReqLength);

    // On error pk_Info->Buffer is NULL
    if (!pk_Info->Buffer || !pk_Info->Length)
        return ERROR_FILE_NOT_FOUND;

    pk_Info->Buffer[pk_Info->Length /2] = 0; // Length in Bytes!

    *ps_NTPath = pk_Info->Buffer;
    return 0;
}

// converts
// "\Device\HarddiskVolume3"                                -> "E:"
// "\Device\HarddiskVolume3\Temp"                           -> "E:\Temp"
// "\Device\HarddiskVolume3\Temp\transparent.jpeg"          -> "E:\Temp\transparent.jpeg"
// "\Device\Harddisk1\DP(1)0-0+6\foto.jpg"                  -> "I:\foto.jpg"
// "\Device\TrueCryptVolumeP\Data\Passwords.txt"            -> "P:\Data\Passwords.txt"
// "\Device\Floppy0\Autoexec.bat"                           -> "A:\Autoexec.bat"
// "\Device\CdRom1\VIDEO_TS\VTS_01_0.VOB"                   -> "H:\VIDEO_TS\VTS_01_0.VOB"
// "\Device\Serial1"                                        -> "COM1"
// "\Device\USBSER000"                                      -> "COM4"
// "\Device\Mup\ComputerName\C$\Boot.ini"                   -> "\\ComputerName\C$\Boot.ini"
// "\Device\LanmanRedirector\ComputerName\C$\Boot.ini"      -> "\\ComputerName\C$\Boot.ini"
// "\Device\LanmanRedirector\ComputerName\Shares\Dance.m3u" -> "\\ComputerName\Shares\Dance.m3u"
// returns an error for any other device type
DWORD GetDosPathFromNtPath(const WCHAR* u16_NTPath, CString* ps_DosPath)
{
    DWORD u32_Error;

    if (wcsnicmp(u16_NTPath, L"\\Device\\Serial", 14) == 0 || // e.g. "Serial1"
        wcsnicmp(u16_NTPath, L"\\Device\\UsbSer", 14) == 0)   // e.g. "USBSER000"
    {
        HKEY h_Key; 
        if (u32_Error = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"Hardware\\DeviceMap\\SerialComm", 0, KEY_QUERY_VALUE, &h_Key))
            return u32_Error;

        WCHAR u16_ComPort[50];

        DWORD u32_Type;
        DWORD u32_Size = sizeof(u16_ComPort); 
        if (u32_Error = RegQueryValueEx(h_Key, u16_NTPath, 0, &u32_Type, (BYTE*)u16_ComPort, &u32_Size))
        {
            RegCloseKey(h_Key);
            return ERROR_UNKNOWN_PORT;
        }

        *ps_DosPath = u16_ComPort;
        RegCloseKey(h_Key);
        return 0;
    }

    if (wcsnicmp(u16_NTPath, L"\\Device\\LanmanRedirector\\", 25) == 0) // Win XP
    {
        *ps_DosPath  = L"\\\\";
        *ps_DosPath += (u16_NTPath + 25);
        return 0;
    }

    if (wcsnicmp(u16_NTPath, L"\\Device\\Mup\\", 12) == 0) // Win 7
    {
        *ps_DosPath  = L"\\\\";
        *ps_DosPath += (u16_NTPath + 12);
        return 0;
    }

    WCHAR u16_Drives[300];
    if (!GetLogicalDriveStrings(300, u16_Drives))
        return GetLastError();

    WCHAR* u16_Drv = u16_Drives;
    while (u16_Drv[0])
    {
        WCHAR* u16_Next = u16_Drv +wcslen(u16_Drv) +1;

        u16_Drv[2] = 0; // the backslash is not allowed for QueryDosDevice()

        WCHAR u16_NtVolume[1000];
        u16_NtVolume[0] = 0;

        // may return multiple strings!
        // returns very weird strings for network shares
        if (!QueryDosDevice(u16_Drv, u16_NtVolume, sizeof(u16_NtVolume) /2))
            return GetLastError();

        int s32_Len = (int)wcslen(u16_NtVolume);
        if (s32_Len > 0 && wcsnicmp(u16_NTPath, u16_NtVolume, s32_Len) == 0)
        {
            *ps_DosPath  =  u16_Drv;
            *ps_DosPath += (u16_NTPath + s32_Len);
            return 0;
        }

        u16_Drv = u16_Next;
    }
    return ERROR_BAD_PATHNAME;
}

头文件:

#pragma warning(disable: 4996) // wcsnicmp deprecated
#include <winternl.h>

// This makro assures that INVALID_HANDLE_VALUE (0xFFFFFFFF) returns FALSE
#define IsConsoleHandle(h) (((((ULONG_PTR)h) & 0x10000003) == 0x3) ? TRUE : FALSE)

enum OBJECT_INFORMATION_CLASS 
{
    ObjectBasicInformation, 
    ObjectNameInformation,
    ObjectTypeInformation, 
    ObjectAllInformation, 
    ObjectDataInformation
};

struct OBJECT_NAME_INFORMATION 
{
    UNICODE_STRING Name; // defined in winternl.h
    WCHAR NameBuffer;
};

typedef NTSTATUS (NTAPI* t_NtQueryObject)(HANDLE Handle, OBJECT_INFORMATION_CLASS Info, PVOID Buffer, ULONG BufferSize, PULONG ReturnLength);   

关于c - 如何获取与打开的 HANDLE 关联的名称,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65170/

相关文章:

c - 函数中标识符列表和参数列表的区别?

在 C 中运行时检查内核版本

windows - 如何使用密码自动登录 Windows 7

windows - 在另一个窗口中运行 Windows 应用程序

C++:如何从 WLAN_BSS_ENTRY 打印 dot11Bssid?

通过在函数中传递字符串来创建获胜条件

C - 相当于 Lua 的 math.rad

javascript - 如何监视使用 WinJs 中的 launchUriAsync 生成的进程

python - 为什么 os.path 同时使用 '\' 和 '/' ?

windows - win32 C api 调用禁用网络适配器?