diff --git a/System/Computer/Computer.cs b/System/Computer/Computer.cs
index 81ccc982..8919341a 100644
--- a/System/Computer/Computer.cs
+++ b/System/Computer/Computer.cs
@@ -1,7 +1,10 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
+using System.Linq;
using System.Runtime.Serialization;
+using Vanara.Security;
+using static Vanara.PInvoke.NetApi32;
namespace Vanara
{
@@ -15,6 +18,7 @@ namespace Vanara
/// The local computer connected by the current account.
public static readonly Computer Local = new Computer();
+ private SharedDevices devices;
private bool initializing;
private string targetServer;
private bool targetServerSet;
@@ -22,7 +26,6 @@ namespace Vanara
private bool userNameSet;
private string userPassword;
private bool userPasswordSet;
- private SharedDevices devices;
/// Creates a new instance of a TaskService connecting to the local machine as the current user.
public Computer() => Connect();
@@ -56,6 +59,19 @@ namespace Vanara
EndInit();
}
+ //public IEnumerable LocalUsers => LocalUser.GetEnum(Target, UserName, UserPassword);
+
+ //public IEnumerable LocalGroups => LocalGroup.GetEnum(Target, UserName, UserPassword);
+
+ /// Gets the open files associated with this device.
+ /// Returns a value.
+ public IEnumerable OpenFiles => Identity.Run(() => NetFileEnum(Target).Select(i => new OpenFile(i)));
+
+ /// Gets the shared devices defined for this computer.
+ /// Returns a value.
+ [Browsable(false)]
+ public SharedDevices SharedDevices => devices ?? (devices = new SharedDevices(this));
+
/// Gets or sets the name of the computer that the user is connected to.
[Category("Data"), DefaultValue(null), Description("The name of the computer to connect to.")]
public string Target
@@ -73,15 +89,6 @@ namespace Vanara
}
}
- /// Gets the shared devices defined for this computer.
- /// Returns a value.
- [Browsable(false)]
- public SharedDevices SharedDevices => devices ?? (devices = new SharedDevices(this));
-
- //public IEnumerable LocalUsers => LocalUser.GetEnum(Target, UserName, UserPassword);
-
- //public IEnumerable LocalGroups => LocalGroup.GetEnum(Target, UserName, UserPassword);
-
/// Gets or sets the user name to be used when connecting to the .
/// The user name.
[Category("Data"), DefaultValue(null), Description("The user name to be used when connecting.")]
@@ -118,6 +125,25 @@ namespace Vanara
}
}
+ internal System.Security.Principal.WindowsIdentity Identity
+ {
+ get
+ {
+ if (UserName is null)
+ return null;
+
+ var nonUpnIdx = UserName.IndexOf('\\');
+ var un = UserName;
+ string dn = null;
+ if (nonUpnIdx >= 0)
+ {
+ dn = UserName.Substring(0, nonUpnIdx);
+ un = UserName.Substring(nonUpnIdx + 1);
+ }
+ return new Vanara.Security.Principal.WindowsLoggedInIdentity(un, dn, UserPassword).AuthenticatedIdentity;
+ }
+ }
+
/// Signals the object that initialization is starting.
public void BeginInit() => initializing = true;
@@ -168,24 +194,5 @@ namespace Vanara
private bool ShouldSerializeTargetServer() => targetServer != null && !targetServer.Trim('\\').Equals(Environment.MachineName.Trim('\\'), StringComparison.InvariantCultureIgnoreCase);
private bool ShouldSerializeUserName() => userName != null && !userName.Equals(Environment.UserName, StringComparison.InvariantCultureIgnoreCase);
-
- internal System.Security.Principal.WindowsIdentity Identity
- {
- get
- {
- if (UserName is null)
- return null;
-
- var nonUpnIdx = UserName.IndexOf('\\');
- var un = UserName;
- string dn = null;
- if (nonUpnIdx >= 0)
- {
- dn = UserName.Substring(0, nonUpnIdx);
- un = UserName.Substring(nonUpnIdx + 1);
- }
- return new Vanara.Security.Principal.WindowsLoggedInIdentity(un, dn, UserPassword).AuthenticatedIdentity;
- }
- }
}
}
\ No newline at end of file
diff --git a/System/Computer/SharedDevice.cs b/System/Computer/SharedDevice.cs
index ff593db7..deb45733 100644
--- a/System/Computer/SharedDevice.cs
+++ b/System/Computer/SharedDevice.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
-using System.ComponentModel;
using System.Linq;
using System.Security.AccessControl;
using System.Security.Principal;
@@ -28,27 +27,101 @@ namespace Vanara
None = SHI1005_FLAGS.CSC_CACHE_NONE,
}
+ /// Represents an open file associated with a share.
+ public class OpenFile
+ {
+ private FILE_INFO_3 fi;
+
+ internal OpenFile(in FILE_INFO_3 i) => fi = i;
+
+ /// Gets the number of file locks on the file, device, or pipe.
+ /// Returns a value.
+ public int FileLockCount => (int)fi.fi3_num_locks;
+
+ /// Gets the path of the opened resource.
+ /// Returns a value.
+ public string FullPath => fi.fi3_pathname;
+
+ /// Gets the identification number assigned to the resource when it is opened.
+ /// Returns a value.
+ public int Id => (int)fi.fi3_id;
+
+ ///
+ /// Gets the string that specifies which user (on servers that have user-level security) or which computer (on servers that have
+ /// share-level security) opened the resource. Note that Windows does not support share-level security.
+ ///
+ /// The name of the user.
+ public string UserName => fi.fi3_username;
+ }
+
+ /// Represents a connection to a shared device.
+ public class ShareConnection
+ {
+ private CONNECTION_INFO_1 ci;
+ private SharedDevice share;
+
+ internal ShareConnection(in CONNECTION_INFO_1 ci, SharedDevice dev)
+ {
+ this.ci = ci; share = dev;
+ }
+
+ ///
+ /// If the server sharing the resource is running with user-level security, this value describes which user made the connection. If
+ /// the server is running with share-level security, this value describes which computer (computername) made the connection. Note
+ /// that Windows does not support share-level security.
+ ///
+ /// Returns a value.
+ public string ConnectedUser => ci.coni1_username;
+
+ /// Gets the number of users on the connection.
+ /// Returns a value.
+ public int ConnectedUserCount => (int)ci.coni1_num_users;
+
+ /// Gets the duration that the connection has been established.
+ /// The duration of the connection.
+ public TimeSpan ConnectionDuration => TimeSpan.FromSeconds(ci.coni1_time);
+
+ /// Gets the connection identification number.
+ /// Returns a value.
+ public int Id => (int)ci.coni1_id;
+
+ /// Gets the number of files currently open as a result of the connection.
+ /// Returns a value.
+ public int OpenFileCount => (int)ci.coni1_num_opens;
+
+ /// Gets the open files associated with this share.
+ /// Returns a value.
+ public IEnumerable OpenFiles => share.Id.Run(() => NetFileEnum(share.Target, share.Path).Where(i => share.Path?.Length > 0 || i.fi3_pathname.StartsWith("\\")).Select(i => new OpenFile(i)));
+ }
+
/// Represents a shared device on a computer.
///
public class SharedDevice : INamedEntity
{
- private readonly WindowsIdentity identity;
- private readonly string target;
private STYPE type = (STYPE)uint.MaxValue;
internal SharedDevice(string target, string netname, WindowsIdentity accessIdentity)
{
- identity = accessIdentity;
- this.target = target;
+ Id = accessIdentity;
+ Target = target;
Name = netname;
}
+ private delegate void Setter(ref T value);
+
+ ///
+ /// Lists all connections made to this shared resource on the target server. If there is more than one user using this connection,
+ /// then it is possible to get more than one structure for the same connection, but with a different user name.
+ ///
+ /// Returns a value.
+ public IEnumerable Connections => Id.Run(() => NetConnectionEnum(Target, Name).Select(ci => new ShareConnection(ci, this)));
+
/// Gets or sets an optional comment about the shared resource.
/// The resource description.
public string Description
{
get => GetInfo().shi1_remark;
- set => SetInfo(new SHARE_INFO_1004 { shi1004_remark = value });
+ set => SetInfo((ref SHARE_INFO_1004 i) => i.shi1004_remark = value, false);
}
/// Gets a value indicating whether this instance is communication device.
@@ -87,12 +160,7 @@ namespace Vanara
public ShareOfflineSettings OfflineSettings
{
get => (ShareOfflineSettings)(GetInfo().shi1005_flags & SHI1005_FLAGS.CSC_MASK_EXT);
- set
- {
- var i = GetInfo();
- i.shi1005_flags = i.shi1005_flags & ~SHI1005_FLAGS.CSC_MASK | (SHI1005_FLAGS)value;
- SetInfo(i);
- }
+ set => SetInfo((ref SHARE_INFO_1005 i) => i.shi1005_flags = i.shi1005_flags & ~SHI1005_FLAGS.CSC_MASK | (SHI1005_FLAGS)value);
}
///
@@ -104,16 +172,8 @@ namespace Vanara
///
public string Path
{
- get
- {
- try { return GetInfo().shi2_path; } catch { return string.Empty; }
- }
- set
- {
- var i = GetInfo();
- i.shi2_path = value;
- SetInfo(i);
- }
+ get { try { return GetInfo().shi2_path; } catch { return string.Empty; } }
+ set => SetInfo((ref SHARE_INFO_2 i) => i.shi2_path = value);
}
/// Gets or sets the permissions of the shared resource.
@@ -122,16 +182,8 @@ namespace Vanara
///
public RawSecurityDescriptor Permissions
{
- get
- {
- try { return GetInfo().shi502_security_descriptor.ToManaged(); } catch { return null; }
- }
- set
- {
- var i = GetInfo();
- i.shi502_security_descriptor = value.ToNative();
- SetInfo(i);
- }
+ get { try { return GetInfo().shi502_security_descriptor.ToManaged(); } catch { return null; } }
+ set => SetInfo((ref SHARE_INFO_502 i) => i.shi502_security_descriptor = value.ToNative());
}
///
@@ -141,18 +193,13 @@ namespace Vanara
/// The maximum number of concurrent connections.
public int UserLimit
{
- get
- {
- try { return unchecked((int)GetInfo().shi2_max_uses); } catch { return -1; }
- }
- set
- {
- var i = GetInfo();
- i.shi2_max_uses = unchecked((uint)value);
- SetInfo(i);
- }
+ get { try { return unchecked((int)GetInfo().shi2_max_uses); } catch { return -1; } }
+ set => SetInfo((ref SHARE_INFO_2 i) => i.shi2_max_uses = unchecked((uint)value));
}
+ internal WindowsIdentity Id { get; private set; }
+ internal string Target { get; private set; }
+
/// Gets the shared resource's permissions for servers running with share-level security.
/// Returns a value.
private ShareLevelAccess Access => GetInfo().shi2_permissions;
@@ -175,37 +222,49 @@ namespace Vanara
Create(target, name, comment, path, STYPE.STYPE_DISKTREE, null);
/// Creates the specified target.
- /// A string that specifies the DNS or NetBIOS name of the remote server on which the function is to execute. If this parameter is
- /// , the local computer is used.
+ ///
+ /// A string that specifies the DNS or NetBIOS name of the remote server on which the function is to execute. If this parameter is
+ /// , the local computer is used.
+ ///
/// The share name of a resource.
/// An optional comment about the shared resource.
- /// The local path for the shared resource. For disks, this is the path being shared. For print queues, this is the name of the print
- /// queue being shared.
+ ///
+ /// The local path for the shared resource. For disks, this is the path being shared. For print queues, this is the name of the print
+ /// queue being shared.
+ ///
/// A combination of values that specify the type of the shared resource.
/// The identity.
- ///
- /// On success, a new instance of represented a newly created shared resource.
- ///
+ /// On success, a new instance of represented a newly created shared resource.
internal static SharedDevice Create(string target, string name, string comment, string path, STYPE type, WindowsIdentity identity)
{
identity.Run(() => NetShareAdd(target, new SHARE_INFO_2 { shi2_netname = name, shi2_remark = comment, shi2_path = path, shi2_max_uses = unchecked((uint)-1), shi2_type = type }));
return new SharedDevice(target, name, identity);
}
- private T GetInfo() where T : struct => identity.Run(() => NetShareGetInfo(target, Name));
+ private T GetInfo() where T : struct => Id.Run(() => NetShareGetInfo(Target, Name));
- private void SetInfo(T s) where T : struct => identity.Run(() => NetShareSetInfo(target, Name, s));
+ private void SetInfo(Setter f, bool getFirst = true) where T : struct
+ {
+ Id.Run(() =>
+ {
+ var value = getFirst ? GetInfo() : default;
+ f(ref value);
+ NetShareSetInfo(Target, Name, value);
+ });
+ }
}
/// Represents all the shared devices on a computers.
public class SharedDevices : Collections.VirtualDictionary
{
- private readonly string target = null;
private readonly WindowsIdentity identity;
+ private readonly string target = null;
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
/// Name of the computer from which to retrieve and manage the shared devices.
- /// The Windows identity used to access the shared device information. If this value , the current identity is used.
+ ///
+ /// The Windows identity used to access the shared device information. If this value , the current identity is used.
+ ///
public SharedDevices(string serverName = null, WindowsIdentity accessIdentity = null) : base(false)
{
target = serverName;
diff --git a/System/Vanara.SystemServices.csproj b/System/Vanara.SystemServices.csproj
index 509b9d73..75933094 100644
--- a/System/Vanara.SystemServices.csproj
+++ b/System/Vanara.SystemServices.csproj
@@ -78,6 +78,7 @@ BackgroundCopyACLFlags, BackgroundCopyCost, BackgroundCopyErrorContext, Backgrou
+
diff --git a/UnitTests/System/ComputerTests.cs b/UnitTests/System/ComputerTests.cs
new file mode 100644
index 00000000..84e2ce42
--- /dev/null
+++ b/UnitTests/System/ComputerTests.cs
@@ -0,0 +1,20 @@
+using System;
+using Vanara.Diagnostics;
+using NUnit.Framework;
+using System.Text;
+using System.Linq;
+
+namespace Vanara.Diagnostics.Tests
+{
+ [TestFixture]
+ public class ComputerTests
+ {
+ [Test]
+ public void EnumSharesTest()
+ {
+ TestContext.WriteLine(string.Join(", ", Computer.Local.SharedDevices.Values.Select(d => d.Name)));
+ //var remote = new Computer(@"\\HALLAN-SVR", "dahall@byu.net", "msitsdav1d");
+ //TestContext.WriteLine(string.Join(", ", remote.SharedDevices.Values.Select(d => d.Name)));
+ }
+ }
+}
\ No newline at end of file
diff --git a/UnitTests/UnitTests.csproj b/UnitTests/UnitTests.csproj
index 7c908e95..a72e9b30 100644
--- a/UnitTests/UnitTests.csproj
+++ b/UnitTests/UnitTests.csproj
@@ -135,6 +135,7 @@
+