Localizar HWND através de um PID (process id)

Putz, as vezes a API do Windows dá raiva. Ao invés de facilitarem o trabalho, dificultam! E o pior:  a falta de documentação faz com que você gaste horas tentando achar solução para um problema ridículo.

Se você um dia precisou localizar um HWND através de um PID, sabe do que estou falando. Se você estiver se perguntando: “Mas por que diabos preciso disso?” . Eu respondo: Se você estiver desenvolvendo um sistema que não permite a execução de 2 processos ao mesmo tempo, vai precisar. E caso precise trocar informações por mensagens do windows, também pode ser que precise.

Bom, continuando com o assunto, vou mostrar como fazer isso usando o velho C++, embora dê pra fazer em Delphi, VB e qualquer outro que suporte as APIs do Windows.

Para executar esse programa, você só precisa criar uma aplicação MFC no Visual c++ ou uma aplicação console e incluir o header windows.h no stdafx.h .

// SimpleWrapper.cpp
//

#include "stdafx.h"

int _tmain(int argc, _TCHAR* argv[])
{

	// cria handle
	HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
	PROCESSENTRY32 *pe = new PROCESSENTRY32;
	pe->dwSize = sizeof(PROCESSENTRY32);

	string nome;
	int pidd=0;
	while( Process32Next(snapshot, pe)!=FALSE)
	{

		string s = pe->szExeFile;
		if (s=="Teste.exe")
		{
			pidd = pe->th32ProcessID;
			nome = pe->szExeFile;
			break;
		}

	}

	HWND h = ::GetTopWindow(0 );
	while ( h )
	{
		DWORD pid;
		DWORD dwTheardId = ::GetWindowThreadProcessId( h,&pid);

         if ( pid ==  pidd)
         {
			 if(GetParent(h)==0 && GetWindow(h, GW_OWNER)!=0){
				break;
			 }
         }

         h = ::GetNextWindow( h , GW_HWNDNEXT);
	}

	SendMessage(h, WM_USER+0x300,0,0);

	return 0;
}

Bom, agora vamos as explicações:

HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
PROCESSENTRY32 *pe = new PROCESSENTRY32;
pe->dwSize = sizeof(PROCESSENTRY32);

  • CreateToolhelp32Snapshot tira uma fotografia no momento da execução, dos processos que estão ativos e te retorna um HANDLE;
  • PROCESSENTRY32 é uma estrutura que é carregada com as informações dos processos, no loop que vou mostrar abaixo. E só para inicializar o tamanho da estrutura, determinamos o valor em dwSize (coisas idiotas da API);

O while, logo na linha abaixo, carrega a estrutura através da função Process32Next, passando a estrutura e o snapshot que tiramos do processo. Após isso, a estrutura é carregada com os dados do processo e basta você fazer as condições necessárias para testar. Depois disso, pegue o pid e saia.

Para fazer o teste, coloque um executável qualquer e faça o teste olhando no Task Manager do Windows o PID.

Bom, completamos a fase de achar o PID, agora precisamos enumerar todas as janelas abertas e validar o pid da janela com o pid que peguei.

Isso é feito através de duas funções:  ::GetTopWindow(0) e ::GetWindowThreadProcessId(handle, dword). Ao criar o loop das janelas, você deve validar se o pid da janela é igual ao pid que você pegou lá em cima e fazer uma validação extra verificando se a janela é o OWNER e o Parent (pid principal).

Pronto, seu HWND saiu de lá de dentro. Que sufoco !

Espero que tenha contribuído com mais essa.

Bookmarksbookmark bookmark bookmark bookmark bookmark bookmark

Popularity: 3%

2 Comments so far

  1. José on December 16th, 2009

    Mesmo assim pode entrar em erro da forem executados os dois processos aos mesmo tempo e a aplicação correr porque a estrutura não está sincronizada com um processo comum, tipo nome da aplicação.

  2. Vagner Santos on September 30th, 2011

    Adaptei esta solução para Delphi.

    Modifiquei a lógica para pegar a quantidade encontrada do executável.

    Colocar esta função antes do begin do dpr, mas depois do uses:

    function QtdeAplicEmExecucao(sExeName : string):integer;
    var
    hSnapShot: THandle;
    pe32: TProcessEntry32;
    begin
    //depende da TlHelp32
    // solução encontrada no Blog do Robson Dantas
    // – http://blogdodantas.dxs.com.br/2009/02/12/localizar-hwnd-atraves-de-um-pid-process-id/
    // adaptação para Delphi encontrada na api TProcessInfo
    // – http://vcldeveloper.com/tips-tricks/how-to-use-tprocessinfo/

    Result := 0;
    hSnapShot := TlHelp32.CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);

    if hSnapShot INVALID_HANDLE_VALUE then
    begin
    try
    pe32.dwSize := SizeOf(pe32);
    if Process32First(hSnapshot,pe32) then
    repeat
    if Trim(pe32.szExeFile) = sExeName then Inc(result);

    pe32.dwSize := SizeOf(pe32);
    until Process32Next(hSnapshot,pe32) = False;
    finally
    CloseHandle(hSnapShot);
    end;
    end
    else
    RaiseLastOSError;

    end;

    Depois é só testar a quantidade:

    bJaEstahAberto := QtdeAplicEmExecucao(ExtractFileName(ParamStr(0))) > 1;

Vale Presente