在另一个应用程序中查找特定 class 的控件
Find control of specific class in another application
我正在从另一个 Windows 应用程序中删除内容。
该应用程序有一个列表框和两个 TRichEdit 控件(以及其他不感兴趣的控件)。
当对列表框使用SendKeys.SendWait("{DOWN}")时,两个TRichEdit框的内容发生变化。那就是我想废弃内容的地方。行得通。
RichEdit1:没问题 - 我使用 SendMessageW()
获取内容
RichEdit2:大问题。每次我在 LIstBox 上使用 SendKeys.SendWait 时,它都会更改 Windows 句柄,所以我无法访问它。
解决方法是为 RichEdit2 找到新的 Windows 句柄。我认为我可以获得 RichEdit 控件的句柄列表和 select 与 RichEdit1 不同的句柄列表。
问题:
如何从不同的 windows 表单应用程序获取特定 class (RichEdit) 的句柄列表?
或
有没有人有更好的解决方案?
C# 中的代码片段将不胜感激。
提前致谢。
关于如何获取 RichEdit window 句柄的问题:
您可以 PInvoke FindWindowEx setting the child window parameter to NULL
to check all child windows and the class name set to the class names of RichEdit control from here:
v1.0 = RICHEDIT
v2.0 & v3.0 = RichEdit20A or RichEdit20W
v4.1 = RICHEDIT50W
v5.0 = RichEdit50W
v6.0 = RichEdit60W
不过,MSDN 指出:
The function searches among windows that are child windows of the desktop.
所以基本上您的搜索深度为 1。如果您的控件嵌套得更深,那么您可能需要将其与 EnumChildWindows 结合使用以执行全深度搜索。
编辑
这是一个关于如何使用描述的方法枚举 windows 并为给定的 class 找到匹配的 windows 的片段,希望您能对其进行微调。
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Collections.Concurrent;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace UIAutomation
{
class Program
{
public delegate bool EnumWindowsProc(IntPtr hwnd, IntPtr lParam);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool EnumChildWindows(IntPtr hwndParent, EnumWindowsProc lpEnumFunc, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
public static bool EnumChildWindowsCallback(IntPtr hWnd, IntPtr lParam)
{
StringBuilder className = new StringBuilder(256);
GetClassName(hWnd, className, className.Capacity);
var windowInformation = new WindowInformation(hWnd, lParam, className.ToString());
_windowLookupMap[hWnd] = windowInformation;
if (lParam != IntPtr.Zero)
{
_windowLookupMap[lParam]._children.Add(windowInformation);
}
EnumChildWindows(hWnd, EnumChildWindowsCallback, hWnd);
return true;
}
class WindowInformation
{
public IntPtr _parent;
public IntPtr _hWnd;
public string _className;
public List<WindowInformation> _children = new List<WindowInformation>();
public WindowInformation(IntPtr hWnd, IntPtr parent, string className)
{
_hWnd = hWnd;
_parent = parent;
_className = className;
}
}
static Dictionary<IntPtr, WindowInformation> _windowLookupMap = new Dictionary<IntPtr, WindowInformation>();
static void FindWindowsByClass(string className, WindowInformation root, ref List<WindowInformation> matchingWindows)
{
if (root._className == className)
{
matchingWindows.Add(root);
}
foreach (var child in root._children)
{
FindWindowsByClass(className, child, ref matchingWindows);
}
}
static void Main(string[] args)
{
var processes = Process.GetProcessesByName("notepad");
StringBuilder className = new StringBuilder(256);
GetClassName(processes[0].MainWindowHandle, className, className.Capacity);
_windowLookupMap[processes[0].MainWindowHandle] = new WindowInformation(processes[0].MainWindowHandle, IntPtr.Zero, className.ToString());
EnumChildWindows(processes[0].MainWindowHandle, EnumChildWindowsCallback, processes[0].MainWindowHandle);
List<WindowInformation> matchingWindows = new List<WindowInformation>();
FindWindowsByClass("Edit", _windowLookupMap.Single(window => window.Value._parent == IntPtr.Zero).Value, ref matchingWindows);
Console.WriteLine("Found {0} matching window handles", matchingWindows.Count);
}
}
}
感谢上面的回答。它非常详细。我最终使用了一种更简单的方法:
private IntPtr FindHandle()
{
while (true)
{
IntPtr handle = FindWindowEx(this.ApplicationHandle,IntPtr.Zero,"TRichEdit", null);
if (handle == null)
{
throw new Exception("No handle found");
}
if (handle != this.Handle_01)
{
return handle;
}
}
}
我正在从另一个 Windows 应用程序中删除内容。 该应用程序有一个列表框和两个 TRichEdit 控件(以及其他不感兴趣的控件)。
当对列表框使用SendKeys.SendWait("{DOWN}")时,两个TRichEdit框的内容发生变化。那就是我想废弃内容的地方。行得通。
RichEdit1:没问题 - 我使用 SendMessageW()
获取内容RichEdit2:大问题。每次我在 LIstBox 上使用 SendKeys.SendWait 时,它都会更改 Windows 句柄,所以我无法访问它。
解决方法是为 RichEdit2 找到新的 Windows 句柄。我认为我可以获得 RichEdit 控件的句柄列表和 select 与 RichEdit1 不同的句柄列表。
问题: 如何从不同的 windows 表单应用程序获取特定 class (RichEdit) 的句柄列表?
或
有没有人有更好的解决方案? C# 中的代码片段将不胜感激。
提前致谢。
关于如何获取 RichEdit window 句柄的问题:
您可以 PInvoke FindWindowEx setting the child window parameter to NULL
to check all child windows and the class name set to the class names of RichEdit control from here:
v1.0 = RICHEDIT
v2.0 & v3.0 = RichEdit20A or RichEdit20W
v4.1 = RICHEDIT50W
v5.0 = RichEdit50W
v6.0 = RichEdit60W
不过,MSDN 指出:
The function searches among windows that are child windows of the desktop.
所以基本上您的搜索深度为 1。如果您的控件嵌套得更深,那么您可能需要将其与 EnumChildWindows 结合使用以执行全深度搜索。
编辑
这是一个关于如何使用描述的方法枚举 windows 并为给定的 class 找到匹配的 windows 的片段,希望您能对其进行微调。
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Collections.Concurrent;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace UIAutomation
{
class Program
{
public delegate bool EnumWindowsProc(IntPtr hwnd, IntPtr lParam);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool EnumChildWindows(IntPtr hwndParent, EnumWindowsProc lpEnumFunc, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
public static bool EnumChildWindowsCallback(IntPtr hWnd, IntPtr lParam)
{
StringBuilder className = new StringBuilder(256);
GetClassName(hWnd, className, className.Capacity);
var windowInformation = new WindowInformation(hWnd, lParam, className.ToString());
_windowLookupMap[hWnd] = windowInformation;
if (lParam != IntPtr.Zero)
{
_windowLookupMap[lParam]._children.Add(windowInformation);
}
EnumChildWindows(hWnd, EnumChildWindowsCallback, hWnd);
return true;
}
class WindowInformation
{
public IntPtr _parent;
public IntPtr _hWnd;
public string _className;
public List<WindowInformation> _children = new List<WindowInformation>();
public WindowInformation(IntPtr hWnd, IntPtr parent, string className)
{
_hWnd = hWnd;
_parent = parent;
_className = className;
}
}
static Dictionary<IntPtr, WindowInformation> _windowLookupMap = new Dictionary<IntPtr, WindowInformation>();
static void FindWindowsByClass(string className, WindowInformation root, ref List<WindowInformation> matchingWindows)
{
if (root._className == className)
{
matchingWindows.Add(root);
}
foreach (var child in root._children)
{
FindWindowsByClass(className, child, ref matchingWindows);
}
}
static void Main(string[] args)
{
var processes = Process.GetProcessesByName("notepad");
StringBuilder className = new StringBuilder(256);
GetClassName(processes[0].MainWindowHandle, className, className.Capacity);
_windowLookupMap[processes[0].MainWindowHandle] = new WindowInformation(processes[0].MainWindowHandle, IntPtr.Zero, className.ToString());
EnumChildWindows(processes[0].MainWindowHandle, EnumChildWindowsCallback, processes[0].MainWindowHandle);
List<WindowInformation> matchingWindows = new List<WindowInformation>();
FindWindowsByClass("Edit", _windowLookupMap.Single(window => window.Value._parent == IntPtr.Zero).Value, ref matchingWindows);
Console.WriteLine("Found {0} matching window handles", matchingWindows.Count);
}
}
}
感谢上面的回答。它非常详细。我最终使用了一种更简单的方法:
private IntPtr FindHandle()
{
while (true)
{
IntPtr handle = FindWindowEx(this.ApplicationHandle,IntPtr.Zero,"TRichEdit", null);
if (handle == null)
{
throw new Exception("No handle found");
}
if (handle != this.Handle_01)
{
return handle;
}
}
}