C#基于winform创建触摸屏可用的模拟键盘

本文介绍了如何在触摸屏设备上创建一个开机自启动的模拟键盘,通过全局钩子监控鼠标事件来触发键盘显示,以及使用SendKeys类模拟键盘输入。详细展示了如何设置窗口行为、使用SendKeys发送按键和使用鼠标左键长按时唤醒键盘。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        创作背景:因项目需求,需要创建一个可以运行在触摸屏上的模拟键盘,开机自启动,启动后显示的是圆形悬浮框,当有输入需求时(改为鼠标左键长按300ms以上),显示键盘,或者双击圆形悬浮框显示键盘。我们知道想要完成此功能,大致需要如下知识储备:1、使用SendKeys类来模拟键盘输入。;2、使用全局钩子来监控某些触发事件(例如何时需要显示键盘等)。在开放过程中,也有一些小tips,在后文涉及到的地方会有详尽描述,文章末尾也有相关源码链接。

        相关功能:1、首先要在窗体代码前添加相关设置,使操作键盘时,窗体不获取焦点(一旦获取焦点,需要输入的进程就无法获取输入光标了)。以下代码放置在窗体的类中即可。

  /// <summary>
  /// 此段代码不可删除
  /// </summary>
  protected override CreateParams CreateParams
  {
      get
            {
                CreateParams cp = base.CreateParams;
                cp.ExStyle |= (int)0x08000000L;
                return cp;
            }
   }

        2、利用SendKeys类来模拟键盘输入。示例代码如下:

/// <summary>
        /// 普通按键
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_SendText_Click(object sender, EventArgs e)
        {
            Button button = (Button)sender;
            string strBtnTxt = button.Text;

            strBtnTxt = StringConvert.GetFinalStr(strBtnTxt);

            if (strBtnTxt.Length == 1)
            {
                if (char.IsLetter(strBtnTxt[0]))
                {
                    SendKeys.Send(strBtnTxt.ToLower());
                }
                else
                {
                    SendKeys.Send(strBtnTxt);
                }
            }
            else if (strBtnTxt.Length > 1)
            {
                SendKeys.Send("{" + strBtnTxt + "}");
            }
        }

        3、使用全局钩子监控鼠标状态。本意是想监控所有进程,看是否存在需要输入的编辑框获取到焦点,但是经过多次尝试之后并没有成功,这里也希望各位大佬指正并给出建议。那我退而求其次,利用鼠标左键长按300毫秒以上的状态来判断是否唤醒键盘。钩子的部分代码如下:

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace WFTouch
{
    public class MouseHook
    {
        private const int WM_MOUSEMOVE = 0x200;
        private const int WM_LBUTTONDOWN = 0x201;
        private const int WM_RBUTTONDOWN = 0x204;
        private const int WM_MBUTTONDOWN = 0x207;
        private const int WM_LBUTTONUP = 0x202;
        private const int WM_RBUTTONUP = 0x205;
        private const int WM_MBUTTONUP = 0x208;
        private const int WM_LBUTTONDBLCLK = 0x203;
        private const int WM_RBUTTONDBLCLK = 0x206;
        private const int WM_MBUTTONDBLCLK = 0x209;


        static int hMouseHook = 0;

        public const int WH_MOUSE_LL = 14;//low level mouse event
        public const int WH_MOUSE = 7;//normal level mouse event

        static HookProc MouseHookProcedure;
        [StructLayout(LayoutKind.Sequential)]
        public class POINT
        {
            public int x;
            public int y;
        }

        [StructLayout(LayoutKind.Sequential)]
        public class MouseHookStruct
        {
            public POINT pt;
            public int hWnd;
            public int wHitTestCode;
            public int dwExtraInfo;
        }

        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern int GetLastError();

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr GetModuleHandle(string lpModuleName);

        [DllImport("kernel32.dll")]
        private static extern int GetCurrentThreadId();//获取在系统中的线程ID

        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern bool UnhookWindowsHookEx(int idHook);

        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam);

        public delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam);

        public MouseHook()
        {
        }

        ~MouseHook()
        {
            Stop();
        }

        public static void Start()
        {
            if (hMouseHook == 0)
            {
                MouseHookProcedure = new HookProc(MouseHookProc);

                hMouseHook = SetWindowsHookEx(WH_MOUSE_LL, MouseHookProcedure, GetModuleHandle("user32"), 0);//第一个参数是WH_MOUSE_LL,表示捕获所有线程的鼠标消息,同时最后一个参数必须是0
                if (hMouseHook == 0)
                {
                    int errorCode = GetLastError();
                }
            }
        }

        public static void Stop()
        {
            bool retMouse = true;
            if (hMouseHook != 0)
            {
                retMouse = UnhookWindowsHookEx(hMouseHook);
                hMouseHook = 0;
            }
        }

        private static int MouseHookProc(int nCode, Int32 wParam, IntPtr lParam)
        {
            StaticClass.strMDC = wParam.ToString();
            //只处理鼠标左键按下的情况
            if (wParam == WM_LBUTTONDOWN && nCode >= 0)
            {
                MouseButtons button = MouseButtons.None;
                int clickCount = 0;

                switch (wParam)
                {
                    case WM_LBUTTONDOWN:
                        button = MouseButtons.Left;
                        clickCount = 1;
                        break;
                    case WM_LBUTTONUP:
                        button = MouseButtons.Left;
                        clickCount = 1;
                        break;
                    case WM_LBUTTONDBLCLK:
                        button = MouseButtons.Left;
                        clickCount = 2;
                        break;
                    case WM_RBUTTONDOWN:
                        button = MouseButtons.Right;
                        clickCount = 1;
                        break;
                    case WM_RBUTTONUP:
                        button = MouseButtons.Right;
                        clickCount = 1;
                        break;
                    case WM_RBUTTONDBLCLK:
                        button = MouseButtons.Right;
                        clickCount = 2;
                        break;
                }
                StaticClass.strMDC = clickCount.ToString();
                MouseHookStruct MyMouseHookStruct = (MouseHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseHookStruct));
                MouseEventArgs e = new MouseEventArgs(button, clickCount, MyMouseHookStruct.pt.x, MyMouseHookStruct.pt.y, 0);
            }
            return CallNextHookEx(hMouseHook, nCode, wParam, lParam);
        }
    }
}

        4、钩子的调用。在主窗口类中进行调用,这里我是使用的Timer控件,可根据需要使用线程也可以。在timer的执行方法中,调用ttttt()方法即可,方法名称是测试的时候随意定义的,确实有点随意,大家担待😀。其中还有其他功能的部分操作,涉及到窗口显示的大小等。

    Stopwatch stopwatchSign = new Stopwatch();
        public void ttttt()
        {
            if (stopwatchSign != null && stopwatchSign.ElapsedMilliseconds >= 300)
            {
                this.WindowState = FormWindowState.Normal;
                this.Width = StaticClass.width;
                this.Height = StaticClass.height;
                stopwatchSign.Reset();
            }
            if (StaticClass.strMDC.Equals("1") && !stopwatchSign.IsRunning && !this.Visible)
            {
                stopwatchSign.Restart();
            }
            if (!StaticClass.strMDC.Equals("1"))
            {
                stopwatchSign.Stop();
            }
        }

        5、圆形悬浮框。话不多说,关键代码如下:

    private void TopForm_Load1(object sender, EventArgs e)
        {
            string strPath = System.AppDomain.CurrentDomain.BaseDirectory + "keyboard.png";
            backgroundImage = new Bitmap(strPath);

            // 设置窗口区域为圆角矩形
            Region = new Region(new Rectangle(0, 0, Width, Height));
            GraphicsPath path = new GraphicsPath();
            path.AddEllipse(0, 0, Width, Height);
            Region = new Region(path);
        }

        protected override void OnResize(EventArgs e)
        {
            //base.OnResize(e);
            base.OnResize(e);
            // 调整背景图片以填满窗口
            this.Invalidate(); // 触发重绘
        }

        protected override void OnPaintBackground(PaintEventArgs e)
        {
            // 不执行任何操作,避免默认的矩形绘制
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);

            // 在窗口上绘制背景图片
            e.Graphics.DrawImage(backgroundImage, 0, 0, Width, Height);
        }

        以上是此项目的一些重要功能点,其余的均是相互调用和细节处理,相对简单。完整的代码在我的主页中可以找到,如果大家么有积分下载,又特别想要源码,可以在文章末尾留言。源码没有进行优化处理,一些测试代码尚有残留,仅供参考。

        代码链接:点击跳转至源码地址

模拟鼠标和键盘 注意:不支持Windows 8 / 8.1。 Interceptor是Windows键盘驱动程序的包装器(包装http://oblita.com/Interception)。 使用驱动程序,Interceptor可以模拟按键和鼠标点击... 使用DirectX的游戏,通常不接受使用SendInput()的击键 Windows的受保护区域,如Windows登录屏幕或UAC调暗屏幕 任何应用程序 因为驱动程序模拟击键和鼠标单击,所以目标窗口必须处于活动状态(即,在发送击键和鼠标点击时,不能在另一个窗口上执行多任务)。 如何使用 下载并构建此项目并在项目中引用其DLL。 下载'interception.dll',这是一个由驱动程序作者编写的独立库。将它放在与可执行文件相同的目录中。这是必需的。 从作者的网页下载并安装“install-interception.exe”。安装后重新启动计算机。 在您的代码中,要加载驱动程序,请调用(阅读下面的代码注释;您必须设置过滤模式以捕获按键事件或发送按键操作!): Input input = new Input(); // Be sure to set your keyboard filter to be able to capture key presses and simulate key presses // KeyboardFilterMode.All captures all events; 'Down' only captures presses for non-special keys; 'Up' only captures releases for non-special keys; 'E0' and 'E1' capture presses/releases for special keys input.KeyboardFilterMode = KeyboardFilterMode.All; // You can set a MouseFilterMode as well, but you don't need to set a MouseFilterMode to simulate mouse clicks // Finally, load the driver input.Load(); 做你的东西。 input.MoveMouseTo(5, 5); // Please note this doesn't use the driver to move the mouse; it uses System.Windows.Forms.Cursor.Position input.MoveMouseBy(25, 25); // Same as above ^ input.SendLeftClick(); input.KeyDelay = 1; // See below for explanation; not necessary in non-game apps input.SendKeys(Keys.Enter); // Presses the ENTER key down and then up (this constitutes a key press) // Or you can do the same thing above using these two lines of code input.SendKeys(Keys.Enter, KeyState.Down); Thread.Sleep(1); // For use in games, be sure to sleep the thread so the game can capture all events. A lagging game cannot process input quickly, and you so you may have to adjust this to as much as 40 millisecond delay. Outside of a game, a delay of even 0 milliseconds can work (instant key presses). input.SendKeys(Keys.Enter, KeyState.Up); input.SendText("hello, I am typing!"); /* All these following characters / numbers / symbols work */ input.SendText("abcdefghijklmnopqrstuvwxyz"); input.SendText("1234567890"); input.SendText("!@#$%^&*()"); input.SendText("[]\\;',./"); input.SendText("{}|:\"?"); // And finally input.Unload(); 笔记: BadImageFormatException如果您没有为解决方案中的所有项目(包括此项目)使用正确的体系结构(x86或x64),则可能会获得。因此,您可能必须下载此项目的源代码才能将其重建为正确的体系结构。这应该很简单,构建过程应该没有错误。 您必须从http://oblita.com/Interception下载'interception.dll' 。 如果你已经完成了以上所有操作(正确安装了拦截驱动程序,将interception.dll放在你的项目文件夹中),你仍然无法发送击键: 驱动程序有一个限制,即它不能在不接收至少一次击键的情况下发送击键。这是因为驱动程序不知道键盘是哪个设备ID,因此它必须等待接收击键以从击键中推断出设备ID。 总之,在发送击键之前,请始终按键盘一次。点按任意键。然后你可以发送击键。这不适用于接收击键,因为通过接收击键,您当然已经按下了一个键。 MoveMouseTo()和MoveMouseBy()完全忽略键盘驱动程序。它使用System.Windows.Forms.Position来设置和获取游标的位置(它为下面的各个函数调用标准的Win32 API)。 原因是,在探索键盘驱动程序的鼠标移动功能时,我注意到它没有按像素单位移动光标,而是似乎通过加速移动光标。当我想将光标移动到某个位置时,这会不断产生不一致的值。因为Win32游标设置API通常不被游戏等阻止,所以我发现只需调用这些标准API即可,而无需使用驱动程序。请注意,这仅适用于设置光标位置。拦截光标仍然可以正常工作。例如,您可以使用Interceptor反转鼠标的x和y轴。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值