Windows 设备句柄的GetFinalPathNameByHandle失败

Windows 设备句柄的GetFinalPathNameByHandle失败,windows,winapi,Windows,Winapi,如果我使用CreateFile为“\\?\NUL”或“\\?\pipe”等路径创建文件句柄,则句柄将映射到为“\Device\Null”或“\Device\NamedPipe”内核设备对象打开的文件对象。由于该函数支持VOLUME\u NAME\u NT属性,该属性已返回“\Device\HarddiskVolume1\”之类的字符串,因此我认为可以为设备句柄获得类似的路径。但是,根据打开文件时使用的访问标志,调用总是失败,可能是由于ERROR\u INVALID\u函数,也可能是ERROR\u

如果我使用
CreateFile
为“\\?\NUL”或“\\?\pipe”等路径创建文件句柄,则句柄将映射到为“\Device\Null”或“\Device\NamedPipe”内核设备对象打开的文件对象。由于该函数支持
VOLUME\u NAME\u NT
属性,该属性已返回“\Device\HarddiskVolume1\”之类的字符串,因此我认为可以为设备句柄获得类似的路径。但是,根据打开文件时使用的访问标志,调用总是失败,可能是由于
ERROR\u INVALID\u函数
,也可能是
ERROR\u INVALID\u参数


事实上,几乎任何对类似函数的调用都会失败——比如
GetFileInformationByHandle
GetFileInformationByHandleEx
,甚至对NT函数的调用,比如
NtQueryInformationFile
——返回
状态\u无效\u参数
。唯一不会失败的函数是
GetFileType
(能够识别管道)、
GetVolumeInformationByHandle
(能够识别驱动程序)和
NtQueryInformationFile
(具有
FileModeInformation

所有这些函数在任何标准文件上使用时都可以工作,但设备文件句柄不支持这些函数。如何从设备句柄获取路径信息?是否有一些
Nt
Io
功能可以工作?如果我唯一拥有的是句柄,那么还有其他方法来识别设备吗?

正如我所指出的,实现对象的驱动程序必须能够处理
IRP\u MJ\u QUERY\u信息
,但如果没有,则可以通过向其传递ObjectNameInformation(1)来获取对象的名称,这将获得带有对象名称的
对象名称\u信息
结构

由于我打算用C#调用它,下面是它的p/Invoke代码:

static class Ntdll
{
    [DllImport("ntdll.dll")]
    static extern int NtQueryObject(
        IntPtr Handle, int ObjectInformationClass,
        IntPtr ObjectInformation, int ObjectInformationLength,
        out int ReturnLength
    );
    
    public static int NtQueryObject(IntPtr Handle, int ObjectInformationClass, IntPtr ObjectInformation, int ObjectInformationLength)
    {
        int length;
        int status = NtQueryObject(Handle, ObjectInformationClass, ObjectInformation, ObjectInformationLength, out length);
        if(status != 0) throw new Win32Exception(RtlNtStatusToDosError(status));
        return length;
    }
    
    [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
    public struct OBJECT_NAME_INFORMATION
    {
        public ushort Length;
        public ushort MaximumLength;
        public string Buffer;
    }
    
    [DebuggerStepThrough]
    public static void NtQueryObject(IntPtr Handle, out OBJECT_NAME_INFORMATION ObjectNameInformation)
    {
        IntPtr buffer = Marshal.AllocHGlobal(1024);
        try{
            Ntdll.NtQueryObject(Handle, 1, buffer, 1024);
            ObjectNameInformation = Marshal.PtrToStructure<Ntdll.OBJECT_NAME_INFORMATION>(buffer);
        }finally{
            Marshal.FreeHGlobal(buffer);
        }
    }
}
静态类Ntdll
{
[DllImport(“ntdll.dll”)]
静态外部int NtQueryObject(
IntPtr句柄,int ObjectInformation类,
IntPtr对象信息,int对象信息长度,
输出返回长度
);
公共静态int-NtQueryObject(IntPtr句柄、int-ObjectInformationClass、IntPtr-ObjectInformation、int-ObjectInformationLength)
{
整数长度;
int status=NtQueryObject(句柄、ObjectInformationClass、ObjectInformation、ObjectInformationLength、out-length);
如果(状态!=0)抛出新的Win32Exception(RTLNTStatustoError(状态));
返回长度;
}
[StructLayout(LayoutKind.Sequential,CharSet=CharSet.Unicode)]
公共结构对象\u名称\u信息
{
公共长度;
公共ushort最大长度;
公共字符串缓冲区;
}
[调试步骤至]
公共静态void NtQueryObject(IntPtr句柄、out对象\u NAME\u信息ObjectNameInformation)
{
IntPtr buffer=Marshal.AllocHGlobal(1024);
试一试{
Ntdll.NtQueryObject(句柄,1,缓冲区,1024);
ObjectNameInformation=Marshal.PtrToStructure(缓冲区);
}最后{
自由全球元帅(缓冲区);
}
}
}
然后,可以通过将“\\?\GlobalRoot”预先添加到缓冲区成员来构建路径。

正如前面所指出的,实现该对象的驱动程序必须能够处理
IRP\u MJ\u QUERY\u信息
,但如果不能,则可以通过向其传递ObjectNameInformation(1)来获取该对象的名称,这将获得带有对象名称的
对象名称\u信息
结构

由于我打算用C#调用它,下面是它的p/Invoke代码:

static class Ntdll
{
    [DllImport("ntdll.dll")]
    static extern int NtQueryObject(
        IntPtr Handle, int ObjectInformationClass,
        IntPtr ObjectInformation, int ObjectInformationLength,
        out int ReturnLength
    );
    
    public static int NtQueryObject(IntPtr Handle, int ObjectInformationClass, IntPtr ObjectInformation, int ObjectInformationLength)
    {
        int length;
        int status = NtQueryObject(Handle, ObjectInformationClass, ObjectInformation, ObjectInformationLength, out length);
        if(status != 0) throw new Win32Exception(RtlNtStatusToDosError(status));
        return length;
    }
    
    [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
    public struct OBJECT_NAME_INFORMATION
    {
        public ushort Length;
        public ushort MaximumLength;
        public string Buffer;
    }
    
    [DebuggerStepThrough]
    public static void NtQueryObject(IntPtr Handle, out OBJECT_NAME_INFORMATION ObjectNameInformation)
    {
        IntPtr buffer = Marshal.AllocHGlobal(1024);
        try{
            Ntdll.NtQueryObject(Handle, 1, buffer, 1024);
            ObjectNameInformation = Marshal.PtrToStructure<Ntdll.OBJECT_NAME_INFORMATION>(buffer);
        }finally{
            Marshal.FreeHGlobal(buffer);
        }
    }
}
静态类Ntdll
{
[DllImport(“ntdll.dll”)]
静态外部int NtQueryObject(
IntPtr句柄,int ObjectInformation类,
IntPtr对象信息,int对象信息长度,
输出返回长度
);
公共静态int-NtQueryObject(IntPtr句柄、int-ObjectInformationClass、IntPtr-ObjectInformation、int-ObjectInformationLength)
{
整数长度;
int status=NtQueryObject(句柄、ObjectInformationClass、ObjectInformation、ObjectInformationLength、out-length);
如果(状态!=0)抛出新的Win32Exception(RTLNTStatustoError(状态));
返回长度;
}
[StructLayout(LayoutKind.Sequential,CharSet=CharSet.Unicode)]
公共结构对象\u名称\u信息
{
公共长度;
公共ushort最大长度;
公共字符串缓冲区;
}
[调试步骤至]
公共静态void NtQueryObject(IntPtr句柄、out对象\u NAME\u信息ObjectNameInformation)
{
IntPtr buffer=Marshal.AllocHGlobal(1024);
试一试{
Ntdll.NtQueryObject(句柄,1,缓冲区,1024);
ObjectNameInformation=Marshal.PtrToStructure(缓冲区);
}最后{
自由全球元帅(缓冲区);
}
}
}

然后,可以通过将
“\\?\GlobalRoot”
预先添加到缓冲区成员来构造路径。

通过Handleex获取文件信息
调用内部
NtQueryInformationFile
IRP\U MJ\U查询信息
发送到驱动程序,在驱动程序上打开设备文件。结果已经取决于驱动程序实现。比如说ntfs.sys(和大多数其他filse系统驱动程序)处理这个请求。但是npfs.sys对它的处理非常有限您可以通过
NtQueryObject
获取设备名称,但是有些设备支持名称空间(例如“\device\ConDrv\Output”),这不会给您名称的剩余部分(例如后一种情况下的“\Output”)。Process Explorer为您提供全名,因为它使用驱动程序从
文件\u对象
获取全名。是的,
NtQueryObject
带有
ObjectNameInformation
信息class@eryksun请不要更改我的文本格式样式:-(但是谢谢你的功能,它工作得很好!@illdans4,好的,那么j