Vanara/System/Computer/NetworkDeviceConnection.cs

342 lines
20 KiB
C#

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
{
/// <summary>Information about a remote resource, usually in reference to a connection to that resource.</summary>
public class NetworkDeviceConnection : IEquatable<NetworkDeviceConnection>
{
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 };
}
/// <summary>Gets the children of this resource if it is a container.</summary>
/// <value>The children resources.</value>
public NetworkDeviceConnectionCollection Children => children ??= new NetworkDeviceConnectionCollection(identity, netRes);
/// <summary>A string that contains a comment supplied by the network provider.</summary>
public string Comment => netRes.lpComment;
/// <summary>
/// A string that contains the name of the provider that owns the resource. This member can be <see langword="null"/> if the
/// provider name is unknown.
/// </summary>
public string Provider => netRes.lpProvider;
/// <summary>The display options for the network object in a network browsing user interface.</summary>
public NETRESOURCEDisplayType ResourceDisplayType => netRes.dwDisplayType;
/// <summary>The type of resource.</summary>
public NETRESOURCEType ResourceType => netRes.dwType;
/// <summary>A set of flags describing how the resource can be used.</summary>
public NETRESOURCEUsage Use => netRes.dwUsage;
/// <summary>The name of a local device. This member is <see langword="null"/> if the connection does not use a device.</summary>
public string LocalName => netRes.lpLocalName;
/// <summary>
/// If the entry is a network resource, this member is a string that specifies the remote network name.
/// <para>If the entry is a current or persistent connection, this member is the network name associated with <see cref="LocalName"/>.</para>
/// </summary>
public string RemoteName => netRes.lpRemoteName;
/// <summary>Makes a connection to a network resource and can redirect a local device to the network resource.</summary>
/// <param name="remoteName">
/// A string that specifies the network resource to connect to. The string must follow the network provider's naming conventions.
/// </param>
/// <param name="localName">
/// 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 <see langword="null"/>, the function makes a connection to the network
/// resource without redirecting a local device.
/// </param>
/// <param name="userName">
/// <para>A pointer to a constant <c>null</c>-terminated string that specifies a user name for making the connection.</para>
/// <para>
/// If lpUserName is <c>NULL</c>, the function uses the default user name. (The user context for the process provides the default
/// user name.)
/// </para>
/// <para>
/// 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.
/// </para>
/// <para>The user-name string represents a security context. It may be specific to a network provider.</para>
/// <para><c>Windows Me/98/95:</c> This parameter must be <c>NULL</c> or an empty string.</para>
/// </param>
/// <param name="password">
/// <para>A pointer to a constant <c>null</c>-terminated string that specifies a password to be used in making the network connection.</para>
/// <para>
/// If lpPassword is <c>NULL</c>, the function uses the current default password associated with the user specified by the
/// lpUserName parameter.
/// </para>
/// <para>If lpPassword points to an empty string, the function does not use a password.</para>
/// <para>
/// 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.
/// </para>
/// <para><c>Windows Me/98/95:</c> This parameter must be <c>NULL</c> or an empty string.</para>
/// </param>
/// <param name="isPrinter">if set to <see langword="true"/>, the resource specified in <paramref name="remoteName"/> is a printer.</param>
/// <param name="flags">A set of connection options.</param>
/// <param name="provider">
/// A string that specifies the network provider to connect to. If <see langword="null"/>, or an empty string, the operating system
/// attempts to determine the correct provider by parsing the string provided in <paramref name="remoteName"/>. If this member is
/// not <see langword="null"/>, 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.
/// </param>
/// <returns>An instance of <see cref="NetworkDeviceConnection"/> for the created connection.</returns>
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());
}
}
/// <summary>Implements the operator !=.</summary>
/// <param name="left">The left.</param>
/// <param name="right">The right.</param>
/// <returns>The result of the operator.</returns>
public static bool operator !=(NetworkDeviceConnection left, NetworkDeviceConnection right) => !(left == right);
/// <summary>Implements the operator ==.</summary>
/// <param name="left">The left.</param>
/// <param name="right">The right.</param>
/// <returns>The result of the operator.</returns>
public static bool operator ==(NetworkDeviceConnection left, NetworkDeviceConnection right) => EqualityComparer<NetworkDeviceConnection>.Default.Equals(left, right);
/// <summary>Determines whether the specified <see cref="System.Object"/>, is equal to this instance.</summary>
/// <param name="obj">The <see cref="System.Object"/> to compare with this instance.</param>
/// <returns><see langword="true"/> if the specified <see cref="System.Object"/> is equal to this instance; otherwise, <see langword="false"/>.</returns>
public override bool Equals(object obj) => Equals(obj as NetworkDeviceConnection);
/// <summary>Indicates whether the current object is equal to another object of the same type.</summary>
/// <param name="other">An object to compare with this object.</param>
/// <returns>true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.</returns>
public bool Equals(NetworkDeviceConnection other) => other != null && LocalName == other.LocalName && RemoteName == other.RemoteName;
/// <summary>Returns a hash code for this instance.</summary>
/// <returns>A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.</returns>
public override int GetHashCode() => netRes.GetHashCode();
}
/// <summary>Provides access to the local machine's remote connections.</summary>
/// <seealso cref="System.Collections.Generic.ICollection{T}"/>
public class NetworkDeviceConnectionCollection : ICollection<NetworkDeviceConnection>
{
internal WindowsIdentity identity;
private NETRESOURCE root;
/// <summary>Initializes a new instance of the <see cref="NetworkDeviceConnectionCollection"/> class.</summary>
public NetworkDeviceConnectionCollection() { }
internal NetworkDeviceConnectionCollection(WindowsIdentity user, NETRESOURCE root)
{
identity = user;
this.root = root;
}
/// <summary>Gets a value indicating whether the <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.</summary>
public bool IsReadOnly => false;
/// <summary>Gets the number of elements contained in the <see cref="T:System.Collections.Generic.ICollection`1"/>.</summary>
public int Count => Enumerate().Count();
/// <summary>
/// Removes an existing network connection. You can also call the function to remove remembered network connections that are not
/// currently connected.
/// </summary>
/// <param name="name">
/// <para>A string that specifies the name of either the redirected local device or the remote network resource to disconnect from.</para>
/// <para>
/// 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.
/// </para>
/// </param>
/// <param name="removePersistence">
/// <para>
/// If <see langword="true"/>, 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.)
/// </para>
/// <para>
/// If <see langword="false"/>, 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.
/// </para>
/// </param>
/// <param name="force">
/// Specifies whether the disconnection should occur if there are open files or jobs on the connection. If this parameter is <see
/// langword="false"/>, the function fails if there are open files or jobs.
/// </param>
public static void RemoveConnection(string name, bool force = false, bool removePersistence = true) =>
WNetCancelConnection2(name, removePersistence ? CONNECT.CONNECT_UPDATE_PROFILE : 0, force).WNetThrowIfFailed();
/// <summary>Makes a connection to a network resource and can redirect a local device to the network resource.</summary>
/// <param name="remoteName">
/// A string that specifies the network resource to connect to. The string must follow the network provider's naming conventions.
/// </param>
/// <param name="localName">
/// 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 <see langword="null"/>, the function makes a connection to the network
/// resource without redirecting a local device.
/// </param>
/// <param name="isPrinter">if set to <see langword="true"/>, the resource specified in <paramref name="remoteName"/> is a printer.</param>
/// <param name="password">
/// <para>A pointer to a constant <c>null</c>-terminated string that specifies a password to be used in making the network connection.</para>
/// <para>
/// If lpPassword is <c>NULL</c>, the function uses the current default password associated with the user specified by the
/// lpUserName parameter.
/// </para>
/// <para>If lpPassword points to an empty string, the function does not use a password.</para>
/// <para>
/// 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.
/// </para>
/// <para><c>Windows Me/98/95:</c> This parameter must be <c>NULL</c> or an empty string.</para>
/// </param>
/// <param name="userName">
/// <para>A pointer to a constant <c>null</c>-terminated string that specifies a user name for making the connection.</para>
/// <para>
/// If lpUserName is <c>NULL</c>, the function uses the default user name. (The user context for the process provides the default
/// user name.)
/// </para>
/// <para>
/// 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.
/// </para>
/// <para>The user-name string represents a security context. It may be specific to a network provider.</para>
/// <para><c>Windows Me/98/95:</c> This parameter must be <c>NULL</c> or an empty string.</para>
/// </param>
/// <param name="flags">A set of connection options.</param>
/// <param name="provider">
/// A string that specifies the network provider to connect to. If <see langword="null"/>, or an empty string, the operating system
/// attempts to determine the correct provider by parsing the string provided in <paramref name="remoteName"/>. If this member is
/// not <see langword="null"/>, 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.
/// </param>
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;
}
/// <summary>Removes all items from the <see cref="T:System.Collections.Generic.ICollection`1"/>.</summary>
public void Clear()
{
foreach (var r in Enumerate().ToList())
Remove(r);
}
/// <summary>
/// Copies the elements of the <see cref="T:System.Collections.Generic.ICollection`1"/> to an <see cref="T:System.Array"/>, starting
/// at a particular <see cref="T:System.Array"/> index.
/// </summary>
/// <param name="array">
/// The one-dimensional <see cref="T:System.Array"/> that is the destination of the elements copied from <see
/// cref="T:System.Collections.Generic.ICollection`1"/>. The <see cref="T:System.Array"/> must have zero-based indexing.
/// </param>
/// <param name="arrayIndex">The zero-based index in <paramref name="array"/> at which copying begins.</param>
public void CopyTo(NetworkDeviceConnection[] array, int arrayIndex)
{
var arr = Enumerate().ToArray();
Array.Copy(arr, 0, array, arrayIndex, arr.Length);
}
/// <summary>Returns an enumerator that iterates through the collection.</summary>
/// <returns>A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection.</returns>
public IEnumerator<NetworkDeviceConnection> GetEnumerator() => Enumerate().GetEnumerator();
/// <summary>Removes the first occurrence of a specific object from the <see cref="T:System.Collections.Generic.ICollection`1"/>.</summary>
/// <param name="item">The object to remove from the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param>
/// <returns>
/// true if <paramref name="item"/> was successfully removed from the <see cref="T:System.Collections.Generic.ICollection`1"/>;
/// otherwise, false. This method also returns false if <paramref name="item"/> is not found in the original <see cref="T:System.Collections.Generic.ICollection`1"/>.
/// </returns>
public bool Remove(NetworkDeviceConnection item) { try { Remove(item.RemoteName, false, true); return true; } catch { return false; } }
/// <summary>
/// Removes an existing network connection. You can also call the function to remove remembered network connections that are not
/// currently connected.
/// </summary>
/// <param name="name">
/// <para>A string that specifies the name of either the redirected local device or the remote network resource to disconnect from.</para>
/// <para>
/// 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.
/// </para>
/// </param>
/// <param name="removePersistence">
/// <para>
/// If <see langword="true"/>, 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.)
/// </para>
/// <para>
/// If <see langword="false"/>, 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.
/// </para>
/// </param>
/// <param name="force">
/// Specifies whether the disconnection should occur if there are open files or jobs on the connection. If this parameter is <see
/// langword="false"/>, the function fails if there are open files or jobs.
/// </param>
public void Remove(string name, bool force = false, bool removePersistence = true) => identity.Run(() => RemoveConnection(name, force, removePersistence));
/// <summary>Adds an item to the <see cref="T:System.Collections.Generic.ICollection`1"/>.</summary>
/// <param name="item">The object to add to the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param>
void ICollection<NetworkDeviceConnection>.Add(NetworkDeviceConnection item) => WNetAddConnection2(item.netRes, null, null, 0);
/// <summary>Determines whether this instance contains the object.</summary>
/// <param name="item">The object to locate in the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param>
/// <returns>
/// true if <paramref name="item"/> is found in the <see cref="T:System.Collections.Generic.ICollection`1"/>; otherwise, false.
/// </returns>
bool ICollection<NetworkDeviceConnection>.Contains(NetworkDeviceConnection item) => Enumerate().Contains(item);
/// <summary>Returns an enumerator that iterates through a collection.</summary>
/// <returns>An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.</returns>
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
private IEnumerable<NetworkDeviceConnection> 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]);
}
}
}