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

时间:2009-02-24 18:35:39

标签: .net windows networking ioexception novell

我有一个在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连接。关于错误的奇怪之处在于用户可以在错误之前和之后写入服务器,所有这些都没有明确登录。

你能解释一下错误发生的原因并提供解决方案吗?

5 个答案:

答案 0 :(得分:44)

尝试访问其他域中的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.
}

答案 1 :(得分: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

答案 2 :(得分:1)

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

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

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

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

答案 3 :(得分:1)

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

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

答案 4 :(得分:0)

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

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

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