Mostrando entradas con la etiqueta api. Mostrar todas las entradas
Mostrando entradas con la etiqueta api. Mostrar todas las entradas

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).

sábado, 12 de diciembre de 2009

Cómo obtener lista de ventanas mediante API´s

Para obtener los manejadores o handles delas ventanas abiertas se puede hacer llamando a la función GetWindow, pero es mas seguro hacerlo mediante la función EnumWindows ya que GetWindow puede refenciar a un handle de una ventana ya destruida o quedarse en un loop`o ciclo infinito, es fácil darse cuenta de esto último mediante el debugger.
La funcion EnumWindows esta contenida en la librería user32.dll y su sintaxis es la siguiente:
BOOL EnumWindows(
WNDENUMPROC lpEnumFunc,
LPARAM lParam
);


El primer parámetro es un puntero a una función callback.
El segundo parámetro de la función es un valor definido por la aplicación; es decir que puedo mandar cualquier parámetro que me sea necesario usar en la función callback.


Para el primer parámetro debemos declarar un delegado. Antes de hacerlo veamos como debe ser la función callback(EnumWindowsProc):
BOOL CALLBACK EnumWindowsProc(
HWND hwnd,
LPARAM lParam
);

El primer parámetro es el handle de la ventana, lo recibirá de su llamador,la función EnumWindows.
El segundo parámetro es un valor definido por la aplicación,lo recibirá de su llamador,la función EnumWindows.
Debe retornar TRUE para seguir enumerando, de lo contrario no lo hará.

Antes es necesario saber que para poder acceder a las API's es necesario agregar la directiva:
using System.Runtime.InteropServices;

Entonces declaramos el delegado y de una vez la función EnumWindows:

//declaro el delegado para la función callback
private delegate bool EnumWindowsCallback(IntPtr hwnd, IntPtr lParam);

//Declaro la función EnumWindows
private static extern bool EnumWindows(EnumWindowsCallback Callback, IntPtr lParam);


Ahora creamos el método al que apuntará el delegado EnumWindowsCallback y El método público que llamará a la función EnumWindows :



//Metodo al que apunta el delegado EnumWinodwsCallback
private static bool Callback(IntPtr hwnd, IntPtr lParam)
{
hwndList.Add(hwnd);

return true; //continua la enumeración
}

//aqui se guradara la lista de handle's
private static List<IntPtr> hwndList;

public static List<IntPtr> GetWindowsList()
{
hwndList = new List<IntPtr>();

EnumWindowsCallback MyCallback = Callback;

bool val = EnumWindows(MyCallback, IntPtr.Zero);

/*retorno la lista de handle's*/
return hwndList;

}

Se recuperará todos los handle's de todas las ventanas abiertas, que son muchas.Si lo que se quiere es recuperar sólo las ventanas con borde y visibles -que en su mayoría son las que tienen los programas o aplicaciones mas comunes y de uso diario- se debe llamar a la función GetWindowLong para saber los estilos de la ventana; es decir, si tiene borde y si es visible, entre otros valores que se pueden consultar.
La función GetWindowLong recupera información de una ventana especificada.
Su sintaxis:
LONG GetWindowLong(
HWND hWnd,
int nIndex

);

El primer parámetro es el handle una ventana especificada, de la que se quiere saber su estilo.
El segundo parámetro sera el valor de GWL_STYLE, pues para este ejemplo es necesario.
En esta página se pueden ver las los valores de sus constantes.
El msdn dice que para que nuestro código sea compatible con 32bits y 64bits debemos llamar a la función GetWindowLongPtr que es prácticamente lo mismo que GetWindowLong -en su declaración-.Sin embargo, en Windows Vista Ultimate de 32bits la función GetWindowLonPtr no funciona, no existe.En cambio GetWindowLongA corre sin problemas en el S.O mencionado.Acerca de ésta dice el msdn que provee un comportamiento mas consistente entre los S.O's Windows. Así que para este ejemplo se llamara a la funcion GetWindowLongA, por lo que la función callback quedará:


//El valor standard de GWL_STYLE es -16
static readonly int GWL_STYLE = -16;
static readonly ulong WS_VISIBLE = 0x10000000;
static readonly ulong WS_BORDER = 0x00800000;
static readonly ulong TARGETWINDOW = WS_BORDER | WS_VISIBLE;

//Declaro la funcion GetWindowLongA
[DllImport("user32.dll")]
private static extern ulong GetWindowLongA(IntPtr hWnd, int nIndex);

//Metodo al que apunta el delegado EnumWinodwsCallback
private static bool Callback(IntPtr hwnd, IntPtr lParam)
{
//agrego ventanas visibles con borde
if ((GetWindowLongA(hwnd, GWL_STYLE) & TARGETWINDOW) == TARGETWINDOW)
{

hwndList.Add(hwnd);
}

return true; //continua la enumeración
}


En
if ((GetWindowLongA(hwnd, GWL_STYLE) & TARGETWINDOW) == TARGETWINDOW)
{

hwndList.Add(hwnd);
}

le decimos si la ventana tiene bordes y es visible que agregue a la lista genérica hwndList.

Para hacer mas intersante la aplicación podemos recuperar también los titulos de las ventanas cuyos handle's hemos recuperado. Esto lo hacemos mediante la función GetWindowText contenida en la librería user32.dll :


/*Declaro la función GetWindowText*/

/// <summary>
/// Recupera el título de un
a ventana(de la barra de título), si es que tiene.
/// </summary>
/// <param name="hWnd">Handle de la ventana de la que se quiere recuperar el título.</param>
/// <param name="lpString">[out]Puntero al buffer que recibe el texto.Si el texto es
/// más largo que el buffer será truncado y terminado con caracter NULL.</param>
/// <param name="nMaxCount">Especifica el maximo´número de caracteres a escribir en
/// el buffer</param>
[DllImport("user32.dll")]
private static extern void GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);


Para que se vea mas ordenado en una clase estática pondré las llamadas a las API's y mostraré en un listbox(WPF) los titulos de ventanas obtenidos.
Clase APICalls.cs :


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

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

namespace GettingWindows
{
public static class ApiCalls
{
//aqui se guradara la lista de handle´s
private static List<IntPtr> hwndList;

//El valor standard de GWL_STYLE
static readonly int GWL_STYLE = -16;
static readonly ulong WS_VISIBLE = 0x10000000;
static readonly ulong WS_BORDER = 0x00800000;
static readonly ulong TARGETWINDOW = WS_BORDER | WS_VISIBLE;


/// <summary>
/// Enumera todas las ventanas de la pantalla.Para eso llama a la función
/// Callback.La funcion continua llamandose hasta
/// que EnumWindowsCallback retorne false.
/// </summary>
/// <param name="lpEnumFunc">Puntero a la funcion EnumWindowsCallback</param>
/// <param name="lParam"></param>
/// <returns></returns>
[DllImport("user32.dll")]
private static extern bool EnumWindows(EnumWindowsCallback Callback, IntPtr lParam);
/// <summary>
/// Defino EnumWindowsCallback a traves de un delegado.
/// Ver el metodo privado estatico Callback.
/// </summary>
/// <param name="hwnd">Handle de la ventana top-level que esta enumerando </param>
/// <param name="lParam"></param>
/// <returns>Para continuar enumeando debe retornar TRUE</returns>
private delegate bool EnumWindowsCallback(IntPtr hwnd, IntPtr lParam);

/// <summary>
/// Recupera la información de una ventana especifica
/// </summary>
/// <param name="hWnd">Handle a la ventana y-indirectamente- a la clase a la cual
/// pertenece la ventana.</param>
/// <param name="nIndex"></param>
/// <returns>retorna cero(0) si la funcion falla</returns>
[DllImport("user32.dll")]
private static extern ulong GetWindowLongA(IntPtr hWnd, int nIndex);

/***/
/// <summary>
/// Recupera el título de una ventana(de la barra de título), si es que tiene.
/// </summary>
/// <param name="hWnd">Handle de la ventana de la que se quiere recuperar el título.</param>
/// <param name="lpString">[out]Puntero al buffer que recibe el texto.Si el texto es
/// más largo que el buffer será truncado y terminado con caracter NULL.</param>
/// <param name="nMaxCount">Especifica el maximo número de caracteres a escribir en
/// el buffer</param>
[DllImport("user32.dll")]
private static extern void GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);


/**************************************************************************************/

//Metodo al que apunta el delegado EnumWinodwsCallback
private static bool Callback(IntPtr hwnd, IntPtr lParam)
{
//agrego ventanas visibles con borde
if ((GetWindowLongA(hwnd, GWL_STYLE) & TARGETWINDOW) == TARGETWINDOW)
{

hwndList.Add(hwnd);
}

return true; //continua la enumeración
}



public static List<string> GetWindowsList()
{
List<string> wndTitles = new List<string>() ;
StringBuilder sb = new StringBuilder(256);

hwndList = new List<IntPtr>();

EnumWindowsCallback MyCallback = Callback;

EnumWindows(MyCallback, IntPtr.Zero);

/*ya llena la lista de handle´s obtengo el titulo de cada ventana*/
foreach (IntPtr h in hwndList)
{
GetWindowText(h, sb, sb.Capacity);

wndTitles.Add(sb.ToString());
}

/*retorno la lista de titulos de ventanas*/
return wndTitles;

}
}
}

Código de la ventana MainWindow.xaml(en XAML):


<Window x:Class="GettingWindows.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="363" Width="322">
<Grid>
<Button Height="27" Margin="26,29,133,0" Name="btnGetList" VerticalAlignment="Top" Click="btnGetList_Click">Mostrar lista de ventanas</Button>
<ListBox Margin="26,80,19,18" Name="lbxList" />
</Grid>
</Window>

Código de MainWindow.cs :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

namespace GettingWindows
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}

private void btnGetList_Click(object sender, RoutedEventArgs e)
{
lbxList.ItemsSource = ApiCalls.GetWindowsList();

}
}
}


Una captura:

domingo, 4 de octubre de 2009

Obteniendo Handle de una ventana mediante API´s - Windows

Se puede obtener el handle de una ventana mediante su título o nombre de clase a la que pertenece llamando a la función FindWindow contenida en la librería user32.dll.Para poder acceder a la función es necesario agregar la directiva:
using System.Runtime.InteropServices;

Declarar la función aplicando el atributo DllImport(definido en System.Runtime.InteropServices).Ademas, notar el modificador extern:

[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowTitle);


Ahora podemos llamarla.Por ejemplo, abrimos el Bloc de notas, nos fijamos en el título de la ventana:
Sin título - Bloc de notas
(Depende en qué idioma esté el S.O)

Para obtener el handle de esta ventana llamamos a la función FindWindow ya declarada mandando como parámetro el titulo del Bloc de notas y al otro parámetro le enviamos un valor null:
IntPtr hWndNotepad = FindWindow(null,"Sin título - Bloc de notas");

Notar que el handle se recibe en un tipo IntPtr.Además, si la función falla retorna un valor Null.

Para comprobar que, efectivamente, hemos obtenido el handle de la ventana requerida haremos una prueba:Cambiar el titulo de la ventana.
Para cambiar el título de una ventana haré uso de la función SetWindowText contenida en la librería user32.dll.Así que la declaro como hice con FindWindow :
[DllImport("user32.dll")]
public static extern bool SetWindowText(IntPtr hWnd, string newTitle);


Ahora ya puedo cambiar el titulo del Bloc de notas, mando como parametro el handle de la ventana y un string con el nuevo titulo:
bool v = SetWindowText(hWndNotepad, "¡¡¡¡¡¡ esto funciona !!!!!!");


Para obtener el handle de la ventana en la que se trabaja; es decir, la activa,es necesario llamar a la función GetForegroundWindow, también contenida en la librería user32.dll.Así que la declaramos como las demás:

[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();


Llamamos a la función:

IntPtr hWndActual=GetForegroundWindow();


Al final el código quedaría:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
//para llamar a apis:
using System.Runtime.InteropServices;

namespace APIsExamples
{
class Program
{
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowTitle);
[DllImport("user32.dll")]
public static extern bool SetWindowText(IntPtr hWnd, string newTitle);
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();

static void Main(string[] args)
{
IntPtr hWndNotepad = FindWindow(null, "Sin título - Bloc de notas");
IntPtr hWndActual = GetForegroundWindow();
Console.WriteLine("El handle del Bloc de notas es {0}", hWndNotepad);
Console.WriteLine("El handle de la ventana activa es {0}", hWndActual);
Console.WriteLine("\nCambiando el titulo de ventana del Bloc de notas...");
bool v = SetWindowText(hWndNotepad, "¡¡¡¡¡¡ esto funciona !!!!!!");
Console.WriteLine("\nListo.La opreación ha devuelto un valor {0}",v);

Console.ReadKey();

}
}
}



Se puede recuperar los handle´s de las ventanas abiertas mediante la función GetWindow, aunque es mas eficiente hacerlo mediante la función EnumWindows.Esto se verá en otro post.