代码之家  ›  专栏  ›  技术社区  ›  Renaud Bompuis

从.NET枚举其他应用程序的窗口/控件

  •  1
  • Renaud Bompuis  · 技术社区  · 16 年前

    我正在开发一个小型实用程序,它需要检测另一个应用程序是否打开了一个MDI子窗口(它是一个现成的win32业务应用程序,在这个应用程序上我既没有源代码也没有控制权)。 在我的应用程序中,我希望能够在特定的MDI子窗口打开时进行轮询或检测。

    在.NET中,迭代正在运行的进程很容易,但是我没有找到一种简单的方法来迭代.NET中给定win32进程的(子)窗口和控件。

    有什么想法吗?

    更新
    谢谢你的回答,他们让我走上了正确的道路。
    我找到了一个 article with a test project 两者兼用 EnumWindows EnumChidWindows 以及其他API调用,以获取有关控件的扩展信息。

    3 回复  |  直到 7 年前
        1
  •  3
  •   Robert Vuković    16 年前

    必须使用本机Win32 API。

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

        2
  •  1
  •   Juanma    16 年前

    您可以使用p/invoke访问EnumWindows和EnumchidWindows,通过任何窗口的子窗口/控件来创建。

        3
  •  1
  •   Peter Talbot    7 年前

    这是一个古老的职位,但也是一个常见的问题。我也遇到过类似的情况,我试图控制现成软件应用程序的行为。我主要是通过使用kixstart成功的,但遇到了使用setfocus和sendkeys的限制,因为在某些情况下,现成的软件显示的窗口标题为空。因此,我开发了一个小实用程序,它通过进程名和窗口标题(或者缺少)来标识可见窗口,并发送一条消息来关闭一个匹配的窗口。

    我看到的EnumWindows回调函数的大多数示例都忽略不可见或标题为空的窗口,而此代码枚举所有窗口,包括不可见的窗口。

    此代码是Visual Basic,使用格式为的配置文件

    PRC=process name
    WIN=window title
    

    希望这对某人有用

    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