c# - 连接到远程共享文件夹导致 "multiple connections not allowed"错误,但尝试断开连接导致 "connection does not exist"

标签 c# windows winapi remote-access network-share

我有一个共享网络文件夹 \\some.domain.net\Shared,其中包含多个共享子文件夹,对不同的用户具有不同的权限。我希望从同一个 Windows 帐户打开到多个子文件夹的连接,但使用不同的凭据 - 这是否可能而无需先断开与同一共享的其他连接?

确切地说:在 C# 方法中,我尝试使用 WNetUseConnection() (p/invoke) 以以下方式连接到特定子文件夹:

ConnectToSharedFolder("\\some.domain.net\Shared\Subfolder1", user, password); // calls WNetUseConnection() internally 

只要没有与 root 文件夹(即 \\some.domain.net\Shared)或 建立连接,这就可以正常工作在 WNetUseConnection() 调用时连接到另一个共享子文件夹(或者,一般来说,\\some.domain.net 上的任何文件夹) 一个子文件夹。即,考虑在连接到子文件夹之前,net use 返回:

Status       Local     Remote
------------------------------------------------
OK                     \\some.domain.net\Shared

现在我还想连接到共享子文件夹 \\some.domain.net\Shared\Subfolder1,如本文顶部所示。这将导致 Windows 错误 1219:

Multiple connections to a server or shared resource by the same user, using more than one user name, are not allowed. Disconnect all previous connections to the server or shared resource and try again. 

因此 Windows(Server 2008 R2)似乎无法识别 \\some.domain.net\Shared\\some.domain.net\Shared\Subfolder1 之间的区别,尽管提供了不同的访问凭据。但是,在出现错误 1219 的情况下尝试使用

取消连接
WNetCancelConnection2(@"\\some.domain.net\Shared\Subfolder1", 0, true); // error 2250

导致错误 2250:

This network connection does not exist.

所以我似乎首先需要手动取消所有打开的连接到 \\some.domain.net\ 因为它看起来一次只能打开一个 - 然而,这不会' 看起来非常可靠,因为另一个进程可能会同时访问连接的共享文件夹。

有没有办法解决这个问题并与同一台远程计算机上的多个共享文件夹建立事件连接?

最佳答案

这是一个古老的话题,但非常实际且存在问题。我会尝试阐明它,因为我已经处理这些问题好几年了。

首先: Windows 不允许您连接到一个网络共享中的多个子文件夹。

其次: Windows 正在通过远程名称识别连接。 因此,您可以使用不同的名称与同一台服务器建立多个连接,例如: www.serverName.com 和 123.123.123.123(通过 ip)- 这些将被视为具有不同凭据的单独连接。

所以我的解决方案是为我的服务器添加别名 IP。我已经为我的服务器创建了 10 个别名,我的应用程序从列表中获取第一个 IP,如果它被阻止,那么下一个等等。

这个解决方案不是很好,但它有效。问题是当您无权访问服务器 IP 时。然后怎样呢?请看下一点:

最后: 然后唯一的解决方案是在使用指定的网络共享后断开用户连接,所有其他问题都从这里开始......连接被许多阻止其他人登录的东西使用。例如有人从网络共享打开 Word 文档 - 现在你无法断开连接!但是 net.exe 不会显示任何连接!另一个 BUT 当您在一段时间(大约一分钟)后关闭 Word 文档时,连接将自动关闭并允许新连接。

我现在的工作是查找阻止连接的系统元素并通知用户:关闭 Word,您将能够登录。 希望可以做到。

附言。我正在使用 WinApi,因为 net.exe 的运行速度要慢得多并且提供的选项更少。

如果有人需要源码:

public ServerWinProcessor(string serverAddress)
  : base(serverAddress)
{

}

[DllImport("mpr.dll")]
public static extern int WNetAddConnection2(ref NETRESOURCE netResource, string password, string username, uint flags);

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

[DllImport("mpr.dll")]
public static extern int WNetOpenEnum(int dwScope, int dwType, int dwUsage, NETRESOURCE2 lpNetResource, out IntPtr lphEnum);

[DllImport("Mpr.dll", EntryPoint = "WNetCloseEnum", CallingConvention = CallingConvention.Winapi)]
private static extern int WNetCloseEnum(IntPtr hEnum);

[DllImport("mpr.dll")]
private static extern int WNetEnumResource(IntPtr hEnum, ref uint lpcCount, IntPtr buffer, ref uint lpBufferSize);

public OperationResult LoginToNetworkShare(string userName, string password, string shareName)
{
  return LoginToNetworkShare(userName, password, shareName, null);
}

public OperationResult LoginToNetworkShare(string userName, string password, string shareName, string shareDrive)
{
  NETRESOURCE nr = new NETRESOURCE();
  nr.dwType = RESOURCETYPE_DISK;
  nr.lpLocalName = shareDrive;
  nr.lpRemoteName = @"\\" + ServerAddress + @"\" + shareName;

  int result = WNetAddConnection2(ref nr, password, userName, CONNECT_TEMPORARY);
  return new OperationResult(result);
}

public Task<OperationResult> LoginToNetworkShareAsync(string userName, string password, string shareName, string shareDrive)
{
  return Task.Factory.StartNew(() =>
  {
    return LoginToNetworkShare(userName, password, shareName, shareDrive);
  });
}

public OperationResult LogoutFromNetworkSharePath(string sharePath)
{
  int result = WNetCancelConnection2(sharePath, CONNECT_UPDATE_PROFILE, true);
  return new OperationResult(result);
}

public OperationResult LogoutFromNetworkShare(string shareName)
{
  int result = WNetCancelConnection2(@"\\" + ServerAddress + @"\" + shareName, CONNECT_UPDATE_PROFILE, true);
  return new OperationResult(result);
}

public OperationResult LogoutFromNetworkShareDrive(string driveLetter)
{
  int result = WNetCancelConnection2(driveLetter, CONNECT_UPDATE_PROFILE, true);
  return new OperationResult(result);
}

private ArrayList EnumerateServers(NETRESOURCE2 pRsrc, int scope, int type, int usage, ResourceDisplayType displayType)
{
  ArrayList netData = new ArrayList();
  ArrayList aData = new ArrayList();
  uint bufferSize = 16384;
  IntPtr buffer = Marshal.AllocHGlobal((int)bufferSize);
  IntPtr handle = IntPtr.Zero;
  int result;
  uint cEntries = 1;

  result = WNetOpenEnum(scope, type, usage, pRsrc, out handle);

  if (result == NO_ERROR)
  {
    do
    {
      result = WNetEnumResource(handle, ref cEntries, buffer, ref bufferSize);

      if (result == NO_ERROR)
      {
        Marshal.PtrToStructure(buffer, pRsrc);

        if (string.IsNullOrWhiteSpace(pRsrc.lpLocalName) == false && pRsrc.lpRemoteName.Contains(ServerAddress))
          if (aData.Contains(pRsrc.lpLocalName) == false)
          {
            aData.Add(pRsrc.lpLocalName);
            netData.Add(new NetworkConnectionInfo(null, pRsrc.lpLocalName));
          }

        if (aData.Contains(pRsrc.lpRemoteName) == false && pRsrc.lpRemoteName.Contains(ServerAddress))
        {
          aData.Add(pRsrc.lpRemoteName);
          netData.Add(new NetworkConnectionInfo(pRsrc.lpRemoteName, null));
        }

        if ((pRsrc.dwUsage & RESOURCEUSAGE_CONTAINER) == RESOURCEUSAGE_CONTAINER)
          netData.AddRange(EnumerateServers(pRsrc, scope, type, usage, displayType));
      }
      else if (result != ERROR_NO_MORE_ITEMS)
        break;
    } while (result != ERROR_NO_MORE_ITEMS);

    WNetCloseEnum(handle);
  }

  Marshal.FreeHGlobal(buffer);
  return netData;
}

public void CloseAllConnections()
{
  NETRESOURCE2 res = new NETRESOURCE2();
  ArrayList aData = EnumerateServers(res, RESOURCE_CONNECTED, 0, 0, ResourceDisplayType.RESOURCEDISPLAYTYPE_NETWORK);

  foreach (NetworkConnectionInfo item in aData)
  {
    if (item.IsRemoteOnly)
      LogoutFromNetworkSharePath(item.RemoteName);
    else
      LogoutFromNetworkShareDrive(item.LocalName);
  }
}
}

和其他类:

public static class Consts
  {
    public const int RESOURCETYPE_DISK = 0x1;
    public const int CONNECT_TEMPORARY = 0x00000004;
    public const int CONNECT_UPDATE_PROFILE = 0x00000001;
    public const int RESOURCE_GLOBALNET = 0x00000002;
    public const int RESOURCE_CONNECTED = 0x00000001;
    public const int RESOURCEDISPLAYTYPE_SERVER = 0x00000002;
    public const int RESOURCEUSAGE_CONTAINER = 0x00000002;

    public const int NO_ERROR = 0x000;
    public const int ERROR_NOT_CONNECTED = 0x8CA;
    public const int ERROR_LOGON_FAILURE = 0x52E;
    public const int ERROR_SESSION_CREDENTIAL_CONFLICT = 0x4C3;
    public const int ERROR_ALREADY_ASSIGNED = 0x55;
    public const int ERROR_INVALID_PASSWORD = 0x56;
    public const int ERROR_INVALID_PARAMETER = 0x57;
    public const int ERROR_NO_MORE_ITEMS = 0x103;
    //public const int ERROR_BAD_PROFILE = 0x4B6;
    //public const int ERROR_CANNOT_OPEN_PROFILE = 0x4B5;
    //public const int ERROR_DEVICE_IN_USE = 0x964;
    //public const int ERROR_EXTENDED_ERROR = 0x4B8;
    //public const int ERROR_OPEN_FILES = 0x961;

    public enum ResourceDisplayType
    {
      RESOURCEDISPLAYTYPE_GENERIC,
      RESOURCEDISPLAYTYPE_DOMAIN,
      RESOURCEDISPLAYTYPE_SERVER,
      RESOURCEDISPLAYTYPE_SHARE,
      RESOURCEDISPLAYTYPE_FILE,
      RESOURCEDISPLAYTYPE_GROUP,
      RESOURCEDISPLAYTYPE_NETWORK,
      RESOURCEDISPLAYTYPE_ROOT,
      RESOURCEDISPLAYTYPE_SHAREADMIN,
      RESOURCEDISPLAYTYPE_DIRECTORY,
      RESOURCEDISPLAYTYPE_TREE,
      RESOURCEDISPLAYTYPE_NDSCONTAINER
    };

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

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

最后一个:

public class NetworkConnectionInfo
  {
    public string RemoteName { get; set; }
    public string LocalName { get; set; }

    public bool IsRemoteOnly { get; set; }

    public NetworkConnectionInfo(string remoteName, string localName)
    {
      RemoteName = remoteName;
      LocalName = localName;

      if (string.IsNullOrWhiteSpace(localName))
        IsRemoteOnly = true;
    }
  }

您不需要 OperationResult,它只是简单的错误容器,不需要。 基类ServerProcessorBase 只包含一个字段serverAddress。

重要提示:如果您无法正确设置它,这将是一个问题制造者:CONNECT_TEMPORARY 选项。如果未设置,Windows 将记住已安装的驱动器,并会在计算机重启后尝试连接它们,导致错误:无法连接某些驱动器:) 烦人:)

关于c# - 连接到远程共享文件夹导致 "multiple connections not allowed"错误,但尝试断开连接导致 "connection does not exist",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20976349/

相关文章:

c# - 策划游戏的主菜单

c++ - 如何创建跨平台窗口处理程序?

c++ - 全局变量的行为在不同的函数中有所不同

c++ - 从编译器运行和作为独立 exe 运行时,进程访问权限似乎有所不同

c# - 如何通过 CancellationToken 停止异步进程?

c# - 将 html 转换为 pdf 时,iTextSharp 中的 HtmlTags 不起作用

mysql - 使用 .sql.gz 文件还原数据库时,gunzip 附近的 SQL 语法错误

c++ - Windows GUI + 控制台输出,Linux 风格

c - 编辑控件和 WM_CONTEXTMENU 消息

c# - 比较对象列表