代码之家  ›  专栏  ›  技术社区  ›  flipdoubt

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

  •  12
  • flipdoubt  · 技术社区  · 17 年前

    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服务器连接。该错误的奇怪之处在于,用户可以在错误发生之前和之后向服务器写入数据,而无需显式登录。

    您能否解释错误发生的原因并提供解决方案?

    5 回复  |  直到 17 年前
        1
  •  48
  •   David    17 年前

    我在尝试访问另一个域中的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.
    }
    
        2
  •  3
  •   Dennis Oosterkamp    15 年前

    对于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
  •  1
  •   Davy Landman    17 年前

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

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

    • 使用 Process Monitor 记录所有api调用并查看错误来源。
    • 在干净的虚拟机/机器上尝试,并尝试在那里复制它
    • 禁用病毒扫描程序
        4
  •  1
  •   alexandrul    17 年前

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

    在我的案例中,作为一个通过代理(squid)访问internet的Active Directory用户,我在浏览时没有任何问题,直到(以随机间隔)收到一个关于缺少凭据的错误,该错误通过浏览器中的页面刷新得到解决,然后在下一个错误之前,一切都正常工作。

        5
  •  0
  •   user2907498    8 年前

    https://msdn.microsoft.com/en-us/library/system.security.principal.windowsimpersonationcontext(v=vs.110).aspx

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

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