Sessão WTS – VB.NET

Neste post irei mostrar um código em VB.NET de uma classe chamada clsWTS. Esta classe retorna algumas informações básicas de uma conexão remota conectada em uma estação.

Esta classe utiliza duas API’s (User32.dll e WtsApi32.dll) do Windows para obter as informações a seguir:

• IsRemoteSession: verifica se é uma sessão remota;
• ClientLoginName: login do client conectado;
• ClientComputerName: nome da máquina na rede do client conectado;
• ClientDomainName: domínio do client conectado;
• ClientIPAddress: endereço IP do client que está realizando a sessão remota;
• SessionName: nome da sessão do client conectado.

Código da classe:

Imports System.Runtime.InteropServices

Public Class clsCWTS

#Region "APIs"

    Private Declare Function WTSQuerySessionInformationA Lib "wtsapi32.dll" (ByVal hServer As Integer, ByVal SessionID As Integer, ByVal WTSInfoClass As WTS_INFO_CLASS, ByRef ppBuffer As Integer, ByRef pBytesReturned As Integer) As Integer
    Private Declare Sub WTSFreeMemory Lib "wtsapi32.dll" (ByVal pMemory As Integer)
    Private Declare Function GetSystemMetrics Lib "user32" (ByVal nIndex As Integer) As Integer
    Private Declare Function lstrlenA Lib "kernel32.dll" (ByVal lpString As Integer) As Integer
    Private Declare Function lstrcpyA Lib "kernel32.dll" (ByVal lpString1 As String, ByVal lpString2 As Integer) As Integer
    Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByVal pDst As Byte, ByVal pSrc As Integer, ByVal ByteLen As Integer)
    Private Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" (ByVal lpLibFileName As String) As Integer
    Private Declare Function GetProcAddress Lib "kernel32" (ByVal hModule As Integer, ByVal lpProcName As String) As Integer
    Private Declare Function FreeLibrary Lib "kernel32" (ByVal hLibModule As Integer) As Integer
    Private Declare Function WTSQuerySessionInformation2 Lib "WtsApi32.dll" Alias "WTSQuerySessionInformationW" (ByVal hServer As Int32, ByVal SessionId As Int32, ByVal WTSInfoClass As Int32, ByRef ppBuffer As IntPtr, ByRef pCount As Int32) As Boolean

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

#End Region

#Region "Constantes"
    Private Const SM_REMOTESESSION As Integer = &H1000
    Private Const WTS_CURRENT_SERVER_HANDLE As Integer = 0&
    Private Const WTS_CURRENT_SERVER = 0&
    Private Const WTS_CURRENT_SESSION As Integer = -1

    Private Const AF_INET = 2
    Private Const AF_IPX = 6
    Private Const AF_NETBIOS = 17
    Private Const AF_UNSPEC = 0
#End Region

#Region "Enums e Structures"
    Private Enum WTS_INFO_CLASS
        WTSInitialProgram
        WTSApplicationName
        WTSWorkingDirectory
        WTSOEMId
        WTSSessionId
        WTSUserName
        WTSWinStationName
        WTSDomainName
        WTSconnectState
        WTSClientBuilderNumber
        WTSClientName
        WTSClientDirectory
        WTSClientProductId
        WTSClientHardwareId
        WTSClientAddress
        WTSClientDisplay
        WTSClientProtocolType
    End Enum

    <StructLayout(LayoutKind.Sequential)> _
    Public Structure _WTS_CLIENT_ADDRESS
        Public AddressFamily As Integer
        <MarshalAs(UnmanagedType.ByValArray, SizeConst:=20)> _
        Public Address() As Byte
    End Structure
#End Region

#Region "Variáveis"
    Private m_bolIsRemoteSession As Boolean
    Private m_strWTSClientName As String
    Private m_strWTSUserName As String
    Private m_strWTSDomainName As String
    Private m_strWTSWinStationName As String
    Private m_strClientIPAddress As String
#End Region

#Region "Inicialização da classe"
    Public Sub New()
        QueryWTSSession()
    End Sub
#End Region

#Region "Properties (ReadOnly)"
    Public ReadOnly Property IsRemoteSession() As Boolean
        Get
            IsRemoteSession = m_bolIsRemoteSession
        End Get
    End Property

    Public ReadOnly Property ClientLoginName() As String
        Get
            ClientLoginName = m_strWTSUserName
        End Get
    End Property

    Public ReadOnly Property ClientComputerName() As String
        Get
            ClientComputerName = m_strWTSClientName
        End Get
    End Property

    Public ReadOnly Property ClientDomainName() As String
        Get
            ClientDomainName = m_strWTSDomainName
        End Get
    End Property

    Public ReadOnly Property ClientIPAddress() As String
        Get
            ClientIPAddress = m_strClientIPAddress
        End Get
    End Property

    Public ReadOnly Property SessionName() As String
        Get
            SessionName = m_strWTSWinStationName
        End Get
    End Property
#End Region

#Region "Funções relacionadas à classe clsCWTS"
    Private Sub QueryWTSSession()

        Dim btLinhaErro As Single = 0

        Try
            Dim lpBuffer As Integer = 0
            Dim lngBytesReturned As Integer = 0

            btLinhaErro = 1
            m_bolIsRemoteSession = (GetSystemMetrics(SM_REMOTESESSION) <> 0)
            If Not m_bolIsRemoteSession Then
                Exit Sub
            End If

            btLinhaErro = 2
            If Not IsProcedureAvailable("wtsapi32.dll", "WTSQuerySessionInformationA") Then
                Exit Sub
            End If

            btLinhaErro = 3
            If WTSQuerySessionInformationA(WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, WTS_INFO_CLASS.WTSClientName, lpBuffer, lngBytesReturned) Then
                m_strWTSClientName = LPSTRtoBSTR(lpBuffer)
                WTSFreeMemory(lpBuffer)
            End If

            btLinhaErro = 4
            If WTSQuerySessionInformationA(WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, WTS_INFO_CLASS.WTSUserName, lpBuffer, lngBytesReturned) Then
                m_strWTSUserName = LPSTRtoBSTR(lpBuffer)
                WTSFreeMemory(lpBuffer)
            End If

            btLinhaErro = 5
            If WTSQuerySessionInformationA(WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, WTS_INFO_CLASS.WTSDomainName, lpBuffer, lngBytesReturned) Then
                m_strWTSDomainName = LPSTRtoBSTR(lpBuffer)
                WTSFreeMemory(lpBuffer)
            End If

            btLinhaErro = 6
            If WTSQuerySessionInformationA(WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, WTS_INFO_CLASS.WTSWinStationName, lpBuffer, lngBytesReturned) Then
                m_strWTSWinStationName = LPSTRtoBSTR(lpBuffer)
                WTSFreeMemory(lpBuffer)
            End If

            btLinhaErro = 7
            Dim iptAddress As IntPtr = IntPtr.Zero
            Dim ptrOpenedServer As IntPtr = WTSOpenServer(String.Empty)
            Dim intReturned As Integer = 0

            If WTSQuerySessionInformation2(ptrOpenedServer, 0, WTS_INFO_CLASS.WTSClientAddress, iptAddress, intReturned) = True Then
                Dim wtsAddress As New _WTS_CLIENT_ADDRESS()
                wtsAddress = CType(Marshal.PtrToStructure(iptAddress, wtsAddress.GetType()), _WTS_CLIENT_ADDRESS)
                m_strClientIPAddress = String.Concat(wtsAddress.Address(2) & "." & wtsAddress.Address(3) & "." & _
                                                      wtsAddress.Address(4) & "." & wtsAddress.Address(5))
            End If

        Catch ex As Exception
            Dim strMsg As String = ex.Message
            Throw New Exception("Error in QueryWTSession. Row: " & btLinhaErro & " Msg: " & strMsg)
        End Try

    End Sub

    Private Function LPSTRtoBSTR(ByVal lpBuffer As Integer) As String
        Dim lngChars As Integer = 0
        Dim strChars As String = String.Empty

        lngChars = lstrlenA(lpBuffer)

        If lngChars > 0 Then
            strChars = New String(Chr(0), lngChars)
            lstrcpyA(strChars, lpBuffer)
        End If

        LPSTRtoBSTR = strChars
    End Function

    Private Function IsProcedureAvailable(ByVal ModuleName As String, ByVal ProcName As String) As Boolean
        Dim hMod As Integer
        Dim pAddr As Integer

        hMod = LoadLibrary(ModuleName)

        If hMod Then
            pAddr = GetProcAddress(hMod, ProcName)
            FreeLibrary(hMod)
        End If

        IsProcedureAvailable = (pAddr <> 0)
    End Function
#End Region

End Class

Um exemplo simples de como obter as informações desta classe:

        Try
            Dim WTS As New clsCWTS
            'Só exibe as informações caso a conexão seja remota (Windows Terminal Services)
            If WTS.IsRemoteSession Then
                MsgBox("Client Computer Name: " & WTS.ClientComputerName & vbCrLf & _
                       "Client Domain Name:" & WTS.ClientDomainName & vbCrLf & _
                       "Client IP Address: " & WTS.ClientIPAddress & vbCrLf & _
                       "Client Login Name: " & WTS.ClientLoginName & vbCrLf & _
                       "Session Name: " & WTS.SessionName)
            Else
                MsgBox("A sessão não é uma sessão remota!")
            End If
        Catch ex As Exception
            MsgBox(ex.Message)
        End Try

Neste link você pode baixar o código-fonte exemplo que criei. O código-fonte foi compilado (x86) com o Visual Studio 2008.

Win32 Classes (Windows)

As classes Win32 são classes (ou tabelas) que possuem informações sobre hardware e recursos do Sistema Operacional. Estas classes são divididas 6 partes. São elas:

– Classes de Hardware: Objetos relacionados ao Hardware;
– Classes de Aplicativos: Objetos relacionados ao software;
– Classes do Sistema Operacional: Objetos relacionados ao SO;
– Classes de Contador de Desempenho: Dados de desempenho bruto e calculado de contadores de desempenho;
– Classes auxiliares de segurança: Classe fornece métodos para converter entre diferentes formatos de descrições de segurança;
– Classes de gerenciamento de serviço WMI: Classes de gerenciamento de serviço WMI.

Dentro destas divisões, existem várias classes, como por exemplo a Win32_Battery. Esta classe mostra informações sobre a bateria, como por exemplo:

Status (tipo String) = Mostra o status atual da bateria.
TimeOnBattery (uint32) = Mostra o tempo que a bateria está conectada sem o carregador.

Lista completa dos dados relacionados a classe Win32_Battery
Lista completa de todas as classes Win32

Código exemplo de como ler estas informações.

VB.NET

Imports System.Management
Module mdlExemplo
    Sub Main()
        Dim objSearcher As ManagementObjectSearcher = Nothing
        Dim objDevice As ManagementObject = Nothing

        Try
            objSearcher = New ManagementObjectSearcher("Select * From Win32_Battery")
            For Each objDevice In objSearcher.Get
                Console.WriteLine(objDevice.Item("StatusInfo").ToString)
            Next
        Catch ex As Exception
            Console.WriteLine(ex.Message)
        End Try
    End Sub

C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Management;

namespace WIN32Class
{
    class Program
    {
        static void Main(string[] args)
        {
            ManagementObjectSearcher objSearcher = null;
            
            try
            {
                objSearcher = new ManagementObjectSearcher("Select * From Win32_Battery");
                foreach(ManagementObject objDevice in objSearcher.Get())
                {
                    Console.WriteLine(objDevice["StatusInfo"].ToString());
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }
}

Threads – VB.NET

Estava procurando estes dias no Google algo referente à Thread par VB.NET e achei um artigo no CodeProject que é muito simples e fácil de entender.
Para quem não sabe a definição de Thread, a baixo tem algumas definições bacanas sobre Thread:  

Um pequeno programa que trabalha como um sub-sistema independente de um programa maior, executando alguma tarefa específica. Um programa dividido em vários threads pode rodar mais rápido que um programa monolítico, pois várias tarefas podem ser executadas simultaneamente. Os vários threads de um programa podem trocar dados entre sí e compartilhar o mesmo espaço de memória e os mesmos recursos do sistema.

Para o programador, existem vantagens e desvantagens em dividir um programa em vários threads. Por um lado isso facilita o desenvolvimento, pois é possível desenvolver o programa em módulos, testando-os isoladamente, ao invés de escrever um único bloco de código. Mas, por outro lado, com vários threads o trabalho torna-se mais complexo, devido à interação entre eles.

Existem diferenças na maneira como os sistemas operacionais executam processos e threads. Por exemplo, o Windows têm mais facilidade para gerenciar programas com apenas um processo e vários threads, do que com vários processos e poucos threads, pois Windows o tempo para criar um processo e alternar entre eles é muito grande. O Linux e outros sistemas baseados no Unix por sua vez é capaz de criar novos processos muito rápido, o que explica o fato de alguns aplicativos, como por exemplo o Apache, rodarem muito mais rápido no Linux do que no Windows, ao serem portados para ele. Porém, ao serem alterados, os mesmos programas podem apresentar um desempenho semelhante nos dois sistemas. É o que a equipe do apache vem procurando fazer nas versões atuais do programa.

Fontes:
Alan Machado

Código Fonte – Captura Screen (VB.NET)

Este aplicativo [Captura Screen], efetua uma série de prints screens em um intervalo de tempo (em segundos passados por um parâmetro).

Ele efetua o print screen e salva esta imagem no seguintes diretórios:
“C:\Screens_Oculto\ddmmyyyy\hh\ddmmyyyy_hhmmss.jpg”

Se você executá-lo sem passar nenhum parâmetro, ele fará num intervalo default que é de 5 segundos.

Para executá-lo com passagem de parâmetro, faça o seguinte procedimento:

Iniciar\Executar\Cmd
Ao abrir a janela do Prompt (cmd), efetue o seguinte comando: “CaminhoDoExecutável\Captura_Screen.exe TempoEmSegundos” e aperte enter.

Este aplicativo roda de “forma oculta”, ou seja, o usuário não visualiza o aplicativo rodando (ao não ser que ele olhe no Task Manager).

Para finalizar o aplicativo, entre no Gerenciador de Tarefas e vá na aba Processos e clique em CapturaScreen e aperte Delete ou clique com o botão direito em cima do executável e clique em Finalizar Processo.

Código fonte em VB.NET 08

Links do Código Fonte e Executável

Alan Machado