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
相关产品推荐
相关产品推荐

