刚学了没多久,还有很多地方没有理解到位,还请大家指正。
当程序运行起来,就会显示在任务管理器中,软件中的每个事件,比如按钮,文本框等一些控件,每个事件都会产生一个句柄,句柄就是这些事件的标识符。如果拿到句柄就可以自己写软件对第三方软件进行控制。同一个软件在不同的电脑中的句柄一般是不一样的,但是获取的过程都是一样的。只要在当前电脑上找到事件的标识符即可。
具体的窗口句柄的结构可以用微软的spy++查看,spy++可以在VS的工具里找到,也可以到安装目录下找到。
拖动这个标记到需要查看的控件上松开,即可显示当前事件的句柄,点击确定搜索,即可确定当前控件所在的窗口位置。
有时候软件打开了但是搜索失败,需要对spy++刷新,只要电脑进程变了,句柄列表没来得及更新,这时候就需要对spy++进行刷新。
有的软件不能获取具体控件的句柄,比如像微信、QQ这样的,只能获取一个顶层的窗口。像这样的,那就说明这种程序的控件其实是不存在的,是画出来的,这种程序叫做directui程序,只能模仿鼠标键盘操作。
不是说所有的窗口都支持spy++来抓取,一般是windows标准窗口才能获取控件句柄,以及发送消息等。
接下来就是寻找句柄:
我用的是VS2022,他可以根据自己写的代码自动添加using,不用人为的添加,所以在这里就省略using。
[DllImport("User32.dll", EntryPoint = "FindWindow")]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("User32.dll", EntryPoint = "FindWindowEx")]
private 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,这个方法只能查找顶层窗口的句柄,不能查找子窗口。
第二个是FindWindowEx,这个方法是用来查找子窗口的。
第三个是SendMessage,这个方法可以向指定的文本框输入数据和字符,还可以点击按钮。
我是要获取Pronterface那个软件的句柄,具体控件为右下角文本框和Send按钮。
这个软件有很多层,而且都是Windows标准控件。
private void button1_Click(object sender, EventArgs e)
{
IntPtr hwnd = FindWindow("wxWindowClassNR", "Pronterface"); //FindWindow只查找顶层窗口
IntPtr htextbox = FindWindowEx(hwnd, IntPtr.Zero, "wxWindowClassNR", null);//(当前为第一层)IntPtr.Zero则表示获得第一个子窗口。第三个参数表示你需要找的子窗口的类型,第四个参数一般为null。
//如果一个窗口中有两个文本框,那么可以用如下操作获得第二个文本框的句柄。
// IntPtr htextbox2 = FindWindowEx(hwnd, htextbox, "EDIT", null);//填上次获得的句柄,可以得到下一个的句柄。
IntPtr htextbox21= FindWindowEx(htextbox, IntPtr.Zero, "wxWindowClassNR", null);//第二层第一个窗口//没问题
IntPtr htextbox22 = FindWindowEx(htextbox, htextbox21, "wxWindowClassNR", null);//第二层第二个窗口//没问题
IntPtr htextbox31 = FindWindowEx(htextbox22, IntPtr.Zero, "wxWindowClassNR", null);//3.1没问题
IntPtr htextbox32 = FindWindowEx(htextbox22, htextbox31, "wxWindowClassNR", null);//3.2没问题
IntPtr htextbox41 = FindWindowEx(htextbox32, IntPtr.Zero, "wxWindowClassNR", null);//4.1//000A105E//659550
IntPtr htextbox51 = FindWindowEx(htextbox41, IntPtr.Zero, "wxWindowClassNR", null);//5.1//0012101C//1183772
IntPtr htextbox52 = FindWindowEx(htextbox41, htextbox51, "wxWindowClassNR", null);//5.2
IntPtr htextbox61 = FindWindowEx(htextbox52, IntPtr.Zero, "wxWindowClassNR", null);//6.1
IntPtr htextbox71 = FindWindowEx(htextbox61, IntPtr.Zero, "wxWindowClassNR", null);//7.1
//8.1和8.2为目标,8.1为文本框,8.2为send按钮
IntPtr htextbox81 = FindWindowEx(htextbox71, IntPtr.Zero, "Edit", null);//8.1
IntPtr htextbox82= FindWindowEx(htextbox71, IntPtr.Zero, "Button", null);//8.2
SendMessage(htextbox81, WM_SETTEXT, IntPtr.Zero, "123232");//发送消息
SendMessage(htextbox82, WM_LBUTTONDOWN, IntPtr.Zero, null);//鼠标按下按钮
SendMessage(htextbox82, WM_LBUTTONUP, IntPtr.Zero, null);//释放鼠标
SendMessage(htextbox81, WM_INITDIALOG, IntPtr.Zero, null);
SendMessage(htextbox81, WM_SETTEXT, IntPtr.Zero, "\t");//发送消息
}
IntPtr hwnd = FindWindow("wxWindowClassNR", "Pronterface");
第一行用了FindWindow方法,用来获取顶层窗口的句柄,获得到的句柄存在hwnd中,可以打印出来看结果,打印出来的是十进制的,spy++里看到的是十六进制的。FindWindow中的第一项是类名,第二项为窗口的标题,一般只用标题即可。下面这样写也可以。
IntPtr hwnd = FindWindow(null, "Pronterface");
接下来就是FindWindowEx方法,查找子窗口。
IntPtr htextbox21= FindWindowEx(htextbox, IntPtr.Zero, "wxWindowClassNR", null);//第二层第一个窗口//没问题
IntPtr htextbox22 = FindWindowEx(htextbox, htextbox21, "wxWindowClassNR", null);//第二层第二个窗口//没问题
FindWindowEx方法有四项,第一项是父级窗口的句柄,就是你想要在哪个大窗口下找。父级和子级是相对的,从外到里,就像剥洋葱,外面就是里面的父,而里面的窗口就是外面窗口的子。
第二项填的内容是个句柄。例如上面的两行代码,如果是IntPtr.Zero,那要找的窗口就是htextbox这个父级窗口下的第一个子窗口,如果是htextbox21,那么要找的窗口就是在htextbox这个父级窗口下,排在htextbox21的下面的一个窗口。以此类推。
第三项为要查找的控件的类名,可以在spy++上查找具体控件的类名
第四项为要查找的子窗口的名字,可以为null。
获取到了句柄值就可以对他们进行操作了。然后介绍SendMessage这个方法,这个方法有四个参数。
SendMessage(htextbox81, WM_SETTEXT, IntPtr.Zero, "123232");//发送消息
SendMessage(htextbox82, WM_LBUTTONDOWN, IntPtr.Zero, null);//鼠标按下按钮
SendMessage(htextbox82, WM_LBUTTONUP, IntPtr.Zero, null);//释放鼠标
SendMessage(htextbox81, WM_SETTEXT, IntPtr.Zero, "\t");//发送消息
第一个参数就是要操作的窗口的句柄,第二项就是要操作什么,不同的值会产生不同的操作。第三项一般为IntPtr.Zero。第四项一般为null,如果是向文本框发送消息,那么第四项就是要发送的字符。
第二项的参数需要提前定义,具体完整的指令表需要去查,这里只提供了一些:
const int WM_INITDIALOG = 0x0110;//初始化
const int WM_SETTEXT = 0x000C;//发送消息
const int WM_LBUTTONDOWN = 0x0201;//按下鼠标左键
const int WM_LBUTTONUP = 0x0202;//释放鼠标左键
const int WM_LBUTTONDBLCLK = 0x203; //双击鼠标左键
const int WM_RBUTTONDOWN = 0x204;//按下鼠标右键
const int WM_RBUTTONUP = 0x205;//释放鼠标右键
const int WM_RBUTTONDBLCLK = 0x206;//双击鼠标右键
这个第二项有点类似于单片机寄存器的值,设置不同的值,会有不同的功能。
我是控制的一个名字为Pronterface的软件,建的winform程序,添加了一个按钮,和两个文本框,文本框用来测试获取的句柄是否正确。然后附上完整代码:文章来源:https://www.toymoban.com/news/detail-596477.html
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Diagnostics;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
using System.Xml.Linq;
using static System.Windows.Forms.VisualStyles.VisualStyleElement.ToolBar;
namespace WindowsFormsApp28
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
[DllImport("User32.dll", EntryPoint = "FindWindow")]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("User32.dll", EntryPoint = "FindWindowEx")]
private 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);
//分隔符,以下几个常量放入事件中可用
const int WM_INITDIALOG = 0x0110;//初始化
const int WM_SETTEXT = 0x000C;
const int WM_LBUTTONDOWN = 0x0201;//按下鼠标左键
const int WM_LBUTTONUP = 0x0202;//释放鼠标左键
const int WM_CLOSE = 0x0010;
const int WM_LBUTTONDBLCLK = 0x203; //双击鼠标左键
const int WM_RBUTTONDOWN = 0x204;//按下鼠标右键
const int WM_RBUTTONUP = 0x205;//释放鼠标右键
const int WM_RBUTTONDBLCLK = 0x206;//双击鼠标右键
private void button1_Click(object sender, EventArgs e)
{
IntPtr hwnd = FindWindow("wxWindowClassNR", "Pronterface"); //FindWindow只查找顶层窗口
IntPtr htextbox = FindWindowEx(hwnd, IntPtr.Zero, "wxWindowClassNR", null);//(当前为第一层)IntPtr.Zero则表示获得第一个子窗口。第三个参数表示你需要找的子窗口的类型,第四个参数一般为null。
//如果一个窗口中有两个文本框,那么可以用如下操作获得第二个文本框的句柄。
// IntPtr htextbox2 = FindWindowEx(hwnd, htextbox, "EDIT", null);//填上次获得的句柄,可以得到下一个的句柄。
IntPtr htextbox21= FindWindowEx(htextbox, IntPtr.Zero, "wxWindowClassNR", null);//第二层第一个窗口//没问题
IntPtr htextbox22 = FindWindowEx(htextbox, htextbox21, "wxWindowClassNR", null);//第二层第二个窗口//没问题
IntPtr htextbox31 = FindWindowEx(htextbox22, IntPtr.Zero, "wxWindowClassNR", null);//3.1没问题
IntPtr htextbox32 = FindWindowEx(htextbox22, htextbox31, "wxWindowClassNR", null);//3.2没问题
IntPtr htextbox41 = FindWindowEx(htextbox32, IntPtr.Zero, "wxWindowClassNR", null);//4.1//000A105E//659550
IntPtr htextbox51 = FindWindowEx(htextbox41, IntPtr.Zero, "wxWindowClassNR", null);//5.1//0012101C//1183772
IntPtr htextbox52 = FindWindowEx(htextbox41, htextbox51, "wxWindowClassNR", null);//5.2
IntPtr htextbox61 = FindWindowEx(htextbox52, IntPtr.Zero, "wxWindowClassNR", null);//6.1
IntPtr htextbox71 = FindWindowEx(htextbox61, IntPtr.Zero, "wxWindowClassNR", null);//7.1
//8.1和8.2为目标,8.1为文本框,8.2为send按钮
IntPtr htextbox81 = FindWindowEx(htextbox71, IntPtr.Zero, "Edit", null);//8.1
IntPtr htextbox82= FindWindowEx(htextbox71, IntPtr.Zero, "Button", null);//8.2
textBox1.Text = (""+htextbox81);
textBox2.Text = ("" + htextbox82);
SendMessage(htextbox81, WM_SETTEXT, IntPtr.Zero, "123232");//发送消息
SendMessage(htextbox82, WM_LBUTTONDOWN, IntPtr.Zero, null);//鼠标按下按钮
SendMessage(htextbox82, WM_LBUTTONUP, IntPtr.Zero, null);//释放鼠标
SendMessage(htextbox81, WM_INITDIALOG, IntPtr.Zero, null);
SendMessage(htextbox81, WM_SETTEXT, IntPtr.Zero, "\t");//发送消息
}
}
}
文章来源地址https://www.toymoban.com/news/detail-596477.html
到了这里,关于c#获取句柄,并对第三方软件的输入框和按钮进行控制的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!