我知道这是一个老问题,但随着 Visual Studio 走向未来,答案会随着时间而改变。
我想分享我的解决方案,它允许您搜索部分窗口标题,当标题标题包含不可预测的文本时,这通常是需要的。例如,如果您想查找 Windows Mail 应用程序的句柄,标题将包含文本“收件箱 - yourremailaccountname”。显然,您不想对帐户名称进行硬编码。这是我的代码,虽然它在 Visual Basic .NET 中,但您可以将其转换为 C#。输入部分标题(即“收件箱-”),单击按钮,您将得到 hwnd 和完整标题。我尝试使用 Process.GetProcesses() 但与 Win API 相比它的速度很慢。
此示例将在 EnumWindows 调用的 lparm 中返回您搜索的窗口句柄(第二个参数由 ref 传递),即使应用程序已最小化,也会将其置于最前面。
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