Last active
March 26, 2024 14:49
-
-
Save jborean93/729410684e8e30f6d79123f0bcd06d9c to your computer and use it in GitHub Desktop.
Tries to replicate qwinsta but return structured objects
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Copyright: (c) 2022, Jordan Borean (@jborean93) <[email protected]> | |
# 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. | |
#> | |
[CmdletBinding()] | |
param ( | |
[Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)] | |
[AllowNull()] | |
[AllowEmptyCollection()] | |
[string[]] | |
$ComputerName | |
) | |
begin { | |
Add-Type -Namespace Wtsapi32 -Name Native -MemberDefinition @' | |
[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; | |
} | |
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; } | |
} | |
[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(IntPtr serverHandle) | |
{ | |
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, | |
}); | |
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 IntPtr WTSOpenServerEx(string serverName) | |
{ | |
IntPtr handle = WTSOpenServerExW(serverName); | |
if (handle == IntPtr.Zero) | |
throw new System.ComponentModel.Win32Exception(); | |
return handle; | |
} | |
'@ | |
$queried = $false | |
} | |
process { | |
foreach ($name in $ComputerName) { | |
$queried = $true | |
$serverHandle = [Wtsapi32.Native]::WTSOpenServerEx($name) | |
try { | |
[Wtsapi32.Native]::WTSEnumerateSessionsEx($serverHandle) | |
} | |
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