在另一个应用程序中查找特定 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;
       }
    }
 }