你能解释一下为啥 DirectoryInfo.GetFiles 会产生这个 IOException 吗?

Posted

技术标签:

【中文标题】你能解释一下为啥 DirectoryInfo.GetFiles 会产生这个 IOException 吗?【英文标题】:Can you explain why DirectoryInfo.GetFiles produces this IOException?你能解释一下为什么 DirectoryInfo.GetFiles 会产生这个 IOException 吗? 【发布时间】:2010-10-09 15:20:47 【问题描述】:

我有一个在 Novell 网络上运行的 WinForms 客户端-服务器应用程序,当连接到网络上的唯一 Windows 2003 Server 时会产生以下错误:

TYPE: System.IO.IOException
MSG: Logon failure: unknown user name or bad password.

SOURCE: mscorlib
SITE: WinIOError

  at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
  at System.IO.Directory.InternalGetFileDirectoryNames(String path,
    String userPathOriginal, String searchPattern, Boolean includeFiles, 
    Boolean includeDirs, SearchOption searchOption)
  at System.IO.DirectoryInfo.GetFiles(String searchPattern, 
    SearchOption searchOption)
  at System.IO.DirectoryInfo.GetFiles(String searchPattern)
  at Ceoimage.Basecamp.DocumentServers.ClientAccessServer.SendQueuedFiles(
    Int32 queueId, Int32 userId, IDocQueueFile[] queueFiles)
  at Ceoimage.Basecamp.ScanDocuments.DataModule.CommitDocumentToQueue(
    QueuedDocumentModelWithCollections doc, IDocQueueFile[] files)

客户的网络管理员通过手动将工作站用户名和密码与服务器上的本地用户同步来管理 Windows Server 连接。这个错误的奇怪之处在于,用户可以在错误之前和之后都写入服务器,而无需显式登录。

您能解释一下为什么会出现错误并提供解决方案吗?

【问题讨论】:

Novell 和 Windows 工作组或域之间可能没有隐式信任。 这种“隐式信任”是可以配置的吗? 能贴出连接服务器的代码段吗? 【参考方案1】:

在尝试访问不同域中的 Windows 服务器的文件系统时,我遇到了同样的问题。问题是运行程序的用户帐户无权访问远程服务器。当您使用 Windows 资源管理器时,Windows 会在幕后进行额外的工作以使其看起来无缝,因为它会猜测您的远程凭据将与您的本地凭据匹配。

如果您将本地驱动器映射到远程服务器,然后在代码中使用本地映射的驱动器,您应该不会遇到问题。如果您无法映射驱动器,但可以硬编码用于远程服务器的凭据,则可以使用以下代码:

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Security.Principal;

namespace Company.Security

    public class ImpersonateUser : IDisposable
    
        [DllImport("advapi32.dll", SetLastError=true)]
        private static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, out IntPtr phToken);

        [DllImport( "kernel32", SetLastError = true )]
        private static extern bool CloseHandle(IntPtr hObject);

        private IntPtr userHandle = IntPtr.Zero;
        private WindowsImpersonationContext impersonationContext;

        public ImpersonateUser( string user, string domain, string password )
        
            if ( ! string.IsNullOrEmpty( user ) )
            
                // Call LogonUser to get a token for the user
                bool loggedOn = LogonUser( user, domain, password,
                    9 /*(int)LogonType.LOGON32_LOGON_NEW_CREDENTIALS*/,
                    3 /*(int)LogonProvider.LOGON32_PROVIDER_WINNT50*/,
                    out userHandle );
                if ( !loggedOn )
                    throw new Win32Exception( Marshal.GetLastWin32Error() );

                // Begin impersonating the user
                impersonationContext = WindowsIdentity.Impersonate( userHandle );
            
        

        public void Dispose()
        
            if ( userHandle != IntPtr.Zero )
                CloseHandle( userHandle );
            if ( impersonationContext != null )
                impersonationContext.Undo();
        
    

然后你可以通过这样做访问远程服务器:

using ( new ImpersonateUser( "UserID", "Domain", "Password" ) )

    // Any IO code within this block will be able to access the remote server.

【讨论】:

我有一个类似的模拟用户的方法,但它似乎只在用户是本地管理员时才有效。你觉得是这样吗? 哪个用户需要成为本地管理员?您的远程用户可能需要成为管理员才能访问共享。 我是说你需要在本地机器上拥有某种“可以模拟用户”权限,而大多数标准用户都没有。 另外,我的问题是间歇性的。某一刻,用户正在访问服务器;下一刻,用户得到了这个 IOException;下一刻,用户正在访问服务器。 前段时间我做了几乎与 ImpersonateUser 相同的类,它也在实现 IDisposable 以在 using 块中使用它。很高兴看到我不是唯一一个。【参考方案2】:

对于 VB.Net 开发人员(如我),这里是 VB.Net 版本:

Imports System
Imports System.ComponentModel
Imports System.Runtime.InteropServices
Imports System.Security.Principal

Namespace Company.Security
    Public Class ImpersonateUser
        Implements IDisposable

        <DllImport("advapi32.dll", SetLastError:=True)> _
        Private Shared Function LogonUser(ByVal lpszUsername As String, ByVal lpszDomain As String, ByVal lpszPassword As String, ByVal dwLogonType As Integer, ByVal dwLogonProvider As Integer, ByRef phToken As IntPtr) As Integer
        End Function

        <DllImport("kernel32", SetLastError:=True)> _
        Private Shared Function CloseHandle(ByVal hObject As IntPtr) As Boolean
        End Function

        Private userHandle As IntPtr = IntPtr.Zero
        Private impersonationContext As WindowsImpersonationContext

        Public Sub New(ByVal user As String, ByVal domain As String, ByVal password As String)
            If Not String.IsNullOrEmpty(user) Then
                Dim loggedOn As Integer = LogonUser(user, domain, password, 9, 3, userHandle)
                If Not loggedOn = 1 Then
                    Throw New Win32Exception(Marshal.GetLastWin32Error())
                End If
                impersonationContext = WindowsIdentity.Impersonate(userHandle)
            End If
        End Sub

        Public Sub Dispose() Implements System.IDisposable.Dispose
            If userHandle <> IntPtr.Zero Then
                CloseHandle(userHandle)
            End If
            If impersonationContext IsNot Nothing Then
                impersonationContext.Undo()
            End If
        End Sub

    End Class
End Namespace

并像这样使用它:

using New ImpersonateUser( "UserID", "Domain", "Password" ) 
    ' ... your code here
End Using

【讨论】:

【参考方案3】:

我认为您应该尝试重现问题,而不是使用数据包监视器查看网络流量并查看失败情况和成功情况之间的区别。

然后编写一个应用程序,它使用来自 windows (P/Invokes) 的原始 api 来重现您的失败情况并尝试找出哪些参数导致错误发生。如果您能够解决问题,那么只需找到如何让组件做您想做的事情。

您可以查看的其他方向(在您可以稳定地重现问题之后):

使用Process Monitor 记录所有api调用并查看错误来自哪里。 在干净的虚拟机/机器上尝试并尝试在那里重现它 禁用病毒扫描程序 更新 Novell 客户端

【讨论】:

【参考方案4】:

恕我直言,这似乎是刷新过期身份验证令牌(或类似的东西)的某种副作用。

我的情况是,作为通过代理 (squid) 访问 Internet 的 Active Directory 用户,我在浏览时没有问题,直到我(以随机时间间隔)收到有关缺少凭据的错误,这可以通过在中的页面刷新来解决浏览器,然后一切正常,直到下一个错误。

【讨论】:

【参考方案5】:

此地址有一个 Microsoft 示例:https://msdn.microsoft.com/en-us/library/system.security.principal.windowsimpersonationcontext(v=vs.110).aspx

但在他们的示例中确实说:“在 Windows Vista 及更高版本上,此示例必须以管理员身份运行。”

因此,只有在大多数平台上运行代码的用户是管理员时,此解决方案才有效。

【讨论】:

以上是关于你能解释一下为啥 DirectoryInfo.GetFiles 会产生这个 IOException 吗?的主要内容,如果未能解决你的问题,请参考以下文章

你能解释一下输出并指出错误吗

你能解释一下STA和MTA吗?

你能解释一下 Python 中的装饰器是啥吗? [关闭]

你能解释一下这个 Go 指针操作的行为吗?

为啥回调比承诺更“紧密耦合”?

你能解释一下这段代码的作用吗?