2008-11-11 3 views
1

Je développe une petite application utilitaire qui doit détecter si un autre a une de ses fenêtres enfants MDI ouvert (c'est un impromptu Win32 application commerciale sur laquelle je n'a ni code source ni contrôle). Depuis mon application, j'aimerais pouvoir interroger ou détecter lorsqu'une fenêtre MDI enfant particulière est ouverte.Énumération de Windows/Contrôle d'une autre application de .Net

Dans .Net, il est facile d'itérer sur les processus en cours d'exécution, mais je n'ai pas trouvé un moyen facile de parcourir les (sous) fenêtres et les contrôles d'un processus Win32 donné de .Net.

Des idées?

Mise à jour
Merci pour les réponses qu'ils me ont obtenu sur le bon chemin.
J'ai trouvé un article with a test project qui utilise à la fois EnumWindows et EnumChidWindows et d'autres appels d'API pour obtenir des informations étendues sur les contrôles.

Répondre

3

Vous devez utiliser l'API Win32 native.

EnumChildWindows (user32)

[DllImport("user32")] 

[return: MarshalAs(UnmanagedType.Bool)] 
public static extern bool EnumChildWindows(IntPtr window, EnumWindowProc callback, IntPtr i); 

/// <summary> 
/// Returns a list of child windows 
/// </summary> 
/// <param name="parent">Parent of the windows to return</param> 
/// <returns>List of child windows</returns> 
public static List<IntPtr> GetChildWindows(IntPtr parent) 
{ 
List<IntPtr> result = new List<IntPtr>(); 
GCHandle listHandle = GCHandle.Alloc(result); 
try 
{ 
    EnumWindowProc childProc = new EnumWindowProc(EnumWindow); 
    EnumChildWindows(parent, childProc, GCHandle.ToIntPtr(listHandle)); 
} 
finally 
{ 
    if (listHandle.IsAllocated) 
    listHandle.Free(); 
} 
return result; 
} 

/// <summary> 
/// Callback method to be used when enumerating windows. 
/// </summary> 
/// <param name="handle">Handle of the next window</param> 
/// <param name="pointer">Pointer to a GCHandle that holds a reference to the list to fill</param> 
/// <returns>True to continue the enumeration, false to bail</returns> 
private static bool EnumWindow(IntPtr handle, IntPtr pointer) 
{ 
GCHandle gch = GCHandle.FromIntPtr(pointer); 
List<IntPtr> list = gch.Target as List<IntPtr>; 
if (list == null) 
{ 
    throw new InvalidCastException("GCHandle Target could not be cast as List<IntPtr>"); 
} 
list.Add(handle); 
// You can modify this to check to see if you want to cancel the operation, then return a null here 
return true; 
} 

/// <summary> 
/// Delegate for the EnumChildWindows method 
/// </summary> 
/// <param name="hWnd">Window handle</param> 
/// <param name="parameter">Caller-defined variable; we use it for a pointer to our list</param> 
/// <returns>True to continue enumerating, false to bail.</returns> 
public delegate bool EnumWindowProc(IntPtr hWnd, IntPtr parameter); 

1

Vous pouvez utiliser P/Invoke pour accéder EnumWindows et EnumChidWindows à itereate à travers les sous-fenêtres/contrôle de toute fenêtre.

1

C'est un ancien poste, mais un problème commun. J'ai une situation similaire où j'essaie de contrôler le comportement d'une application logicielle disponible sur le marché. Je parvins la plupart du temps en utilisant Kixstart, mais il est venu contre les limites de l'utilisation SETFOCUS et sendkeys car à certains points le logiciel impromptu affiche les fenêtres où WindowTitle est vide. J'ai donc développé un petit utilitaire qui identifie Windows visible par leur ProcessName ainsi que WindowTitle (ou son absence) et envoie un message pour fermer une fenêtre qui correspond.

La plupart des exemples que j'ai vu des fonctions de rappel EnumWindows ignorer les fenêtres qui ne sont pas visibles ou qui ont des titres vides, alors que ce code énumère toutes les fenêtres, y compris les invisibles.

Ce code est Visual Basic et utilise un fichier de configuration dans le format

PRC=process name 
WIN=window title 

Espérons que cela est utile à quelqu'un

Imports System 
Imports System.IO 
Imports System.Runtime.InteropServices 
Imports System.Text 
Module Module1 
    Dim hShellWindow As IntPtr = GetShellWindow() 
    Dim dictWindows As New Dictionary(Of IntPtr, String) 
    Dim dictChildWindows As New Dictionary(Of IntPtr, String) 
    Dim currentProcessID As Integer = -1 
    <DllImport("USER32.DLL")> 
    Function GetShellWindow() As IntPtr 
    End Function 
    <DllImport("USER32.DLL")> 
    Function GetForegroundWindow() As IntPtr 
    End Function 
    <DllImport("USER32.DLL")> 
    Function GetWindowText(ByVal hWnd As IntPtr, ByVal lpString As StringBuilder, ByVal nMaxCount As Integer) As Integer 
    End Function 
    <DllImport("USER32.DLL")> 
    Function GetWindowTextLength(ByVal hWnd As IntPtr) As Integer 
    End Function 
    <DllImport("user32.dll", SetLastError:=True)> 
    Function GetWindowThreadProcessId(ByVal hWnd As IntPtr, <Out()> ByRef lpdwProcessId As UInt32) As UInt32 
    End Function 
    <DllImport("USER32.DLL")> 
    Function IsWindowVisible(ByVal hWnd As IntPtr) As Boolean 
    End Function 
    Delegate Function EnumWindowsProc(ByVal hWnd As IntPtr, ByVal lParam As Integer) As Boolean 
    <DllImport("USER32.DLL")> 
    Function EnumWindows(ByVal enumFunc As EnumWindowsProc, ByVal lParam As Integer) As Boolean 
    End Function 
    <DllImport("USER32.DLL")> 
    Function EnumChildWindows(ByVal hWndParent As System.IntPtr, ByVal lpEnumFunc As EnumWindowsProc, ByVal lParam As Integer) As Boolean 
    End Function 
    <DllImport("USER32.DLL")> 
    Function PostMessage(ByVal hwnd As Integer, ByVal message As UInteger, ByVal wParam As Integer, ByVal lParam As Integer) As Boolean 
    End Function 
    <DllImport("USER32.DLL")> 
    Function SendMessage(ByVal hWnd As IntPtr, ByVal Msg As UInteger, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr 
    End Function 

    Function enumWindowsInternal(ByVal hWnd As IntPtr, ByVal lParam As Integer) As Boolean 
     Dim a As String = "" 
     Dim length As Integer = GetWindowTextLength(hWnd) 
     If (length > 0) Then 
      Dim stringBuilder As New System.Text.StringBuilder(length) 
      GetWindowText(hWnd, stringBuilder, (length + 1)) 
      a = stringBuilder.ToString 
     End If 
     dictWindows.Add(hWnd, a) 
     EnumChildWindows(hWnd, AddressOf enumChildWindowsInternal, 0) 
     Return True 
    End Function 
    Function enumChildWindowsInternal(ByVal hWnd As IntPtr, ByVal lParam As Integer) As Boolean 
     Dim a As String = "" 
     Dim length As Integer = GetWindowTextLength(hWnd) 
     If (length > 0) Then 
      Dim stringBuilder As New System.Text.StringBuilder(length) 
      GetWindowText(hWnd, stringBuilder, (length + 1)) 
      a = stringBuilder.ToString 
     End If 
     dictChildWindows.Add(hWnd, a) 
     Return True 
    End Function 
    Function cleanstring(ByVal a As String) As String 
     Dim c As String = "" 
     Dim b As String = "" 
     Dim i As Integer 
     Do While i < Len(a) 
      i = i + 1 
      c = Mid(a, i, 1) 
      If Asc(c) > 31 And Asc(c) < 128 Then 
       b = b & c 
      End If 
     Loop 
     cleanstring = b 
    End Function 
    Sub Main() 
     ' 
     ' 
     Dim a As String = "" 
     Dim b As String = "" 
     Dim c As String = "" 
     Dim d As String = "" 
     Dim f As String = "C:\FIS5\WK.txt" 
     Dim a1 As String = "" 
     Dim a2 As String = "" 
     Dim p As Process 
     Dim windows As IDictionary(Of IntPtr, String) 
     Dim kvp As KeyValuePair(Of IntPtr, String) 
     Dim windowPid As UInt32 
     Dim hWnd As IntPtr 
     Dim fhWnd As IntPtr 
     Dim WM_CLOSE As UInteger = &H12 
     Dim WM_SYSCOMMAND As UInteger = &H112 
     Dim SC_CLOSE As UInteger = &HF060 
     Dim x As Boolean = True 
     Dim y As IntPtr 
     Dim processes As Process() = Process.GetProcesses 
     Dim params As String = File.ReadAllText("C:\FIS5\WindowKiller.txt") 
     Dim words As String() = params.Split(vbCrLf) 
     Dim word As String 
     Dim myprocname As String = "" 
     Dim mywinname As String = "" 
     Dim i As Integer = 0 
     Dim v1 As Integer = 0 
     For Each word In words 
      word = Trim(cleanstring(word)).ToUpper 
      i = InStr(word, "=", CompareMethod.Text) 
      ' a = a & word & " " & i.ToString & vbCrLf 
      If i = 4 And 4 < Len(word) Then 
       If Left(word, 4) = "PRC=" Then 
        myprocname = Mid(word, 5) 
       End If 
       If Left(word, 4) = "WIN=" Then 
        mywinname = Mid(word, 5) 
       End If 
      End If 
     Next 
     a = a & params & vbCrLf & "procname=" & myprocname & ", winname=" & mywinname & vbCrLf 
     fhWnd = GetForegroundWindow() 
     dictWindows.Clear() 
     dictChildWindows.Clear() 
     EnumWindows(AddressOf enumWindowsInternal, 0) 
     windows = dictChildWindows 
     For Each kvp In windows 
      hWnd = kvp.Key 
      GetWindowThreadProcessId(hWnd, windowPid) 
      b = "" 
      c = "" 
      For Each p In processes 
       If p.Id = windowPid Then 
        b = p.ProcessName 
        c = p.Id.ToString 
       End If 
      Next 
      d = "hidden" 
      If IsWindowVisible(hWnd) Then 
       d = "visible" 
      End If 
      If hWnd = fhWnd Then 
       d = d & ", foreground" 
      End If 
      a = a & "Child window=" & hWnd.ToString & ", processname=" & b & ", procid=" & c & ", windowname=" & kvp.Value & ", " & d & vbCrLf 
     Next 
     windows = dictWindows 
     For Each kvp In windows 
      v1 = 0 
      hWnd = kvp.Key 
      GetWindowThreadProcessId(hWnd, windowPid) 
      b = "" 
      c = "" 
      For Each p In processes 
       If p.Id = windowPid Then 
        b = p.ProcessName 
        c = p.Id.ToString 
       End If 
      Next 
      d = "hidden" 
      If IsWindowVisible(hWnd) Then 
       d = "visible" 
       v1 = 1 
      End If 
      If hWnd = fhWnd Then 
       d = d & ", foreground" 
      End If 
      word = kvp.Value 
      a = a & "Window=" & hWnd.ToString & ", processname=" & b & ", procid=" & c & ", windowname=" & word & ", " & d & vbCrLf 
      If Trim(cleanstring(b).ToUpper) = myprocname Then 
       a = a & "procname match" & vbCrLf 
       If Trim(cleanstring(word)).ToUpper = mywinname And v1 <> 0 Then 
        a = a & "ATTEMPTING To CLOSE: " & b & " # " & word & " # " & c & vbCrLf 
        ' x = PostMessage(hWnd, WM_CLOSE, 0, 0) 
        'If x Then 
        'a = a & "PostMessage returned True" & vbCrLf 
        'Else 
        'a = a & "PostMessage returned False" & vbCrLf 
        'End If 
        y = SendMessage(hWnd, WM_SYSCOMMAND, SC_CLOSE, 0) 
        a = a & "SendMessage returned " & y.ToString & vbCrLf 
       End If 
      End If 
     Next 
     My.Computer.FileSystem.WriteAllText(f, a, False) 
    End Sub 
End Module 
+0

Merci d'envoyer, de nouvelles réponses à de vieilles questions toujours aider! –

Questions connexes