【问题标题】:Accessing a Shared File (UNC) From a Remote, Non-Trusted Domain With Credentials使用凭证从远程、不受信任的域访问共享文件 (UNC)
【发布时间】:2010-10-14 03:02:54
【问题描述】:

我们遇到了一个需要解决的有趣情况,而我的搜索结果为零。因此,我向 SO 社区寻求帮助。

问题是这样的:我们需要通过远程文件共享/UNC 以编程方式访问不在我们域中且不在受信任的外部域中的共享文件。当然,我们需要向远程机器提供凭据。

通常,可以通过以下两种方式之一解决此问题:

  1. 将文件共享映射为驱动器并提供当时的凭据。这通常使用NET USE 命令或复制NET USE 的Win32 函数来完成。
  2. 使用 UNC 路径访问文件,就像远程计算机在域中一样,并确保在远程计算机上以本地用户身份复制运行程序的帐户(包括密码)。基本上利用了当用户尝试访问共享文件时 Windows 将自动提供当前用户凭据的事实。
  3. 不要使用远程文件共享。使用 FTP(或其他方式)传输文件,在本地处理它,然后将其传输回来。

出于各种原因,我们的安全/网络架构师拒绝了前两种方法。第二种方法显然是一个安全漏洞;如果远程计算机受到威胁,则本地计算机现在处于危险之中。第一种方法不能令人满意,因为新安装的驱动器是本地计算机上其他程序在程序访问文件期间可用的共享资源。尽管这很可能是暂时的,但在他们看来这仍然是一个漏洞。

他们对第三个选项持开放态度,但远程网络管理员坚持使用 SFTP 而不是 FTPS,而 FtpWebRequest 仅支持 FTPS。 SFTP 对防火墙更友好的选项,我可以使用几个库来实现这种方法,但如果可以的话,我更愿意减少我的依赖关系。

我在 MSDN 中搜索了使用远程文件共享的托管或 win32 方法,但我没有找到任何有用的方法。

所以我问:还有其他方法吗?我是否错过了一个超级机密的 win32 功能,它可以满足我的需求?还是我必须追求选项 3 的一些变体?

【问题讨论】:

  • 我已经看到使用JScape tools 以非常简单的方式实现了选项3。你可以试一试。它不是免费的,但它可以发挥作用。
  • 我已经用模拟方法解决了这个问题,但这是在域外的两台机器之间。我不知道从域到域外的计算机是否会出现问题。 stackoverflow.com/questions/17221476/…

标签: c# windows unc file-sharing


【解决方案1】:

解决问题的方法是使用名为 WNetUseConnection 的 Win32 API。
使用此函数连接到具有身份验证的 UNC 路径,而不是映射驱动器。。。 p>

这将允许您连接到远程计算机,即使它不在同一个域中,即使它具有不同的用户名和密码。

一旦您使用了 WNetUseConnection,您就可以通过 UNC 路径访问该文件,就像您在同一个域中一样。最好的方法可能是通过管理内置共享。
示例:\\computername\c$\program files\Folder\file.txt

这里是一些使用 WNetUseConnection 的示例 C# 代码。
注意,对于 NetResource,您应该为 lpLocalName 和 lpProvider 传递 null。 dwType 应该是 RESOURCETYPE_DISK。 lpRemoteName 应该是 \\ComputerName。

using System;
using System.Runtime.InteropServices ;
using System.Threading;

namespace ExtremeMirror
{
    public class PinvokeWindowsNetworking
    {
        #region Consts
        const int RESOURCE_CONNECTED = 0x00000001;
        const int RESOURCE_GLOBALNET = 0x00000002;
        const int RESOURCE_REMEMBERED = 0x00000003;

        const int RESOURCETYPE_ANY = 0x00000000;
        const int RESOURCETYPE_DISK = 0x00000001;
        const int RESOURCETYPE_PRINT = 0x00000002;

        const int RESOURCEDISPLAYTYPE_GENERIC = 0x00000000;
        const int RESOURCEDISPLAYTYPE_DOMAIN = 0x00000001;
        const int RESOURCEDISPLAYTYPE_SERVER = 0x00000002;
        const int RESOURCEDISPLAYTYPE_SHARE = 0x00000003;
        const int RESOURCEDISPLAYTYPE_FILE = 0x00000004;
        const int RESOURCEDISPLAYTYPE_GROUP = 0x00000005;

        const int RESOURCEUSAGE_CONNECTABLE = 0x00000001;
        const int RESOURCEUSAGE_CONTAINER = 0x00000002;


        const int CONNECT_INTERACTIVE = 0x00000008;
        const int CONNECT_PROMPT = 0x00000010;
        const int CONNECT_REDIRECT = 0x00000080;
        const int CONNECT_UPDATE_PROFILE = 0x00000001;
        const int CONNECT_COMMANDLINE = 0x00000800;
        const int CONNECT_CMD_SAVECRED = 0x00001000;

        const int CONNECT_LOCALDRIVE = 0x00000100;
        #endregion

        #region Errors
        const int NO_ERROR = 0;

        const int ERROR_ACCESS_DENIED = 5;
        const int ERROR_ALREADY_ASSIGNED = 85;
        const int ERROR_BAD_DEVICE = 1200;
        const int ERROR_BAD_NET_NAME = 67;
        const int ERROR_BAD_PROVIDER = 1204;
        const int ERROR_CANCELLED = 1223;
        const int ERROR_EXTENDED_ERROR = 1208;
        const int ERROR_INVALID_ADDRESS = 487;
        const int ERROR_INVALID_PARAMETER = 87;
        const int ERROR_INVALID_PASSWORD = 1216;
        const int ERROR_MORE_DATA = 234;
        const int ERROR_NO_MORE_ITEMS = 259;
        const int ERROR_NO_NET_OR_BAD_PATH = 1203;
        const int ERROR_NO_NETWORK = 1222;

        const int ERROR_BAD_PROFILE = 1206;
        const int ERROR_CANNOT_OPEN_PROFILE = 1205;
        const int ERROR_DEVICE_IN_USE = 2404;
        const int ERROR_NOT_CONNECTED = 2250;
        const int ERROR_OPEN_FILES  = 2401;

        private struct ErrorClass 
        {
            public int num;
            public string message;
            public ErrorClass(int num, string message) 
            {
                this.num = num;
                this.message = message;
            }
        }


        // Created with excel formula:
        // ="new ErrorClass("&A1&", """&PROPER(SUBSTITUTE(MID(A1,7,LEN(A1)-6), "_", " "))&"""), "
        private static ErrorClass[] ERROR_LIST = new ErrorClass[] {
            new ErrorClass(ERROR_ACCESS_DENIED, "Error: Access Denied"), 
            new ErrorClass(ERROR_ALREADY_ASSIGNED, "Error: Already Assigned"), 
            new ErrorClass(ERROR_BAD_DEVICE, "Error: Bad Device"), 
            new ErrorClass(ERROR_BAD_NET_NAME, "Error: Bad Net Name"), 
            new ErrorClass(ERROR_BAD_PROVIDER, "Error: Bad Provider"), 
            new ErrorClass(ERROR_CANCELLED, "Error: Cancelled"), 
            new ErrorClass(ERROR_EXTENDED_ERROR, "Error: Extended Error"), 
            new ErrorClass(ERROR_INVALID_ADDRESS, "Error: Invalid Address"), 
            new ErrorClass(ERROR_INVALID_PARAMETER, "Error: Invalid Parameter"), 
            new ErrorClass(ERROR_INVALID_PASSWORD, "Error: Invalid Password"), 
            new ErrorClass(ERROR_MORE_DATA, "Error: More Data"), 
            new ErrorClass(ERROR_NO_MORE_ITEMS, "Error: No More Items"), 
            new ErrorClass(ERROR_NO_NET_OR_BAD_PATH, "Error: No Net Or Bad Path"), 
            new ErrorClass(ERROR_NO_NETWORK, "Error: No Network"), 
            new ErrorClass(ERROR_BAD_PROFILE, "Error: Bad Profile"), 
            new ErrorClass(ERROR_CANNOT_OPEN_PROFILE, "Error: Cannot Open Profile"), 
            new ErrorClass(ERROR_DEVICE_IN_USE, "Error: Device In Use"), 
            new ErrorClass(ERROR_EXTENDED_ERROR, "Error: Extended Error"), 
            new ErrorClass(ERROR_NOT_CONNECTED, "Error: Not Connected"), 
            new ErrorClass(ERROR_OPEN_FILES, "Error: Open Files"), 
        };

        private static string getErrorForNumber(int errNum) 
        {
            foreach (ErrorClass er in ERROR_LIST) 
            {
                if (er.num == errNum) return er.message;
            }
            return "Error: Unknown, " + errNum;
        }
        #endregion

        [DllImport("Mpr.dll")] private static extern int WNetUseConnection(
            IntPtr hwndOwner,
            NETRESOURCE lpNetResource,
            string lpPassword,
            string lpUserID,
            int dwFlags,
            string lpAccessName,
            string lpBufferSize,
            string lpResult
        );

        [DllImport("Mpr.dll")] private static extern int WNetCancelConnection2(
            string lpName,
            int dwFlags,
            bool fForce
        );

        [StructLayout(LayoutKind.Sequential)] private class NETRESOURCE
        { 
            public int dwScope = 0;
            public int dwType = 0;
            public int dwDisplayType = 0;
            public int dwUsage = 0;
            public string lpLocalName = "";
            public string lpRemoteName = "";
            public string lpComment = "";
            public string lpProvider = "";
        }


        public static string connectToRemote(string remoteUNC, string username, string password) 
        {
            return connectToRemote(remoteUNC, username, password, false);
        }

        public static string connectToRemote(string remoteUNC, string username, string password, bool promptUser) 
        {
            NETRESOURCE nr = new NETRESOURCE();
            nr.dwType = RESOURCETYPE_DISK;
            nr.lpRemoteName = remoteUNC;
            //          nr.lpLocalName = "F:";

            int ret;
            if (promptUser) 
                ret = WNetUseConnection(IntPtr.Zero, nr, "", "", CONNECT_INTERACTIVE | CONNECT_PROMPT, null, null, null);
            else 
                ret = WNetUseConnection(IntPtr.Zero, nr, password, username, 0, null, null, null);

            if (ret == NO_ERROR) return null;
            return getErrorForNumber(ret);
        }

        public static string disconnectRemote(string remoteUNC) 
        {
            int ret = WNetCancelConnection2(remoteUNC, CONNECT_UPDATE_PROFILE, false);
            if (ret == NO_ERROR) return null;
            return getErrorForNumber(ret);
        }
    }
}

【讨论】:

  • 有没有办法使用这些函数来使用当前凭据显式打开/关闭与网络机器的连接,即不提供用户名和密码?我对在访问文件共享后关闭连接特别感兴趣。
  • 不用于连接,除非计算机本身没有用户名或密码。断开连接肯定可以。你甚至可以通过命令行来代替。
  • 嗨,布莱恩。您链接的文档说您可以为用户名和密码传递 NULL 以使用当前凭据。我会做一些测试看看这是否有效。
  • 为用户名/密码传递 null 允许我连接,但我如何证明我已经断开连接?服务器上有什么我可以看的吗?在 Server 2003 上,我可以观看会话,但是当我的应用程序不使用这些 API 时,当前会话列表的更新速度同样快。
  • 是否应该通过调用WNetCancelConnection2 手动关闭使用WNetUseConnection 打开的连接?还是有空闲超时(或其他一些机制)而我们不必费心?
【解决方案2】:

对于寻求快速解决方案的人,您可以使用我最近写的NetworkShareAccesser(基于this answer(非常感谢!)):

用法:

using (NetworkShareAccesser.Access(REMOTE_COMPUTER_NAME, DOMAIN, USER_NAME, PASSWORD))
{
    File.Copy(@"C:\Some\File\To\copy.txt", @"\\REMOTE-COMPUTER\My\Shared\Target\file.txt");
}

警告:请绝对确保调用了NetworkShareAccesserDispose(即使您的应用程序崩溃!),否则打开的连接将保留在Windows 上。您可以通过打开cmd提示并输入net use来查看所有打开的连接。

守则:

/// <summary>
/// Provides access to a network share.
/// </summary>
public class NetworkShareAccesser : IDisposable
{
    private string _remoteUncName;
    private string _remoteComputerName;

    public string RemoteComputerName
    {
        get
        {
            return this._remoteComputerName;
        }
        set
        {
            this._remoteComputerName = value;
            this._remoteUncName = @"\\" + this._remoteComputerName;
        }
    }

    public string UserName
    {
        get;
        set;
    }
    public string Password
    {
        get;
        set;
    }

    #region Consts

    private const int RESOURCE_CONNECTED = 0x00000001;
    private const int RESOURCE_GLOBALNET = 0x00000002;
    private const int RESOURCE_REMEMBERED = 0x00000003;

    private const int RESOURCETYPE_ANY = 0x00000000;
    private const int RESOURCETYPE_DISK = 0x00000001;
    private const int RESOURCETYPE_PRINT = 0x00000002;

    private const int RESOURCEDISPLAYTYPE_GENERIC = 0x00000000;
    private const int RESOURCEDISPLAYTYPE_DOMAIN = 0x00000001;
    private const int RESOURCEDISPLAYTYPE_SERVER = 0x00000002;
    private const int RESOURCEDISPLAYTYPE_SHARE = 0x00000003;
    private const int RESOURCEDISPLAYTYPE_FILE = 0x00000004;
    private const int RESOURCEDISPLAYTYPE_GROUP = 0x00000005;

    private const int RESOURCEUSAGE_CONNECTABLE = 0x00000001;
    private const int RESOURCEUSAGE_CONTAINER = 0x00000002;


    private const int CONNECT_INTERACTIVE = 0x00000008;
    private const int CONNECT_PROMPT = 0x00000010;
    private const int CONNECT_REDIRECT = 0x00000080;
    private const int CONNECT_UPDATE_PROFILE = 0x00000001;
    private const int CONNECT_COMMANDLINE = 0x00000800;
    private const int CONNECT_CMD_SAVECRED = 0x00001000;

    private const int CONNECT_LOCALDRIVE = 0x00000100;

    #endregion

    #region Errors

    private const int NO_ERROR = 0;

    private const int ERROR_ACCESS_DENIED = 5;
    private const int ERROR_ALREADY_ASSIGNED = 85;
    private const int ERROR_BAD_DEVICE = 1200;
    private const int ERROR_BAD_NET_NAME = 67;
    private const int ERROR_BAD_PROVIDER = 1204;
    private const int ERROR_CANCELLED = 1223;
    private const int ERROR_EXTENDED_ERROR = 1208;
    private const int ERROR_INVALID_ADDRESS = 487;
    private const int ERROR_INVALID_PARAMETER = 87;
    private const int ERROR_INVALID_PASSWORD = 1216;
    private const int ERROR_MORE_DATA = 234;
    private const int ERROR_NO_MORE_ITEMS = 259;
    private const int ERROR_NO_NET_OR_BAD_PATH = 1203;
    private const int ERROR_NO_NETWORK = 1222;

    private const int ERROR_BAD_PROFILE = 1206;
    private const int ERROR_CANNOT_OPEN_PROFILE = 1205;
    private const int ERROR_DEVICE_IN_USE = 2404;
    private const int ERROR_NOT_CONNECTED = 2250;
    private const int ERROR_OPEN_FILES = 2401;

    #endregion

    #region PInvoke Signatures

    [DllImport("Mpr.dll")]
    private static extern int WNetUseConnection(
        IntPtr hwndOwner,
        NETRESOURCE lpNetResource,
        string lpPassword,
        string lpUserID,
        int dwFlags,
        string lpAccessName,
        string lpBufferSize,
        string lpResult
        );

    [DllImport("Mpr.dll")]
    private static extern int WNetCancelConnection2(
        string lpName,
        int dwFlags,
        bool fForce
        );

    [StructLayout(LayoutKind.Sequential)]
    private class NETRESOURCE
    {
        public int dwScope = 0;
        public int dwType = 0;
        public int dwDisplayType = 0;
        public int dwUsage = 0;
        public string lpLocalName = "";
        public string lpRemoteName = "";
        public string lpComment = "";
        public string lpProvider = "";
    }

    #endregion

    /// <summary>
    /// Creates a NetworkShareAccesser for the given computer name. The user will be promted to enter credentials
    /// </summary>
    /// <param name="remoteComputerName"></param>
    /// <returns></returns>
    public static NetworkShareAccesser Access(string remoteComputerName)
    {
        return new NetworkShareAccesser(remoteComputerName);
    }

    /// <summary>
    /// Creates a NetworkShareAccesser for the given computer name using the given domain/computer name, username and password
    /// </summary>
    /// <param name="remoteComputerName"></param>
    /// <param name="domainOrComuterName"></param>
    /// <param name="userName"></param>
    /// <param name="password"></param>
    public static NetworkShareAccesser Access(string remoteComputerName, string domainOrComuterName, string userName, string password)
    {
        return new NetworkShareAccesser(remoteComputerName,
                                        domainOrComuterName + @"\" + userName,
                                        password);
    }

    /// <summary>
    /// Creates a NetworkShareAccesser for the given computer name using the given username (format: domainOrComputername\Username) and password
    /// </summary>
    /// <param name="remoteComputerName"></param>
    /// <param name="userName"></param>
    /// <param name="password"></param>
    public static NetworkShareAccesser Access(string remoteComputerName, string userName, string password)
    {
        return new NetworkShareAccesser(remoteComputerName, 
                                        userName,
                                        password);
    }

    private NetworkShareAccesser(string remoteComputerName)
    {
        RemoteComputerName = remoteComputerName;               

        this.ConnectToShare(this._remoteUncName, null, null, true);
    }

    private NetworkShareAccesser(string remoteComputerName, string userName, string password)
    {
        RemoteComputerName = remoteComputerName;
        UserName = userName;
        Password = password;

        this.ConnectToShare(this._remoteUncName, this.UserName, this.Password, false);
    }

    private void ConnectToShare(string remoteUnc, string username, string password, bool promptUser)
    {
        NETRESOURCE nr = new NETRESOURCE
        {
            dwType = RESOURCETYPE_DISK,
            lpRemoteName = remoteUnc
        };

        int result;
        if (promptUser)
        {
            result = WNetUseConnection(IntPtr.Zero, nr, "", "", CONNECT_INTERACTIVE | CONNECT_PROMPT, null, null, null);
        }
        else
        {
            result = WNetUseConnection(IntPtr.Zero, nr, password, username, 0, null, null, null);
        }

        if (result != NO_ERROR)
        {
            throw new Win32Exception(result);
        }
    }

    private void DisconnectFromShare(string remoteUnc)
    {
        int result = WNetCancelConnection2(remoteUnc, CONNECT_UPDATE_PROFILE, false);
        if (result != NO_ERROR)
        {
            throw new Win32Exception(result);
        }
    }

    /// <summary>
    /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
    /// </summary>
    /// <filterpriority>2</filterpriority>
    public void Dispose()
    {
        this.DisconnectFromShare(this._remoteUncName);
    }
}

【讨论】:

  • 对于DllImportWin32Exception,您还需要using System.Runtime.InteropServices;using System.ComponentModel;
  • 这个解决方案已经停止了我漫长的一天搜索。谢谢!!!根据需要工作得很好。
  • 我正在尝试通过远程计算机上的本地用户帐户使用您的解决方案,但我不断收到拒绝访问错误。您的解决方案是否仅适用于网络帐户?
  • 远程机器上存在帐号,但不是网络帐号。这是一个本地机器帐户。我尝试将域设置为机器的名称。我还授予了共享文件夹上本地用户帐户的完全权限,但我的访问被拒绝。关于为什么会发生这种情况的任何想法?谢谢。
  • 注意:处理对象似乎不会从系统中擦除凭据(Windows 10);连接被“取消”后,我可以访问远程计算机上的文件。重新登录我的用户帐户或重新启动我的计算机似乎会清除此内部缓存。
【解决方案3】:

AFAIK,您无需将 UNC 路径映射到驱动器号即可为服务器建立凭据。我经常使用批处理脚本,例如:

net use \\myserver /user:username password

:: do something with \\myserver\the\file\i\want.xml

net use /delete \\my.server.com

但是,与您的程序在同一帐户上运行的任何程序仍然能够访问username:password 可以访问的所有内容。一种可能的解决方案是将您的程序隔离在其自己的本地用户帐户中(UNC 访问权限是调用 NET USE 的帐户的本地帐户)。

注意:跨域使用 SMB 并不是很好地利用这项技术,IMO。如果安全性如此重要,那么 SMB 缺乏加密这一事实本身就有点令人沮丧。

【讨论】:

  • 如果您认为 UNC 访问权限仅适用于调用 NET USE 的帐户是正确的,那么这可能是一种可行的方法。但是,您确定我们需要使用本地帐户吗? NET USE 调用不会是调用它的机器本地的吗?你给了我一个很好的研究路径
  • AFAIK,我可能错了,UNC 访问权限仅对调用 NET USE 的特定安全主体(SAM 帐户等)可用。您可以通过使用 RunAs 映射路径然后尝试从另一个帐户访问它来验证这一点。
  • 就我而言,我不得不使用 net use \\myserver /user:username@domain 密码,因为用户位于不同的域中。
【解决方案4】:

我会推荐NetUseAdd,而不是WNetUseConnection。 WNetUseConnection 是一个已被 WNetUseConnection2 和 WNetUseConnection3 取代的旧功能,但所有这些功能都创建了一个在 Windows 资源管理器中可见的网络设备。 NetUseAdd 相当于在 DOS 提示符下调用 net use 以在远程计算机上进行身份验证。

如果您调用 NetUseAdd,那么后续访问该目录的尝试应该会成功。

【讨论】:

  • @Adam Robinson:这不是真的。没有这样的 WNetUseConnection2 和 WNetUseConnection3。我认为您正在考虑 WNetAddConnection 被 WNetAddConnection2 和 WnetAddConnection3 取代。此外,您提供的有关它的信息也不真实。
  • WNetUseConnection 类似于 WNetAddConnection3,但它还具有创建映射本地驱动器的可选功能。您不必使用它。
  • @BrianR.Bondy 它们确实存在,只是没有实现为 C#。来源:docs.microsoft.com/da-dk/windows/win32/api/lmuse/… 引用:“您还可以使用 WNetAddConnection2 和 WNetAddConnection3 函数将本地设备重定向到网络资源。”
【解决方案5】:

虽然我不了解自己,但我当然希望 #2 是不正确的...我想 Windows 不会自动提供我的登录信息(至少是我的所有密码!)任何机器,更不用说我不信任的机器了。

无论如何,您是否探索过模拟架构?您的代码将如下所示:

using (System.Security.Principal.WindowsImpersonationContext context = System.Security.Principal.WindowsIdentity.Impersonate(token))
{
    // Do network operations here

    context.Undo();
}

在这种情况下,token 变量是一个 IntPtr。为了获取此变量的值,您必须调用非托管 LogonUser Windows API 函数。快速访问pinvoke.net 给我们以下签名:

[System.Runtime.InteropServices.DllImport("advapi32.dll", SetLastError = true)]
public static extern bool LogonUser(
    string lpszUsername,
    string lpszDomain,
    string lpszPassword,
    int dwLogonType,
    int dwLogonProvider,
    out IntPtr phToken
);

用户名、域和密码应该看起来相当明显。查看可以传递给 dwLogonType 和 dwLogonProvider 的各种值,以确定最适合您需求的值。

此代码尚未经过测试,因为我在这里没有可以验证的第二个域,但这有望使您走上正轨。

【讨论】:

  • 当您尝试使用来自不受信任域的登录 ID 时,模拟将不起作用。用户 id 必须能够在本地登录。
  • 是的,我们尝试了这条路线,结果就像@Moose 所说的那样:该域不受信任,因此无法进行模拟。
  • 是的,一旦我看到该评论,这就是我使用 NetUseAdd 发布答案的原因(它与 WNetUseConnection 和 WNetAddConnection 函数之间的主要区别在于 NetUseAdd 不会使连接在 Windows 资源管理器中可见)。
  • 模拟在同一个域上都不起作用,在我的测试中,它一直响应我拒绝访问尝试使用管理员帐户(两台机器上的管理员)读取共享文件夹中的文件。所以,我认为这不是正确的方法。
【解决方案6】:

这里是一个最小的 POC 类,去掉了所有的杂物

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;

public class UncShareWithCredentials : IDisposable
{
    private string _uncShare;

    public UncShareWithCredentials(string uncShare, string userName, string password)
    {
        var nr = new Native.NETRESOURCE
        {
            dwType = Native.RESOURCETYPE_DISK,
            lpRemoteName = uncShare
        };

        int result = Native.WNetUseConnection(IntPtr.Zero, nr, password, userName, 0, null, null, null);
        if (result != Native.NO_ERROR)
        {
            throw new Win32Exception(result);
        }
        _uncShare = uncShare;
    }

    public void Dispose()
    {
        if (!string.IsNullOrEmpty(_uncShare))
        {
            Native.WNetCancelConnection2(_uncShare, Native.CONNECT_UPDATE_PROFILE, false);
            _uncShare = null;
        }
    }

    private class Native
    {
        public const int RESOURCETYPE_DISK = 0x00000001;
        public const int CONNECT_UPDATE_PROFILE = 0x00000001;
        public const int NO_ERROR = 0;

        [DllImport("mpr.dll")]
        public static extern int WNetUseConnection(IntPtr hwndOwner, NETRESOURCE lpNetResource, string lpPassword, string lpUserID,
            int dwFlags, string lpAccessName, string lpBufferSize, string lpResult);

        [DllImport("mpr.dll")]
        public static extern int WNetCancelConnection2(string lpName, int dwFlags, bool fForce);

        [StructLayout(LayoutKind.Sequential)]
        public class NETRESOURCE
        {
            public int dwScope;
            public int dwType;
            public int dwDisplayType;
            public int dwUsage;
            public string lpLocalName;
            public string lpRemoteName;
            public string lpComment;
            public string lpProvider;
        }
    }
}

您可以直接使用\\server\share\folder w/WNetUseConnection,无需事先将其剥离到\\server 部分。

【讨论】:

    【解决方案7】:

    大多数 SFTP 服务器也支持 SCP,这可以更容易找到库。您甚至可以从您的代码中调用现有客户端,例如 PuTTY 中包含的 pscp。

    如果您使用的文件类型很简单,例如文本或 XML 文件,您甚至可以编写自己的客户端/服务器实现来使用 .NET Remoting 或 Web 之类的东西来操作文件服务。

    【讨论】:

      【解决方案8】:

      我附上我的 vb.net 代码 基于 brian 参考

      Imports System.ComponentModel
      
      Imports System.Runtime.InteropServices
      
      Public Class PinvokeWindowsNetworking
      
      Const NO_ERROR As Integer = 0
      
      
      
      Private Structure ErrorClass
      
          Public num As Integer
      
          Public message As String
      
      
      
          Public Sub New(ByVal num As Integer, ByVal message As String)
      
              Me.num = num
      
              Me.message = message
      
          End Sub
      
      End Structure
      
      
      
      Private Shared ERROR_LIST As ErrorClass() = New ErrorClass() {
      
          New ErrorClass(5, "Error: Access Denied"),
      
          New ErrorClass(85, "Error: Already Assigned"),
      
          New ErrorClass(1200, "Error: Bad Device"),
      
          New ErrorClass(67, "Error: Bad Net Name"),
      
          New ErrorClass(1204, "Error: Bad Provider"),
      
          New ErrorClass(1223, "Error: Cancelled"),
      
          New ErrorClass(1208, "Error: Extended Error"),
      
          New ErrorClass(487, "Error: Invalid Address"),
      
          New ErrorClass(87, "Error: Invalid Parameter"),
      
          New ErrorClass(1216, "Error: Invalid Password"),
      
          New ErrorClass(234, "Error: More Data"),
      
          New ErrorClass(259, "Error: No More Items"),
      
          New ErrorClass(1203, "Error: No Net Or Bad Path"),
      
          New ErrorClass(1222, "Error: No Network"),
      
          New ErrorClass(1206, "Error: Bad Profile"),
      
          New ErrorClass(1205, "Error: Cannot Open Profile"),
      
          New ErrorClass(2404, "Error: Device In Use"),
      
          New ErrorClass(2250, "Error: Not Connected"),
      
          New ErrorClass(2401, "Error: Open Files")}
      
      
      
      Private Shared Function getErrorForNumber(ByVal errNum As Integer) As String
      
          For Each er As ErrorClass In ERROR_LIST
      
              If er.num = errNum Then Return er.message
      
          Next
      
      
      
          Try
      
              Throw New Win32Exception(errNum)
      
          Catch ex As Exception
      
              Return "Error: Unknown, " & errNum & " " & ex.Message
      
          End Try
      
      
      
          Return "Error: Unknown, " & errNum
      
      End Function
      
      
      
      <DllImport("Mpr.dll")>
      
      Private Shared Function WNetUseConnection(ByVal hwndOwner As IntPtr, ByVal lpNetResource As NETRESOURCE, ByVal lpPassword As String, ByVal lpUserID As String, ByVal dwFlags As Integer, ByVal lpAccessName As String, ByVal lpBufferSize As String, ByVal lpResult As String) As Integer
      
      End Function
      
      
      
      <DllImport("Mpr.dll")>
      
      Private Shared Function WNetCancelConnection2(ByVal lpName As String, ByVal dwFlags As Integer, ByVal fForce As Boolean) As Integer
      
      End Function
      
      
      
      <StructLayout(LayoutKind.Sequential)>
      
      Private Class NETRESOURCE
      
          Public dwScope As Integer = 0
      
          Public dwType As Integer = 0
      
          Public dwDisplayType As Integer = 0
      
          Public dwUsage As Integer = 0
      
          Public lpLocalName As String = ""
      
          Public lpRemoteName As String = ""
      
          Public lpComment As String = ""
      
          Public lpProvider As String = ""
      
      End Class
      
      
      
      Public Shared Function connectToRemote(ByVal remoteUNC As String, ByVal username As String, ByVal password As String) As String
      
          Return connectToRemote(remoteUNC, username, password, False)
      
      End Function
      
      
      
      Public Shared Function connectToRemote(ByVal remoteUNC As String, ByVal username As String, ByVal password As String, ByVal promptUser As Boolean) As String
      
          Dim nr As NETRESOURCE = New NETRESOURCE()
      
          nr.dwType = ResourceTypes.Disk
      
          nr.lpRemoteName = remoteUNC
      
          Dim ret As Integer
      
      
      
          If promptUser Then
      
              ret = WNetUseConnection(IntPtr.Zero, nr, "", "", Connects.Interactive Or Connects.Prompt, Nothing, Nothing, Nothing)
      
          Else
      
              ret = WNetUseConnection(IntPtr.Zero, nr, password, username, 0, Nothing, Nothing, Nothing)
      
          End If
      
      
      
          If ret = NO_ERROR Then Return Nothing
      
          Return getErrorForNumber(ret)
      
      End Function
      
      
      
      Public Shared Function disconnectRemote(ByVal remoteUNC As String) As String
      
          Dim ret As Integer = WNetCancelConnection2(remoteUNC, Connects.UpdateProfile, False)
      
          If ret = NO_ERROR Then Return Nothing
      
          Return getErrorForNumber(ret)
      
      End Function
      
      
      Enum Resources As Integer
      
          Connected = &H1
      
          GlobalNet = &H2
      
          Remembered = &H3
      
      End Enum
      
      
      Enum ResourceTypes As Integer
      
          Any = &H0
      
          Disk = &H1
      
          Print = &H2
      
      End Enum
      
      
      Enum ResourceDisplayTypes As Integer
      
          Generic = &H0
      
          Domain = &H1
      
          Server = &H2
      
          Share = &H3
      
          File = &H4
      
          Group = &H5
      
      End Enum
      
      
      Enum ResourceUsages As Integer
      
          Connectable = &H1
      
          Container = &H2
      
      End Enum
      
      
      Enum Connects As Integer
      
          Interactive = &H8
      
          Prompt = &H10
      
          Redirect = &H80
      
          UpdateProfile = &H1
      
          CommandLine = &H800
      
          CmdSaveCred = &H1000
      
          LocalDrive = &H100
      
      End Enum
      
      
      End Class
      

      如何使用它

      Dim login = PinvokeWindowsNetworking.connectToRemote("\\ComputerName", "ComputerName\UserName", "Password")
      
          If IsNothing(login) Then
      
      
      
              'do your thing on the shared folder
      
      
      
             PinvokeWindowsNetworking.disconnectRemote("\\ComputerName")
      
          End If
      

      【讨论】:

        【解决方案9】:

        我向 MS 寻求答案。第一个解决方案假定运行应用程序进程的用户帐户有权访问共享文件夹或驱动器(相同域)。确保您的 DNS 已解析或尝试使用 IP 地址。只需执行以下操作:

         DirectoryInfo di = new DirectoryInfo(PATH);
         var files = di.EnumerateFiles("*.*", SearchOption.AllDirectories);
        

        如果您想要跨不同域的 .NET 2.0 使用凭据,请遵循此模型:

        WebRequest req = FileWebRequest.Create(new Uri(@"\\<server Name>\Dir\test.txt"));
        
                req.Credentials = new NetworkCredential(@"<Domain>\<User>", "<Password>");
                req.PreAuthenticate = true;
        
                WebResponse d = req.GetResponse();
                FileStream fs = File.Create("test.txt");
        
                // here you can check that the cast was successful if you want. 
                fs = d.GetResponseStream() as FileStream;
                fs.Close();
        

        【讨论】:

        • 看起来很有趣
        猜你喜欢
        • 2011-11-25
        • 1970-01-01
        • 2019-01-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-06-07
        • 1970-01-01
        相关资源
        最近更新 更多