viernes 17 de octubre de 2008
[CSharp]Fun with Windows Handlers (1/3)
Hay una cosa segura: .Net tiene sus limitaciones. Al menos, si lo comparamos con las operaciones que teníamos disponibles con la API Win32, veremos que no podemos hacer absolutamente todo lo que hacíamos con ésta última. Para solucionar este eventual problema tenemos Platform Invocation Services, más conocido como PInvoke (o P/Invoke), que nos permite hacer llamadas a código nativo implementadas en una DLL desde código administrado.
Debido a que las estructuras de datos utilizadas en código nativo son distintas a las de código administrado, debemos realizar un proceso conocido como Marshaling, que grosso modo viene a significar transportar datos entre diferentes contextos. Muy genérico ¿verdad?.
Este proceso de adecuación de los datos es prácticamente automático. Lo único que tenemos que hacer es indicarle al framework .net la función de la Dll que queremos utilizar, y decirle cómo debe traducir las estructuras de datos. Y ni siquiera tenemos que perder el tiempo en ello, ya que gracias a la página al wiki PInvoke.net, tenemos acceso a un motor de búsqueda donde sólo debemos introducir el nombre de la función que buscamos y obtendremos el código necesario para usarla, listo para cortar y pegar. Aún así daremos una pequeña explicación:
Para usar una función que exporta una Dll tenemos que seguir los siguientes pasos:
Crear una función estática y con el keyword extern.
Marcamos la función con el atributo DllImport, al cual pasamos como parámetro el nombre de la Dll que contiene al método.
La función por supuesto debe tener los parámetros adecuados a lo tipos de .NET.
Por ejemplo, la función Win32 API FindWindow tiene esta definición:
HWND FindWindow( LPCTSTR lpClassName, LPCTSTR lpWindowName );Esta función recibe un puntero a una cadena con la nombre de la clase con la que se registró la ventana (ojo esto no tiene que ver con clases de POO, sino con otro tipo de clase), y otro puntero a una cadena con el nombre de la ventana (lo que sería el título o caption), retornando el HANDLE de la ventana si la encuentra. Bien, esto se traduce en .NET con el siguiente método de clase (en .NET no existen las funciones por separado, todo tiene que ser parte de un objeto)
public static class Win32API{ [DllImport("user32.dll")]public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);}Vemos que el Handler de la ventana se traduce por una estructura System.IntPtr, y los punteros a cadenas por strings. Ahora podremos llamar a la función, por ejemplo:
IntPtr hwn = FindWindow(null, "título de la ventana");
Y buscaría la ventana con dicho título ignorando su clase de ventana, retornando el handler a la ventana (o IntPtr.Zero si no se encuentra una ventana con ese título)
Teniendo el Handler de una ventana podemos controlar prácticamente todo si sabemos lo que hacemos. Y esta función nos permite buscar y obtener el handler de cualquier ventana. Aunque no sea de nuestra aplicación. Aunque ni siquiera haya sido generada por código .NET...
Veremos algunos conceptos más en la siguiente entrada.
PS: Como nota curiosa, podemos llamar a nuestro método estático como queramos, siempre que en el atributo [DllImport] le indiquemos el nombre real de la función de la Dll que pretendemos importar:
[DllImport("user32.dll", EntryPoint="FindWindow")]
public static extern IntPtr BuscaHandlerDeVentana(string lpClassName, string lpWindowName);
Como vemos si el parámetro EntryPoint no está disponible, se toma como el mismo el nombre del método estático.
PPS: Existen otras maneras de obtener un handle de la ventana. Si tenemos un objeto System.Windows.Forms.Control (lo que incluye un System.Windows.Forms.Form), éste expone la propiedad Handle, que nos devuelve el Handler de la ventana.
Del mismo modo la clase System.Diagnostics.Process, expone el método estático GetProcesses(), que nos devuelve una lista con todos los Procesos en ejecución (podemos buscar por PID, nombre, etc). A su vez una instancia de la clase System.Diagnostics.Process expone el método Handle, con el Handler del proceso (que suele coincidir con el de la ventana principal).
UPDATE: La clase Process expone una propiedad denominada MainWindowHandle, que SI devuelve el handle de la ventana principal del proceso :)





0 Comentarios:
Publicar un comentario en la entrada