Skip to content

Instantly share code, notes, and snippets.

@mattcargile
Forked from jborean93/Get-WTSSessionInfo.ps1
Last active March 26, 2024 18:29
Show Gist options
  • Save mattcargile/3702a84428d17a255114f9c59a293bd3 to your computer and use it in GitHub Desktop.
Save mattcargile/3702a84428d17a255114f9c59a293bd3 to your computer and use it in GitHub Desktop.
Tries to replicate qwinsta but return structured objects
# Copyright: (c) 2022, Jordan Borean (@jborean93) <jborean93@gmail.com>
# MIT License (see LICENSE or https://opensource.org/licenses/MIT)
Function Get-WTSSessionInfo {
<#
.SYNOPSIS
Enumerates sessions on a Windows host.
.DESCRIPTION
Enumerates all the sessions available on a Windows host through the WTSEnumerateSessionsExW API.
.PARAMETER ComputerName
A list of hosts to query the sessions for. Omit or set to an empty array to check the local host.
.EXAMPLE
Get-WTSSessionInfo
.NOTES
The output object is modeled after https://docs.microsoft.com/en-us/windows/win32/api/wtsapi32/ns-wtsapi32-wts_session_info_1w.
#>
[Alias('gwtsi')]
[CmdletBinding()]
param (
[Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)]
[AllowNull()]
[AllowEmptyCollection()]
[string[]]
$ComputerName
)
begin {
Add-Type -Namespace Wtsapi32 -Name Native -MemberDefinition @'
using System;
using System.Runtime.InteropServices;
namespace Wtsapi32
{
public enum WtsConnectState
{
Active,
Connected,
ConnectQuery,
Shadow,
Disconnected,
Idle,
Listen,
Reset,
Down,
Init
}
public class SessionInfo
{
public int SessionId { get; set; }
public WtsConnectState State { get; set; }
public string SessionName { get; set; }
public string HostName { get; set; }
public string UserName { get; set; }
public string DomainName { get; set; }
public string FarmName { get; set; }
public string ComputerName { get; set; }
}
public class Native
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct WTS_SESSION_INFO_1W
{
public int ExecEnvId;
public WtsConnectState State;
public int SessionId;
public string pSessionName;
public string pHostName;
public string pUserName;
public string pDomainName;
public string pFarmName;
}
[DllImport("Wtsapi32.dll")]
public static extern void WTSCloseServer(IntPtr hServer);
[DllImport("Wtsapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool WTSEnumerateSessionsExW(
IntPtr hServer,
ref int pLevel,
int Filter,
out IntPtr ppSessionInfo,
out int pCount);
public static SessionInfo[] WTSEnumerateSessionsEx(System.Collections.Hashtable serverInfo)
{
IntPtr serverHandle = (IntPtr)serverInfo["serverHandle"];
string serverName = (string)serverInfo["serverName"];
int level = 1;
IntPtr rawInfo;
int count;
if (!WTSEnumerateSessionsExW(serverHandle, ref level, 0, out rawInfo, out count))
throw new System.ComponentModel.Win32Exception();
try
{
var infoCollection = new System.Collections.Generic.List<SessionInfo>();
IntPtr currentOffset = rawInfo;
for (int i = 0; i < count; i++)
{
WTS_SESSION_INFO_1W info = Marshal.PtrToStructure<WTS_SESSION_INFO_1W>(currentOffset);
infoCollection.Add(new SessionInfo()
{
SessionId = info.SessionId,
State = info.State,
SessionName = info.pSessionName,
HostName = info.pHostName,
UserName = info.pUserName,
DomainName = info.pDomainName,
FarmName = info.pFarmName,
ComputerName = serverName
});
currentOffset = IntPtr.Add(currentOffset, Marshal.SizeOf(typeof(WTS_SESSION_INFO_1W)));
}
return infoCollection.ToArray();
}
finally
{
WTSFreeMemoryExW(2, rawInfo, count);
}
}
[DllImport("Wtsapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool WTSFreeMemoryExW(
int WTSTypeClass,
IntPtr pMemory,
int NumberOfEntries);
[DllImport("Wtsapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern IntPtr WTSOpenServerExW(string pServerName);
public static System.Collections.Hashtable WTSOpenServerEx(string serverName)
{
IntPtr serverHandle = WTSOpenServerExW(serverName);
if (serverHandle == IntPtr.Zero)
throw new System.ComponentModel.Win32Exception();
return new System.Collections.Hashtable() { { "serverHandle", serverHandle }, { "serverName", serverName } };
}
[DllImport("Wtsapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool WTSLogoffSession(IntPtr hServer, int SessionId);
}
}
'@
$queried = $false
}
process {
foreach ($name in $ComputerName) {
$queried = $true
$serverHandle = [Wtsapi32.Native]::WTSOpenServerEx($name)
try {
[Wtsapi32.Native+SessionInfo[]]$wts = [Wtsapi32.Native]::WTSEnumerateSessionsEx($serverHandle)
foreach ($s in $wts) {
$s.ComputerName = $name
}
$wts
}
finally {
[Wtsapi32.Native]::WTSCloseServer($serverHandle)
}
}
}
end {
if (-not $queried) {
[Wtsapi32.Native]::WTSEnumerateSessionsEx([IntPtr]::Zero)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment