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.

Monitoramento de Serviços do SQL Server – VBScript

Neste artigo mostrarei um script (vbscript) que gera um arquivo no Excel com algumas informações sobre os serviços do SQL Server de um ou mais servidores.

O script irá retornar os seguintes campos:

• System Name: hostname do servidor;
• Display Name: nome do serviço que é mostrado no services.msc;
• Name: nome do serviço;
• Started: se o serviço está ou não iniciado;
• Start Mode: se o serviço está desabilitado, se é iniciado manualmente ou automaticamente;
• Logon Name: qual conta sobe o serviço;
• State: o status do serviço: running (rodando) ou stopped (parado).

Caso o serviço esteja parado, a linha estará com a fonte com cor vermelha.

Este script efetua um SELECT em uma classe (ou tabelas) do Windows – para mais informações destas classes, veja este post. Nesta classe ele coleta as informações referentes aos serviços que possuem o “SQL” no nome. Lembrando que ele não olha nada no banco de dados, ou efetua alguma conexão com o banco de dados, etc. Ele olha diretamente estas informações no SO!

Importante: Este script necessita de um driver, no caso o “Microsoft Excel Driver”. Verifique se você possuí o driver instalado: para isso, vá em Control Panel->Administrative Tools->Data Sources (ODBC), dê um duplo clique. Ele irá mostrar uma série de abas, na aba User DSN (a primeria aba), clique em Add. Veja se está listado o “Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)” ou “Microsoft Excel Driver (*.xls)”:

driverexcel

Caso você não possua o driver, você pode baixar neste link do MSDN.

Obs.: O usuário que executa o script precisa ter permissão para ler estas classes nos servidores onde serão feitas as verificações.

Para executar o script, é preciso criar um arquivo.txt com a lista dos servidores a serem verificados. No script o arquivo é este : C:\Maquinas.txt. Você pode mudar o caminho e o nome do arquivo no seguinte trecho código:

Set InputFile = fso.OpenTextFile(“C:\Maquinas.txt“)

Exemplo: C:\Maquinas.txt

localhost

O script do arquivo verifica_servicos.vbs é este:

Set objExcel = CreateObject("Excel.Application")
objExcel.Visible = True
objExcel.Workbooks.Add
intRow = 2

objExcel.Cells(1, 1).Value = "System Name"
objExcel.Cells(1, 2).Value = "Display Name"
objExcel.Cells(1, 3).Value = "Name"
objExcel.Cells(1, 4).Value = "Started"
objExcel.Cells(1, 5).Value = "Start Mode"
objExcel.Cells(1, 6).Value = "Logon Name"
objExcel.Cells(1, 7).Value = "State"

Set Fso = CreateObject("Scripting.FileSystemObject")
Set InputFile = fso.OpenTextFile("C:\Maquinas.txt")
Do While Not (InputFile.atEndOfStream)
	strComputer = InputFile.ReadLine

	Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
	Set colItems = objWMIService.ExecQuery("Select * From Win32_Service Where Name Like '%SQL%'")

	For Each objItem in colItems
		objExcel.Cells(intRow, 1).Value = objItem.SystemName
		objExcel.Cells(intRow, 2).Value = objItem.DisplayName
		objExcel.Cells(intRow, 3).Value = objItem.Name
		objExcel.Cells(intRow, 4).Value = objItem.Started
		objExcel.Cells(intRow, 5).Value = objItem.StartMode
		objExcel.Cells(intRow, 6).Value = objItem.StartName
		objExcel.Cells(intRow, 7).Value = objItem.State

		If objExcel.Cells(intRow, 7).Value = "Stopped" Then
			objExcel.Cells(intRow, 7).EntireRow.Font.ColorIndex = 3
		End If

		intRow = intRow + 1
	Next
Loop

objExcel.Range("A1:G1").Select
objExcel.Selection.Interior.ColorIndex = 19
objExcel.Selection.Font.ColorIndex = 11
objExcel.Selection.Font.Bold = True
objExcel.Cells.EntireColumn.AutoFit

Set objSheet = objExcel.ActiveWorkbook.Worksheets(1)
Set objRange = objExcel.Range("B2")
objRange.Sort objRange,1,,,,,,1

Execute o verifica_servicos.vbs, o resultado será:

result

Caso você queria saber mais sobre como utilizar o objeto Excel.Application, veja este link.

É isso aí!

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);
            }
        }
    }
}

Domanin-Driven Design

De alguns anos para cá, surgiu uma nova metodologia, esta chama DDD (Domain-Driven Design).
Esta metodologia é a metodologia do futuro de acordo com alguns analistas de sistemas que já utilizam da mesma.

DDD: Domain-Driven Design

DDD

O que é a metodologia DDD?

DDD é uma metodologia desenvolvida por Eric Evans. Trata-se de uma metodologia de desenvolvimento que prega que todo o material feito pela equipe durante o desenvolvimento de um software deve estar diretamente relacionado com a “linguagem” dita pelo cliente (sub entende-se que o cliente seja especialista na área ou ramo da qual o software será aplicado). Entre outras palavras, o DDD visa o desenvolvimento a partir de um domínio. O que deixa em destaque a complexidade no desenvolvimento de software.

Esta metodologia implica nas alterações de dados. Ao invés de receber entidades inteiras no novo estado para salvar, eles recebem objetos que representam as alterações de estado das entidades. Além disso, permitem salvamentos em ordem aleatória, permitindo que o sistema tenha seus dados ligeiramente inconsistentes em certos momentos, enquanto parte das alterações ainda não foram salvas. À grosso modo: O desenvolvedor programa em cima de camadas de abstração :)

Antes de mais nada, vamos definir o que é um Domínio!

Domínio é um conjunto de objetos que, de forma abstrata, descreve aspectos (problemas) de um determinado domínio complexo.

O DDD possuí alguns enfoques interessantes, como por exemplo:
– Focalizar no modelo: Elaborar uma representação abstrata do domínio que nos ajude em cumprir o propósito;
– Linguagens onipresente: Os programadores partilham de uma linguagem comum;
– É Orientado à Objeto e combina com outras metodologias ágeis, como TDD, BDD

Um dos itens que pesquisei sobre o DDD, é o chamado “linguagem comum”. Vamos abordar mais sobre logo a baixo:

Linguagens Comuns
As vezes quando eu estou conversando com algum amigo sobre programação ou patterns e minha esposa está por perto ela fica muito perdida e diz que não consegue compreender nada do que estamos falando. O fato é que nós desenvolvedores possuímos uma linguagem muito específica que somente outro integrante de nossa “tribo” consegue compreender bem. Da mesma forma um profissional da área de risco financeiro por exemplo (falo isso por experiência própria) consegue falar um dialeto que pode ser completamente incompreensível para a maioria de nós desenvolvedores. Tente imaginar o que acontece quando estes especialistas do ramos de análise de risco financeiro contratam nós desenvolvedores para desenvolver um software? Em muitas situações como esta (e eu já tive a oportunidade de viver algumas) o software acaba não tendo sucesso por falta de uma comunicação clara entre os desenvolvedores e o cliente.
O DDD tenta acabar com este problema de comunicação diminuindo a dissonância entre o que é falado pelo time de desenvolvimento e pelo time de especialistas e para isso apresenta varias técnicas interessantes.
O design de um software (forma com que o mesmo é arquitetado) é aperfeiçoado a medida que o entendimento sobre o domínio aumenta. A cada refatoração o nosso design passa a expressar mais o domínio. Para isso tentamos fazer com que o modelo seja a linguagem falada pelo time de desenvolvedores de especialistas.
Diullei Gomes

Isto é uma das poucas coisas que li até agora sobre DDD.
A medida que vou lendo mais e mais, fico de postar no blog :)

Para quem quiser saber mais sobre DDD, só clicar neste link.

Agradecimentos à Diullei Gomes e ao fórum .NET Ponto

Alan Machado

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

Alocação dinâmica de memória (C/C++)

Alocação dinâmica de memória (C/C++)

 

Alocação de memória é uma técnica de “controle” de memória, utilizados em programas de maiores dimensões. É importante para que um programador tenha idéia do que se trata e como se utiliza alocação de memória, leia-se buffers para entrada e saída de dados.

Existem duas funções fornecidas pelas bibliotecas C para controle de memória alocada dinamicamente.
Podemos fazer alocação de memória de tamanho (size) em bytes e para liberar memória alocada com o malloc.

Alocação de memória:
void *malloca(size_t size)

Liberar memória:
void free(void *ptr)

O operador sizeof é útil para especificar o valor do argumento de malloc, ou seja, o tamanho a ser aloca em bytes. Exemplo:

ptr = malloc(n * sizeof(int));

Lembrando sempre, que a memória tem suas limitações, ou seja, não podemos alocar espaço na memória quando bem querermos ou quando quisermos! É importante, antes de usar o malloc, verificar se ele retorna um valor não nulo:

ptr = malloc(n * sizeof(int));
if(ptr = NULL){

printf(‘Espaço na memória’);
return 0;
}

Normalmente, quando você utiliza o recurso da memória alocada dinacamente, pode ocorrer dois tipos de erro:

Fugas de memória (memory leaks): Quando a memória não pode ser liberada quando ela não é mais necessária. Isto pode conduzir o encerramento de um programa automaticamente (sem nenhum aviso prévio).

Danling pointers: Acontece quando os ponteiros utilização blocos de memória já liberados. Isto provoca a famosa mensagem de erro: “Segmentation violation: core dumped.”

Um dos maiores problemas do C em relação a estes tipos de erro é a indentificação do mesmo. Para evitar estes tipos de erro, o programador deve ser cuidadoso e usar um padrão conveniente na hora utilizar os ponteiros e alocação de memória dinâmica em seu código.

Uma prática comum em alocação dinâmica de memória é alocação de buffers, que são utilizados para guardar dados a ler de dispositivos periféricos de entrada ou escrever me dispositivos periféricos de saída. Exemplo:

Para ler algum dado:
ssize_t read(int fd, void *buf, size_t count);

Para escrever algum dado:
ssize_t write(int fd, void *buf, size_t count)

Não se esqueça! Ambas funções deve conter um endereço buffer de memória!

Podemos usar duas alternativas na alocação de buffer de memória:

Alocação estática:
char buf[BUFSIZ];
[…]
n = read(fd, buf, BUFSIZ);

Alocação dinâmica:
char *buf;
[…]
if((buf = malloc(BUFSIZ)) = NULL){
printf(‘Fora da memória!\n’);
exit(1);
}
n = read(fd, buf, BUFSIZ);

Lembre-se! É de suma importância usar um apontador inicializado nestas funções, como a read() por exemplo.

É um resumo sobre alocação dinâmica de memória. Sempre tome cuidado na hora de programar!

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