This is an old topic, but very relevant and problematic. I will try to shed light on this, because I have been dealing with such problems for a couple of years.
First of all: Windows does not allow you to connect to multiple subfolders in the same network share.
In second place: Windows identifies the connection by the remote name. Thus, you can establish more than one connection to the same server with different names, such as: www.serverName.com and 123.123.123.123 (via ip) - they will be considered as separate connections with different credentials.
So I decided to add an IP alias to my server. I created ten aliases for my server, and my application took the first IP address from the list, then if it was blocked, then the next, etc.
This solution is not very good, but it works. The problem is that you do not have access to the IP address of the server. What then? See the next item:
Last of all: Then the only solution is to disconnect the user after using the specified network share, and here all the other problems begin ... connections are used by many things that block others from entering the system. For example, someone opens a Word document from a network share - now you canβt disconnect! BUT net.exe will not show any connections! Another BUT, when you close a Word document after a while (about a minute), the connection automatically closes and allows new connections.
My job now is to find system elements that block connections and notify the user: close Word and you can log in. Hope this can be done.
PS. I all work with WinApi because net.exe is much slower and offers fewer options.
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;
And the last 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 do not need OperationResult, it is just a container of errors, it is not needed. The ServerProcessorBase base class contains only one serverAddress field.
IMPORTANT: This is a problem if you do not configure it correctly: the CONNECT_TEMPORARY option. If not installed, then Windows will remember the mapped drives and try to connect them after the computer restarts due to an error: it is not possible to connect some drives :) annoying :)