美文网首页
Windows下模拟鼠标与键盘输入,及其在LabVIEW中的运用

Windows下模拟鼠标与键盘输入,及其在LabVIEW中的运用

作者: 飞多多 | 来源:发表于2019-09-28 21:06 被阅读0次

    前两天,我正在开开心心地撸着我的python,“领导”(没有其他合适的称谓了 )突然找到我说,他需要一个dll(动态链接库),功能是实现鼠标在指定的位置实现单击、双击、右击以及键盘输入,因为人家的软件是成品,没有可提供的接口使用。然后他就走了,剩下一脸懵逼的我。

    跑起来

    windows编程什么的我从来都没有接触过,确切的说,我连VS都没有用过。硬着头皮上,好在windows官方的支持文档十分充足,比如<a href="https://docs.microsoft.com/en-us/dotnet/framework/winforms/how-to-simulate-mouse-and-keyboard-events-in-code">这个</a>。最开始的时候,就算有完整的代码,我都不知道怎么让它跑起来,最后还求助了我<a href="http://hp.stuhome.net/">室友</a>,要不说我就服我的室友呢。果然很快就解决了“跑起来”的问题。在VS新建VC++桌面应用程序,然后代码粘贴,关键的是我们需要在“解决方案资源管理器”右键->属性->常规->公共语言运行时支持->添加公共语言运行时支持。然后我们就能在“引用”中的框架下顺利添加我们需要的依赖框架。然后程序就能顺利的跑起来了。如果要跑出效果,我们还要祭出spy++大杀器:在“工具”->spy++->搜索->查找窗口,我们就通过移动那个“瞄准器”来获得我们关心的窗口的“类名”和“名称”了,这方便我们在编程针对关心的窗口进行编程,比如针对FindWindow()这种函数,spy++灰常好用。

    跑向目的地

    我们已经通过官方的example知道怎么跑起来了,现在我们要让程序跑出我们需要的效果。但其实本小节和上小节没什么太大的关系。因为上一节的example是VC++的,这一节我们用的C#。

    怎么说呢?c#这东西很像c艹,又很像java,刚用起来的时候有点怪怪的感觉。windows中c#编程,我们需要先指明运用的命名空间,也就是通过指明框架中各种命名空间来达到引用的效果,有点类似于c++中的include效果。当需要使用链接库中函数的时候,我们需要先声明,但声明的形式应与链接库中函数的相同。具体如下

    [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
    private static extern int SetCursorPos(int x, int y);
    [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
    private static extern void mouse_event(int dwFlags, int dx, int dy, int dwData, int dwExtraInfo);
    [DllImport("User32.dll")]
    private static extern IntPtr FindWindowEx(IntPtr hwndParent,IntPtr hwndChildAfter,string lpszClass,string lpszWindows);
    

    这里的private是指定函数为私有成员。这里的关键就是SetCursorPos和mouse_event两个函数,一个是设置鼠标的位置,一个是产生鼠标事件。网上有很多光于两个函数的释义,这里就不过多说明了。然后,我这里给出整个有效的代码,其中包含了一些其他的窗口函数,将来对windows下窗口编程也许用得着,就mark一下吧:

    using System;
    using System.Threading;
    using System.Runtime.InteropServices;
    using System.Drawing;
    using System.Windows.Forms;
    
    namespace SimulateKeyPress
    {
        class Form1 : Form
        {
            const int MOUSEEVENTF_MOVE = 0x0001;
            const int MOUSEEVENTF_LEFTDOWN = 0x0002; 
            const int MOUSEEVENTF_LEFTUP = 0x0004; 
            const int MOUSEEVENTF_RIGHTDOWN = 0x0008;
            const int MOUSEEVENTF_RIGHTUP = 0x0010; 
            const int MOUSEEVENTF_MIDDLEDOWN = 0x0020;
            const int MOUSEEVENTF_MIDDLEUP = 0x0040; 
            const int MOUSEEVENTF_ABSOLUTE = 0x8000;
    
            private Button button1 = new Button();
            private Button button2 = new Button();
            private TextBox textbox1 = new TextBox();
            private TextBox textbox2 = new TextBox();
            private Cursor cursor = new Cursor(Cursor.Current.Handle);
            private Point ptold;
            private const int WM_SETTEXT = 0x000C;
            [STAThread]
            public static void Main()
            {
                Application.EnableVisualStyles();
                Application.Run(new Form1());
            }
    
            public Form1()
            {
                button1.Location = new Point(10, 10);
                button1.TabIndex = 0;
                button1.Text = "button1";
                button1.AutoSize = true;
                button1.MouseUp += new MouseEventHandler(button1_Click);
    
                button2.Location = new Point(10, 40);
                button2.TabIndex = 1;
                button2.Text = "button2";
                button2.AutoSize = true;
                button2.MouseUp += new MouseEventHandler(button2_Click);
    
                
                textbox1.Location = new Point(10, 70);
                textbox1.TabIndex = 3;  //set the tabindex greater than the textbox2
                textbox1.Text = "textbox1";
                textbox1.AutoSize = true;
    
                textbox2.Location = new Point(10, 100);
                textbox2.TabIndex = 2;
                textbox2.Text = "textbox2";
                textbox2.AutoSize = true;
                //textbox2.MouseUp += new MouseEventHandler(button2_Click);
    
                this.DoubleClick += new EventHandler(Form1_DoubleClick);
                this.Controls.Add(button1);
                this.Controls.Add(button2);
                this.Controls.Add(textbox1);
                this.Controls.Add(textbox2);
    
            }
    
            // Get a handle to an application window.
            [DllImport("USER32.DLL", CharSet = CharSet.Unicode)]
            public static extern IntPtr FindWindow(string lpClassName,string lpWindowName);
            [DllImport("USER32.DLL", CharSet = CharSet.Unicode)]
            public static extern IntPtr GetWindow(IntPtr hwndParent, int uCmd);
    
            //[DllImport("USER32.DLL")]
            //public static extern bool EnumChildWindows(IntPtr hWndParent, bool lpEnumFunc, string lParam);
    
            // Activate an application window.
            [DllImport("User32.dll")]
            private static extern IntPtr FindWindowEx(IntPtr hwndParent,IntPtr hwndChildAfter,string lpszClass,string lpszWindows);
            [DllImport("User32.dll")]
            public static extern bool SetForegroundWindow(IntPtr hWnd);
            [DllImport("USER32.DLL", CharSet = CharSet.Auto, SetLastError = false)]
            public static extern IntPtr SendMessage(HandleRef hWnd, uint Msg, IntPtr wParam, string lParam);
            [DllImport("USER32.DLL")]
            public static extern Int32 SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, string lParam);
    
            [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
            public static extern int SetCursorPos(int x, int y);
            [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
            static extern void mouse_event(int dwFlags, int dx, int dy, int dwData, int dwExtraInfo);
    
            [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
            public static extern bool GetCursorPos(Point lpPoint);
    
            // Send a series of key presses to the Calculator application.
            private void button1_Click(object sender, MouseEventArgs e)
            {
                if (e.Button == MouseButtons.Left)
                { 
                    MessageBox.Show("button1_Left");
                    SetCursorPos(100, 120);
                    mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
                    mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
                    Thread.Sleep(1000);
                    SendKeys.SendWait("123");
                }
                else if (e.Button == MouseButtons.Right)
                    MessageBox.Show("button1_Right");
    
                /*
                IntPtr myHwnd = FindWindow("WindowsForms10.Window.8.app.0.141b42a_r9_ad1", null);
    
                if (myHwnd != IntPtr.Zero)
                {
                    IntPtr myEditHwnd = FindWindowEx(myHwnd, IntPtr.Zero,null, "button1");
                    if (myEditHwnd != IntPtr.Zero)
                    {
                        //SendMessage(myEditHwnd, 0x0021, IntPtr.Zero, null);
                        //IntPtr myEditHwnd2 = GetWindow(myEditHwnd, 2);//the number reprent the Cmd GW_HWNDNEXT
                        //if(myEditHwnd2 != IntPtr.Zero)
                        //{
                            //SendMessage(myEditHwnd2, 0x0021, IntPtr.Zero, null);
                        //}
                    }
                    else MessageBox.Show("not find the button");
                }
               else MessageBox.Show("not find the window");
     
                // Get a handle to the Calculator application. The window class
                // and window name were obtained using the Spy++ tool.
                IntPtr windowHandl = FindWindow("CalcFrame", "Calculator");
                // Verify that Calculator is a running process.
                if (windowHandl == IntPtr.Zero)
                {
                    //MessageBox.Show("Calculator is not running.");
                    return;
                }
                // Make Calculator the foreground application and send it 
                // a set of calculations.
                SetForegroundWindow(windowHandl);
                SendKeys.SendWait("123");
                */
            }
    
            MouseEventArgs fake_event;
            private void button2_Click(object sender, MouseEventArgs e)
            {
                if (e.Button == MouseButtons.Left)
                    fake_event = new MouseEventArgs(MouseButtons.Left, 1, 0, 0, 0);
                else if (e.Button == MouseButtons.Right)
                    fake_event = new MouseEventArgs(MouseButtons.Right, 1, 0, 0, 0);
    
                //IntPtr myTestHandle = new IntPtr(0x004506F8);
                //HandleRef hrefHwndTarget = new HandleRef(null, myHwnd);
                //SendMessage(hrefHwndTarget, WM_SETTEXT, IntPtr.Zero, "mmp");
    
                IntPtr myHwnd = FindWindow("WindowsForms10.Window.8.app.0.141b42a_r9_ad1", null);
    
                if (myHwnd != IntPtr.Zero)
                {
                    IntPtr myEditHwnd = FindWindowEx(myHwnd, IntPtr.Zero, "WindowsForms10.EDIT.app.0.141b42a_r9_ad1", null);
                    if (myEditHwnd != IntPtr.Zero)
                    {
                        SendMessage(myEditHwnd, WM_SETTEXT, IntPtr.Zero, "mmp");
                        IntPtr myEditHwnd2 = GetWindow(myEditHwnd, 2);//the number reprent the Cmd GW_HWNDNEXT
                        if(myEditHwnd2 != IntPtr.Zero)
                        {
                            SendMessage(myEditHwnd2, WM_SETTEXT, IntPtr.Zero, "mmp");
                        }
                    }
                    else MessageBox.Show("not find the textbox");
                }
               else MessageBox.Show("not find the window");
               button1_Click(this, fake_event);
    
            }
            // Send a key to the button when the user double-clicks anywhere 
            // on the form.
            private void Form1_DoubleClick(object sender, EventArgs e)
            {
                // Send the enter key to the button, which raises the click 
                // event for the button. This works because the tab stop of 
                // the button is 0.
                SendKeys.Send("{ENTER}");
            }
        }
    }
    

    单击button1,将给出提示信息,其后,鼠标自动移至(100,120)的位置,然后双击。然后睡眠100ms,最后输入123。如果我们在这个位置是一个txt文件,那么这个demo就会打开txt,然后输入123。到这里,我们就基本完成了功能。

    LabVIEW调用

    虽然在窗口编程下,我们达到了效果。但是要实现最最最开始的业务需求,我们应该编译成dll,并在LabVIEW中调用成功才算数。VS->文件->新建->项目->.NET类库,输入下面的代码

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Threading;
    using System.Drawing;
    using System.Windows.Forms;
    using System.Runtime.InteropServices;
    
    namespace CTRL
    {
        public class Ms_ctrl
        {
            const int MOUSEEVENTF_MOVE = 0x0001;
            const int MOUSEEVENTF_LEFTDOWN = 0x0002;
            const int MOUSEEVENTF_LEFTUP = 0x0004;
            const int MOUSEEVENTF_RIGHTDOWN = 0x0008;
            const int MOUSEEVENTF_RIGHTUP = 0x0010;
            const int MOUSEEVENTF_MIDDLEDOWN = 0x0020;
            const int MOUSEEVENTF_MIDDLEUP = 0x0040;
            const int MOUSEEVENTF_ABSOLUTE = 0x8000;
    
            [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
            public static extern int SetCursorPos(int x, int y);
            [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
            static extern void mouse_event(int dwFlags, int dx, int dy, int dwData, int dwExtraInfo);
    
            public int Input_xyc(int x, int y, string c)
            {
                SetCursorPos(x, y);
                mouse_event(MOUSEEVENTF_LEFTUP | MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
                mouse_event(MOUSEEVENTF_LEFTUP | MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
                Thread.Sleep(1000);
                SendKeys.SendWait(c);
                SendKeys.SendWait("{ENTER}");
                return (x+y);
            }
        }
    }
    

    这里我们的return类型故意写成int类型,是为了检验效果,最好的应该是bool类型。然后生成->重新生成解决方案。我们就在相应的文件下生成了dll。

    然后我们打开LabVIEW->创建项目->VI->前面板->“添加两个数字输入控件,一个字符串输入控件,一个数字显示控件”->后面板->互连接口-> .NET->构造器节点->浏览到我们刚才生成的dll->确定->右键->创建->***类的方法->找到我们需要的函数(在这里是<code class="">Input_xyc(int x, int y, string c)</code>)->将两者的引用关系连线->数字输入、显示控件和字符串控件连线,然后我们输入合适的数字和字符串,点击运行,就能看到效果了。

    program_dialog.png

    当然还是在相应的坐标出双击,然后输入字符串。最后显示控件为(x+y)。
    <h1>吐槽</h1>
    windows的编程还真是博大精深,分为API和MSDN两大类,c++API是更底层的借口函数,更灵活,MSDN是他们的高级封装,使用更简便。但往往很多时候,我找到了一个好用的函数,却发现他是另一个框架下的,我的内心是是跟拒绝的。

    刚开始的,我开始浏览文档的时候,一会儿c++一会儿C#一会儿API一会儿MSDN,脑壳都给我整痛了。不过说到底,还是自己没有理顺理好。其实,这个解决方案我最早还用VC++的API写了一下,,发现并不是很方便。有些控件的handle是不容易获得的,在windows的消息机制下就不好处理了。

    最后的最后,以上的所有文字都是我看windows编程两三天得出的心得感受,好不夸张地说,其中存在非常非常多的错误,望各位读至此的朋友不吝指正,万望海涵。

    相关文章

      网友评论

          本文标题:Windows下模拟鼠标与键盘输入,及其在LabVIEW中的运用

          本文链接:https://www.haomeiwen.com/subject/zojructx.html