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