Iré al grano, utilizando la función Process.GetProcesses() puedo obtener información del los procesos del sistema así como el Handle o el MainWindowHandle de la ventana principal de un proceso.
El problema es que en algunos casos MainWindowHandle devuelve 0
Recordé un viejo código de VB que tenía por ahí. Y tras un buen rato probando lo conseguí pasar a VB.NET, ya que usa APIs y si no se usa bien devuelve error o valores nulos.
Se necesita el ID de un proceso para obtener el handle principal, y dado que la clase Process no devuelve valor nulo con los IDs obtengo todos los Handles en la lista.
Ahí va el code :
////OBTENER HANDLE PROCESO////
Código
#Region "Obtener Handle Proceso" Public Module modProcessHandle <DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _ Private Function FindWindow(ByVal lpClassName As String, _ ByVal lpWindowName As String) As IntPtr End Function <DllImport("user32.dll", ExactSpelling:=True, CharSet:=CharSet.Auto)> _ Public Function GetParent(ByVal hWnd As IntPtr) As IntPtr End Function <DllImport("user32.dll", ExactSpelling:=True, CharSet:=CharSet.Auto)> _ Private Function GetWindow(ByVal hWnd As IntPtr, _ ByVal wCmd As Integer) As IntPtr End Function <DllImport("user32.dll", ExactSpelling:=True, CharSet:=CharSet.Auto)> _ Private Function GetWindowThreadProcessId(ByVal hWnd As IntPtr, _ ByRef lpdwProcessId As Integer) As IntPtr End Function Public Function GetProcessHandle(ByVal ProcessId As Integer) As IntPtr Dim HwndTemporal As IntPtr Dim idProc As Integer = 0 Dim HWND As IntPtr = IntPtr.Zero Const GW_HWNDNEXT = 2 HwndTemporal = FindWindow(Nothing, Nothing) ' Loop hasta que encuentra una coincidencia o no hay más identificadores de ventana: Do Until HwndTemporal = IntPtr.Zero ' Comprueba que no tenga ninguna ventana 'Parent' If GetParent(HwndTemporal) = IntPtr.Zero Then GetWindowThreadProcessId(HwndTemporal, idProc) If ProcessId.Equals(idProc) Then HWND = HwndTemporal 'Devuelve el handle encontrado Return HWND Exit Do ' Salir de bucle End If End If idProc = 0 'Siguiente identificador de ventana HwndTemporal = GetWindow(HwndTemporal, GW_HWNDNEXT) Loop Return HWND End Function End Module #End Region
Aquí un ejemplo de como usarlo para obtener una lista con los procesos en ejecución sus handles y su Class Name.
Código
Option Strict On Option Explicit On Imports System.Runtime.InteropServices Imports System.Text Public Class Form1 <DllImport("user32.dll", CharSet:=CharSet.Auto)> Private Shared Function GetClassName(ByVal hWnd As IntPtr, _ ByVal lpClassName As System.Text.StringBuilder, _ ByVal nMaxCount As Integer) As Integer End Function Dim ClassName As StringBuilder = New StringBuilder(256) Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click ListView1.Columns.Add("Name", 100) ListView1.Columns.Add("MainHandle", 100) ListView1.Columns.Add("ClassName", 200) ListView1.Columns.Add("MainHandle", 100) ListView1.Columns.Add("ClassName", 200) ListView1.View = View.Details ListView1.FullRowSelect = True 'Recorre los procesos y agrega la información al ListView For Each Proceso In Process.GetProcesses() Dim Handle As IntPtr = modProcessHandle.GetProcessHandle(Proceso.Id) If Handle <> IntPtr.Zero Then 'Nombre Proceso ListView1.Items.Add(Proceso.ProcessName) 'MainWindowHandle ListView1.Items(ListView1.Items.Count - 1).SubItems.Add _ (Conversion.Hex(Proceso.MainWindowHandle.ToInt32)) 'ClassName ClassName.Clear() GetClassName(CType(Proceso.MainWindowHandle.ToInt32, IntPtr), ClassName, ClassName.Capacity) ListView1.Items(ListView1.Items.Count - 1).SubItems.Add(ClassName.ToString) 'MainWindowHandle ListView1.Items(ListView1.Items.Count - 1).SubItems.Add(Conversion.Hex(CInt(Handle))) 'ClassName ClassName.Clear() GetClassName(Handle, ClassName, ClassName.Capacity) ListView1.Items(ListView1.Items.Count - 1).SubItems.Add(ClassName.ToString) End If Next End Sub Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load End Sub End Class #Region "Obtener Handle Proceso" Public Module modProcessHandle <DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _ Private Function FindWindow(ByVal lpClassName As String, _ ByVal lpWindowName As String) As IntPtr End Function <DllImport("user32.dll", ExactSpelling:=True, CharSet:=CharSet.Auto)> _ Public Function GetParent(ByVal hWnd As IntPtr) As IntPtr End Function <DllImport("user32.dll", ExactSpelling:=True, CharSet:=CharSet.Auto)> _ Private Function GetWindow(ByVal hWnd As IntPtr, _ ByVal wCmd As Integer) As IntPtr End Function <DllImport("user32.dll", ExactSpelling:=True, CharSet:=CharSet.Auto)> _ Private Function GetWindowThreadProcessId(ByVal hWnd As IntPtr, _ ByRef lpdwProcessId As Integer) As IntPtr End Function Public Function GetProcessHandle(ByVal ProcessId As Integer) As IntPtr Dim HwndTemporal As IntPtr Dim idProc As Integer = 0 Dim HWND As IntPtr = IntPtr.Zero Const GW_HWNDNEXT = 2 HwndTemporal = FindWindow(Nothing, Nothing) ' Loop hasta que encuentra una coincidencia o no hay más identificadores de ventana: Do Until HwndTemporal = IntPtr.Zero ' Comprueba que no tenga ninguna ventana 'Parent' If GetParent(HwndTemporal) = IntPtr.Zero Then GetWindowThreadProcessId(HwndTemporal, idProc) If ProcessId.Equals(idProc) Then HWND = HwndTemporal 'Devuelve el handle encontrado Return HWND Exit Do ' Salir de bucle End If End If idProc = 0 'Siguiente identificador de ventana HwndTemporal = GetWindow(HwndTemporal, GW_HWNDNEXT) Loop Return HWND End Function End Module #End Region
Como demostración he hecho que se muestre en una columna los handles obtenidos con la clase Process y otra columna con los handles obtenidos con el snippet. Se puede ver que con algunos procesos ocultos, es decir no son visibles, se devuelve valor 0, pero con el snippet se obtiene el handle sin problemas. Además se obtienen los nombres de clase (ClassName) que sin los Handles de las ventanas no es posible obtenerlos. Por lo menos que yo sepa.
La primera columna ClassName se obtiene con MainWindowHandle del Procces. La segunda columna ClassName se obtiene con los handles obtenidos con el snippet.
Si hay otra forma más sencilla, decídmelo
El código original VB6 está en el siguiente enlace:
CodeVB
Espero que os sirva...
Saludos