`
ljl_xyf
  • 浏览: 619792 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

自己制作软键盘的几个关键技术解析

    博客分类:
  • c#
阅读更多

在嵌入式应用和一些安全软件中经常需要不通过物理键盘输入,虽然微软提供了也一个软键盘,但这个软件盘不能定制界面不能自动感应当前光标是否处于输入状态,所以有时候我们还是需要自己来实现这个软键盘。本文将讲解自己实现软键盘时涉及到的几个关键技术。

一、浮动窗体的实现

软键盘的窗体和普通窗体不一样,这个窗体在成为当前窗体时,不会影响其它进程的窗体的光标焦点。也就是说虽然这个窗体现在为当前激活的前台窗体,但光标仍然停在其他进程的窗体上。


如上图所示,虽然软键盘在记事本的前面,但光标仍然在记事本上。

要实现这个技术,我们必须要把当前窗体设置为浮动工具条才行。这里我给出 C# Winform 的实现方法:

        private
 const
 int
 WS_EX_TOOLWINDOW = 0x00000080;
        private
 const
 int
 WS_EX_NOACTIVATE = 0x08000000;

        protected
 override
 CreateParams CreateParams
        {
            get
            {
                CreateParams cp = base
.CreateParams;
                cp.ExStyle |= (WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW);
                cp.Parent = IntPtr.Zero; // Keep this line only if you used UserControl

                return
 cp;

                //return base.CreateParams;

            }
        } 

.csharpcode pre { margin: 0em; }.csharpcode .rem { color: rgb(0, 128, 0); }.csharpcode .kwrd { color: rgb(0, 0, 255); }.csharpcode .str { color: rgb(0, 96, 128); }.csharpcode .op { color: rgb(0, 0, 192); }.csharpcode .preproc { color: rgb(204, 102, 51); }.csharpcode .asp { background-color: rgb(255, 255, 0); }.csharpcode .html { color: rgb(128, 0, 0); }.csharpcode .attr { color: rgb(255, 0, 0); }.csharpcode .alt { background-color: rgb(244, 244, 244); width: 100%; margin: 0em; }.csharpcode .lnum { color: rgb(96, 96, 96); }

如上代码就是将 Winform 指定为浮动工具条窗体。只要在winform 的类中重载 CreateParams 函数,并按上述代码编写就可以了。

二、如何检测当前处于输入状态

在一些嵌入式设备中,我们没有物理键盘,所有的输入都是通过触摸屏和软键盘输入。那么这个时候,我们必须要做到只有处于输入状态时才弹出软键盘,否则如果软键盘一直在界面上,既不美观也妨碍其他程序的正常使用。

要实现这个功能,我们能想到的最直接的方法是 windows 是否会在当前处于输入状态下时发一个什么事件,或者通过什么钩子程序来实现。但我研究了很久,没有找到这种方法。如果哪位知道这种方法,不妨在回复中告诉我。

我目前找到的方法是定时询问 windows 的当前窗体是否处于输入状态(http://www.my400800.cn )。

            IntPtr hWnd = GetForegroundWindow();

            uint
 processId;
            uint
 threadid = GetWindowThreadProcessId(hWnd, out
 processId);

            GUITHREADINFO lpgui = new
 GUITHREADINFO();
            lpgui.cbSize = Marshal.SizeOf(lpgui);

            if
 (GetGUIThreadInfo(threadid, ref
 lpgui))
            {
                if
 (lpgui.hwndCaret != 0)
                {
                    return
 hWnd;
                }
            }
.csharpcode pre { margin: 0em; }.csharpcode .rem { color: rgb(0, 128, 0); }.csharpcode .kwrd { color: rgb(0, 0, 255); }.csharpcode .str { color: rgb(0, 96, 128); }.csharpcode .op { color: rgb(0, 0, 192); }.csharpcode .preproc { color: rgb(204, 102, 51); }.csharpcode .asp { background-color: rgb(255, 255, 0); }.csharpcode .html { color: rgb(128, 0, 0); }.csharpcode .attr { color: rgb(255, 0, 0); }.csharpcode .alt { background-color: rgb(244, 244, 244); width: 100%; margin: 0em; }.csharpcode .lnum { color: rgb(96, 96, 96); }

如上面代码所示

首先我们通过  GetForegroundWindows API 得到当前窗体的句柄。然后我们再通过 GetGUIThreadInfo 得到当前窗体的一些属性。这些属性在 GUITHREADINFO 中定义

        public
 struct
 GUITHREADINFO
        {
            public
 int
 cbSize;
            public
 int
 flags;
            public
 int
 hwndActive;
            public
 int
 hwndFocus;
            public
 int
 hwndCapture;
            public
 int
 hwndMenuOwner;
            public
 int
 hwndMoveSize;
            public
 int
 hwndCaret;
            public
 System.Drawing.Rectangle rcCaret;
        }

.csharpcode pre { margin: 0em; }.csharpcode .rem { color: rgb(0, 128, 0); }.csharpcode .kwrd { color: rgb(0, 0, 255); }.csharpcode .str { color: rgb(0, 96, 128); }.csharpcode .op { color: rgb(0, 0, 192); }.csharpcode .preproc { color: rgb(204, 102, 51); }.csharpcode .asp { background-color: rgb(255, 255, 0); }.csharpcode .html { color: rgb(128, 0, 0); }.csharpcode .attr { color: rgb(255, 0, 0); }.csharpcode .alt { background-color: rgb(244, 244, 244); width: 100%; margin: 0em; }.csharpcode .lnum { color: rgb(96, 96, 96); } 上面是  GUITHREADINFO  结构。我们可以通过这个信息得到当前窗体中当前焦点的子窗口句柄,当前获得光标的子窗口句柄,当前正激活的子窗体句柄等等。这里我们只要用到当前获得光标 的子窗口句柄,就是 hwndCaret 。如果hwndCaret 不为0,则表示当前窗体处于可输入状态。

相关API函数的 C# 定义如下:

        [DllImport("user32.dll"
)]
        [return
: MarshalAs(UnmanagedType.Bool)]
        public
 static
 extern
 bool
 GetGUIThreadInfo(uint
 idThread, ref
 GUITHREADINFO lpgui);
        [DllImport("user32.dll"
)]
        public
 static
 extern
 IntPtr GetForegroundWindow();
        [DllImport("user32.dll"
, SetLastError = true
)]
        static
 extern
 uint
 GetWindowThreadProcessId(IntPtr hWnd, out
 uint
 lpdwProcessId);
.csharpcode pre { margin: 0em; }.csharpcode .rem { color: rgb(0, 128, 0); }.csharpcode .kwrd { color: rgb(0, 0, 255); }.csharpcode .str { color: rgb(0, 96, 128); }.csharpcode .op { color: rgb(0, 0, 192); }.csharpcode .preproc { color: rgb(204, 102, 51); }.csharpcode .asp { background-color: rgb(255, 255, 0); }.csharpcode .html { color: rgb(128, 0, 0); }.csharpcode .attr { color: rgb(255, 0, 0); }.csharpcode .alt { background-color: rgb(244, 244, 244); width: 100%; margin: 0em; }.csharpcode .lnum { color: rgb(96, 96, 96); }

三、模拟键盘输入

模拟键盘输入比较简单,.Net 提供了一个静态函数来模拟键盘输入

System.Windows.Forms.SendKeys.Send

这个函数很简单,而且微软的帮助也很全面了,我这里就不多说了。

另外我们还可以用更加底层的 API 函数来模拟键盘的输入

        [DllImport("user32.dll"
)]
        static
 extern
 void
 keybd_event(byte
 bVk, byte
 bScan, uint
 dwFlags,
           int
 dwExtraInfo);
.csharpcode pre { margin: 0em; }.csharpcode .rem { color: rgb(0, 128, 0); }.csharpcode .kwrd { color: rgb(0, 0, 255); }.csharpcode .str { color: rgb(0, 96, 128); }.csharpcode .op { color: rgb(0, 0, 192); }.csharpcode .preproc { color: rgb(204, 102, 51); }.csharpcode .asp { background-color: rgb(255, 255, 0); }.csharpcode .html { color: rgb(128, 0, 0); }.csharpcode .attr { color: rgb(255, 0, 0); }.csharpcode .alt { background-color: rgb(244, 244, 244); width: 100%; margin: 0em; }.csharpcode .lnum { color: rgb(96, 96, 96); }

这个函数是 keybd_event,关于这个函数的使用,微软的帮助也写的很清楚,这里也不重述了。



分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics