22 de outubro de 2009

Obtendo informações sobre as Threads de um processo

Recebi um feedback baseado no que publiquei no último post (Obtendo informações sobre processos em execução) perguntando se é possível fazer a mesma coisa com Threads. Isto é, dado um handle para um processo é possível obter informações sobre as Threads criadas por ele ?

A resposta é sim, é possível obter uma lista das Threads que estão associadas a um determinado processo. O método para obter esta lista, no entanto, difere um pouco daquele que usei para obter a lista de processos. O problema é que a criação de Threads por um programa é dinâmica e o números de Threads existentes num momento pode não ser mais o mesmo no momento seguinte, dificultando a obtenção de uma lista. (Ok, a lista de processos ativos também pode variar bastante de um momento para outro; volto nesse tópico mais abaixo.)

Para contornar esse problema, a Microsoft disponibilizou na API do Windows uma função chamada CreateToolhelp32Snapshot cujo objetivo é tirar um "instantâneo" do estado de um processo, aí incluindo informações sobre as Threads existentes no momento da chamada da função. No Delphi 2005, ela pode ser encontrada na unit TlHelp32 e possui a seguinte declaração:
function CreateToolhelp32Snapshot(dwFlags, th32ProcessID: DWORD): THandle;

Quais informações sobre o processo serão salvas por esta função depende do valor passado no parâmetro dwFlags . No caso de Threads, há um porém. Especificando a flag TH32CS_SNAPTHREAD, a função ignora o parâmetro com a identificação do processo e captura informações sobre todas as Threads existentes no Windows no momento da execução.
Para percorrer a lista de Threads, há duas outras funções na mesma unit, que são Thread32First (para obter informações sobre a primeira thread) e Thread32Next (para continuar a busca, obtendo as demais). Como estas funções listarão todas as Threads do sistema, é preciso pinçar as informações que interessam. Para isso, use a estrutura THREADENTRY32 (preenchida como retorno em ambas as funções) para comparar a identificação do processo que criou a Thread com a identificação do processo que você deseja. Veja abaixo uma forma comum de usar essas funções:
procedure TForm1.Button1Click(Sender: TObject);
var hSnap, procId : THandle;
cont : integer;
continua : boolean;
threadInfo: TThreadEntry32;
begin
hSnap := CreateToolhelp32Snapshot (TH32CS_SNAPTHREAD, 0);

procId := GetCurrentProcessId();

if hSnap <> INVALID_HANDLE_VALUE then
begin
cont := 0;
threadInfo.dwSize := sizeof (threadInfo);
continua := Thread32First(hSnap, threadInfo);
while continua do
begin
if procId = threadInfo.th32OwnerProcessID then
Inc (cont);
continua := Thread32Next(hSnap, threadInfo);
end;
end;

CloseHandle (hSnap);
end;

Neste exemplo, eu apenas conto quantas threads foram criadas pelo meu próprio programa. Faço isso recuperando o ID do meu processo com GetCurrentProcessId e comparando este ID com aquele contido no campo th32OwnerProcessID da estrutura associada à Thread. Este campo indica qual foi o processo que criou a Thread em questão.

Poderia ainda ter usado a identificação de cada Thread, conforme alimentado no campo th32ThreadID, para obter mais informações ou executar alguma das operações disponíveis na API do Windows para Processos e Threads.

Quando não for mais precisar do snapshot obtido pelo CreateToolhelp32Snapshot, libere os recursos associados a ele através de uma chamada à função CloseHandle.

Usando essa mesma técnica, podemos conseguir a lista dos processos em execução no Windows. Para isso, use as funções Process32First e Process32Next.

2 comentários :

Anônimo disse...

Eu consigo listar todas as threads de um processo externo, mas eu gostaria de finalizar/suspender/resumir determinadas threads, você pode me mostrar como com algum exemplo?

Luís Gustavo Fabbro disse...

A estrutura THREADENTRY32 mostrada no post acima traz o handle para as threads encontradas. Esse handle pode ser usado em quaisquer das funções de thread da API do Windows, tais como SuspendThread (para suspender temporariamente a execução de uma thread) e a TerminateThread (para encerrar a execução de uma thread). Lembre-se apenas de que o usuário executando o programa tem que ter permissão no Windows para acionar essas funções.

[]s

Postar um comentário

OBS: Os comentários enviados a este Blog são submetidos a moderação. Por isso, eles serão publicados somente após aprovação.

Observação: somente um membro deste blog pode postar um comentário.