miércoles, 18 de agosto de 2010

Obtener ruta o path de Ejecutable mediante Handle de Ventana y API's

Una forma de hacer esto es llamando a la función GetModuleFileNameEx contenida en la librería Psapi.dll. La firma de esta función :


DWORD WINAPI GetModuleFileNameEx(
__in HANDLE hProcess,
__in_opt HMODULE hModule,
__out LPTSTR lpFilename,
__in DWORD nSize
);

//Declarado en c#:
[DllImport("Psapi.dll")]
public static extern uint GetModuleFileNameEx(IntPtr hProcess, IntPtr hModule, [Out] StringBuilder lpBaseName, [In] [MarshalAs(UnmanagedType.U4)] int nSize);



Donde:
-hProcess : Handle del proceso
-hModule : Handle al modulo.Si es NULL retornará la ruta del archivo ejecutable del proceso especificado en hProcess
-Puntero al buffer que reibirá la ruta del ejecutable.
-nSize : El tamaño del buffer, en caracteres.

Como ya vimos, el primer parametro(hProcess) es un handle de proceso y no un handle de ventana.
Para obtener un handle de proceso mediante el handle de ventana, es necesario abrir el proceso, obtener el handle(de proceso), usarlo para obtener la ruta del ejecutable y cerrar el proceso.
Primero, para abrir un proceso, necesito llamar a la función OpenProcess que se encuentra en la librería kernel32.dll. La firma de la función es:


HANDLE WINAPI OpenProcess(
__in DWORD dwDesiredAccess,
__in BOOL bInheritHandle,
__in DWORD dwProcessId
);


//Declarado en c#:
[DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(UInt32 dwDesiredAccess, Int32 bInheritHandle, UInt32 dwProcessId);


Donde:
-dwDesiredAccess : acceso al proceso, Ver valores AQUI
-bInheritHandle : Si es true los procesos creados por este proceso heredarán el handle.
-dwProcessId : Id del proceso a ser abierto.

Aquí se presenta otro incoveniente, para abrir un proceso se necesita su identificador o Id y sólo contamos con el handle de ventana.
Para Obtener el id de un proceso mediante el handle de ventana llamamos a la función GetWindowThreadProcessId, el cual se encuentra en la librería Psapi.dll :


DWORD WINAPI GetWindowThreadProcessId(
__in HWND hWnd,
__out_opt LPDWORD lpdwProcessId
);


//Declarado en C# :
[DllImport("Psapi.dll")]
public static extern uint GetModuleFileNameEx(IntPtr hProcess, IntPtr hModule, [Out] StringBuilder lpBaseName, [In] [MarshalAs(UnmanagedType.U4)] int nSize);


Donde :
-hWnd : Handle de la ventana
-lpdwProcessId : Puntero a una variable dónde se almacenará el Id del proceso

Ya tenemos declaradas casi todas las funciones que usaremos,antes de comenzar on el código c# veamos la declaración de la función que nos falta, CloseHandle, esta se encarga de cerrar un proceso abierto :


BOOL WINAPI CloseHandle(
__in HANDLE hObject
);

//Declarada en C# :
[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseHandle(IntPtr hObject);


Donde :
-hObject : Handle del proceso a cerrar.

En un post anterior ya se ha visto como obtener el handle de una ventana, incluso los handles de todas las ventanas.
Aqui una clase con un Método que retorna la ruta del ejecutable mediante el handle de la ventana que recibe como parámetro:



using System;
using System.Collections.Generic;
using System.Text;

//Requerido para las API´s:
using System.Runtime.InteropServices;

namespace GetIcon_Win_APIs
{
public class WinApis
{
[DllImport("user32.dll")]
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);

[DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(UInt32 dwDesiredAccess, Int32 bInheritHandle, UInt32 dwProcessId);

[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseHandle(IntPtr hObject);

[DllImport("Psapi.dll")]
public static extern uint GetModuleFileNameEx(IntPtr hProcess, IntPtr hModule, [Out] StringBuilder lpBaseName, [In] [MarshalAs(UnmanagedType.U4)] int nSize);


public static string GetExePath(IntPtr hwnd)
{
//recupero el id del proceso
uint idProc = 0;
GetWindowThreadProcessId(hwnd, out idProc);

//obtengo el path del ejecutable de la aplicacion
StringBuilder path = new StringBuilder(1024);

//abro el proceso obteniendo el handle
IntPtr hProcess = OpenProcess(1040, 0, idProc);

//recupero la ruta o path del ejecutable
GetModuleFileNameEx(hProcess, IntPtr.Zero, path, path.Capacity);

//cierro el proceso abierto
CloseHandle(hProcess);

return path.ToString();
}


}

}//namespace






Observaciones:
En Windows Vista y 7 algunos procesos no pueden ser abiertos y en consecuencia no se puede obtener la ruta del ejecutable, esto se debe a los permisos.Lo mencionado no sucede si se corre el VS o la aplicación(ésta) como administrador.Pero esto sólo con algunas aplicaciones que incluso para ponerlas a correr normalmente ocupan permisos y piden confirmacion(sólo en Vista y 7).

No hay comentarios:

Publicar un comentario