连接到远程共享文件夹会导致"多个连接不允许"错误,但尝试断开连接会导致"连接不存在"

w12*_*128 7 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 
Run Code Online (Sandbox Code Playgroud)

只要在调用连接到子文件夹时没有与文件夹(即\\some.domain.net\Shared)或其他共享子文件夹(或通常是任何文件夹\\some.domain.net)建立连接,这就可以正常工作.即,在连接到子文件夹之前,请考虑返回:WNetUseConnection()net use

Status       Local     Remote
------------------------------------------------
OK                     \\some.domain.net\Shared
Run Code Online (Sandbox Code Playgroud)

现在我想连接到共享子文件夹\\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. 
Run Code Online (Sandbox Code Playgroud)

因此,尽管提供了不同的访问凭据,但Windows(Server 2008 R2)似乎并未认识到\\some.domain.net\Shared和之间存在\\some.domain.net\Shared\Subfolder1差异.但是,尝试在使用错误1219的情况下取消连接

WNetCancelConnection2(@"\\some.domain.net\Shared\Subfolder1", 0, true); // error 2250
Run Code Online (Sandbox Code Playgroud)

导致错误2250:

This network connection does not exist.
Run Code Online (Sandbox Code Playgroud)

因此,我似乎首先需要手动取消所有打开的连接,\\some.domain.net\因为它看起来一次只能打开一个 - 但是,由于另一个进程可能同时访问连接的共享文件夹,这似乎不太健壮时间.

有没有办法解决这个问题并且在同一台远程计算机上有多个共享文件夹的活动连接?

小智 6

这是一个古老的话题,但非常现实且有问题。我会尽力阐明这一点,因为几年来我一直在处理此类问题。

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

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

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

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

最后:那么唯一的解决方案是在使用指定的网络共享后断开用户连接,然后所有其他问题就开始了...连接被许多阻止其他人登录的东西使用。例如,有人从网络共享打开Word文档 -现在您无法断开连接!但 net.exe 不会显示任何连接!另一个但是,当您在一段时间(大约一分钟)后关闭 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);
  }
}
}
Run Code Online (Sandbox Code Playgroud)

以及其他类:

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;
    };
  }
Run Code Online (Sandbox Code Playgroud)

还有最后一个:

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;
    }
  }
Run Code Online (Sandbox Code Playgroud)

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

重要提示:如果您不正确设置它,就会出现问题:CONNECT_TEMPORARY 选项。如果未设置,则 Windows 将记住已安装的驱动器,并在计算机重新启动后尝试连接它们,从而导致错误:无法连接某些驱动器:) 烦人的:)


sim*_*rcl 2

好吧——就是问题所在。它给出了一些建议的解决方案;两者对我来说听起来都有点像男人,但对你来说可能没问题。听起来这种行为是设计使然(可能是出于安全考虑)。

干杯-