using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Security.Principal; using System.Text; using Vanara.Extensions; using Vanara.Security; using static Vanara.PInvoke.Mpr; namespace Vanara { /// Information about a remote resource, usually in reference to a connection to that resource. public class NetworkDeviceConnection : IEquatable { internal readonly NETRESOURCE netRes = default; internal WindowsIdentity identity; private NetworkDeviceConnectionCollection children; internal NetworkDeviceConnection(NETRESOURCE r, WindowsIdentity user) { if (r is null) throw new ArgumentNullException(nameof(r)); netRes = r; identity = user; } internal NetworkDeviceConnection(string remoteName, string localName = null, string provider = null, NETRESOURCEType type = NETRESOURCEType.RESOURCETYPE_ANY) { if (string.IsNullOrEmpty(remoteName)) throw new ArgumentNullException(nameof(remoteName)); netRes = new NETRESOURCE(remoteName, localName, provider) { dwType = type }; } /// Gets the children of this resource if it is a container. /// The children resources. public NetworkDeviceConnectionCollection Children => children ??= new NetworkDeviceConnectionCollection(identity, netRes); /// A string that contains a comment supplied by the network provider. public string Comment => netRes.lpComment; /// /// A string that contains the name of the provider that owns the resource. This member can be if the /// provider name is unknown. /// public string Provider => netRes.lpProvider; /// The display options for the network object in a network browsing user interface. public NETRESOURCEDisplayType ResourceDisplayType => netRes.dwDisplayType; /// The type of resource. public NETRESOURCEType ResourceType => netRes.dwType; /// A set of flags describing how the resource can be used. public NETRESOURCEUsage Use => netRes.dwUsage; /// The name of a local device. This member is if the connection does not use a device. public string LocalName => netRes.lpLocalName; /// /// If the entry is a network resource, this member is a string that specifies the remote network name. /// If the entry is a current or persistent connection, this member is the network name associated with . /// public string RemoteName => netRes.lpRemoteName; /// Makes a connection to a network resource and can redirect a local device to the network resource. /// /// A string that specifies the network resource to connect to. The string must follow the network provider's naming conventions. /// /// /// A string that specifies the name of a local device to redirect, such as "F:" or "LPT1". The string is treated in a /// case-insensitive manner. If the string is empty or , the function makes a connection to the network /// resource without redirecting a local device. /// /// /// A pointer to a constant null-terminated string that specifies a user name for making the connection. /// /// If lpUserName is NULL, the function uses the default user name. (The user context for the process provides the default /// user name.) /// /// /// The lpUserName parameter is specified when users want to connect to a network resource for which they have been assigned a user /// name or account other than the default user name or account. /// /// The user-name string represents a security context. It may be specific to a network provider. /// Windows Me/98/95: This parameter must be NULL or an empty string. /// /// /// A pointer to a constant null-terminated string that specifies a password to be used in making the network connection. /// /// If lpPassword is NULL, the function uses the current default password associated with the user specified by the /// lpUserName parameter. /// /// If lpPassword points to an empty string, the function does not use a password. /// /// If the connection fails because of an invalid password and the CONNECT_INTERACTIVE value is set in the dwFlags parameter, the /// function displays a dialog box asking the user to type the password. /// /// Windows Me/98/95: This parameter must be NULL or an empty string. /// /// if set to , the resource specified in is a printer. /// A set of connection options. /// /// A string that specifies the network provider to connect to. If , or an empty string, the operating system /// attempts to determine the correct provider by parsing the string provided in . If this member is /// not , the operating system attempts to make a connection only to the named network provider. You should /// set this member only if you know the network provider you want to use. Otherwise, let the operating system determine which /// provider the network name maps to. /// /// An instance of for the created connection. public static NetworkDeviceConnection Create(string remoteName, string localName = null, string userName = null, string password = null, bool isPrinter = false, CONNECT flags = 0, string provider = null) { if (localName == "*") { var sbSz = 261U; var sb = new StringBuilder((int)sbSz); var nr = new NETRESOURCE(remoteName, null, provider) { dwType = NETRESOURCEType.RESOURCETYPE_DISK }; WNetUseConnection(default, nr, password, userName, flags | CONNECT.CONNECT_REDIRECT, sb, ref sbSz, out _).WNetThrowIfFailed(); nr.lpLocalName = sb.ToString(); return new NetworkDeviceConnection(nr, WindowsIdentity.GetCurrent()); } else { var nr = new NETRESOURCE(remoteName, localName, provider) { dwType = isPrinter ? NETRESOURCEType.RESOURCETYPE_PRINT : (localName is null ? NETRESOURCEType.RESOURCETYPE_ANY : NETRESOURCEType.RESOURCETYPE_DISK) }; WNetAddConnection2(nr, password, userName, flags).WNetThrowIfFailed(); return new NetworkDeviceConnection(nr, WindowsIdentity.GetCurrent()); } } /// Implements the operator !=. /// The left. /// The right. /// The result of the operator. public static bool operator !=(NetworkDeviceConnection left, NetworkDeviceConnection right) => !(left == right); /// Implements the operator ==. /// The left. /// The right. /// The result of the operator. public static bool operator ==(NetworkDeviceConnection left, NetworkDeviceConnection right) => EqualityComparer.Default.Equals(left, right); /// Determines whether the specified , is equal to this instance. /// The to compare with this instance. /// if the specified is equal to this instance; otherwise, . public override bool Equals(object obj) => Equals(obj as NetworkDeviceConnection); /// Indicates whether the current object is equal to another object of the same type. /// An object to compare with this object. /// true if the current object is equal to the parameter; otherwise, false. public bool Equals(NetworkDeviceConnection other) => other != null && LocalName == other.LocalName && RemoteName == other.RemoteName; /// Returns a hash code for this instance. /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. public override int GetHashCode() => netRes.GetHashCode(); } /// Provides access to the local machine's remote connections. /// public class NetworkDeviceConnectionCollection : ICollection { internal WindowsIdentity identity; private NETRESOURCE root; /// Initializes a new instance of the class. public NetworkDeviceConnectionCollection() { } internal NetworkDeviceConnectionCollection(WindowsIdentity user, NETRESOURCE root) { identity = user; this.root = root; } /// Gets a value indicating whether the is read-only. public bool IsReadOnly => false; /// Gets the number of elements contained in the . public int Count => Enumerate().Count(); /// /// Removes an existing network connection. You can also call the function to remove remembered network connections that are not /// currently connected. /// /// /// A string that specifies the name of either the redirected local device or the remote network resource to disconnect from. /// /// If this parameter specifies a redirected local device, the function cancels only the specified device redirection. If the /// parameter specifies a remote network resource, all connections without devices are canceled. /// /// /// /// /// If , the system updates the user profile with the information that the connection is no longer a /// persistent one. The system will not restore this connection during subsequent logon operations. (Disconnecting resources using /// remote names has no effect on persistent connections.) /// /// /// If , the system does not update information about the connection. If the connection was marked as /// persistent in the registry, the system continues to restore the connection at the next logon. /// /// /// /// Specifies whether the disconnection should occur if there are open files or jobs on the connection. If this parameter is , the function fails if there are open files or jobs. /// public static void RemoveConnection(string name, bool force = false, bool removePersistence = true) => WNetCancelConnection2(name, removePersistence ? CONNECT.CONNECT_UPDATE_PROFILE : 0, force).WNetThrowIfFailed(); /// Makes a connection to a network resource and can redirect a local device to the network resource. /// /// A string that specifies the network resource to connect to. The string must follow the network provider's naming conventions. /// /// /// A string that specifies the name of a local device to redirect, such as "F:" or "LPT1". The string is treated in a /// case-insensitive manner. If the string is empty or , the function makes a connection to the network /// resource without redirecting a local device. /// /// if set to , the resource specified in is a printer. /// /// A pointer to a constant null-terminated string that specifies a password to be used in making the network connection. /// /// If lpPassword is NULL, the function uses the current default password associated with the user specified by the /// lpUserName parameter. /// /// If lpPassword points to an empty string, the function does not use a password. /// /// If the connection fails because of an invalid password and the CONNECT_INTERACTIVE value is set in the dwFlags parameter, the /// function displays a dialog box asking the user to type the password. /// /// Windows Me/98/95: This parameter must be NULL or an empty string. /// /// /// A pointer to a constant null-terminated string that specifies a user name for making the connection. /// /// If lpUserName is NULL, the function uses the default user name. (The user context for the process provides the default /// user name.) /// /// /// The lpUserName parameter is specified when users want to connect to a network resource for which they have been assigned a user /// name or account other than the default user name or account. /// /// The user-name string represents a security context. It may be specific to a network provider. /// Windows Me/98/95: This parameter must be NULL or an empty string. /// /// A set of connection options. /// /// A string that specifies the network provider to connect to. If , or an empty string, the operating system /// attempts to determine the correct provider by parsing the string provided in . If this member is /// not , the operating system attempts to make a connection only to the named network provider. You should /// set this member only if you know the network provider you want to use. Otherwise, let the operating system determine which /// provider the network name maps to. /// public string Add(string remoteName, string localName = null, string userName = null, string password = null, bool isPrinter = false, CONNECT flags = 0, string provider = null) { var nr = identity.Run(() => NetworkDeviceConnection.Create(remoteName, localName, userName, password, isPrinter, flags, provider)); nr.identity = identity; return nr.LocalName; } /// Removes all items from the . public void Clear() { foreach (var r in Enumerate().ToList()) Remove(r); } /// /// Copies the elements of the to an , starting /// at a particular index. /// /// /// The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. /// /// The zero-based index in at which copying begins. public void CopyTo(NetworkDeviceConnection[] array, int arrayIndex) { var arr = Enumerate().ToArray(); Array.Copy(arr, 0, array, arrayIndex, arr.Length); } /// Returns an enumerator that iterates through the collection. /// A that can be used to iterate through the collection. public IEnumerator GetEnumerator() => Enumerate().GetEnumerator(); /// Removes the first occurrence of a specific object from the . /// The object to remove from the . /// /// true if was successfully removed from the ; /// otherwise, false. This method also returns false if is not found in the original . /// public bool Remove(NetworkDeviceConnection item) { try { Remove(item.RemoteName, false, true); return true; } catch { return false; } } /// /// Removes an existing network connection. You can also call the function to remove remembered network connections that are not /// currently connected. /// /// /// A string that specifies the name of either the redirected local device or the remote network resource to disconnect from. /// /// If this parameter specifies a redirected local device, the function cancels only the specified device redirection. If the /// parameter specifies a remote network resource, all connections without devices are canceled. /// /// /// /// /// If , the system updates the user profile with the information that the connection is no longer a /// persistent one. The system will not restore this connection during subsequent logon operations. (Disconnecting resources using /// remote names has no effect on persistent connections.) /// /// /// If , the system does not update information about the connection. If the connection was marked as /// persistent in the registry, the system continues to restore the connection at the next logon. /// /// /// /// Specifies whether the disconnection should occur if there are open files or jobs on the connection. If this parameter is , the function fails if there are open files or jobs. /// public void Remove(string name, bool force = false, bool removePersistence = true) => identity.Run(() => RemoveConnection(name, force, removePersistence)); /// Adds an item to the . /// The object to add to the . void ICollection.Add(NetworkDeviceConnection item) => WNetAddConnection2(item.netRes, null, null, 0); /// Determines whether this instance contains the object. /// The object to locate in the . /// /// true if is found in the ; otherwise, false. /// bool ICollection.Contains(NetworkDeviceConnection item) => Enumerate().Contains(item); /// Returns an enumerator that iterates through a collection. /// An object that can be used to iterate through the collection. IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); private IEnumerable Enumerate() { return identity.Run(() => root is null || root.dwUsage.IsFlagSet(NETRESOURCEUsage.RESOURCEUSAGE_CONTAINER) ? WNetEnumResources(root, NETRESOURCEScope.RESOURCE_CONNECTED).Select(r => new NetworkDeviceConnection(r, identity)) : new NetworkDeviceConnection[0]); } } }