25

最終的に WPF アプリケーションの UI テストを実行するための API となるアプリケーションに取り組んでいます。

私たちが取り組んでいる初期テストのある時点で、2 つの Windows セキュリティ ポップアップが表示されます。10 回ループするコードがいくつかあります。FindWindowByCaption メソッドを使用してポップアップの 1 つのハンドルを取得し、情報を入力して [OK] をクリックします。

10 回中 9 回は問題なく動作しますが、競合状態のように見えるものが時折見られます。私の疑いは、ウィンドウの1つだけが開いているときにループが開始され、情報を入力している間に2番目のウィンドウが開いてフォーカスを盗むということです。この後、無期限にハングします。

私が疑問に思っているのは、ループを開始する前に 2 つになるまで待機できるように、特定のキャプションのすべてのウィンドウ ハンドルを取得する方法があるかどうかです。

4

3 に答える 3

0

これは古い質問であることは承知していますが、Visual Studio が将来に移行するにつれて、答えが変わるものです。

タイトル キャプションに予測できないテキストが含まれている場合に必要になることが多い、部分的なウィンドウ タイトルを検索できるソリューションを共有したいと思います。たとえば、Windows メール アプリケーションのハンドルを検索する場合、タイトルには "Inbox - yourremailaccountname" というテキストが含まれます。明らかに、アカウント名をハードコーディングしたくありません。Visual Basic .NET のコードですが、C# に変換できます。タイトルの一部 (「受信トレイ -」など) を入力し、ボタンをクリックすると、タイトルと完全なタイトルが表示されます。Process.GetProcesses() を使用してみましたが、Win API に比べてかなり遅くなりました。

この例では、検索のウィンドウ ハンドルを EnumWindows 呼び出しの lparm (ref によって渡される 2 番目のパラメーター) に返し、最小化されている場合でもアプリケーションを前面に表示します。

Imports System.Runtime.InteropServices
Imports System.Text
Public Class Form1
    <DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> Private Shared Function EnumWindows(ByVal lpEnumFunc As EnumWindowsProcDelegate, ByRef lParam As IntPtr) As Boolean
    End Function
    Private Delegate Function EnumWindowsProcDelegate(ByVal hWnd As IntPtr, ByRef lParam As IntPtr) As Integer

    <DllImport("user32.dll")>
    Private Shared Function GetWindowTextLength(ByVal hWnd As IntPtr) As Integer
    End Function

    <DllImport("user32.dll")>
    Private Shared Function GetWindowText(ByVal hWnd As IntPtr, ByVal lpString As StringBuilder, ByVal nMaxCount As Integer) As Integer
    End Function

    <DllImport("user32", EntryPoint:="SendMessageA", CharSet:=CharSet.Ansi, SetLastError:=True, ExactSpelling:=True)> Public Shared Function SendMessage(ByVal hwnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, ByRef lParam As Integer) As Integer
    End Function

    <DllImport("user32.dll")>
    Private Shared Function SetForegroundWindow(ByVal hWnd As IntPtr) As Boolean
    End Function

    <DllImport("user32.dll", SetLastError:=True)>
    Private Shared Function SetActiveWindow(ByVal hWnd As IntPtr) As Integer
    End Function

    <DllImport("user32.dll", SetLastError:=True)>
    Private Shared Function SetWindowPos(ByVal hWnd As IntPtr, hWndInsertAfter As IntPtr, x As Integer, y As Integer, cx As Integer, cy As Integer, uFlags As UInt32) As Boolean
    End Function

    <DllImport("user32.dll", SetLastError:=True)>
    Private Shared Function RedrawWindow(ByVal hWnd As IntPtr, lprcUpdate As Integer, hrgnUpdate As Integer, uFlags As UInt32) As Boolean
    End Function

    Public Const WM_SYSCOMMAND As Integer = &H112
    Public Const SC_RESTORE = &HF120
    Public Const SWP_SHOWWINDOW As Integer = &H40
    Public Const SWP_NOSIZE As Integer = &H1
    Public Const SWP_NOMOVE As Integer = &H2
    Public Const RDW_FRAME As Int32 = 1024 'Updates the nonclient area if included in the redraw area. RDW_INVALIDATE must also be specified.
    Public Const RDW_INVALIDATE As Int32 = 1 'Invalidates the redraw area.
    Public Const RDW_ALLCHILDREN As Int32 = 128 'Redraw operation includes child windows if present in the redraw area.

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim strPartialTitle As String = TextBox1.Text
        Dim intptrByRefFoundHwnd As IntPtr = Marshal.StringToHGlobalAnsi(strPartialTitle)
        Dim delegateEnumWindowsProcDelegate As EnumWindowsProcDelegate
        delegateEnumWindowsProcDelegate = New EnumWindowsProcDelegate(AddressOf EnumWindowsProc)
        EnumWindows(delegateEnumWindowsProcDelegate, intptrByRefFoundHwnd)
        LabelHwndAndWindowTitle.Text = intptrByRefFoundHwnd
        BringWindowToFront(intptrByRefFoundHwnd)
    End Sub

    Function EnumWindowsProc(ByVal hWnd As IntPtr, ByRef lParam As IntPtr) As Integer
        Dim strPartialTitle As String = Marshal.PtrToStringAnsi(lParam)
        Dim length As Integer = GetWindowTextLength(hWnd)
        Dim stringBuilder As New StringBuilder(length)
        GetWindowText(hWnd, stringBuilder, (length + 1))
        If stringBuilder.ToString.Trim.Length > 2 Then
            If stringBuilder.ToString.ToLower.Contains(strPartialTitle.ToLower) Then
                Debug.WriteLine(hWnd.ToString & ": " & stringBuilder.ToString)
                lParam = hWnd ' Pop hwnd to top, returns in lParm of EnumWindows Call (2nd parameter)
                Return False
            End If
        End If
        Return True
    End Function

    Private Sub BringWindowToFront(hwnd As IntPtr)
        SendMessage(hwnd, WM_SYSCOMMAND, SC_RESTORE, 0) ' restore the minimize window
        SetForegroundWindow(hwnd)
        SetActiveWindow(hwnd)
        SetWindowPos(hwnd, IntPtr.Zero, 0, 0, 0, 0, SWP_SHOWWINDOW Or SWP_NOMOVE Or SWP_NOSIZE)
        'redraw to prevent the window blank.
        RedrawWindow(hwnd, IntPtr.Zero, 0, RDW_FRAME Or RDW_INVALIDATE Or RDW_ALLCHILDREN)
    End Sub

End Class
于 2019-01-26T03:37:01.433 に答える