【问题标题】:Enumerating Windows/Controls of another application from .Net从 .Net 枚举另一个应用程序的 Windows/控件
【发布时间】:2010-09-21 19:16:20
【问题描述】:

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

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

有什么想法吗?

更新
感谢他们让我走上正确道路的答案。
我发现了一个article with a test project,它同时使用EnumWindowsEnumChidWindows 以及其他API 调用来获取有关控件的扩展信息。

【问题讨论】:

    标签: .net automation


    【解决方案1】:

    您可以使用 P/Invoke 访问 EnumWindows 和 EnumChidWindows 以遍历任何窗口的子窗口/控件。

    【讨论】:

      【解决方案2】:

      您必须使用本机 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);
      

      【讨论】:

        【解决方案3】:

        这是一个旧帖子,但一个常见问题。我有类似的情况,我试图控制现成软件应用程序的行为。我主要通过使用 Kixstart 成功,但遇到了使用 SETFOCUS 和 SENDKEYS 的限制,因为在某些时候,现成的软件会显示 WindowTitle 为空白的窗口。所以我开发了一个小实用程序,它通过 ProcessName 和 WindowTitle(或缺少)来识别可见的窗口,并发送一条消息来关闭匹配的窗口。

        我见过的大多数 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
        

        【讨论】:

        • 感谢您发布此内容,对旧问题的新答案总是有帮助!
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-01-04
        • 2010-12-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-09-29
        • 1970-01-01
        相关资源
        最近更新 更多