Hoje vamos tratar do gerenciamento de processos e threads em aplicações Windows Forms. |
Você pode usar o componente Process para obter uma lista de processos em execução, iniciar e parar processos e retornar informação sobre os processos em execução em um computador.
A classe Process esta no namespace using System.Diagnostics;
Recursos usados
Objetivos
Aprendizado
Conceitos Básicos
Um processo é um aplicativo em execução. Uma Thread ou segmento é a unidade básica para a qual o sistema operacional aloca tempo do processador. Uma thread pode executar qualquer parte do código do processo, incluindo partes atualmente sendo executadas por outra thread.
Assim podemos usar uma instância do componente Process para determinar se um processo parou de responder. Se o valor retornado for igual a true você pode forçar o componente a parar o processo ou avisar o usuário e oferecer a ele opções de escolha sobre o que fazer.
Nota: Processos de 32 bits não podem acessar os módulos de um processo de 64 bits. Se você tentar obter informações sobre um processo de 64 bits a partir de um processo de 32 bits, você receberá uma exceção Win32Exception.
O componente Process pode ser usado para iniciar processo no seu sistema pela chamada do método Start.
O método Start é usado em conjunto com a propriedade StartInfo.
A propriedade StartInfo define propriedades que são passadas para o método Start.
Antes de chamar o método Start você precisa especificar o nome do arquivo do processo a iniciar definindo a propriedade FileName ou informando o caminho completo do processo e, no caso de aplicações Windows, informar apenas o nome do processo.
Podemos usar essa sobrecarga do método Start para iniciar um recurso do processo e associá-lo com o componente do processo atual. O valor de retorno true indica que um novo recurso do processo foi iniciado. Se o recurso de processo especificado pelo membro FileName da propriedade StartInfo já está em execução no computador, nenhum recurso adicional do processo é iniciado. Em vez disso, o recurso de processo em execução é reutilizado e false é retornado.
Vejamos um exemplo bem básico de uma aplicação Windows Forms que contém um formulário com um controle Button que no seu evento Click chama o processo para executar o NotePad (Bloco de Notas):
Abra o Visual Studio 2012 Express for Windows Desktop e clique em New Project;
Selecione o template Visual C# -> Windows -> Windows Forms Application e informe o nome Processo_Threads e clique em OK;
Para cada exemplo iremos incluir um novo projeto na solução usando o menu File -> Add -> New Project;
A seguir o código e o resultado da execução do exemplo:
using System.Diagnostics; using System.Windows.Forms;
namespace
Processo_Threads
private void btnExecutarProcesso_Click(object sender, EventArgs e) |
Obtendo informação do processo atual
Quais informações podemos obter da classe Process ? A seguir temos uma tabela com uma relação dos principais membros da classe:
BasePriority |
Obtêm a base prioritária do processo associado. |
Container |
Obtêm a IContainer que contém o componente |
EnableRaisingEvents |
Obtêm ou define se o evento Exited será disparado quando o processo terminar. |
ExitCode |
Obtêm um valor para o processo associado quando o mesmo é encerrado. |
ExitTime |
Obtêm a hora que o processo associado encerrou. |
Handle |
Retorna o handle do processo associado nativo. |
HandleCount |
Obtêm o número de handles abertos pelo processo. |
HasExited |
Obtêm um valor indicando se o processo associado terminou. |
Id |
Obtêm o identificador único para o processo associado. |
MachineName |
Obtêm o nome do computador no qual o processo associado esta rodando. |
MainModule |
Obtêm o módulo principal do processo associado. |
MainWindowHandle |
Obtêm o handle da janela para a janela principal do processo associado. |
MainWindowTitle |
Obtêm o titulo da janela principal do processo. |
ProcessName |
Obtêm o nome do processo. |
Responding |
Obtêm um valor indicando se a interface do usuário do processo esta respondendo |
Site |
Obtêm ou define o Site do componente. |
StartTime |
Obtêm a hora que o processo associado foi iniciado. |
Threads |
Obtêm o conjunto de threads que estão rodando no processo associado. |
TotalProcessorTime |
Obtêm a tempo total de processamento para este processo. |
UserProcessorTime |
Obtêm o tempo de processamento este processo. |
VirtualMemrySize |
Obtêm o tamanho da memória virtual do processo. |
WorkingSet |
Obtêm a memória física associada ao processo atual. |
No exemplo a seguir temos a exibição de algumas informações do processo em execução em minha máquina local.
O código esta associado ao evento Click de um botão de comando - btnInfo - em um formulário Windows Forms e o resultado esta sendo exibido em um listbox - lbInfo :
using System; using System.Diagnostics; using System.Windows.Forms;
namespace Processos_Informacoes
private void btnInfo_Click(object sender, EventArgs e) |
O método GetCurrentProcess() obtém um novo componente Process e o associa ao processo ativo.
Parando um Processo
Você pode usar a
propriedade Responding para determinar se a interface do usuário de um
processo está respondendo. Quando você tenta ler a propriedade Respondendo , um
pedido é enviado para a interface de usuário do processo de destino . Se houver
uma resposta imediata, o valor da propriedade de retorno é true; se não
houver resposta da interface o valor retornado será false. Esta
propriedade é útil se você precisar forçar um aplicativo que esta
'congelado' a ser fechado.
Há dois métodos que você pode usar para parar um processo com um componente
Process. O método utilizado depende do tipo de processo a ser parado:
O exemplo a seguir mostra como determinar se o bloco de notas está respondendo. Se a propriedade Response for igual a true, chamamos o método CloseMainWindow para fechar o aplicativo . Se a propriedade Response for igual a false, chamamos o método Kill para forçar o fechamento do processo.
using System;
using System.Diagnostics;
using System.Windows.Forms;
namespace Processo_Parar
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void btnPararProcesso_Click(object sender, EventArgs e)
{
// Define um array
Process[] macProcessos;
Process macProcess = null;
macProcess = Process.Start("NotePad.exe");
if (macProcess.Responding)
{
MessageBox.Show("Status Processo do NotePad = Em execução");
// Retorna um array contendo todas as instãncias do NotePad
macProcessos = Process.GetProcessesByName("Notepad");
// Percorre todos os processos e usando o método
// CloseMainWindow() para encerrar o Processo
foreach (Process processo in macProcessos)
{
processo.CloseMainWindow();
}
}
else
{
MessageBox.Show("Status Processo do NotePad = Não Respondendo");
}
}
}
}
|
Esperando um processo terminar
Um processo é dito ser
ocioso quando sua janela principal está aguardando uma entrada do sistema. A fim
de testar o processo para o seu estado de repouso, você deve primeiro ligar um
componente de processo a ele. Você pode chamar o método WaitForInputIdle
antes do processo de destino executar uma ação.
O método WaitForInputIdle instrui um componente do processo para aguardar
o processo associado inserir um estado ocioso. O método é útil, por exemplo,
quando o aplicativo espera por um processo para terminar de criar a sua janela
principal antes de se comunicar com essa janela. O método WaitForInputIdle
só funciona com processos que têm uma interface de usuário.
O método WaitForExit(int32) instrui o componente Process a esperar um número especificado de milisegundos para que o processo associado encerre. O método é usado para fazer a thread atual esperar até que o processo associado termine. Se um valor não for fornecido o processo aguarda indefinidamente.
Quando um processo associado termina (quer por shutdown do sistema operacional ou através de uma encerramento anormal) o sistema armazena informações sobre o processo e retorna o componente que chamou o método WaitForExit(int32).
Vejamos um exemplo de utilização de cada um destes métodos:
1- WaitForExit(int32)
using System;
using System.Diagnostics;
using System.Windows.Forms;
namespace Processo_Esperando_Terminar
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void btnEsperandoTerminar_Click(object sender, EventArgs e)
{
Process macProcesso = null;
try
{
// inicia o processo
macProcesso = Process.Start("NotePad.exe");
// exibe estatisticas do processo ate o usuário fechar o programa
do
{
if (!macProcesso.HasExited)
{
// Atualiza os valores das propriedades do processo atual
macProcesso.Refresh();
lbInfo.Items.Add("");
// Exibe estatisticas
lbInfo.Items.Add(macProcesso.ToString());
lbInfo.Items.Add("---------------------------------------------------------------");
lbInfo.Items.Add(" Uso da memória física : " + macProcesso.WorkingSet64.ToString());
lbInfo.Items.Add(" Tempo processador usuário : " + macProcesso.UserProcessorTime.ToString());
lbInfo.Items.Add(" tempo processador total : " + macProcesso.TotalProcessorTime.ToString());
if (macProcesso.Responding)
{
lbInfo.Items.Add("Status = Rodando");
}
else
{
lbInfo.Items.Add("Status = Não Respondendo");
}
}
}
while (!macProcesso.WaitForExit(1000));
lbInfo.Items.Add("Código de saida do Processo : " + macProcesso.ExitCode.ToString());
}
finally
{
if (macProcesso != null)
{
macProcesso.Close();
}
}
}
}
}
|
2- WaitForInputIdle
Faz com que o componente Process aguarde indefinidamente o processo associado entrar um estado ocioso.
Essa sobrecarga só se aplica a processos com uma interface de usuário e, portanto, um loop de mensagem.
using System.Diagnostics;
using System.Threading;
using System.Windows.Forms;
namespace Processo_Esperando_Terminar
{
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
//cria e inicia um processo para o NotePad
Process process = new Process();
process.StartInfo.FileName = "Notepad";
process.Start();
process.WaitForInputIdle();
//espera 5 segundos
Thread.Sleep(5000);
if (!process.CloseMainWindow())
{
//mata o processo
process.Kill();
}
}
}
}
|
Atualizar um controle de formulário do Windows
a partir de uma thread em segundo plano
Os controles Windows Forms só podem ser executados
no segmento em que eles foram criados, ou seja, eles não são thread-safe
. Se você quer obter ou definir propriedades ou chamar métodos em um controle a
partir de uma thread em segundo plano a chamada deve ser empacotada para o
segmento que criou o controle.
Assim se você tentar chamar um método de um formulário windows em uma thread distinta, uma exceção será disparada, e, dependendo de como você implementou o tratamento de exceção no seu código a aplicação pode encerrar. Se você não estiver efetuando um tratamento de exceção será exibida a seguinte mensagem:
Então para seu próprio
bem tenha em mente sempre o seguinte: "Quando você estiver trabalhando
com controles em formulários windows tenha sempre certeza de somente acessar os
controles a partir da thread na qual eles foram criados. senão..."
Criar e iniciar a
thread
Quando uma thread é criada , uma nova instância da classe Thread é criada
usando um construtor que leva o delegado ThreadStart como seu único
parâmetro. No entanto, a thread não inicia a execução até que o método Start
seja chamado. Quando Start é chamado, a execução começa na primeira linha
do método referenciado pelo delegado ThreadStart .
private Thread timerThread;
timerThread = new
Thread(new ThreadStart(ThreadProc));
timerThread.IsBackground = true;
timerThread.Start();
Chamar uma função a partir
da thread
O delegado MethodInvoker fornece um delegado simples que é usado para
chamar um método com uma lista de parâmetros vazia. Esse delegado pode ser usado
para fazer chamadas para o método de invocação de um controle, ou quando você
precisa de um simples delegado , mas não quer definir um você mesmo. Quando você
cria um delegado MethodInvoker , você identifica o método que manipulará
o evento. Para associar o evento com o manipulador de eventos , adicione uma
instância do delegado para o evento. O manipulador de eventos é chamado sempre
que o evento ocorre, a menos que você remova o delegado.
BeginInvoke
executa o delegado dado no segmento que possui o identificador da janela
subjacente do controle. O delegado é chamado de forma assíncrona e este método
retorna imediatamente.
Você pode chamar isso de qualquer thread, mesmo a thread que possui o
identificador do controle. Se o identificador do controle ainda não existe, isso
fará o acompanhamento da cadeia o pai do controle até encontrar um controle ou
formulário que tem um identificador de janela.
Se nenhum identificador apropriado puder ser encontrado, BeginInvoke irá
lançar uma exceção. Exceções dentro do método de delegado são considerados não
tratadas e serão enviadas ao manipulador de exceção não tratadas da aplicação.
Exemplo:
// Esta thread em segundo plano é chamada a partir do
delegado ThreadStart
// Esta função é chamada a partir da thread em segundo
plano |
Esquematizando o processo temos :
- Chamadas assíncronas são feitas usando delegados. Um delegado é um objeto que envolve uma função. Delegados fornecem uma função síncrona e também fornece métodos para chamar a função envolveu de forma assíncrona.
- Esses métodos são BeginInvoke() e EndInvoke(). As listas de parâmetros destes métodos dependem da assinatura da função de delegado.
- BeginInvoke() é usado para iniciar a chamada assíncrona do método . Ele tem os mesmos parâmetros que o nome da função , e dois parâmetros adicionais. BeginInvoke () retorna imediatamente e não esperar que a chamada assíncrona para ser concluído. BeginInvoke( ) retorna um objeto IAsyncResult .
- A função EndInvoke() é utilizado para recuperar os resultados do método de chamada assíncrona . Ele pode ser chamado a qualquer momento após o método BeginInvoke() . Se a chamada assíncrona ainda não concluída, EndInvoke() bloqueia até que ela seja concluída. Os parâmetros da função EndInvoke() incluem os parâmetros out e ref que a função embrulhado tem , mais o objeto IAsyncResult que é retornado pelo método BeginInvoke ().
Como usar multithreading em aplicações com formulários windows ?
Veja bem, quando você for chamar um método de um controle que esta em um formulário windows fora da thread de origem do controle você tem que executar a chamada na thread de origem. (a chamada tem que ser 'marshalled').
Existem duas formas de você fazer isto:
De forma assíncrona - Usar o método BeginInvoke que força o método a ser executado na thread que criou o formulário ou controle. Se você usar BeginInvoke a chamada irá retornar imediatamente. Se você precisar obter o valor de retorno de um delegate invocado de forma assíncrona você pode usar o EndInvoke com o IAsyncResult retornado pelo BeginInvoke para esperar até que o delegate invocado assincronamente tenha sido completado.
De forma síncrona - Usar o método Invoke para obter o mesmo efeito. Quando você usar Invoke a thread atual será bloqueada até que o delegate tenha sido executado.
Todos os controles de formulários windows possuem o método Invoke e a propriedade InvokeRequired.
Você usa o método Invoke para fazer com que o controle transfira a chamada para a thread na qual ele foi criado e então executar a tarefa proposta.
A propriedade InvokeRequired retorna um valor true/false (false indica que a thread atual é a thread da fila de mensagem) indicando se quem chamou o método precisa usar o método Invoke quando a chamada é feita a um método do controle. Exemplo de uso:
Para encerrar utilize sempre estas duas regras de ouro (golden rules)
1- Nunca invoque qualquer método ou propriedade em um controle criado em outra thread sem usar os métodos/propriedades: Invoke, BeginInvoke, EndInvoke or CreateGraphics, e InvokeRequired.
Cada controle esta vinculado a uma thread que executa a sua fila de mensagens. Se você tentar acessar ou alterar qualquer coisa na interface,(por exemplo alterar a propriedade Text) a partir de uma thread diferente você corre o risco de travar o seu programa.
2- Nunca execute uma tarefa que vai consumir muito tempo em uma thread de um controle/formulário windows forms.
Se o seu código esta sendo executado em uma thread de controle/formulário nenhum outro código está rodando nesta thread. O que significa que você não irá receber notificações de eventos, seus controles não serão redesenhados , etc. E você terá que esperar que o código termine de ser executado para realizar outras tarefas. Geralmente os programadores VB poderiam ficar tentados a usar uma chamada a Application.DoEvents. Mas isto não é aconselhável.
Depois de toda essa teoria na segunda parte do artigo veremos a sua aplicação em uma aplicação Windows Forms usando threads.
João 1:14 E o Verbo se fez carne, e habitou entre nós, cheio de graça e de verdade; e vimos a sua glória, como a glória do unigênito do Pai.
Veja os
Destaques e novidades do SUPER DVD Visual Basic
(sempre atualizado) : clique e confira !
Quer migrar para o VB .NET ?
Quer aprender C# ??
|
Gostou ?
Compartilhe no Facebook
Compartilhe no Twitter
Referências: