Перечисление Windows / Элементы управления другого приложения из .Net - PullRequest
1 голос
/ 11 ноября 2008

Я разрабатываю небольшое служебное приложение, которое должно определить, есть ли у другого одно из дочерних окон MDI (это готовое бизнес-приложение Win32, над которым у меня нет ни исходного кода, ни элемента управления). Из моего приложения я хотел бы иметь возможность опрашивать или определять, когда открыто конкретное дочернее окно MDI.

В .Net легко перебирать запущенные процессы, но я не нашел простого способа перебирать (под) окна и элементы управления определенного процесса Win32 из .Net.

Есть идеи?

Обновление
Спасибо за ответы, они дали мне правильный путь.
Я нашел статью с тестовым проектом , в котором используются EnumWindows и EnumChidWindows и другие вызовы API для получения расширенной информации об элементах управления.

Ответы [ 3 ]

3 голосов
/ 11 ноября 2008

Вы должны использовать собственный Win32 API.

EnumChildWindows (user32)

</p> <pre><code>[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 голос
/ 21 апреля 2017

Это старый пост, но распространенная проблема. У меня похожая ситуация, когда я пытаюсь контролировать поведение готового программного приложения. Я в основном преуспел с помощью Kixstart, но столкнулся с ограничениями использования SETFOCUS и SENDKEYS, потому что в определенных точках готовое программное обеспечение отображает окна, где WindowTitle пуст. Поэтому я разработал небольшую утилиту, которая идентифицирует видимые Windows по их 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
1 голос
/ 11 ноября 2008

Вы можете использовать P / Invoke для доступа к EnumWindows и EnumChidWindows для итерации через подокна / элементы управления любого окна.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...