zoukankan      html  css  js  c++  java
  • Windows应用程序开发笔记-控制和获取其他程序窗口控件内容

    从以下需求引出的

    在自己开发的程序中获取第三方应用程序窗口中文本框的内容,或者控制按钮的点击等行为

    有两种方案:

    1. 使用windows api函数FindWindow,FindWindowEx,SendMessage,EnumChildWindows等来完成获取和控制。函数的具体参数、使用说明等在微软网站有详细介绍,以c#中使用FindWindow为例:

    public partial class Form1 : Form
    {
    public Form1()
    {
    InitializeComponent();
    }
    
    const int WM_SETTEXT = 0x000C;
    const int WM_GETTEXT = 0x000D;
    const int WM_CLICK = 0x00F5;
    
    const int WM_LBUTTONDOWN = 0x0201;
    const int WM_LBUTTONUP = 0x0202;
    const int WM_CLOSE = 0x0010;
    const int WM_GETTEXTLENGTH = 0x000E;
    
    /// <summary>
    /// Windows API函数,查找窗口句柄,在查找时不区分大小写。关于句柄以及FindWindow函数的参数,用法等在https://www.cnblogs.com/gyc19920704/p/5430964.html 有说明。
    /// </summary>
    /// <param name="lpClassName">spy++工具中的 类 没有则为null</param>
    /// <param name="lpWindowName">spy++工具中的 标题 没有则为null</param>
    /// <returns></returns>
    [DllImport("user32.dll", EntryPoint = "FindWindow")]
    public extern static IntPtr FindWindow(string lpClassName, string lpWindowName);
    
    //查找窗口内控件句柄
    [DllImport("user32.dll", EntryPoint = "FindWindowEx", SetLastError = true)]
    public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
    
    //发送消息
    [DllImport("user32.dll", EntryPoint = "SendMessageA")]
    private static extern int SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, StringBuilder lParam);
    
    [DllImport("User32.dll", EntryPoint = "SendMessage")]
    private static extern int SendMessage1(IntPtr hWnd, int Msg, int wParam, StringBuilder lParam);
    
    [DllImport("user32.dll")]
    public static extern int EnumChildWindows(IntPtr hWndParent, CallBack lpfn, int lParam);
    
    [DllImport("user32.dll")]
    public static extern int GetWindowText(IntPtr hwnd, StringBuilder sb, int length);
    
    public delegate bool CallBack(IntPtr hwnd, int lParam);
    
    
    private void button1_Click(object sender, EventArgs e)
    {
    IntPtr mW = FindWindow(null, "MyForm");
    if (mW != IntPtr.Zero)
    {
    MessageBox.Show("找到程序窗口!");
    
    IntPtr et = FindWindowEx(mW, IntPtr.Zero, "WindowsForms10.EDIT.app.0.141b42a_r7_ad1", null); //第三个参数使用spy++找的"类"
    if (et != IntPtr.Zero)
    {
    MessageBox.Show("找到文本框!");
    
    StringBuilder s1 = new StringBuilder(512);
    //SendMessage(et, 0x000d, 510, s1); //0x000d是向控件发送获取文本消息的消息编号
    //SendMessage1(et, WM_SETTEXT, IntPtr.Zero, DateTime.Now.ToString());
    
    MessageBox.Show(s1.ToString());
    //WindowsForms10.BUTTON.app.0.141b42a_r7_ad1
    IntPtr hbutton = FindWindowEx(mW, IntPtr.Zero, "WindowsForms10.BUTTON.app.0.141b42a_r7_ad1", null);
    //SendMessage1(hbutton, WM_CLICK, IntPtr.Zero, null);
    
    //SendMessage1(hbutton, WM_LBUTTONDOWN, IntPtr.Zero, null);
    //SendMessage1(hbutton, WM_LBUTTONUP, IntPtr.Zero, null);
    }
    else
    {
    MessageBox.Show("没找到文本框!");
    }
    }
    else
    {
    MessageBox.Show("没有窗口!");
    }
    }
    
    /// <summary>
            /// 查找窗体内的控件句柄
            /// </summary>
            /// <param name="hwnd">父窗体句柄</param>
            /// <param name="lpszWindow">控件标题(Text)</param>
            /// <param name="bChild">设定是否在子窗体中查找</param>
            /// <returns>控件句柄,没找到返回IntPtr.Zero</returns>
            public static IntPtr FindWindowExMy(IntPtr hwnd, string lpszWindow, bool bChild)
    {
    IntPtr iResult = IntPtr.Zero;
                // 首先在父窗体上查找控件
                iResult = FindWindowEx(hwnd, IntPtr.Zero, null, lpszWindow);
                // 如果找到直接返回控件句柄
                if (iResult != IntPtr.Zero)
    {
    return iResult;
    }
    
                // 如果设定了不在子窗体中查找
                if (!bChild)
    {
    return iResult;
    }
    
                // 枚举子窗体,查找所有子窗体里的控件句柄
                int i = EnumChildWindows(
    hwnd,
    (h, l) =>
    {
    IntPtr f1 = FindWindowEx(h, IntPtr.Zero, null, lpszWindow);
    if (f1 == IntPtr.Zero)
    {
    return true;
    }
    else
    {
    StringBuilder title = new StringBuilder(200);
    int len;
    len = GetWindowText(hwnd, title, 200);
    
    iResult = f1;
    return false;
    }
    },
    0);
                // 返回查找结果
                return iResult;
    }
    
    
    public bool Report(IntPtr hwnd, int lParam)
    {
    Console.Write("Window handle is :");
    Console.WriteLine(hwnd);
    return true;
    }
    // public delegate bool CallBack(int hwnd, int lParam);
    [DllImport("user32")]
    public static extern int EnumWindows(CallBack x, int y);
    
     
    
    private void button2_Click(object sender, EventArgs e)
    {
    CallBack myCallBack = new CallBack(Report);
    EnumWindows(myCallBack, 0);
    //-----------------------------------------------------------------------------------------------
    IntPtr mW = FindWindow(null, "MyForm");
    if (mW != IntPtr.Zero)
    {
    MessageBox.Show("已找到程序窗口!");
    
    IntPtr et = FindWindowExMy(mW, "button1", true);
    //IntPtr et = FindWindowEx(mW, IntPtr.Zero, "WindowsForms10.EDIT.app.0.141b42a_r7_ad1", null); //第三个参数使用spy++找的"类"。对于.net winform窗口,每个控件有特定的值
    //IntPtr et = (IntPtr)0x0001021A;//句柄 在程序窗口关闭重启后是会变化的。
    if (et != IntPtr.Zero)
    {
    MessageBox.Show("已找到文本框!");
    SendMessage(et, WM_SETTEXT, IntPtr.Zero, new StringBuilder("xxx123"));//为什么不生效?
    
    StringBuilder s1 = new StringBuilder(512);
    SendMessage(et, WM_GETTEXT, (IntPtr)512, s1); //0x000d是向控件发送获取文本消息的消息编号
    
    MessageBox.Show(s1.ToString());
    
    //SendMessage(et, WM_SETTEXT, IntPtr.Zero, new StringBuilder("xxx123"));
    
    SendMessage(et, WM_CLICK, IntPtr.Zero, null);
    
    //SendMessage1(et, WM_LBUTTONDOWN, IntPtr.Zero, null);
    //SendMessage1(et, WM_LBUTTONUP, IntPtr.Zero, null);
    }
    else
    {
    MessageBox.Show("没找到文本框!");
    }
    }
    else
    {
    MessageBox.Show("没有找到程序窗口!");
    }
    }
    }

    获取句柄的辅助工具有spy++或WinSpy,以下以spy++为例,在visual studio中有集成,“工具=>获取工具和功能=>单个组件=>C++核心功能”,

    打开此工具后按ctrl f 然后点图标拖到要查找的其他程序窗口上即可显示句柄

    方案2:使用.Net下的UIAutomation技术,网上此类文章很多,例如:https://blog.csdn.net/lassewang/article/details/6693917 有很详细的例子

    以下摘自:https://www.cnblogs.com/miaosha5s/p/4993321.html

    一.界面的自动化操作

    .Ui自动化测试

    .软件外挂 

    二.Win32基础知识

    a.Win32中一切元素皆窗口,窗口之间有父子关系。整个桌面是一个“根窗口”。

    b.进程:

    根据进程id拿到进程对象Process process = Process.GetProcessById(processId);

    启动一个进程:Process process = Process.Start(exe路径);

    杀死一个进程process.Kill() 

    三.UIAutonation基础

    1、需要添加对UIAutomationClient、 UIAutomationProvider、 UIAutomationTypes的引用

    2、AutomationElement.RootElement是窗口根元素

    AutomationElement.FromHandle(IntPtr hwnd)从窗口句柄拿到AutomationElement对象。

    3、遍历:

    mainElement.FindAll(TreeScope.Descendants,

    new PropertyCondition(AutomationElement.ClassNameProperty, "TLabeledEdit"));

    TreeScope.Descendants代表递归从所有子孙元素中递归查找;如果是从直接子节点查找,则使用TreeScope.Children。

    Condition是过滤条件,可以根据类名等查找,如果是不指定查询条件则使用Condition.True Condition。

    FindFirst是查到第一个。 

    4、点击按钮、设置文本、读取文本使用Pattern来实现。不是所有Pattern都支持

    (1) 设置控件的值

    ValuePattern

    valuePattern = (ValuePattern)element.GetCurrentPattern(ValuePattern.Pattern);

    valuePattern.SetValue("rupeng.com");

    (2) 得到文本控件的值

    TextPattern

    valuePattern = (TextPattern)element.GetCurrentPattern(TextPattern.Pattern);

    string v= valuePattern.DocumentRange.GetText(-1);

    (3) 调用控件,比如点击按钮

    var clickPattern = (InvokePattern)element.GetCurrentPattern(InvokePattern.Pattern);

    clickPattern.Invoke();

  • 相关阅读:
    python_捕获异常
    requests二次封装_捕获异常
    python_flask模块
    python_redis模块
    python_requests模块
    使用pstack和gdb调试死锁
    如何编写go代码
    GDB调试命令手册
    core文件相关
    shared_ptr的线程安全性
  • 原文地址:https://www.cnblogs.com/dikongpulu/p/14883943.html
Copyright © 2011-2022 走看看