通过句柄ID操作其他应用程序,控制按钮点击,获取文本框数据


如下图所示,在应用程序中打开另外一个应用程序,这里同时打开两个Form1,在应用程序中控制另一个应用程序中的控件,可以点击button按钮,以及设置或者读取textbox控件的内容。
在这里插入图片描述

在应用程序中启动另一个程序

Process p1 = new Process();

string fexePath = @"..\..\..\insideFrom\bin\Debug\net5.0-windows\insideFrom.exe"; // 外部exe位置

p1.StartInfo.FileName = fexePath;
p1.StartInfo.WindowStyle = ProcessWindowStyle.Normal;
p1.Start();

while (p1.MainWindowHandle.ToInt32() == 0)
{
	System.Threading.Thread.Sleep(100);
}
SetParent(p1.MainWindowHandle, panel1.Handle);
ShowWindow(p1.MainWindowHandle, (int)ProcessWindowStyle.Maximized);

p2.StartInfo.FileName = fexePath;
p2.StartInfo.WindowStyle = ProcessWindowStyle.Normal;
p2.Start();

创建一个程序进程p1,fexePath为需要执行的外部程序的exe可执行文件,使用SetParent函数将p1的父窗体设置为panel1,最后启动进程即可。
SetParent和ShowWindow函数为windows的API库函数,使用C#开发的话需要引入声明,如下

[DllImport("User32.dll", EntryPoint = "SetParent")]
private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll", EntryPoint = "ShowWindow")]
private static extern int ShowWindow(IntPtr hwnd, int nCmdShow);

获取窗口句柄控制其他程序窗口的Windows API

获取窗口句柄控制其他程序窗口主要有三个Windows API函数

[DllImport("User32.dll", EntryPoint = "FindWindow")]
public extern static IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("User32.dll", EntryPoint = "FindWindowEx")]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpClassName, string lpWindowName);
[DllImport("User32.dll", EntryPoint = "SendMessage")]
private static extern int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, string lParam);

FindWindow函数是获取窗体句柄的函数,参数1是窗体类型名称,参数2是窗体名也叫标题,这两个参数知道一个即可,另一个可以填null。但是如果是用窗口类型查找,则可能只能得到其中的一个窗口。因此通过标题进行查找是非常方便的。
FindWindowEx函数是获取子窗体的句柄函数,窗体上的按钮,文本框即可看成是子窗体,参数1是父窗体句柄,参数2是是同一类型中的第几个子窗口。填IntPtr.Zero则表示获得第一个子窗口,填另一个子窗体句柄则表示获得下一个子窗体。参数3表示你需要找的子窗口的类型,参数4表示子窗体名称,可以为null。
SendMessage函数是发送窗体消息,消息包括鼠标按键按下,键盘按下等等消息。参数1是窗口句柄,或者窗口中控件的句柄,参数2是消息的类型Flag,这些值是在API的头文件WinUser.h中定义好的。但是如果你要是在C#中用,就自己去定义他们,可以参考WinUser.h文件或者参考Windows消息大全。参数3和参数4都是消息的具体内容。
本例程用到的消息有

const int WM_SETTEXT = 0x000C;
const int WM_GETTEXT = 0x000D; //应用程序发送此消息来复制对应窗口的文本到缓冲区
const int WM_GETTEXTLENGTH = 0x000E;
const int WM_LBUTTONDOWN = 0x0201;
const int WM_LBUTTONUP = 0x0202;

点击按钮控件

//IntPtr maindHwnd = FindWindow(null, "Form1"); //获得标题外部运行程序的句柄
IntPtr maindHwnd = p1.MainWindowHandle;//通过进程获取句柄
if (maindHwnd != IntPtr.Zero)
{ 
    IntPtr hbuttonbox = FindWindowEx(maindHwnd, IntPtr.Zero, null, "button1");//通过按键名称获取按键ID

    SendMessage(hbuttonbox, WM_LBUTTONDOWN, IntPtr.Zero, null);//按下鼠标左键
    SendMessage(hbuttonbox, WM_LBUTTONUP, IntPtr.Zero, null);//抬起鼠标左键
}
else
{
    MessageBox.Show("没有找到窗口");
}

首先获取窗体句柄,如果是外部运行的程序直接通过FindWindow函数根据窗体名称来查找,如上注释的代码所示,但是本例程外部窗体通过进程打开,可以直接使用进程获取句柄,获取到窗体句柄后通过FindWindowEx更具按键名称获取按钮的句柄,然后使用SendMessage函数发送一个鼠标左键按下和鼠标左键抬起消息,至此就实现的按钮控件

设置文本数据

//IntPtr maindHwnd = FindWindow(null, "Form1"); //获得标题外部运行程序的句柄
IntPtr maindHwnd = p1.MainWindowHandle;//通过进程获取句柄
if (maindHwnd != IntPtr.Zero)
{                                                      
   IntPtr htextbox1 = FindWindowEx(maindHwnd, IntPtr.Zero, "WindowsForms10.Edit.app.0.378734a_r3_ad1", null);//通过类型获取第一个文本编辑框
   IntPtr htextbox2 = FindWindowEx(maindHwnd, htextbox1, "WindowsForms10.Edit.app.0.378734a_r3_ad1", null);//通过类型获取第二个文本编辑框

   SendMessage(htextbox1, WM_SETTEXT, IntPtr.Zero, textBox1.Text);//填写文本框。
}
else
{
   MessageBox.Show("没有找到窗口");
}

首先获取窗体句柄,而后通过FindWindowEx获取文本框的句柄,由于文本框不像按钮控件有标题,所以只能通过文本框的类型来获取,另外由于文本框有两个,所以获取两个文本框的办法为先指定参数2为IntPtr.Zero获取到第一个,然后在将第一个文本框句柄作为参数2获取第二个。最后使用SendMessage发送设置文本消息,发送的消息内容为textBox1.Text的内容。

获取文本数据

//IntPtr maindHwnd = FindWindow(null, "Form1"); //获得标题外部运行程序的句柄
IntPtr maindHwnd = p1.MainWindowHandle;//通过进程获取句柄
if (maindHwnd != IntPtr.Zero)
{
    IntPtr htextbox1 = FindWindowEx(maindHwnd, IntPtr.Zero, "WindowsForms10.Edit.app.0.378734a_r3_ad1", null);//通过类型获取第一个文本编辑框
    IntPtr htextbox2 = FindWindowEx(maindHwnd, htextbox1, "WindowsForms10.Edit.app.0.378734a_r3_ad1", null);//通过类型获取第二个文本编辑框

    StringBuilder title = new StringBuilder();
    Int32 size = SendMessage(htextbox2, WM_GETTEXTLENGTH, IntPtr.Zero, null);//获取长度
    if (size > 0)
    {
        title = new StringBuilder(size + 1);
        SendMessage(htextbox2, (int)WM_GETTEXT, title.Capacity, title);//读取文本框的数据
    }
    textBox2.Text = title.ToString();//显示
}
else
{
    MessageBox.Show("没有找到窗口");
}

首先获取窗体句柄以及文本框的句柄,然后使用SendMessage发送获取长度消息,获取到文本框的长度,然后再使用SendMessage发送获取文本消息获取文本内容,获取到的内容在title变量中,最后将title显示到textBox2控件上。

Spy++工具获取句柄ID,类型和标题

可以看到在使用FindWindowEx工具获取文本框的时候用到的文本框类型非常复杂,测试发现不用不同环境开发出来的应用程序上是子窗体类型不尽相同,所以知道我要获取的窗体的类型很关键,同时如果能直接知道窗体的句柄ID也非常方便控制,当然测试发现句柄ID随着程序的每次打开是不一样的,所以用着来作为索引是不合适的,不过是句柄ID或是类型和标题我们都能够通过Spy++工具来查看。
Spy++工具一般安装了Visual studioIDE就以及安装好了,安装路径为\Microsoft Visual Studio\2019\Professional\Common7\Tools下。
打开Spy++工具如下图所示
在这里插入图片描述
点击查找窗口,或者Ctrl+F
在这里插入图片描述
点击图示上 查找工具拖拽到需要查看的窗体上松开就能看到窗体的信息,如上所示包括句柄,标题,类型,矩形等。

  • 9
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值