我正在开发一个小型实用程序应用程序,需要检测另一个是否有一个MDI子窗口打开(这是一个现成的Win32业务应用程序,我既没有源代码也没有控制权)。 从我的应用程序,我希望能够轮询或检测特定MDI子窗口何时打开。
在.Net中,很容易迭代运行的进程,但我还没有找到一种简单的方法来迭代来自.Net的给定Win32进程的(子)窗口和控件。
有什么想法吗?
更新
感谢他们的答案,他们让我走上了正确的道路
我发现article with a test project同时使用EnumWindows
和EnumChidWindows
以及其他API调用来获取有关控件的扩展信息。
答案 0 :(得分:3)
您必须使用本机Win32 API。
[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);
[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 :(得分:1)
您可以使用P / Invoke访问EnumWindows和EnumChildWindows来遍历任何窗口的子窗口/控件。
答案 2 :(得分:1)
这是一个老帖子,但是一个常见的问题。我有类似的情况,我试图控制现成的软件应用程序的行为。我主要使用Kixstart成功,但遇到了使用SETFOCUS和SENDKEYS的限制,因为在某些点上,现成的软件显示WindowTitle为空白的窗口。所以我开发了一个小工具,通过他们的ProcessName和WindowTitle(或者没有)来识别可见的Windows,并发送一条消息来关闭一个匹配的窗口。
我见过的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