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

2023-12-01

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


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

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

我还研究了其他方法,例如GetFileInformationByHandleEx() and GetFinalPathNameByHandle(),但这些是无用的,因为它们只返回路径+文件名,但没有驱动器。此外GetFinalPathNameByHandle()还有挂虫。

The GetMappedFileName()MSDN 中的方法(由 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);   
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何获取与打开的 HANDLE 关联的名称 的相关文章

  • 将 new 与 decltype 一起使用

    T t T is an implementation detail t new T want to avoid naming T to allow for flexibility t new decltype t error cannot
  • 为什么 F# 的默认集合是排序的,而 C# 的不是?

    当从 C 世界迁移到 F 最惯用的可能 思维方式时 我发现了这个有趣的差异 在 C 的 OOP mutable 世界中 默认的集合集合似乎是HashSet https learn microsoft com en us dotnet api
  • 如何在另一个应用程序中挂钩 api 调用

    我正在尝试挂钩另一个应用程序的 ExtTextOut 和 DrawTextExt GDI 方法调用 我知道我需要使用 GetProcAddress 来查找 gdi32 dll 中那些方法的地址 并用我的函数的地址覆盖我想要挂钩的进程中的地址
  • 删除是如何工作的? [复制]

    这个问题在这里已经有答案了 可能的重复 C 编程 free 如何知道要释放多少 https stackoverflow com questions 1518711 c programming how does free know how m
  • 将完整模板参数值映射到原始类型

    我想将数字映射到类型 在这个例子中 我将创建一个函数 将 sizeof 结果映射到有符号的原始类型 我想知道是否有更好的方法来完成我在现代 C 中所做的事情 即采用模板化值并将其转换为类型 现在 这可以将大小转换为已知类型 但我似乎无法在标
  • Gwan C#,如何获取HTTP标头?

    我需要它来重写 url 以了解我正在处理哪个友好的 url 用于用户代理和其他东西 EDIT public class Gwan MethodImplAttribute MethodImplOptions InternalCall exte
  • 在 omp 并行 for 循环中使用 unique_ptr 会导致 SEG.FAULT

    采取以下代码 include
  • C# 开源 NMEA 解析器 [已关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我正在寻找 C 开源 NMEA 解析器 嗯 我自己也不熟悉 但是一些快速搜索显示了一个代码项目 htt
  • 使用 LINQ 更新 IEnumerable 对象的简单方法

    假设我有一个这样的业务对象 class Employee public string name public int id public string desgination public int grade List
  • 访问 ascx 文件中的母版页控件

    我有一个母版页文件 其中包含 2 个面板控件中的 2 个菜单 我还使用控件来检查用户是否登录并获取用户类型 根据我想要显示 隐藏面板的类型 控件本身不在母版页中引用 而是通过 CMS 系统动态引用 我想在用户控件中使用findcontrol
  • 根据对象变量搜索对象列表

    我有一个对象列表 这些对象具有三个变量 ID 名称和值 这个列表中可能有很多对象 我需要根据ID或Name找到一个对象 并更改值 例子 class objec public string Name public int UID public
  • 使用 C# 和 wpf 创建类似 Dock 的应用程序

    我需要创建一个与我们购买笔记本电脑时获得的应用程序类似的应用程序 仅当鼠标指针到达窗口顶部时它才可见 那么我怎样才能使用 C 4 0 来做到这一点呢 http www notebookcheck net uploads pics win2
  • C#6 中的长字符串插值行

    我发现 虽然字符串插值在应用于现有代码库的字符串 Format 调用时非常好 但考虑到通常首选的列限制 字符串对于单行来说很快就会变得太长 特别是当被插值的表达式很复杂时 使用格式字符串 您将获得一个可以拆分为多行的变量列表 var str
  • 在 OpenGL 中渲染纹理 1 到 1

    所以我想做的是使用 OpenGL 和 C 将纹理渲染到平面上 作为显示图像的一种方式 但是我需要确保在渲染纹理时没有对纹理进行任何处理 抗锯齿 插值 平滑 模糊等 这是 OpenGL 处理渲染纹理的默认方式吗 或者是否需要设置一些标志才能禁
  • WIX 工具集 - 自定义操作命令无法正确检测 Windows 10 版本

    我有一个安装程序 可以部署并运行单独的 exe文件以将驱动程序部署到系统 不幸的是 微软似乎已经放弃了对VersionNTWindows 10 及更高版本中的宏 所以我实施了另一篇文章中建议的方法 https stackoverflow c
  • ASP.NET MVC 路由:如何从 URL 中省略“索引”

    我有一个名为 StuffController 的控制器 具有无参数索引操作 我希望从表单中的 URL 调用此操作mysite com stuff 我的控制器定义为 public class StuffController BaseContr
  • 逆向工程 ASP.NET Web 应用程序

    我有一个 ASP NET Web 应用程序 我没有源代码 该 bin 包含 10 个程序集和一个 compiled 文件 我在 App Code dll 上使用 Reflector 它向我显示了类和命名空间之类的东西 但它太混乱了 有没有什
  • 如何停止无限循环?

    我正在编写一个程序 该程序将计算三角形或正方形的面积 然后提示用户是否希望计算另一个 我的代码已经运行到可以计算任一形状的面积的程度 但随后不再继续执行代码的其余部分 例如 如果选择了正方形 则计算面积 然后返回到正方形边长的提示 我假设这
  • 如何调试 .NET 运行时中的内部错误?

    我正在尝试调试一些处理大文件的工作 代码本身works 但 NET 运行时本身会报告零星错误 对于上下文 这里的处理是一个 1 5GB 文件 仅加载到内存中一次 在循环中处理和释放 故意尝试重现此否则不可预测的错误 我的测试片段基本上是 t
  • INotifyPropertyChanged 和 propertyName

    我一直不确定它的含义propertyName实施时INotifyPropertyChanged 所以一般来说你实现INotifyPropertyChanged as public class Data INotifyPropertyChan

随机推荐