c#获取句柄,并对第三方软件的输入框和按钮进行控制

文章介绍了如何通过编程来获取并控制软件的句柄,特别是使用FindWindow和FindWindowEx函数查找窗口及子窗口的句柄,以及如何利用SendMessage函数模拟用户操作,如输入文本和点击按钮。文章以Pronterface软件为例,展示了查找特定控件句柄的步骤,并解释了不同软件对句柄的处理方式,尤其是对于DirectUI程序的处理。
摘要由CSDN通过智能技术生成

刚学了没多久,还有很多地方没有理解到位,还请大家指正。

当程序运行起来,就会显示在任务管理器中,软件中的每个事件,比如按钮,文本框等一些控件,每个事件都会产生一个句柄,句柄就是这些事件的标识符。如果拿到句柄就可以自己写软件对第三方软件进行控制。同一个软件在不同的电脑中的句柄一般是不一样的,但是获取的过程都是一样的。只要在当前电脑上找到事件的标识符即可。

具体的窗口句柄的结构可以用微软的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程序,添加了一个按钮,和两个文本框,文本框用来测试获取的句柄是否正确。然后附上完整代码:

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");//发送消息

        }




    }
}

### 回答1: 在UE4中启动第三方软件并将其嵌入窗口的过程可以通过蓝图和C++代码实现。在蓝图中,可以使用“Execute Console Command”节点来执行一些命令行命令。事先使用命令行启动第三方软件,并将其的窗口句柄获取到,然后使用“Set Parent Window”节点将其嵌入到UE4窗口中。这种方法比较简单,但是需要一些命令行知识和手动启动第三方软件。 在C++代码中,可以使用Windows API的相关函数来启动第三方软件并将其嵌入到UE4窗口中。首先要获取UE4窗口的句柄,然后启动第三方软件获取其窗口句柄,最后使用API函数“SetParent”将第三方软件的窗口嵌入到UE4窗口中。需要注意的是,在C++代码中使用API函数,需要一些Windows编程基础和对UE4引擎的深度理解。 无论是使用蓝图还是C++代码,在嵌入第三方软件的过程中,需要注意一些细节问题,例如窗口大小,透明度等等。同时,因为每个第三方软件的窗口操作不同,所以需要根据具体的软件特点来进行调整。嵌入第三方软件可以极大的扩展UE4的功能,让开发者可以更加灵活的进行开发。 ### 回答2: UE4是一款非常出色的游戏引擎,尤其是在游戏开发领域中广受欢迎。但随着游戏的发展和增强,开始需要调用第三方软件或为其他目的嵌入第三方软件。本文将探讨如何在UE4中启动第三方软件并嵌入窗口。 启动第三方软件 UE4引擎提供了开发者使用的许多不同功能,其中之一是System Library。这个库提供了许多关于系统功能的函数,可以通过调用这些函数来实现系统相关的任务。在我们的情况下,我们将使用System Library中的Launcher功能来启动第三方软件。 我们可以使用FPlatformProcess::CreateProc函数来启动第三方软件。这个函数需要三个参数:软件的完整路径、命令行参数和是否显示软件的标志。例如下面的代码: ```cpp FPlatformProcess::CreateProc(TEXT("C:/Program Files/ExampleApp/ExampleApp.exe"), TEXT(" -option1"), true, false, false, nullptr, 0, nullptr, nullptr); ``` 嵌入第三方软件 一旦我们启动了第三方软件,我们可能需要将其嵌入到我们的UE4应用程序中。具体地说,我们可能需要在我们的UE4窗口中嵌入一个Web浏览器或视频播放器等。 要将第三方软件嵌入到UE4窗口中,我们可以使用WinAPI中提供的CreateWindowEx函数。这个函数允许我们创建一个新的窗口或子窗口,并将其嵌入到我们的UE4窗口中。 下面是实现此操作的典型代码片段: ```cpp HWND ParentWindow = (HWND)SomeUE4WindowHandle; HWND ChildWindow = CreateWindowEx(WS_EX_CLIENTEDGE, _T("SomeClassName"), _T("SomeName"), WS_CHILD | WS_VISIBLE, 0, 0, 640, 480, ParentWindow, NULL, NULL, NULL); ``` 熟悉WinAPI编程的人可能很熟悉这个代码。简单地说,它使用CreateWindowEx函数创建了一个新的窗口,并将其作为子窗口嵌入到父窗口中(在本例中为SomeUE4WindowHandle)。 在这个例子中,我们创建了一个名为“SomeName”的子窗口,并将其嵌入到我们的UE4窗口中。此外,注意我们使用了WS_CHILD和WS_VISIBLE标志,这两个标志告诉Windows将窗口作为子窗口创建,并将其设置为可见。 总结 在UE4中启动第三方软件并嵌入窗口可能有点棘手,但使用System Library和WinAPI应该很容易实现。如果你需要在你的UE4应用程序中使用其他软件,这些技巧可能会帮助你实现你的目标。 ### 回答3: UE4是一款强大的游戏引擎,开发者可以通过其提供的各种工具和功能来制作出高品质的游戏。在UE4中,我们可以通过使用蓝图来实现启动第三方软件并嵌入窗口的功能,下面是步骤: 第一步:设置启动参数 在UE4中,我们可以使用命令行来启动第三方软件。因此,我们需要设置好要启动软件的路径和命令行参数。这可以使用节点“Run Program”来完成。在节点的“Executable path”输入框中填入要启动的软件路径,在“Command line arguments”输入框中填入要传递给软件的命令行参数。例如,要启动一个记事本软件,我们可以这样设置: 第二步:创建嵌入窗口 在UE4中,我们可以使用“UMG Widget Component”组件来创建嵌入式窗口。在创建嵌入式窗口之前,我们需要先在项目中打开UMG编辑器,然后创建一个新的UMG小部件来显示Windows窗口。在UMG编辑器中,我们可以自由地添加各种控件、元素等,以达到我们想要的效果。完成后,将其保存为UMG小部件。 接下来,在UE4中创建一个新的Actor,并将其浏览器组件中的Root组件替换为“UMG Widget Component”组件。然后,将我们刚才创建的UMG小部件拖动到“UMG Widget Component”组件的“Widget Class”属性中。这将使我们的UMG小部件显示在Actor的浏览器中,从而创建了嵌入式窗口。 第三步:嵌入窗口和第三方软件 要将第三方软件嵌入到我们创建的UMG小部件中,我们需要使用Windows API。在UE4中,我们可以使用第三方插件“WindowsMixedReality”来直接使用Windows API。因此,我们需要先下载并安装此插件。 在UE4中,我们可以使用“Windows Mix Reality”插件的“AttachNativeWindowToWidget”函数来将第三方软件附加到我们创建的UMG小部件中。在此函数中,我们需要传入所需的参数,例如我们所启动的软件句柄和我们的UMG小部件的句柄,以及所需的位置和大小等。这将使第三方软件显示在我们的UMG小部件中,从而实现了将第三方软件嵌入到我们的游戏引擎中。 总之,通过上述步骤,我们可以成功地在UE4中启动第三方软件并将其嵌入到我们的游戏引擎中。这将使我们的游戏引擎拥有更丰富的功能和更高的可玩性。
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值