简体   繁体   English

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

[英]Connecting to a remote shared folder results in “multiple connections not allowed” error, but trying to disconnect causes “connection does not exist”

I have a shared network folder \\\\some.domain.net\\Shared that contains multiple shared subfolders with different permissions for different users. 我有一个共享网络文件夹\\\\some.domain.net\\Shared ,它包含多个共享子文件夹,具有不同用户的不同权限。 I wish to open connections to multiple subfolders from the same Windows account, but with different credentials - is this possible without having to disconnect other connections to the same share first? 我希望从同一个Windows帐户打开多个子文件夹的连接,但是使用不同的凭据 - 这是否可以在不必先断开与同一共享的其他连接的情况下进行?

To be exact: in a C# method, I try to connect to a specific subfolder using WNetUseConnection() (p/invoke) in the manner of: 确切地说:在C#方法中,我尝试使用WNetUseConnection() (p / invoke)以下列方式连接到特定的子文件夹:

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

This works fine as long as there is no connection already established to the either root folder (ie \\\\some.domain.net\\Shared ) or another shared subfolder (or, in general, to any folder on \\\\some.domain.net ) at the moment of WNetUseConnection() invocation to connect to a subfolder . 只要没有已建立到任一根文件夹(即\\\\some.domain.net\\Shared )或其他共享子文件夹(或通常, \\\\some.domain.net上的任何文件夹)的连接,这都可以\\\\some.domain.net )在WNetUseConnection()调用时连接到子文件夹 Ie, consider that before connecting to a subfolder, net use returns: 即,在连接到子文件夹之前,请考虑net use返回:

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

Now I want to also connect to a shared subfolder \\\\some.domain.net\\Shared\\Subfolder1 as shown at the top of this post. 现在我想连接到共享子文件夹\\\\some.domain.net\\Shared\\Subfolder1 ,如本文顶部所示。 This will result in windows error 1219: 这将导致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. 

So Windows (Server 2008 R2) doesn't seem to recognize a difference between \\\\some.domain.net\\Shared and \\\\some.domain.net\\Shared\\Subfolder1 , despite different access credentials provided. 因此,尽管提供了不同的访问凭据,但Windows(Server 2008 R2)似乎并未识别出\\\\some.domain.net\\Shared\\\\some.domain.net\\Shared\\Subfolder1区别。 However, trying to cancel the connection in case of error 1219 by using 但是,尝试在使用错误1219的情况下取消连接

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

results in error 2250: 导致错误2250:

This network connection does not exist.

So it seems that I would first need to manually cancel all open connections to \\\\some.domain.net\\ as it looks like only one can be opened at a time - however, this doesn't seem very robust as another process might be accessing the connected shared folder at the same time. 因此,我似乎首先需要手动取消所有打开的连接到\\\\some.domain.net\\因为它看起来一次只能打开一个 - 但是,这似乎不是很强大,因为另一个进程可能是同时访问连接的共享文件夹。

Are there ways to resolve this and have active connections to multiple shared folders on the same remote machine? 有没有办法解决这个问题并且在同一台远程计算机上有多个共享文件夹的活动连接?

It's an old topic but very actual and problematic. 这是一个古老的话题,但非常实际和有问题。 I'll try to put some light on it cos I've been dealing whit such problems for couple of years now. 我会尝试对它进行一些说明,因为我已经处理了几年这样的问题了。

First of all: Windows doesnt allow you to connect to multiple subfolders in one network share. 首先:Windows不允许您连接到一个网络共享中的多个子文件夹。

Second of all: Windows is identifying connection by remote name. 其次:Windows通过远程名称识别连接。 So you can establish more than one connection to the same server whith different names like: www.serverName.com and 123.123.123.123 (by ip) - these will be treated as separate connections with different credentials. 因此,您可以建立多个不同名称的同一服务器的连接,例如:www.serverName.com和123.123.123.123(通过ip) - 这些将被视为具有不同凭据的单独连接。

So my solution was to add alias IP to my server. 所以我的解决方案是将别名IP添加到我的服务器。 I've created ten aliases of my server and my app was taking first IP from the list then if it was blocked then next etc. 我已经创建了10个服务器的别名,我的应用程序从列表中获取了第一个IP然后是否被阻止然后是下一个等等。

This solution is not very good but it works. 这个解决方案不是很好但是有效。 problem is when you dont have access to server IP. 问题是当你没有访问服务器IP时。 What then? 然后怎样呢? See next point: 见下一点:

Last of all: Then the only solution is to disconnect user after using specified network share and here begins all other problems... connections are used by many things that are blocking others to log in. For example someone opens Word document from network share - now you cannot disconnect! 最后:然后唯一的解决方案是在使用指定的网络共享后断开用户,这里开始所有其他问题...连接被许多阻止他人登录的东西使用。例如有人从网络共享打开Word文档 - 现在你不能断开! BUT net.exe will not show any connections! 但是net.exe不会显示任何连接! Another BUT when you close Word document after some time (about a minute) connection will by automatically closed and will allow new connections. 另一个但是在一段时间(大约一分钟)连接后关闭Word文档将自动关闭并允许新连接。

My work is now directed to find whitch system elements are blocking connections and notify user: Close Word and you will be able to login. 我的工作现在指向查找whitch系统元素阻止连接并通知用户:关闭Word,您将能够登录。 Hopfully it can be done. 有意思的是,它可以完成。

PS. PS。 I'm working all with WinApi cos net.exe is working much slower and offers less options. 我正在使用WinApi cos net.exe工作速度慢得多,提供的选项更少。

If someone needs source code: 如果有人需要源代码:

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);
  }
}
}

And other classes: 和其他课程:

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;
    };
  }

And the las one: 而拉斯一:

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;
    }
  }

You dont need OperationResult it is just simple error container, not needed. 你不需要OperationResult它只是简单的错误容器,不需要。 Base class ServerProcessorBase contains only one field serverAddress. 基类ServerProcessorBase仅包含一个字段serverAddress。

IMPORTANT: This is a problem maker when you wont set it up properly: CONNECT_TEMPORARY option. 重要提示:如果您没有正确设置它,这是一个问题制造者:CONNECT_TEMPORARY选项。 If not set then windows will remember mounted drives and will try to connect them after computer restart ofcource causing error: can not connect some drives :) anoying :) 如果没有设置,那么Windows将记住已安装的驱动器,并将尝试连接它们后计算机重新启动源导致错误:无法连接一些驱动器:) anoying :)

OK - this is the problem. 好的 - 就是问题所在。 It gives a couple of suggested solutions; 它给出了一些建议的解决方案; both sound a bit manky to me, but might be OK for you. 这对我来说听起来有点古怪,但对你来说可能还行。 It sounds like this behavior is by design (probably a security consideration). 听起来这种行为是设计的(可能是安全考虑因素)。

Cheers - 干杯 -

I want to share the solution I used for error code 1219 while mapping a drive with a shared path using WNetCancelConnection2() even though this a different function call, I feel this approach may resolve. 我想分享我用于错误代码1219的解决方案,同时使用WNetCancelConnection2()映射驱动器与共享路径,即使这是一个不同的函数调用,我觉得这种方法可能会解决。

First of all, you need to make sure how your computers are organized in a network. 首先,您需要确保计算机在网络中的组织方式。

If it is in Domain: 如果它在域中:

Your username should be [DomainName]\\[UserName], sometimes you can simply use [UserName]. 您的用户名应为[DomainName] \\ [UserName],有时您只需使用[UserName]即可。

var userName = string.IsNullOrEmpty(credentials.Domain) ? credentials.UserName : string.Format(@"{0}\{1}", credentials.Domain, credentials.UserName);

If it is in Workgroup: 如果它在工作组中:

Your username should be [ServerName]\\[UserName], never use [UserName]. 您的用户名应为[ServerName] \\ [UserName],永远不要使用[UserName]。

Here ServerName is the hostname of your shared path. 这里ServerName是共享路径的主机名。

var userName = string.Format(@"{0}\{1}", serverMachineName, credentials.UserName);

Note : Workgroup solution works only if the passed username is the current login username. 注意 :仅当传递的用户名是当前登录用户名时,Workgroup解决方案才有效。 If you are using Windows Service then just change the log on as with the specific user credentials 如果您使用的是Windows服务,则只需使用特定的用户凭据更改登录

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM