我使用“DeviceIoControl”时遇到文件访问错误是什么?

问题描述 投票:0回答:1

我正在 Windows 中递归文件夹结构,获取每个文件和文件夹的唯一文件系统标识符。对于每个文件夹,我使用

CreateFile()
DeviceIoControl()
WinAPI 函数来访问文件夹 ID,根据此问题的答案:

windows 中唯一的文件标识符

但是,对于某些路径(不是全部),尽管能够在资源管理器中浏览它们,甚至

System.IO.Path.Exists()
断言它们存在并且可访问,
DeviceIoControl()
返回错误代码 2,
ERROR_FILE_NOT_FOUND

最后的测试似乎确认这不是文件夹权限的问题,但有趣的是,该错误专门与未找到文件有关,而不是与未找到路径有关(错误代码为 3,

ERROR_PATH_NOT_FOUND
)。我已确认
System.IO.File.Exists()
正确拒绝所有文件夹,即使
DeviceIoControl()
确实访问了路径并返回有意义的值。

DeviceIoControl()
具体对什么敏感,为什么会在有效路径上绊倒?

  • Windows 10

  • Visual Studio Express 2022

  • .NET 8.0

WinAPI 调用:

Imports System.Runtime.InteropServices
Imports Microsoft.Win32.SafeHandles

Public Class WinAPI
  Public Shared GENERIC_READ As Integer = &H80000000
  Public Shared FILE_FLAG_BACKUP_SEMANTICS As Integer = &H2000000
  Public Shared OPEN_EXISTING As Integer = &H3

  <StructLayout(LayoutKind.Sequential)>
  Public Structure FILE_OBJECTID_BUFFER
    Public Structure Union

      <MarshalAs(UnmanagedType.ByValArray, SizeConst:=16)>
      Public BirthVolumeID() As Byte

      <MarshalAs(UnmanagedType.ByValArray, SizeConst:=16)>
      Public BirthObjectId As Byte()

      <MarshalAs(UnmanagedType.ByValArray, SizeConst:=16)>
      Public DomainID As Byte()
    End Structure

    <MarshalAs(UnmanagedType.ByValArray, SizeConst:=16)>
    Public ObjectID As Byte()

    Public BirthInfo As Union

    '<MarshalAs(UnmanagedType.ByValArray, SizeConst:=48)>
    'Public ExtendedInfo As Byte()
  End Structure

  Public Structure FILETIME
    Public dwLowDateTime As UInteger
    Public dwHighDateTime As UInteger
  End Structure

  <StructLayout(LayoutKind.Sequential)>
  Public Structure BY_HANDLE_FILE_INFORMATION
    Public FileAttributes As UInteger
    Public CreationTime As FILETIME
    Public LastAccessTime As FILETIME
    Public LastWriteTime As FILETIME
    Public VolumeSerialNumber As UInteger
    Public FileSizeHigh As UInteger
    Public FileSizeLow As UInteger
    Public NumberOfLinks As UInteger
    Public FileIndexHigh As UInteger
    Public FileIndexLow As UInteger
  End Structure

  <DllImport("kernel32.dll", SetLastError:=True)>
  Public Shared Function DeviceIoControl(
                                        hDevice As SafeFileHandle,
                                        dwIoControlCode As UInteger,
                                        lpInBuffer As IntPtr,
                                        nInBufferSize As UInteger,
                                        lpOutBuffer As IntPtr,
                                        nOutBufferSize As Integer,
                                        ByRef lpBytesReturned As UInteger,
                                        lpOverlapped As IntPtr
                                        ) As Boolean
  End Function

  <DllImport("kernel32.dll", SetLastError:=True)>
  Public Shared Function CreateFile(
                                   fileName As String,
                                   dwDesiredAccess As Integer,
                                   dwShareMode As System.IO.FileShare,
                                   securityAttrs_MustBeZero As IntPtr,
                                   dwCreationDisposition As System.IO.FileMode,
                                   dwFlagsAndAttributes As Integer,
                                   hTemplateFile_MustBeZero As IntPtr
                                   ) As SafeFileHandle
  End Function

  <DllImport("kernel32.dll", SetLastError:=True)>
  Public Shared Function GetFileInformationByHandle(
                                                   hFile As IntPtr,
                                                   ByRef lpFileInformation As BY_HANDLE_FILE_INFORMATION) As Boolean
  End Function

End Class

实现功能:

Imports System.Runtime.InteropServices
Imports Microsoft.Win32.SafeHandles

Public Class FileAccess
  Shared FSCTL_GET_OBJECT_ID As UInteger = &H9009C

  Public Shared Function GetFileId(path As String) As UInt128


    Using fs = IO.File.Open(
      path,
      IO.FileMode.OpenOrCreate,
      IO.FileAccess.ReadWrite,
      IO.FileShare.ReadWrite)


      Dim info As WinAPI.BY_HANDLE_FILE_INFORMATION
      WinAPI.GetFileInformationByHandle(fs.Handle, info)
      Dim fileID As UInt128 = info.FileIndexHigh
      fileID <<= 32
      fileID = fileID Or info.FileIndexLow
      Return fileID
      'Return String.Format("{0:x}", ((info.FileIndexHigh << 32) Or info.FileIndexLow))
    End Using

  End Function

  Public Shared Function GetFolderID(path As String) As UInt128
    Dim result As UInt128

    Dim buffer As WinAPI.FILE_OBJECTID_BUFFER = GetFolderIdBuffer(path)
    result = BitConverter.GetUint128(buffer.ObjectID)

    Return result
  End Function

  Private Shared Function GetFolderIdBuffer(path As String) As WinAPI.FILE_OBJECTID_BUFFER
    Using hFile As SafeFileHandle = WinAPI.CreateFile(
            path,
            WinAPI.GENERIC_READ, IO.FileShare.Read,
            IntPtr.Zero,
            WinAPI.OPEN_EXISTING,
            WinAPI.FILE_FLAG_BACKUP_SEMANTICS,
            IntPtr.Zero
            )
      If hFile Is Nothing Or hFile.IsInvalid Then Throw New System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error())

      Dim buffer As New WinAPI.FILE_OBJECTID_BUFFER

      Dim nOutBufferSize As Integer = Marshal.SizeOf(buffer)
      Dim lpOutBuffer As IntPtr = Marshal.AllocHGlobal(nOutBufferSize)
      Dim lpBytesReturned As UInteger

      Dim result As Boolean =
                WinAPI.DeviceIoControl(
                    hFile, FSCTL_GET_OBJECT_ID,
                    IntPtr.Zero, 0,
                    lpOutBuffer, nOutBufferSize,
                    lpBytesReturned,
                    IntPtr.Zero
                    )

      If Not result Then Throw New System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error())

      Dim type As Type = GetType(WinAPI.FILE_OBJECTID_BUFFER)

      buffer = Marshal.PtrToStructure(lpOutBuffer, type)

      Marshal.FreeHGlobal(lpOutBuffer)
      Return buffer
    End Using
  End Function


End Class

食用方法:

Dim folderID As UInt128 = FileAccess.GetFolderID(path)
.net winapi directory filesystems
1个回答
0
投票

来自

FSCTL_GET_OBJECT_ID
文档

如果没有与指定句柄关联的对象标识符,则不会创建任何对象标识符并返回错误。

它没有说 which 错误,但 ERROR_FILE_NOT_FOUND 可能是匹配的。

接下来说

要创建对象标识符,请使用

FSCTL_SET_OBJECT_ID
要检索现有对象标识符或在一步中没有现有对象标识符时生成对象标识符,请使用
FSCTL_CREATE_OR_GET_OBJECT_ID

为您提供解决方案。


注意:

DeviceIoControl
没有单一的语义,因为它为您提供了一种方法来分派到不同驱动程序中的大量不同功能(您的情况可能是对 NTFS 文件系统驱动程序的调用),由 IOCTL 代码标识,每个不同的代码都有不同的前置条件和后置条件。

© www.soinside.com 2019 - 2024. All rights reserved.