From 83f899a6652ff8c32cd9d0ecacb1d3439511e22b Mon Sep 17 00:00:00 2001 From: NN <580536+NN---@users.noreply.github.com> Date: Fri, 25 Feb 2022 20:13:16 +0200 Subject: [PATCH] Correct alignment for WTSINFO and WTSINFOEX. (#279) Thanks for your replies. I'm convinced. I'll merge. Thanks for your input and contributions. --- PInvoke/WTSApi32/WTSApi32.cs | 70 +++++++++++++++++++++++------ UnitTests/PInvoke/WTSApi32/WTSApi32Tests.cs | 59 ++++++++++++++++++++++++ 2 files changed, 116 insertions(+), 13 deletions(-) diff --git a/PInvoke/WTSApi32/WTSApi32.cs b/PInvoke/WTSApi32/WTSApi32.cs index 1558db59..b1ddaec2 100644 --- a/PInvoke/WTSApi32/WTSApi32.cs +++ b/PInvoke/WTSApi32/WTSApi32.cs @@ -3,7 +3,6 @@ using System.Runtime.InteropServices; using System.Text; using Vanara.Extensions; using Vanara.InteropServices; -using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME; namespace Vanara.PInvoke { @@ -1103,7 +1102,7 @@ namespace Vanara.PInvoke /// Retrieves the child session identifier, if present. /// - /// The address of a ULONG variable that receives the child session identifier. This will be ( ULONG)–1 if there is no + /// The address of a ULONG variable that receives the child session identifier. This will be ( ULONG)–1 if there is no /// child session for the current session. /// /// Returns nonzero if the function succeeds or zero otherwise. @@ -1684,7 +1683,7 @@ namespace Vanara.PInvoke /// /// /// If FALSE, the function returns immediately and the pResponse parameter returns IDASYNC. 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. + /// information messages (such as print job–notification messages) that do not need to return the user's response to the calling program. /// /// /// @@ -2970,19 +2969,34 @@ namespace Vanara.PInvoke public string UserName; /// The most recent client connection time. - public FILETIME ConnectTime; + public long ConnectTimeUTC; + + /// The most recent client connection time. + public DateTime ConnectTime => DateTime.FromFileTimeUtc(ConnectTimeUTC); /// The last client disconnection time. - public FILETIME DisconnectTime; + public long DisconnectTimeUTC; + + /// The last client disconnection time. + public DateTime DisconnectTime => DateTime.FromFileTimeUtc(DisconnectTimeUTC); /// The time of the last user input in the session. - public FILETIME LastInputTime; + public long LastInputTimeUTC; + + /// The time of the last user input in the session. + public DateTime LastInputTime => DateTime.FromFileTimeUtc(LogonTimeUTC); /// The time that the user logged on to the session. - public FILETIME LogonTime; + public long LogonTimeUTC; + + /// The time that the user logged on to the session. + public DateTime LogonTime => DateTime.FromFileTimeUtc(LogonTimeUTC); /// The time that the WTSINFO data structure was called. - public FILETIME CurrentTime; + public long CurrentTimeUTC; + + /// The time that the WTSINFO data structure was called. + public DateTime CurrentTime => DateTime.FromFileTimeUtc(CurrentTimeUTC); } /// @@ -3066,31 +3080,61 @@ namespace Vanara.PInvoke /// 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). /// - public FILETIME LogonTime; + public long LogonTimeUTC; + + /// + /// 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). + /// + public DateTime LogonTime => DateTime.FromFileTimeUtc(LogonTimeUTC); /// /// 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. /// - public FILETIME ConnectTime; + public long ConnectTimeUTC; + + /// + /// 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. + /// + public DateTime ConnectTime => DateTime.FromFileTimeUtc(ConnectTimeUTC); /// /// 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. /// - public FILETIME DisconnectTime; + public long DisconnectTimeUTC; + + /// + /// 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. + /// + public DateTime DisconnectTime => DateTime.FromFileTimeUtc(DisconnectTimeUTC); /// /// 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. /// - public FILETIME LastInputTime; + public long LastInputTimeUTC; + + /// + /// 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. + /// + public DateTime LastInputTime => DateTime.FromFileTimeUtc(LastInputTimeUTC); /// /// 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. /// - public FILETIME CurrentTime; + public long CurrentTimeUTC; + + /// + /// 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. + /// + public DateTime CurrentTime => DateTime.FromFileTimeUtc(CurrentTimeUTC); /// /// The number of bytes of uncompressed Remote Desktop Protocol (RDP) data sent from the client to the server since the client connected. diff --git a/UnitTests/PInvoke/WTSApi32/WTSApi32Tests.cs b/UnitTests/PInvoke/WTSApi32/WTSApi32Tests.cs index ae43c47f..9b2b8176 100644 --- a/UnitTests/PInvoke/WTSApi32/WTSApi32Tests.cs +++ b/UnitTests/PInvoke/WTSApi32/WTSApi32Tests.cs @@ -25,5 +25,64 @@ namespace Vanara.PInvoke.Tests Assert.That(WTSEnumerateServers(null, out var servers), ResultIs.Successful); servers.WriteValues(); } + + [Test] + public void WTSEnumerateSessionsExTest() + { + Assert.That(WTSEnumerateSessionsEx(HWTSSERVER.WTS_CURRENT_SERVER_HANDLE, out var sessionList), + ResultIs.Successful); + + foreach (var session in sessionList) + { + if (WTSQuerySessionInformation( + HWTSSERVER.WTS_CURRENT_SERVER_HANDLE, + session.SessionId, + WTS_INFO_CLASS.WTSSessionInfo, + out var pSessionInfo, + out var bytesReturned)) + { + using (pSessionInfo) + { + var wtsInfo = pSessionInfo.ToStructure(bytesReturned); + Assert.That(() => _ = wtsInfo.SessionId, Throws.Nothing); + Assert.That(() => _ = wtsInfo.UserName, Throws.Nothing); + Assert.That(() => _ = wtsInfo.CurrentTime, Throws.Nothing); + Assert.That(() => _ = wtsInfo.ConnectTime, Throws.Nothing); + Assert.That(() => _ = wtsInfo.LastInputTime, Throws.Nothing); + } + } + } + } + + [Test] + public void WTSEnumerateSessionsExSessionInfoExTest() + { + Assert.That(WTSEnumerateSessionsEx(HWTSSERVER.WTS_CURRENT_SERVER_HANDLE, out var sessionList), + ResultIs.Successful); + + foreach (var session in sessionList) + { + if (WTSQuerySessionInformation( + HWTSSERVER.WTS_CURRENT_SERVER_HANDLE, + session.SessionId, + WTS_INFO_CLASS.WTSSessionInfoEx, + out var pSessionInfo, + out var bytesReturned)) + { + using (pSessionInfo) + { + var wtsInfoEx = pSessionInfo.ToStructure(bytesReturned); + if (wtsInfoEx.Level == 1) + { + Assert.That(() => _ = wtsInfoEx.Data.WTSInfoExLevel1.SessionId, Throws.Nothing); + Assert.That(() => _ = wtsInfoEx.Data.WTSInfoExLevel1.UserName, Throws.Nothing); + Assert.That(() => _ = wtsInfoEx.Data.WTSInfoExLevel1.CurrentTime, Throws.Nothing); + Assert.That(() => _ = wtsInfoEx.Data.WTSInfoExLevel1.ConnectTime, Throws.Nothing); + Assert.That(() => _ = wtsInfoEx.Data.WTSInfoExLevel1.LastInputTime, Throws.Nothing); + } + } + } + } + } } } \ No newline at end of file