You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NetFileEnum API调用始终返回5的VB.NET代码问题求助

解决NetFileEnum返回错误码5(访问被拒绝)的问题

问题背景

开发应用时通过FileSystemWatcher捕获文件变更,需要获取打开特定网络文件的用户(非文件所有者)。将C#代码转为VB.NET后编译正常,但NetFileEnum始终返回错误码5(预期为0),导致无法获取文件ID及对应用户。

错误原因分析

错误码5对应ERROR_ACCESS_DENIED(访问被拒绝),主要由以下几点导致:

  • 服务器参数错误:调用NetFileEnum时传入Nothing作为servername,但目标文件在网络驱动器(如M:\),必须指定共享文件所在的服务器名称。
  • 路径格式错误:basepath参数需要传入服务器端的共享路径(如\\ServerName\Share\temp\abc.xls),而非本地映射的驱动器路径(M:\temp\abc.xls)。
  • 权限不足:当前运行程序的账号没有足够权限查询目标服务器的文件共享会话信息。
  • P/Invoke声明潜在问题:结构体的CharSet和Pack设置需与API要求严格匹配。

修正后的代码实现

1. 完整模块代码

Imports System.IO
Imports System.Runtime.InteropServices
Imports System.Text

Module FileOpenByUser
    ' 解析网络驱动器对应的服务器和共享路径
    Private Function GetUNCPath(ByVal localPath As String) As String
        If Not Path.IsPathRooted(localPath) Then
            Return localPath
        End If

        Dim root = Path.GetPathRoot(localPath)
        If root.Length <> 3 OrElse Not Char.IsLetter(root(0)) OrElse root(1) <> ":"c OrElse root(2) <> "\"c Then
            Return localPath
        End If

        Dim driveLetter = root(0)
        Dim uncPath As New StringBuilder(256)
        Dim bufferSize As Integer() = {uncPath.Capacity}
        Dim result = WNetGetConnection(driveLetter.ToString(), uncPath, bufferSize)
        If result = 0 Then
            Return Path.Combine(uncPath.ToString(), localPath.Substring(3))
        End If
        Return localPath
    End Function

    <DllImport("mpr.dll", CharSet:=CharSet.Unicode, SetLastError:=True)>
    Private Function WNetGetConnection(ByVal localName As String, ByVal remoteName As StringBuilder, ByRef bufferSize As Integer()) As Integer
    End Function

    <DllImport("Netapi32.dll", SetLastError:=True)>
    Private Function NetApiBufferFree(ByVal Buffer As IntPtr) As Integer
    End Function

    <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Unicode, Pack:=4)>
    Structure FILE_INFO_3
        Public fi3_id As Integer
        Public fi3_permission As Integer
        Public fi3_num_locks As Integer
        Public fi3_pathname As String
        Public fi3_username As String
    End Structure

    <DllImport("netapi32.dll", SetLastError:=True, CharSet:=CharSet.Unicode)>
    Private Function NetFileEnum(
        ByVal servername As String,
        ByVal basepath As String,
        ByVal username As String,
        ByVal level As Integer,
        ByRef bufptr As IntPtr,
        ByVal prefmaxlen As Integer,
        <Out> ByRef entriesread As Integer,
        <Out> ByRef totalentries As Integer,
        ByVal resume_handle As IntPtr
    ) As Integer
    End Function

    <DllImport("netapi32.dll", SetLastError:=True, CharSet:=CharSet.Unicode)>
    Private Function NetFileGetInfo(
        ByVal servername As String,
        ByVal fileid As Integer,
        ByVal level As Integer,
        ByRef bufptr As IntPtr
    ) As Integer
    End Function

    Public Function GetFileIdFromPath(ByVal filePath As String) As Integer
        Const MAX_PREFERRED_LENGTH As Integer = -1
        Dim uncPath = GetUNCPath(filePath)
        If uncPath = filePath Then
            ' 非网络路径,无法通过NetFileEnum查询
            Return -1
        End If

        ' 拆分服务器名和共享路径
        Dim serverName = uncPath.Substring(2).Split("\"c)(0)
        Dim basePath = uncPath.Substring(2 + serverName.Length)

        Dim dwReadEntries As Integer
        Dim dwTotalEntries As Integer
        Dim pBuffer As IntPtr = IntPtr.Zero
        Dim dwStatus As Integer = NetFileEnum(serverName, basePath, Nothing, 3, pBuffer, MAX_PREFERRED_LENGTH, dwReadEntries, dwTotalEntries, IntPtr.Zero)

        If dwStatus = 0 Then
            Dim entrySize = Marshal.SizeOf(GetType(FILE_INFO_3))
            For dwIndex As Integer = 0 To dwReadEntries - 1
                Dim iPtr = IntPtr.Add(pBuffer, dwIndex * entrySize)
                Dim pCurrent = CType(Marshal.PtrToStructure(iPtr, GetType(FILE_INFO_3)), FILE_INFO_3)
                Dim fileId = pCurrent.fi3_id
                NetApiBufferFree(pBuffer)
                Return fileId
            Next
        End If

        NetApiBufferFree(pBuffer)
        Return -1
    End Function

    Public Function GetUsernameHandlingFile(ByVal fileId As Integer, ByVal serverName As String) As String
        Dim defaultValue As String = "[Unknown User]"
        If fileId = -1 Then
            Return defaultValue
        End If

        Dim pBuffer_Info As IntPtr = IntPtr.Zero
        Dim dwStatus_Info As Integer = NetFileGetInfo(serverName, fileId, 3, pBuffer_Info)

        If dwStatus_Info = 0 Then
            Dim pCurrent_Info = CType(Marshal.PtrToStructure(pBuffer_Info, GetType(FILE_INFO_3)), FILE_INFO_3)
            NetApiBufferFree(pBuffer_Info)
            Return pCurrent_Info.fi3_username
        End If

        NetApiBufferFree(pBuffer_Info)
        Return defaultValue
    End Function

    Public Function GetUsernameHandlingFile(ByVal filePath As String) As String
        Dim uncPath = GetUNCPath(filePath)
        If uncPath = filePath Then
            Return "[Unknown User]"
        End If
        Dim serverName = uncPath.Substring(2).Split("\"c)(0)
        Dim fileId = GetFileIdFromPath(filePath)
        Return GetUsernameHandlingFile(fileId, serverName)
    End Function
End Module

关键修正点说明

  • 路径解析:通过WNetGetConnection将本地映射驱动器转换为UNC路径,拆分出服务器名和共享路径,作为NetFileEnum的合法参数。
  • P/Invoke参数修正:servername传入实际的服务器名称,basepath传入服务器端的相对共享路径,不再传Nothing。
  • 结构体CharSet:将FILE_INFO_3的CharSet改为Unicode,与NetFileEnum的声明保持一致,避免字符串 marshaling 错误。
  • 权限要求:确保运行程序的账号拥有目标服务器的共享文件查询权限,必要时需以管理员身份运行程序。

内容的提问来源于stack exchange,提问作者FredyWenger

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 03:50:38