Click here to Skip to main content
15,896,063 members
Articles / Desktop Programming / Win32

VB.NET wrappers for much of the Windows API

Rate me:
Please Sign up or sign in to vote.
5.00/5 (3 votes)
15 Apr 2012CPOL3 min read 17.5K   785   12  
Includes most API functions except for graphics.
Imports System.Runtime.InteropServices
Imports System.ComponentModel
Imports System.Text
Imports System.Diagnostics

Public Class WtsApi

    <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)> _
    Public Structure WTS_SESSION_INFO
        Public SessionId As Integer
        <MarshalAs(UnmanagedType.LPStr)> Public pWinStationName As String
        Public state As WTS_CONNECTSTATE_CLASS
    End Structure

    Public Class WtsSession
        Public SessionId As Integer
        Public StationName As String
        Public ConnectionState As WTS_CONNECTSTATE_CLASS
        Public UserDomain As String
        Public UserName As String
        Public UserSid As String
    End Class

    Public Enum WTS_CONNECTSTATE_CLASS
        WTSActive
        WTSConnected
        WTSConnectQuery
        WTSShadow
        WTSDisconnected
        WTSIdle
        WTSListen
        WTSReset
        WTSDown
        WTSInit
    End Enum

    Public Enum WTS_INFO_CLASS
        WTSInitialProgram = 0
        WTSApplicationName = 1
        WTSWorkingDirectory = 2
        WTSOEMId = 3
        WTSSessionId = 4
        WTSUserName = 5
        WTSWinStationName = 6
        WTSDomainName = 7
        WTSConnectState = 8
        WTSClientBuildNumber = 9
        WTSClientName = 10
        WTSClientDirectory = 11
        WTSClientProductId = 12
        WTSClientHardwareId = 13
        WTSClientAddress = 14
        WTSClientDisplay = 15
        WTSClientProtocolType = 16
        WTSIdleTime = 17
        WTSLogonTime = 18
        WTSIncomingBytes = 19
        WTSOutgoingBytes = 20
        WTSIncomingFrames = 21
        WTSOutgoingFrames = 22
    End Enum

    'WTSQueryUserToken errors
    Private Const ERROR_PRIVILEGE_NOT_HELD As Integer = 1314 'needs tcb privilege
    Private Const ERROR_INVALID_PARAMETER As Integer = 87
    Private Const ERROR_ACCESS_DENIED As Integer = 5 'has tcb privilege, but it must be running as LocalSystem
    Private Const ERROR_CTX_WINSTATION_NOT_FOUND As Integer = 7022
    Private Const ERROR_NO_TOKEN As Integer = 1008

    Private Const WTS_NO_SESSION As Integer = &HFFFFFFFF
    Private Const WTS_CURRENT_SERVER_HANDLE As Long = 0
    Private Const WTS_CURRENT_SESSION As Long = -1

    <DllImport("wtsapi32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
    Private Shared Function WTSEnumerateSessions( _
        ByVal hServer As IntPtr, _
        <MarshalAs(UnmanagedType.U4)> ByVal Reserved As Integer, _
        <MarshalAs(UnmanagedType.U4)> ByVal Version As Integer, _
        ByRef ppSessionInfo As IntPtr, _
        <MarshalAs(UnmanagedType.U4)> ByRef pCount As Integer) As Integer
    End Function

    <DllImport("wtsapi32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
    Private Shared Function WTSQuerySessionInformation( _
         ByVal hServer As Integer, _
         ByVal SessionId As Integer, _
         ByVal InfoClass As WTS_INFO_CLASS, _
         ByRef ppBuffer As IntPtr, _
         ByRef pCount As Int32) As Integer
    End Function

    <DllImport("wtsapi32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
    Private Shared Function WTSFreeMemory( _
        ByVal memory As IntPtr) As Integer
    End Function

    <DllImport("wtsapi32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
    Private Shared Function WTSOpenServer( _
        ByVal pServerName As String) As IntPtr
    End Function

    <DllImport("wtsapi32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
    Private Shared Sub WTSCloseServer( _
        ByVal hServer As IntPtr)
    End Sub

    <DllImport("kernel32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
    Public Shared Function WTSGetActiveConsoleSessionId() As Integer
    End Function

    <DllImport("Wtsapi32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
    Private Shared Function WTSDisconnectSession( _
        ByVal hServer As Integer, _
        ByVal SessionID As Integer, _
        ByVal bWait As Boolean) As Integer
    End Function

    <DllImport("Wtsapi32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
    Private Shared Function WTSLogoffSession( _
        ByVal hServer As Integer, _
        ByVal Sessionid As Integer, _
        ByVal bWait As Boolean) As Integer
    End Function

    <DllImport("Wtsapi32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
    Public Shared Function WTSQueryUserToken( _
        ByVal Sessionid As Integer, _
        ByRef pHandle As IntPtr) As Integer
    End Function

    Public Shared Function WaitForSessionRelease(ByVal username As String, ByVal milliseconds As Integer) As Boolean
        Dim began As DateTime = Now

        While Now <= began.AddMilliseconds(milliseconds)
            If WtsApi.UserIsLoggedIn(username) Then
                Logger.WriteEntry("Waiting for session to release: " + username, EventLogEntryType.Warning)
                System.Threading.Thread.Sleep(milliseconds)
            Else
                Return True
            End If
        End While

        Return False
    End Function

    'Public Shared Function WaitForSessionRelease(ByVal sessionId As Integer, ByVal milliseconds As Integer) As Boolean
    '    Dim began As DateTime = Now

    '    While Now < began.AddMilliseconds(milliseconds)
    '        Try
    '            If Not String.IsNullOrEmpty(GetSessionInfo(sessionId, WTS_INFO_CLASS.WTSConnectState)) Then
    '                'continue
    '            End If
    '        Catch ex As Exception
    '            'session went out of scope
    '            Return True
    '        End Try
    '        System.Threading.Thread.Sleep(500)
    '    End While

    '    Return False
    'End Function

    Public Shared Function UserIsLoggedIn(ByVal username As String) As Boolean
        Try
            If WtsApi.GetSession(username) Is Nothing Then
                Return False
            Else
                Return True
            End If
        Catch ex As Exception
            Return True
        End Try
    End Function

    Public Shared Function GetUserToken(ByVal sessionID As Integer) As IntPtr
        Dim result As Integer
        Dim token As IntPtr = IntPtr.Zero

        KernelApi.AddPrivilege(KernelApi.SE_TCB_PRIVILEGE)

        result = WTSQueryUserToken(sessionID, token)
        If result = 0 Then
            Throw New Win32Exception(Marshal.GetLastWin32Error())
        End If
        Return token
    End Function

    Public Shared Function GetUserName(ByVal sessionID As Integer) As String
        Return GetSessionInfo(sessionID, WTS_INFO_CLASS.WTSUserName)
    End Function

    Public Shared Function GetUserDomain(ByVal sessionID As Integer) As String
        Return GetSessionInfo(sessionID, WTS_INFO_CLASS.WTSDomainName)
    End Function

    Public Shared Function GetUserNameFull(ByVal sessionid As Integer) As String
        Return GetUserDomain(sessionid).ToUpper() + "\" + GetUserName(sessionid)
    End Function

    Public Shared Function GetCurrentSessionId() As Integer
        Return CInt(GetSessionInfo(WTS_CURRENT_SESSION, WTS_INFO_CLASS.WTSSessionId))
    End Function

    Public Shared Function GetCurrentSessionInfo(ByVal info As WTS_INFO_CLASS) As String
        Return GetSessionInfo(WTS_CURRENT_SESSION, info)
    End Function

    Public Shared Function GetSessionInfo(ByVal sessionID As Integer, ByVal info As WTS_INFO_CLASS) As String
        Dim ret As String = ""
        Dim ppBuffer As IntPtr = IntPtr.Zero
        Dim bufSize As Integer
        Dim result As Integer
        Try
            result = WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, sessionID, info, ppBuffer, bufSize)
            If result = 0 Then
                Throw New Win32Exception(Marshal.GetLastWin32Error())
            End If

            Select Case info
                Case WTS_INFO_CLASS.WTSApplicationName
                    ret = Marshal.PtrToStringAuto(ppBuffer)
                Case WTS_INFO_CLASS.WTSClientName
                    ret = Marshal.PtrToStringAuto(ppBuffer)
                Case WTS_INFO_CLASS.WTSDomainName
                    ret = Marshal.PtrToStringAuto(ppBuffer)
                Case WTS_INFO_CLASS.WTSInitialProgram
                    ret = Marshal.PtrToStringAuto(ppBuffer)
                Case WTS_INFO_CLASS.WTSUserName
                    ret = Marshal.PtrToStringAuto(ppBuffer)
                Case WTS_INFO_CLASS.WTSWinStationName
                    ret = Marshal.PtrToStringAuto(ppBuffer)
                Case WTS_INFO_CLASS.WTSWorkingDirectory
                    ret = Marshal.PtrToStringAuto(ppBuffer)
                Case WTS_INFO_CLASS.WTSConnectState
                    ret = CType(Marshal.ReadInt32(ppBuffer), WTS_CONNECTSTATE_CLASS).ToString()
                Case WTS_INFO_CLASS.WTSSessionId
                    ret = Convert.ToString(Marshal.ReadInt32(ppBuffer))
                Case Else
                    Throw New ArgumentOutOfRangeException(info.ToString())
            End Select
            Return ret
        Finally
            WTSFreeMemory(ppBuffer)
        End Try
    End Function

    Public Shared Function GetSession(ByVal username As String) As WtsSession
        username = Utilities.GetUserName(username)

        For Each s As WtsApi.WtsSession In WtsApi.GetSessions()
            If Utilities.GetUserName(s.UserName).ToLower() = username.ToLower() Then
                Return s
            End If
        Next
        Return Nothing
    End Function

    Public Shared Function GetActiveSession() As WtsSession
        Dim retries As Integer = 0
        Try
Retry:
            Dim sessions() As WtsApi.WtsSession = WtsApi.GetSessions()

            'priority to WtsActive sessions
            For Each s As WtsApi.WtsSession In sessions
                If s.ConnectionState = WTS_CONNECTSTATE_CLASS.WTSActive Then
                    Return s
                End If
            Next
            For Each s As WtsApi.WtsSession In sessions
                If s.ConnectionState = WTS_CONNECTSTATE_CLASS.WTSConnected Then
                    Return s
                End If
            Next
        Catch ex As Exception
            If retries < 3 Then
                retries += 1
                Threading.Thread.Sleep(500)
                GoTo Retry
            End If
            Logger.WriteEntry("Error retrieving active session: " + ex.Message, EventLogEntryType.Error)
        End Try
        Return Nothing
    End Function

    Public Shared Function ActiveSessionIsLoggedIn() As Boolean
        Dim s As WtsApi.WtsSession
        s = GetActiveSession()
        If s Is Nothing Then
            Return False
        ElseIf s.UserName = "" Then
            Return False
        Else
            Return True
        End If
    End Function

    Public Shared Function GetSession(ByVal sessionId As Integer) As WtsSession
        For Each s As WtsSession In WtsApi.GetSessions()
            If s.SessionId = sessionId Then
                Return s
            End If
        Next
        Return Nothing
    End Function

    Public Shared Function GetSessions() As WtsSession()
        Dim ppSessionInfo As IntPtr = IntPtr.Zero
        Dim result As Integer
        Dim sessionCount As Integer
        Dim structSize As Integer
        Dim structIndexer As Integer

        Try
            result = WTSEnumerateSessions(IntPtr.Zero, 0, 1, ppSessionInfo, sessionCount)
            If result = 0 Then
                Throw New Win32Exception(Marshal.GetLastWin32Error())
            End If

            structSize = Marshal.SizeOf(GetType(WTS_SESSION_INFO))
            structIndexer = CInt(ppSessionInfo)

            Dim sessions(sessionCount - 1) As WtsSession

            For i As Integer = 0 To sessionCount - 1
                Dim si As WTS_SESSION_INFO
                si = CType(Marshal.PtrToStructure(CType(structIndexer, IntPtr), GetType(WTS_SESSION_INFO)), WTS_SESSION_INFO)

                sessions(i) = New WtsSession()
                sessions(i).SessionId = si.SessionId
                sessions(i).ConnectionState = si.state
                sessions(i).StationName = si.pWinStationName
                Dim domain As String = GetSessionInfo(si.SessionId, WTS_INFO_CLASS.WTSDomainName)
                Dim user As String = GetSessionInfo(si.SessionId, WTS_INFO_CLASS.WTSUserName)
                sessions(i).UserDomain = domain
                sessions(i).UserName = user
                sessions(i).UserSid = KernelApi.GetSID(domain + "\" + user)

                structIndexer += structSize
            Next
            Return sessions
        Catch ex As Exception
            Throw New ApplicationException("Error enumerating WtsSessions: " + ex.Message)
        Finally
            WTSFreeMemory(ppSessionInfo)
        End Try
    End Function

    Public Shared Sub LogoffSession(ByVal sessionId As Integer, ByVal synchronous As Boolean)
        Dim result As Integer
        result = WtsApi.WTSLogoffSession(WTS_CURRENT_SERVER_HANDLE, sessionId, synchronous)
        If result = 0 Then
            Throw New Win32Exception(Marshal.GetLastWin32Error())
        End If
    End Sub

    Public Shared Sub DisconnectSession(ByVal sessionId As Integer, ByVal synchronous As Boolean)
        Dim result As Integer
        result = WtsApi.WTSDisconnectSession(WTS_CURRENT_SERVER_HANDLE, sessionId, synchronous)
        If result = 0 Then
            Throw New Win32Exception(Marshal.GetLastWin32Error())
        End If
    End Sub

    Public Shared Function AllUsersLoggedOff() As Boolean
        For Each s As WtsSession In WtsApi.GetSessions()
            If s.UserName <> "" Then Return False
        Next
        Return True
    End Function

    Public Shared Function WaitForConnectedSession(ByVal timeoutMilliseconds As Integer) As WtsSession
        Dim session As WtsSession = Nothing
        Dim elapsed As Integer = 0

        While elapsed < timeoutMilliseconds
            Try
                For Each s As WtsSession In WtsApi.GetSessions()
                    If s.ConnectionState = WTS_CONNECTSTATE_CLASS.WTSConnected Then
                        session = s
                        Exit While
                    End If
                Next
            Catch ex As Exception
                'ignore
            End Try
            Threading.Thread.Sleep(500)
            elapsed += 500
        End While

        Return session
    End Function

End Class

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions