mirror of https://github.com/dahall/Vanara.git
466 lines
23 KiB
C#
466 lines
23 KiB
C#
using System;
|
||
using System.Collections;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Net;
|
||
using System.Runtime.InteropServices;
|
||
using System.Security.Principal;
|
||
using Vanara.Extensions;
|
||
using Vanara.PInvoke;
|
||
using Vanara.Security;
|
||
using static Vanara.PInvoke.WTSApi32;
|
||
|
||
namespace Vanara;
|
||
|
||
/// <summary>Represents a Windows session.</summary>
|
||
public class Session : INamedEntity
|
||
{
|
||
private readonly SafeHWTSSERVER hServer;
|
||
private WTS_SESSION_INFO_1 sessionInfo;
|
||
|
||
internal Session(string target, in WTS_SESSION_INFO_1 info)
|
||
{
|
||
sessionInfo = info;
|
||
//Id = new WindowsIdentity($"{info.pDomainName}\\{info.pUserName}");
|
||
hServer = target is null ? SafeHWTSSERVER.Current : WTSOpenServerEx(target);
|
||
}
|
||
|
||
private Session() { }
|
||
|
||
/// <summary>
|
||
/// The network type and network address of the client. For more information, see WTS_CLIENT_ADDRESS. The IP address is offset by two
|
||
/// bytes from the start of the Address member of the WTS_CLIENT_ADDRESS structure.
|
||
/// </summary>
|
||
public WTS_CLIENT_ADDRESS ClientAddress => QueryInfo<WTS_CLIENT_ADDRESS>(WTS_INFO_CLASS.WTSClientAddress);
|
||
//{
|
||
// get
|
||
// {
|
||
// var ca = QueryInfo<WTS_CLIENT_ADDRESS>(WTS_INFO_CLASS.WTSClientAddress);
|
||
// switch ((Ws2_32.ADDRESS_FAMILY)ca.AddressFamily)
|
||
// {
|
||
// case Ws2_32.ADDRESS_FAMILY.AF_INET:
|
||
// try { return IPAddress.Parse(string.Join(".", ca.Address.Skip(2).Take(4))); }
|
||
// catch { return null; }
|
||
// case Ws2_32.ADDRESS_FAMILY.AF_INET6:
|
||
// try { return new(ca.Address.Take(16).ToArray()); }
|
||
// catch { return null; }
|
||
// default:
|
||
// return new(ca.Address);
|
||
// }
|
||
// }
|
||
//}
|
||
|
||
/// <summary>A ULONG value that contains the build number of the client.</summary>
|
||
public uint ClientBuildNumber => QueryInfo<uint>(WTS_INFO_CLASS.WTSClientBuildNumber);
|
||
|
||
/// <summary>A string that contains the directory in which the client is installed.</summary>
|
||
public string ClientDirectory => QueryInfo<string>(WTS_INFO_CLASS.WTSClientDirectory);
|
||
|
||
/// <summary>Information about the display resolution of the client. For more information, see WTS_CLIENT_DISPLAY.</summary>
|
||
public WTS_CLIENT_DISPLAY ClientDisplay => QueryInfo<WTS_CLIENT_DISPLAY>(WTS_INFO_CLASS.WTSClientDisplay);
|
||
|
||
/// <summary>A string that contains the name of the client.</summary>
|
||
public string ClientName => QueryInfo<string>(WTS_INFO_CLASS.WTSClientName);
|
||
|
||
/// <summary>A USHORT client-specific product identifier.</summary>
|
||
public ushort ClientProductId => QueryInfo<ushort>(WTS_INFO_CLASS.WTSClientProductId);
|
||
|
||
/// <summary>A value that specifies information about the protocol type for the session.</summary>
|
||
public SessionProtocolType ClientProtocolType => QueryInfo<SessionProtocolType>(WTS_INFO_CLASS.WTSClientProtocolType);
|
||
|
||
/// <summary>The session's current connection state. For more information, see WTS_CONNECTSTATE_CLASS.</summary>
|
||
public WTS_CONNECTSTATE_CLASS ConnectionState => QueryInfo<WTS_CONNECTSTATE_CLASS>(WTS_INFO_CLASS.WTSConnectState);
|
||
|
||
/// <summary>
|
||
/// The time of the most recent client connection to the session. This value is stored as a large integer that represents the number of
|
||
/// 100-nanosecond intervals since January 1, 1601 Coordinated Universal Time.
|
||
/// </summary>
|
||
public DateTime ConnectTime => DateTime.FromFileTimeUtc(SessionInfo.ConnectTimeUTC);
|
||
|
||
/// <summary>
|
||
/// The time that this structure was filled. This value is stored as a large integer that represents the number of 100-nanosecond
|
||
/// intervals since January 1, 1601 Coordinated Universal Time.
|
||
/// </summary>
|
||
public DateTime CurrentTime => DateTime.FromFileTimeUtc(SessionInfo.CurrentTimeUTC);
|
||
|
||
/// <summary>The device ID of the network adapter.</summary>
|
||
public string DeviceId => ClientInfo.DeviceId;
|
||
|
||
/// <summary>
|
||
/// <para>Specifies whether the printer connected to the client is the default printer for the user.</para>
|
||
/// <para>0</para>
|
||
/// <para>The printer connected to the client is not the default printer for the user.</para>
|
||
/// <para>1</para>
|
||
/// <para>The printer connected to the client is the default printer for the user.</para>
|
||
/// </summary>
|
||
public bool DisableDefaultMainClientPrinter => ConfigInfo.fDisableDefaultMainClientPrinter;
|
||
|
||
/// <summary>
|
||
/// <para>Specifies whether the client can use printer redirection.</para>
|
||
/// <para>0</para>
|
||
/// <para>Enable client printer redirection.</para>
|
||
/// <para>1</para>
|
||
/// <para>Disable client printer redirection.</para>
|
||
/// </summary>
|
||
public bool DisablePrinterRedirection => ConfigInfo.fDisablePrinterRedirection;
|
||
|
||
/// <summary>
|
||
/// The time of the most recent client disconnection to the session. This value is stored as a large integer that represents the number
|
||
/// of 100-nanosecond intervals since January 1, 1601 Coordinated Universal Time.
|
||
/// </summary>
|
||
public DateTime DisconnectTime => DateTime.FromFileTimeUtc(SessionInfo.DisconnectTimeUTC);
|
||
|
||
/// <summary>
|
||
/// A string that contains the domain name of the user who is logged on to the session. If no user is logged on to the session, the
|
||
/// string contains <see langword="null"/>.
|
||
/// </summary>
|
||
public string DomainName => sessionInfo.pDomainName;
|
||
|
||
/// <summary>
|
||
/// A string that contains the name of the farm that the virtual machine is joined to. If the session is not running on a virtual machine
|
||
/// that is joined to a farm, the string contains <see langword="null"/>.
|
||
/// </summary>
|
||
public string FarmName => sessionInfo.pFarmName;
|
||
|
||
/// <summary>
|
||
/// A string that contains the name of the computer that the session is running on. If the session is running directly on an RD Session
|
||
/// Host server or RD Virtualization Host server, the string contains <see langword="null"/>. If the session is running on a virtual
|
||
/// machine, the string contains the name of the virtual machine.
|
||
/// </summary>
|
||
public string HostName => sessionInfo.pHostName;
|
||
|
||
/// <summary>
|
||
/// The number of bytes of uncompressed Remote Desktop Protocol (RDP) data sent from the client to the server since the client connected.
|
||
/// </summary>
|
||
public uint IncomingBytes => SessionInfo.IncomingBytes;
|
||
|
||
/// <summary>The number of bytes of compressed RDP data sent from the client to the server since the client connected.</summary>
|
||
public uint IncomingCompressedBytes => SessionInfo.IncomingCompressedBytes;
|
||
|
||
/// <summary>The number of frames of RDP data sent from the client to the server since the client connected.</summary>
|
||
public uint IncomingFrames => SessionInfo.IncomingFrames;
|
||
|
||
/// <summary>A string that contains the name of the initial program that Remote Desktop Services runs when the user logs on.</summary>
|
||
public string InitialProgram { get { try { return QueryInfo<string>(WTS_INFO_CLASS.WTSInitialProgram); } catch { return string.Empty; } } }
|
||
|
||
/// <summary>
|
||
/// Determines whether the current session is a remote session. The WTSQuerySessionInformation function returns a value of TRUE to
|
||
/// indicate that the current session is a remote session, and FALSE to indicate that the current session is a local session. This value
|
||
/// can only be used for the local machine, so the hServer parameter of the WTSQuerySessionInformation function must contain
|
||
/// WTS_CURRENT_SERVER_HANDLE. Windows Server 2008 and Windows Vista: This value is not supported.
|
||
/// </summary>
|
||
public bool IsRemoteSession => WTSQuerySessionInformation(hServer, SessionId, WTS_INFO_CLASS.WTSIsRemoteSession, out _, out _);
|
||
|
||
/// <summary>
|
||
/// The time of the last user input in the session. This value is stored as a large integer that represents the number of 100-nanosecond
|
||
/// intervals since January 1, 1601 Coordinated Universal Time.
|
||
/// </summary>
|
||
public DateTime LastInputTime => DateTime.FromFileTimeUtc(SessionInfo.LastInputTimeUTC);
|
||
|
||
/// <summary>
|
||
/// The time that the user logged on to the session. This value is stored as a large integer that represents the number of 100-nanosecond
|
||
/// intervals since January 1, 1601 Coordinated Universal Time (Greenwich Mean Time).
|
||
/// </summary>
|
||
public DateTime LogonTime => DateTime.FromFileTimeUtc(SessionInfo.LogonTimeUTC);
|
||
|
||
/// <summary>A string that contains the name of this session. For example, "services", "console", or "RDP-Tcp#0".</summary>
|
||
public string Name => sessionInfo.pSessionName;
|
||
|
||
/// <summary>The number of bytes of uncompressed RDP data sent from the server to the client since the client connected.</summary>
|
||
public uint OutgoingBytes => SessionInfo.OutgoingBytes;
|
||
|
||
/// <summary>The number of bytes of compressed RDP data sent from the server to the client since the client connected.</summary>
|
||
public uint OutgoingCompressedBytes => SessionInfo.OutgoingCompressedBytes;
|
||
|
||
/// <summary>The number of frames of RDP data sent from the server to the client since the client connected.</summary>
|
||
public uint OutgoingFrames => SessionInfo.OutgoingFrames;
|
||
|
||
/// <summary>
|
||
/// A WTS_SESSION_ADDRESS structure that contains the IPv4 address assigned to the session. If the session does not have a virtual IP
|
||
/// address, the WTSQuerySessionInformation function returns ERROR_NOT_SUPPORTED.Windows Server 2008 and Windows
|
||
/// Vista: This value is not supported.
|
||
/// </summary>
|
||
public IPAddress SessionAddressV4
|
||
{
|
||
get
|
||
{
|
||
try { return new(QueryInfo<WTS_SESSION_ADDRESS>(WTS_INFO_CLASS.WTSSessionAddressV4).Address.Take(4).ToArray()); }
|
||
catch { return null; }
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// <para>The state of the session. This can be one or more of the following values.</para>
|
||
/// <para>WTS_SESSIONSTATE_UNKNOWN (4294967295 (0xFFFFFFFF))</para>
|
||
/// <para>The session state is not known.</para>
|
||
/// <para>WTS_SESSIONSTATE_LOCK (0 (0x0))</para>
|
||
/// <para>The session is locked.</para>
|
||
/// <para>WTS_SESSIONSTATE_UNLOCK (1 (0x1))</para>
|
||
/// <para>The session is unlocked.</para>
|
||
/// <para>
|
||
/// <c>Windows Server 2008 R2 and Windows 7:</c> Due to a code defect, the usage of the <c>WTS_SESSIONSTATE_LOCK</c> and
|
||
/// <c>WTS_SESSIONSTATE_UNLOCK</c> flags is reversed. That is, <c>WTS_SESSIONSTATE_LOCK</c> indicates that the session is unlocked, and
|
||
/// <c>WTS_SESSIONSTATE_UNLOCK</c> indicates the session is locked.
|
||
/// </para>
|
||
/// </summary>
|
||
public WTS_SESSIONSTATE SessionFlags => SessionInfoEx.Data.WTSInfoExLevel1.SessionFlags;
|
||
|
||
/// <summary>A session identifier assigned by the RD Session Host server, RD Virtualization Host server, or virtual machine.</summary>
|
||
public uint SessionId => sessionInfo.SessionId;
|
||
|
||
/// <summary>
|
||
/// <para>
|
||
/// The remote control setting. Remote control allows a user to remotely monitor the on-screen operations of another user. This member
|
||
/// can be one of the following values.
|
||
/// </para>
|
||
/// <para>0</para>
|
||
/// <para>Remote control is disabled.</para>
|
||
/// <para>1</para>
|
||
/// <para>The user of remote control has full control of the user's session, with the user's permission.</para>
|
||
/// <para>2</para>
|
||
/// <para>The user of remote control has full control of the user's session; the user's permission is not required.</para>
|
||
/// <para>3</para>
|
||
/// <para>
|
||
/// The user of remote control can view the session remotely, with the user's permission; the remote user cannot actively control the session.
|
||
/// </para>
|
||
/// <para>4</para>
|
||
/// <para>
|
||
/// The user of remote control can view the session remotely but not actively control the session; the user's permission is not required.
|
||
/// </para>
|
||
/// </summary>
|
||
public uint ShadowSettings => ConfigInfo.ShadowSettings;
|
||
|
||
/// <summary>
|
||
/// Gets a string that specifies the DNS or NetBIOS name of the remote server on which the local group resides. If this value is <see
|
||
/// langword="null"/>, the local computer is assumed.
|
||
/// </summary>
|
||
/// <value>The target server.</value>
|
||
public string Target { get; }
|
||
|
||
/// <summary>
|
||
/// A string that contains the name of the user who is logged on to the session. If no user is logged on to the session, the string
|
||
/// contains <see langword="null"/>.
|
||
/// </summary>
|
||
public string UserName => sessionInfo.pUserName;
|
||
|
||
/// <summary>A string that contains the name of the Remote Desktop Services session.</summary>
|
||
public string WinStationName => QueryInfo<string>(WTS_INFO_CLASS.WTSWinStationName);
|
||
|
||
/// <summary>A string that contains the default directory used when launching the initial program.</summary>
|
||
public string WorkingDirectory => QueryInfo<string>(WTS_INFO_CLASS.WTSWorkingDirectory);
|
||
|
||
internal WindowsIdentity Id { get; private set; }
|
||
|
||
/// <summary>Information about a Remote Desktop Connection (RDC) client. For more information, see WTSCLIENT.</summary>
|
||
private WTSCLIENT ClientInfo => QueryInfo<WTSCLIENT>(WTS_INFO_CLASS.WTSClientInfo);
|
||
|
||
/// <summary>
|
||
/// A WTSCONFIGINFO structure that contains information about the configuration of a RD Session Host server. Windows Server 2008 and
|
||
/// Windows Vista: This value is not supported.
|
||
/// </summary>
|
||
private WTSCONFIGINFO ConfigInfo => QueryInfo<WTSCONFIGINFO>(WTS_INFO_CLASS.WTSConfigInfo);
|
||
|
||
/// <summary>
|
||
/// information about a session on a RD Session Host server. For more information, see WTSINFOEX. Windows Server 2008 and Windows Vista:
|
||
/// This value is not supported.
|
||
/// </summary>
|
||
private WTSINFO SessionInfo => QueryInfo<WTSINFO>(WTS_INFO_CLASS.WTSSessionInfo);
|
||
|
||
/// <summary>
|
||
/// Extended information about a session on a RD Session Host server. For more information, see WTSINFOEX. Windows Server 2008 and
|
||
/// Windows Vista: This value is not supported.
|
||
/// </summary>
|
||
private WTSINFOEX SessionInfoEx => QueryInfo<WTSINFOEX>(WTS_INFO_CLASS.WTSSessionInfoEx);
|
||
|
||
/// <summary>
|
||
/// Disconnects the logged-on user from this session without closing the session. If the user
|
||
/// subsequently logs on to the same Remote Desktop Session Host (RD Session Host) server, the user is reconnected to the same session.
|
||
/// </summary>
|
||
/// <param name="wait">
|
||
/// Indicates whether the operation is synchronous. Specify <c>TRUE</c> to wait for the operation to complete, or <c>FALSE</c> to
|
||
/// return immediately.
|
||
/// </param>
|
||
/// <returns>
|
||
/// <para>If the function succeeds, the return value is a nonzero value.</para>
|
||
/// <para>If the function fails, the return value is zero. To get extended error information, call GetLastError.</para>
|
||
/// </returns>
|
||
/// <remarks>
|
||
/// <para>
|
||
/// To be able to disconnect another user's session, you need to have the Disconnect permission. For more information, see Remote
|
||
/// Desktop Services Permissions. To modify permissions on a session, use the Remote Desktop Services Configuration administrative tool.
|
||
/// </para>
|
||
/// <para>
|
||
/// To disconnect sessions running on a virtual machine hosted on a RD Virtualization Host server, you must be a member of the
|
||
/// Administrators group on the RD Virtualization Host server.
|
||
/// </para>
|
||
/// </remarks>
|
||
public bool Disconnect(bool wait) => WTSDisconnectSession(hServer, SessionId, wait);
|
||
|
||
/// <summary>Logs off a specified Remote Desktop Services session.</summary>
|
||
/// <param name="wait">
|
||
/// <para>Indicates whether the operation is synchronous.</para>
|
||
/// <para>If wait is <c>TRUE</c>, the function returns when the session is logged off.</para>
|
||
/// <para>
|
||
/// If wait is <c>FALSE</c>, the function returns immediately. To verify that the session has been logged off, specify the session
|
||
/// identifier in a call to the WTSQuerySessionInformation function. <c>WTSQuerySessionInformation</c> returns zero if the session
|
||
/// is logged off.
|
||
/// </para>
|
||
/// </param>
|
||
/// <returns>
|
||
/// <para>If the function succeeds, the return value is a nonzero value.</para>
|
||
/// <para>If the function fails, the return value is zero. To get extended error information, call GetLastError.</para>
|
||
/// </returns>
|
||
/// <remarks>
|
||
/// <para>
|
||
/// To be able to log off another user's session, you need to have the Reset permission. For more information, see Remote Desktop
|
||
/// Services Permissions. To modify permissions on a session, use the Remote Desktop Services Configuration administrative tool.
|
||
/// </para>
|
||
/// <para>
|
||
/// To log off sessions running on a virtual machine hosted on a RD Virtualization Host server, you must be a member of the
|
||
/// Administrators group on the RD Virtualization Host server.
|
||
/// </para>
|
||
/// </remarks>
|
||
public bool Logoff(bool wait) => WTSLogoffSession(hServer, SessionId, wait);
|
||
|
||
/// <summary>Displays a message box on the client desktop of a specified Remote Desktop Services session.</summary>
|
||
/// <param name="title">A pointer to a null-terminated string for the title bar of the message box.</param>
|
||
/// <param name="message">A pointer to a null-terminated string that contains the message to display.</param>
|
||
/// <param name="style">
|
||
/// The contents and behavior of the message box. This value is typically <c>MB_OK</c>. For a complete list of values, see the uType
|
||
/// parameter of the MessageBox function.
|
||
/// </param>
|
||
/// <param name="timeout">
|
||
/// The time, in seconds, that the <c>WTSSendMessage</c> function waits for the user's response. If the user does not respond within the
|
||
/// time-out interval, the response parameter returns <c>IDTIMEOUT</c>. If the timeout parameter is zero, <c>WTSSendMessage</c> waits
|
||
/// indefinitely for the user to respond.
|
||
/// </param>
|
||
/// <param name="wait">
|
||
/// <para>
|
||
/// If <c>TRUE</c>, <c>WTSSendMessage</c> does not return until the user responds or the time-out interval elapses. If the timeout
|
||
/// parameter is zero, the function does not return until the user responds.
|
||
/// </para>
|
||
/// <para>
|
||
/// If <c>FALSE</c>, the function returns immediately and the response parameter returns <c>IDASYNC</c>. Use this method for simple
|
||
/// information messages (such as print job–notification messages) that do not need to return the user's response to the calling program.
|
||
/// </para>
|
||
/// </param>
|
||
/// <param name="response">
|
||
/// <para>A pointer to a variable that receives the user's response, which can be one of the following values.</para>
|
||
/// <para>IDABORT (3)</para>
|
||
/// <para><c>Abort</c></para>
|
||
/// <para>IDCANCEL (2)</para>
|
||
/// <para><c>Cancel</c></para>
|
||
/// <para>IDCONTINUE (11)</para>
|
||
/// <para><c>Continue</c></para>
|
||
/// <para>IDIGNORE (5)</para>
|
||
/// <para><c>Ignore</c></para>
|
||
/// <para>IDNO (7)</para>
|
||
/// <para><c>No</c></para>
|
||
/// <para>IDOK (1)</para>
|
||
/// <para><c>OK</c></para>
|
||
/// <para>IDRETRY (4)</para>
|
||
/// <para><c>Retry</c></para>
|
||
/// <para>IDTRYAGAIN (10)</para>
|
||
/// <para><c>Try Again</c></para>
|
||
/// <para>IDYES (6)</para>
|
||
/// <para><c>Yes</c></para>
|
||
/// <para>IDASYNC (32001 (0x7D01))</para>
|
||
/// <para>The wait parameter was <c>FALSE</c>, so the function returned without waiting for a response.</para>
|
||
/// <para>IDTIMEOUT (32000 (0x7D00))</para>
|
||
/// <para>The wait parameter was <c>TRUE</c> and the time-out interval elapsed.</para>
|
||
/// </param>
|
||
/// <returns>
|
||
/// <para>If the function succeeds, the return value is a nonzero value.</para>
|
||
/// <para>If the function fails, the return value is zero. To get extended error information, call GetLastError.</para>
|
||
/// </returns>
|
||
public bool SendMessage(string title, string message, User32.MB_FLAGS style, TimeSpan timeout, bool wait, out User32.MB_RESULT response)
|
||
{
|
||
var ret = WTSSendMessage(hServer, SessionId, title, StringHelper.GetByteCount(title), message, StringHelper.GetByteCount(message), (uint)style,
|
||
(uint)timeout.TotalSeconds, out var resp, wait);
|
||
response = (User32.MB_RESULT)resp;
|
||
return ret;
|
||
}
|
||
|
||
private T QueryInfo<T>(WTS_INFO_CLASS c)
|
||
{
|
||
Win32Error.ThrowLastErrorIfFalse(WTSQuerySessionInformation(hServer, SessionId, c, out var mem, out var sz));
|
||
return typeof(T) == typeof(string) ? (T)(object)mem.ToString(sz) : mem.ToStructure<T>(sz);
|
||
}
|
||
}
|
||
|
||
/// <summary>A collection of Windows sessions for the specified target computer.</summary>
|
||
public class Sessions : IReadOnlyDictionary<uint, Session>
|
||
{
|
||
private readonly SafeHWTSSERVER hServer;
|
||
private readonly WindowsIdentity identity;
|
||
private readonly string target = null;
|
||
|
||
/// <summary>Initializes a new instance of the <see cref="SharedDevices"/> class.</summary>
|
||
/// <param name="serverName">Name of the computer from which to retrieve and manage the shared devices.</param>
|
||
/// <param name="accessIdentity">
|
||
/// The Windows identity used to access the shared device information. If this value <see langword="null"/>, the current identity is used.
|
||
/// </param>
|
||
public Sessions(string serverName = null, WindowsIdentity accessIdentity = null) : base()
|
||
{
|
||
target = serverName;
|
||
identity = accessIdentity ?? WindowsIdentity.GetCurrent();
|
||
hServer = target is null ? SafeHWTSSERVER.Current : WTSOpenServerEx(target);
|
||
}
|
||
|
||
internal Sessions(Computer computer) : this(computer.Target, computer.Identity)
|
||
{
|
||
}
|
||
|
||
/// <summary>Gets the number of elements in the collection.</summary>
|
||
public int Count => EnumSessions().Length;
|
||
|
||
/// <summary>Gets an <see cref="ICollection{TValue}"/> containing the keys in the <see cref="IDictionary{TKey, TValue}"/>.</summary>
|
||
/// <value>An <see cref="ICollection{TValue}"/> containing the keys in the object that implements <see cref="IDictionary{TKey, TValue}"/>.</value>
|
||
public IEnumerable<uint> Keys => Array.ConvertAll(EnumSessions(), i => i.SessionId);
|
||
|
||
/// <summary>Gets an <see cref="ICollection{TValue}"/> containing the values in the <see cref="IDictionary{TKey, TValue}"/>.</summary>
|
||
/// <value>An <see cref="ICollection{TValue}"/> containing the values in the object that implements <see cref="IDictionary{TKey, TValue}"/>.</value>
|
||
public IEnumerable<Session> Values => Array.ConvertAll(EnumSessions(), i => new Session(target, i));
|
||
|
||
/// <summary>Gets the <see cref="Session"/> with the specified key.</summary>
|
||
/// <value>The <see cref="Session"/>.</value>
|
||
/// <param name="key">The key.</param>
|
||
/// <returns>The <see cref="Session"/> instance with the specified session id.</returns>
|
||
/// <exception cref="System.ArgumentOutOfRangeException">key</exception>
|
||
public Session this[uint key] => TryGetValue(key, out var sess) ? sess : throw new ArgumentOutOfRangeException(nameof(key));
|
||
|
||
/// <summary>Determines whether the read-only dictionary contains an element that has the specified key.</summary>
|
||
/// <param name="key">The key to locate.</param>
|
||
/// <returns><see langword="true"/> if the read-only dictionary contains an element that has the specified key; otherwise, <see langword="false"/>.</returns>
|
||
public bool ContainsKey(uint key) => EnumSessions().Any(s => Equals(key, s.SessionId));
|
||
|
||
/// <summary>Returns an enumerator that iterates through the collection.</summary>
|
||
/// <returns>An enumerator that can be used to iterate through the collection.</returns>
|
||
public IEnumerator<KeyValuePair<uint, Session>> GetEnumerator() => EnumSessions().ToDictionary(i => i.SessionId, i => new Session(target, i)).GetEnumerator();
|
||
|
||
/// <summary>Gets the value associated with the specified key.</summary>
|
||
/// <param name="key">The key whose value to get.</param>
|
||
/// <param name="value">
|
||
/// When this method returns, the value associated with the specified key, if the key is found; otherwise, the default value for the type
|
||
/// of the <paramref name="value"/> parameter. This parameter is passed uninitialized.
|
||
/// </param>
|
||
/// <returns>
|
||
/// <see langword="true"/> if the <see cref="T:System.Collections.Generic.IDictionary`2"/> contains an element with the key; otherwise,
|
||
/// <see langword="false"/>.
|
||
/// </returns>
|
||
public bool TryGetValue(uint key, out Session value)
|
||
{
|
||
var si = EnumSessions().FirstOrDefault(s => Equals(key, s.SessionId));
|
||
if (si.SessionId == 0)
|
||
{
|
||
value = null;
|
||
return false;
|
||
}
|
||
value = new Session(target, si);
|
||
return true;
|
||
}
|
||
|
||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||
|
||
private WTS_SESSION_INFO_1[] EnumSessions() => identity.Run(() => { Win32Error.ThrowLastErrorIfFalse(WTSEnumerateSessionsEx(hServer, out var sessions)); return sessions; });
|
||
} |