Grabbing Information of a Terminal Services Session Programatically

By | September 21, 2010

Like me when I created this application you might be wondering how can you grab information about a terminal services session programatically might be because you have an application that checks the Users IP to do some additional configuration on the application surface / environment or you might have an application that you need to render differently depending on the clients resolution on the terminal serices.

Well without a Terminal Services Session getting this information will be really easy take getting an IP from the application as an example


System.Net.IPAddress[] YourIPs = System.Net.Dns.GetHostAddresses(System.Net.Dns.GetHostName());

for (int i = 0; i < YourIPs.Length; i++)
{
    Console.WriteLine("IP Address {0}: {1} ", i, YourIPs[i].ToString());
}

or Getting a resolution

using System.Drawing;
using System.Windows.Forms;
Console.WriteLine(SystemInformation.PrimaryMonitorSize.ToString());

as you can see its really easy as there are .Net References for it but doing it in a Terminal Service Session will give you a differenstory as you will get the server environment results.  Also you have to consider that there are no .Net references for Terminal Services Session and we have to import Wtsapi32.dll to achieve the results you want.

So what is Wtsapi32.dll?  It is a Remote Desktop Services API that provide additional functionality which are related to a Remote Desktop Environment.  To get starting I will give you a sample dwon below on how would we import this dll to our application as well as how to consume the methods it exposes.

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

class Program
{
#region Constants
public const int WTS_CURRENT_SESSION = -1;
#endregion

#region Dll Imports
[DllImport("wtsapi32.dll")]
static extern int WTSEnumerateSessions(
    IntPtr pServer,
    [MarshalAs(UnmanagedType.U4)] int iReserved,
    [MarshalAs(UnmanagedType.U4)] int iVersion,
    ref IntPtr pSessionInfo,
    [MarshalAs(UnmanagedType.U4)] ref int iCount);

[DllImport("Wtsapi32.dll")]
public static extern bool WTSQuerySessionInformation(
    System.IntPtr pServer,
    int iSessionID,
    WTS_INFO_CLASS oInfoClass,
    out System.IntPtr pBuffer,
    out uint iBytesReturned);

[DllImport("wtsapi32.dll")]
static extern void WTSFreeMemory(
    IntPtr pMemory);
#endregion

#region Structures
//Structure for Terminal Service Client IP Address
[StructLayout(LayoutKind.Sequential)]
private struct WTS_CLIENT_ADDRESS
{
    public int iAddressFamily;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
    public byte[] bAddress;
}

//Structure for Terminal Service Session Info
[StructLayout(LayoutKind.Sequential)]
private struct WTS_SESSION_INFO
{
    public int iSessionID;
    [MarshalAs(UnmanagedType.LPStr)]
    public string sWinsWorkstationName;
    public WTS_CONNECTSTATE_CLASS oState;
}

//Structure for Terminal Service Session Client Display
[StructLayout(LayoutKind.Sequential)]
private struct WTS_CLIENT_DISPLAY
{
    public int iHorizontalResolution;
    public int iVerticalResolution;
    //1 = The display uses 4 bits per pixel for a maximum of 16 colors.
    //2 = The display uses 8 bits per pixel for a maximum of 256 colors.
    //4 = The display uses 16 bits per pixel for a maximum of 2^16 colors.
    //8 = The display uses 3-byte RGB values for a maximum of 2^24 colors.
    //16 = The display uses 15 bits per pixel for a maximum of 2^15 colors.
    public int iColorDepth;
}
#endregion

#region Enumurations
public enum WTS_CONNECTSTATE_CLASS
{
    WTSActive,
    WTSConnected,
    WTSConnectQuery,
    WTSShadow,
    WTSDisconnected,
    WTSIdle,
    WTSListen,
    WTSReset,
    WTSDown,
    WTSInit
}

public enum WTS_INFO_CLASS
{
    WTSInitialProgram,
    WTSApplicationName,
    WTSWorkingDirectory,
    WTSOEMId,
    WTSSessionId,
    WTSUserName,
    WTSWinStationName,
    WTSDomainName,
    WTSConnectState,
    WTSClientBuildNumber,
    WTSClientName,
    WTSClientDirectory,
    WTSClientProductId,
    WTSClientHardwareId,
    WTSClientAddress,
    WTSClientDisplay,
    WTSClientProtocolType,
    WTSIdleTime,
    WTSLogonTime,
    WTSIncomingBytes,
    WTSOutgoingBytes,
    WTSIncomingFrames,
    WTSOutgoingFrames,
    WTSClientInfo,
    WTSSessionInfo,
    WTSConfigInfo,
    WTSValidationInfo,
    WTSSessionAddressV4,
    WTSIsRemoteSession

}
#endregion

static void Main(string[] args)
{
    IntPtr pServer = IntPtr.Zero;
    string sUserName = string.Empty;
    string sDomain = string.Empty;
    string sClientApplicationDirectory = string.Empty;
    string sIPAddress = string.Empty;

    WTS_CLIENT_ADDRESS oClientAddres = new WTS_CLIENT_ADDRESS();
    WTS_CLIENT_DISPLAY oClientDisplay = new WTS_CLIENT_DISPLAY();

    IntPtr pSessionInfo = IntPtr.Zero;

    int iCount = 0;
    int iReturnValue = WTSEnumerateSessions(pServer, 0, 1, ref pSessionInfo, ref iCount);
    int iDataSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO));

    int iCurrent = (int)pSessionInfo;

    if (iReturnValue != 0)
    {
        //Go to all sessions
        for (int i = 0; i < iCount; i++)
        {
            WTS_SESSION_INFO oSessionInfo = (WTS_SESSION_INFO)Marshal.PtrToStructure((System.IntPtr)iCurrent, typeof(WTS_SESSION_INFO));
            iCurrent += iDataSize;

            uint iReturned = 0;

            //Get the IP address of the Terminal Services User
            IntPtr pAddress = IntPtr.Zero;
            if (WTSQuerySessionInformation(pServer, oSessionInfo.iSessionID, WTS_INFO_CLASS.WTSClientAddress, out pAddress, out iReturned) == true)
            {
                oClientAddres = (WTS_CLIENT_ADDRESS)Marshal.PtrToStructure(pAddress, oClientAddres.GetType());
                sIPAddress = oClientAddres.bAddress[2] + "." + oClientAddres.bAddress[3] + "." + oClientAddres.bAddress[4] + "." + oClientAddres.bAddress[5];
            }
            //Get the User Name of the Terminal Services User
            if (WTSQuerySessionInformation(pServer, oSessionInfo.iSessionID, WTS_INFO_CLASS.WTSUserName, out pAddress, out iReturned) == true)
            {
                sUserName = Marshal.PtrToStringAnsi(pAddress);
            }
            //Get the Domain Name of the Terminal Services User
            if (WTSQuerySessionInformation(pServer, oSessionInfo.iSessionID, WTS_INFO_CLASS.WTSDomainName, out pAddress, out iReturned) == true)
            {
                sDomain = Marshal.PtrToStringAnsi(pAddress);
            }
            //Get the Display Information  of the Terminal Services User
            if (WTSQuerySessionInformation(pServer, oSessionInfo.iSessionID, WTS_INFO_CLASS.WTSClientDisplay, out pAddress, out iReturned) == true)
            {
                oClientDisplay = (WTS_CLIENT_DISPLAY)Marshal.PtrToStructure(pAddress, oClientDisplay.GetType());
            }
            //Get the Application Directory of the Terminal Services User
            if (WTSQuerySessionInformation(pServer, oSessionInfo.iSessionID, WTS_INFO_CLASS.WTSClientDirectory, out pAddress, out iReturned) == true)
            {
                sClientApplicationDirectory = Marshal.PtrToStringAnsi(pAddress);
            }

            Console.WriteLine("Session ID : " + oSessionInfo.iSessionID);
            Console.WriteLine("Session State : " + oSessionInfo.oState);
            Console.WriteLine("Workstation Name : " + oSessionInfo.sWinsWorkstationName);
            Console.WriteLine("IP Address : " + sIPAddress);
            Console.WriteLine("User Name : " + sDomain + @"" + sUserName);
            Console.WriteLine("Client Display Resolution: " + oClientDisplay.iHorizontalResolution + " x " + oClientDisplay.iVerticalResolution);
            Console.WriteLine("Client Display Colour Depth: " + oClientDisplay.iColorDepth);
            Console.WriteLine("Client Application Directory: " + sClientApplicationDirectory);

            Console.WriteLine("-----------------------");
        }

        WTSFreeMemory(pSessionInfo);
    }
}
}

And here is the result


7 thoughts on “Grabbing Information of a Terminal Services Session Programatically

  1. Dan Ports

    If you’d rather not deal with the P/Invokes, you might be interested to learn about Cassia, a .NET library which wraps the WTS API and provides access to all of the session information you mentioned.

    Reply
  2. DinoRondelly

    What about pulling out incoming bytes and frams, outgoing bytes and frames as well as idle time and log on time? I was able to get the rest of the information you provided working but for some reason when i use the WTSQuerySessionInformation for the 5 values listed above it always comes back as false.

    Reply
  3. jay_99999

    This is just what I’m looking for but I have only ever used vb.net. My skills aren’t good enough to convert myself – I don’t suppose anyone has a vb.net version they could share?

    Reply
      1. Andrea

        Imports System
        Imports System.Diagnostics
        Imports System.Runtime.InteropServices
        Imports log4net

        Public Class TerminalServer

        #Region “Constants”
        Private Const WTS_CURRENT_SESSION As Integer = -1
        #End Region

        #Region “Dll Imports”
        ‘ _
        ‘Private Shared Function WTSEnumerateSessions(ByVal hwnd As IntPtr, ByVal lpString As StringBuilder, ByVal cch As Integer) As Integer
        ‘ End Function

        _
        Private Shared Function WTSEnumerateSessions( _
        ByVal hServer As IntPtr, _
        _
        ByVal Reserved As Int32, _
        _
        ByVal Version As Int32, _
        ByRef ppSessionInfo As IntPtr, _
        _
        ByRef pCount As Int32) As Int32
        End Function

        ‘[DllImport(“wtsapi32.dll”)]
        ‘static extern int WTSEnumerateSessions(
        ‘ IntPtr pServer,
        ‘ [MarshalAs(UnmanagedType.U4)] int iReserved,
        ‘ [MarshalAs(UnmanagedType.U4)] int iVersion,
        ‘ ref IntPtr pSessionInfo,
        ‘ [MarshalAs(UnmanagedType.U4)] ref int iCount);

        ‘[DllImport(“Wtsapi32.dll”)]
        ‘public static extern bool WTSQuerySessionInformation(
        ‘ System.IntPtr pServer,
        ‘ int iSessionID,
        ‘ WTS_INFO_CLASS oInfoClass,
        ‘ out System.IntPtr pBuffer,
        ‘ out uint iBytesReturned);
        Private Declare Auto Function WTSQuerySessionInformation Lib “wtsapi32.dll” ( _
        ByVal hServer As System.IntPtr, _
        ByVal SessionId As Int32, _
        ByVal InfoClass As WTS_INFO_CLASS, _
        ByRef ppBuffer As IntPtr, _
        ByRef pCount As UInt32) As Boolean

        ‘[DllImport(“wtsapi32.dll”)]
        ‘static extern void WTSFreeMemory(
        ‘ IntPtr pMemory);
        _
        Private Shared Sub WTSFreeMemory(ByVal pMemory As IntPtr)
        End Sub

        #End Region

        #Region “Structures”
        ‘Structure for Terminal Service Client IP Address
        _
        Private Structure WTS_CLIENT_ADDRESS
        Public iAddressFamily As Integer ‘AF_INET, (AF_INET6), AF_IPX, AF_NETBIOS, AF_UNSPEC
        _
        Public bAddress() As Byte
        End Structure

        ‘Structure for Terminal Service Session Info
        _
        Private Structure WTS_SESSION_INFO
        Public iSessionID As Integer
        _
        Public sWinsWorkstationName As String
        Public oState As WTS_CONNECTSTATE_CLASS
        End Structure

        ‘Structure for Terminal Service Session Client Display
        _
        Private Structure WTS_CLIENT_DISPLAY

        Public iHorizontalResolution As Integer
        Public iVerticalResolution As Integer
        ‘1 = The display uses 4 bits per pixel for a maximum of 16 colors.
        ‘2 = The display uses 8 bits per pixel for a maximum of 256 colors.
        ‘4 = The display uses 16 bits per pixel for a maximum of 2^16 colors.
        ‘8 = The display uses 3-byte RGB values for a maximum of 2^24 colors.
        ’16 = The display uses 15 bits per pixel for a maximum of 2^15 colors.
        Public iColorDepth As Integer
        End Structure
        #End Region

        #Region “Enumurations”
        Private Enum WTS_CONNECTSTATE_CLASS
        WTSActive
        WTSConnected
        WTSConnectQuery
        WTSShadow
        WTSDisconnected
        WTSIdle
        WTSListen
        WTSReset
        WTSDown
        WTSInit
        End Enum

        Private Enum WTS_INFO_CLASS
        WTSInitialProgram
        WTSApplicationName
        WTSWorkingDirectory
        WTSOEMId
        WTSSessionId
        WTSUserName
        WTSWinStationName
        WTSDomainName
        WTSConnectState
        WTSClientBuildNumber
        WTSClientName
        WTSClientDirectory
        WTSClientProductId
        WTSClientHardwareId
        WTSClientAddress
        WTSClientDisplay
        WTSClientProtocolType
        WTSIdleTime
        WTSLogonTime
        WTSIncomingBytes
        WTSOutgoingBytes
        WTSIncomingFrames
        WTSOutgoingFrames
        WTSClientInfo
        WTSSessionInfo
        WTSConfigInfo
        WTSValidationInfo
        WTSSessionAddressV4
        WTSIsRemoteSession
        End Enum
        #End Region

        ‘fonte: http://www.codeproject.com/Articles/111430/Grabbing-Information-of-a-Terminal-Services-Sessio
        ‘Public Sub Test()
        ‘ Dim pServer As IntPtr = IntPtr.Zero
        ‘ Dim sUserName As String = String.Empty
        ‘ Dim sDomain As String = String.Empty
        ‘ Dim sClientName As String = String.Empty
        ‘ Dim sSessionId As String = String.Empty
        ‘ Dim sClientApplicationDirectory As String = String.Empty
        ‘ Dim sIPAddress As String = String.Empty

        ‘ Dim oClientAddres As WTS_CLIENT_ADDRESS = New WTS_CLIENT_ADDRESS()
        ‘ Dim oClientDisplay As WTS_CLIENT_DISPLAY = New WTS_CLIENT_DISPLAY()

        ‘ Dim pSessionInfo As IntPtr = IntPtr.Zero

        ‘ Dim iCount As Integer = 0
        ‘ Dim iReturnValue As Integer = WTSEnumerateSessions(pServer, 0, 1, pSessionInfo, iCount)
        ‘ ‘dim iDataSize as Integer = Marshal.SizeOf(typeof(WTS_SESSION_INFO))
        ‘ Dim iDataSize As Integer = Marshal.SizeOf(GetType(WTS_SESSION_INFO))

        ‘ Dim iCurrent As Integer = pSessionInfo.ToInt32
        ‘ Dim _Message As String = String.Empty

        ‘ Dim _ThisProcess As Process = Process.GetCurrentProcess()
        ‘ Dim _SessionId As Integer = _ThisProcess.SessionId
        ‘ _Message += “Session ID:” + _SessionId.ToString

        ‘ If (iReturnValue 0) Then

        ‘ ‘Go to all sessions
        ‘ For i As Integer = 0 To iCount – 1
        ‘ _Message += ” ” + i.ToString

        ‘ Dim oSessionInfo As WTS_SESSION_INFO = _
        ‘ CType(Marshal.PtrToStructure(New IntPtr(iCurrent), _
        ‘ GetType(WTS_SESSION_INFO)), WTS_SESSION_INFO)
        ‘ iCurrent += iDataSize

        ‘ Dim iReturned As UInteger = 0

        ‘ ‘Get the IP address of the Terminal Services User
        ‘ Dim pAddress As IntPtr = IntPtr.Zero
        ‘ If (WTSQuerySessionInformation(pServer, _
        ‘ oSessionInfo.iSessionID, WTS_INFO_CLASS.WTSClientAddress, _
        ‘ pAddress, iReturned) = True) Then

        ‘ oClientAddres = DirectCast(Marshal.PtrToStructure _
        ‘ (pAddress, oClientAddres.GetType()), WTS_CLIENT_ADDRESS)
        ‘ sIPAddress = oClientAddres.bAddress(2).ToString() + “.” + _
        ‘ oClientAddres.bAddress(3).ToString() + “.” + oClientAddres.bAddress(4).ToString() _
        ‘ + “.” + oClientAddres.bAddress(5).ToString()
        ‘ _Message += ” IPAddress:” + sIPAddress
        ‘ End If
        ‘ ‘Get the User Name of the Terminal Services User
        ‘ If (WTSQuerySessionInformation(pServer, _
        ‘ oSessionInfo.iSessionID, WTS_INFO_CLASS.WTSUserName, _
        ‘ pAddress, iReturned) = True) Then
        ‘ ‘sUserName = Marshal.PtrToStringAnsi(pAddress) ‘, CType(iReturned, Integer))
        ‘ sUserName = Marshal.PtrToStringUni(pAddress)
        ‘ _Message += ” UserName:” + sUserName
        ‘ End If
        ‘ ‘Get the Domain Name of the Terminal Services User
        ‘ If (WTSQuerySessionInformation(pServer, _
        ‘ oSessionInfo.iSessionID, WTS_INFO_CLASS.WTSDomainName, _
        ‘ pAddress, iReturned) = True) Then
        ‘ ‘sDomain = Marshal.PtrToStringAnsi(pAddress, CType(iReturned, Integer))
        ‘ sDomain = Marshal.PtrToStringUni(pAddress)
        ‘ _Message += ” Domain:” + sDomain
        ‘ End If
        ‘ ‘Get the User Name of the Terminal Services User
        ‘ If (WTSQuerySessionInformation(pServer, _
        ‘ oSessionInfo.iSessionID, WTS_INFO_CLASS.WTSClientName, _
        ‘ pAddress, iReturned) = True) Then
        ‘ ‘sUserName = Marshal.PtrToStringAnsi(pAddress) ‘, CType(iReturned, Integer))
        ‘ sClientName = Marshal.PtrToStringUni(pAddress)
        ‘ _Message += ” ClientName:” + sClientName
        ‘ End If
        ‘ ‘Get the Domain Name of the Terminal Services User
        ‘ If (WTSQuerySessionInformation(pServer, _
        ‘ oSessionInfo.iSessionID, WTS_INFO_CLASS.WTSSessionId, _
        ‘ pAddress, iReturned) = True) Then
        ‘ ‘sDomain = Marshal.PtrToStringAnsi(pAddress, CType(iReturned, Integer))
        ‘ sSessionId = Marshal.PtrToStringUni(pAddress)
        ‘ _Message += ” SessionId:” + sSessionId
        ‘ End If
        ‘ ”Get the Display Information of the Terminal Services User
        ‘ ‘If (WTSQuerySessionInformation(pServer, _
        ‘ ‘ oSessionInfo.iSessionID, WTS_INFO_CLASS.WTSClientDisplay, _
        ‘ ‘ pAddress, iReturned) = True) Then
        ‘ ‘ oClientDisplay = CType(Marshal.PtrToStructure _
        ‘ ‘ (pAddress, oClientDisplay.GetType()), WTS_CLIENT_DISPLAY)
        ‘ ‘End If
        ‘ ”Get the Application Directory of the Terminal Services User
        ‘ ‘If (WTSQuerySessionInformation(pServer, oSessionInfo.iSessionID, _
        ‘ ‘ WTS_INFO_CLASS.WTSClientDirectory, pAddress, iReturned) = True) Then
        ‘ ‘ ‘sClientApplicationDirectory = Marshal.PtrToStringAnsi(pAddress)
        ‘ ‘ sClientApplicationDirectory = Marshal.PtrToStringUni(pAddress)
        ‘ ‘End If
        ‘ Next

        ‘ WTSFreeMemory(pSessionInfo)
        ‘ End If

        ‘ Dim _logger As ILog = LogManager.GetLogger(“IDE”)
        ‘ _logger.Debug(_Message)
        ‘End Sub

        Public Function ClientName(ByVal p_LocalName As String) As String
        Dim pServer As IntPtr = IntPtr.Zero
        Dim sClientName As String = p_LocalName

        Dim pSessionInfo As IntPtr = IntPtr.Zero

        Dim iCount As Integer = 0
        Dim iReturnValue As Integer = WTSEnumerateSessions(pServer, 0, 1, pSessionInfo, iCount)
        ‘dim iDataSize as Integer = Marshal.SizeOf(typeof(WTS_SESSION_INFO))
        Dim iDataSize As Integer = Marshal.SizeOf(GetType(WTS_SESSION_INFO))

        Dim iCurrent As Integer = pSessionInfo.ToInt32
        Dim oSessionInfo As WTS_SESSION_INFO
        If (iReturnValue 0) Then

        Dim _ThisProcess As Process = Process.GetCurrentProcess()
        Dim _SessionId As Integer = _ThisProcess.SessionId
        If _SessionId 0 Then
        sClientName = _Name
        End If
        End If
        End If
        WTSFreeMemory(pSessionInfo)
        End If
        Return sClientName
        End Function
        End Class

        Reply

Leave a Reply