在使用输入法的时候,为了输入一些特殊字符我们往往会用到软键盘和符号大全。符号大全和软件盘是一类程序,主要实现的就是通过界面点击向目标程序输出对应的符号。
这里介绍一下如何通过QT编写软键盘类程序,实现软键盘主要有两个关键问题要解决。
1.创建一个没有焦点的界面程序
2.向目标程序发送对应的输入字符,实现符号上屏

之所以要创建没有焦点的界面程序,是为了防止软键盘在点击的过程中抢占目标程序的焦点。QT要实现无焦点程序,需要对窗口的属性进行设置,设置成无焦点属性,设置方法如下。

LONG exs = GetWindowLong((HWND)this->winId(),GWL_EXSTYLE);
 exs |= WS_EX_NOACTIVATE;
 exs |= WS_EX_APPWINDOW;
 exs |= WS_EX_TOPMOST;
 SetWindowLong((HWND)this->winId(),GWL_EXSTYLE,exs);

这里主要对窗口设置了三个属性,WS_EX_NOACTIVATE会让窗口不会被激活,因此在点击窗口的时候窗口不会抢占目标程序的焦点,WS_EX_APPWINDOW属性是为了使程序窗口是普通进程窗口,这样在最小化的时候才能在任务栏上显示对应的图标,WS_EX_TOPMOST是为了让窗口始终保持在最上层。

通过上面的属性设置,产生的界面在点击的过程中就不会抢占目标程序的焦点。

windows向目标程序发送文本的方法有很多种,有keybd_event和sendinput,
keybd_event函数会产生一次按键事件。系统通过这个事件来产生WM_KEYUP或WM_KEYDOWN消息,键盘驱动程序的中断处理程序调用keybd_event函数。在Windows NT中该函数己被使用SendInput替代。通过SendInput函数我们可以向当前的焦点程序发送对应的按键事件,从而实现符号的输入。实现方法如下:

void InputService::SendKeys(QString msg)
{
    wstring data = msg.toStdWString();
    SendUnicode(data.at(0));
}
void SendUnicode(const wchar_t data)
{
    INPUT input[4];
    HWND current_hwnd = GetForegroundWindow();
    SetFocus(current_hwnd);

    input[0].type = INPUT_KEYBOARD;
    input[0].ki.wVk = 0;
    input[0].ki.wScan = data;
    input[0].ki.dwFlags = KEYEVENTF_UNICODE;
    input[0].ki.time = 0;
    input[0].ki.dwExtraInfo = GetMessageExtraInfo();
    SendInput(1, &input[0], sizeof(INPUT));

    input[1].type = INPUT_KEYBOARD;
    input[1].ki.wVk = 0;
    input[1].ki.wScan = data;
    input[1].ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP;
    input[1].ki.time = 0;
    input[1].ki.dwExtraInfo = GetMessageExtraInfo();
    SendInput(1, &input[1], sizeof(INPUT));
 }


解决了这两个关键问题之后,后面的问题就是单纯的业务实现问题了。通过上面的方法可以实现普通字符和Unicode字符的输入,但是还是会存在一些问题。比如,如果当前的输入法是中文输入法,SendInput发送a-z的字符的时候会调用对应的输入法而阻碍符号的输入。还有SendInput需要目标程序实现了对Unicode字符输入的支持,否则发送的符号会失败。
针对上面的问题,现有的最佳的解决方案是通过剪切板辅助通信,首先将符号拷贝到剪切板,然后向目标程序发送Ctrl+V指令,通过剪切板实现伪通信。效果是一样的。实现方法如下:

void InputService::SendKeys(QString msg)
{
    wstring data = msg.toStdWString();

    QClipboard* clipborad = QApplication::clipboard();
    clipborad->setText(msg);
    SendCopyMessage();
}
void InputService::SendCopyMessage()
{
    // Create a generic keyboard event structure
    INPUT ip;
    ip.type = INPUT_KEYBOARD;
    ip.ki.wScan = 0;
    ip.ki.time = 0;
    ip.ki.dwExtraInfo = 0;
    // Press the "Ctrl" key
    ip.ki.wVk = VK_CONTROL;
    ip.ki.dwFlags = 0; // 0 for key press
    SendInput(1, &ip, sizeof(INPUT));

    // Press the "V" key
    ip.ki.wVk = 'V';
    ip.ki.dwFlags = 0; // 0 for key press
    SendInput(1, &ip, sizeof(INPUT));

    // Release the "V" key
    ip.ki.wVk = 'V';
    ip.ki.dwFlags = KEYEVENTF_KEYUP;
    SendInput(1, &ip, sizeof(INPUT));

    // Release the "Ctrl" key
    ip.ki.wVk = VK_CONTROL;
    ip.ki.dwFlags = KEYEVENTF_KEYUP;
    SendInput(1, &ip, sizeof(INPUT));
}

解决了这两个关键问题之后,后面的问题就是单纯的业务实现问题了。

相关推荐

基于文本服务框架(tSF)的拼音输入法研究与实现

摘要:目前的输入法大多采用输入法管理器-输入法编辑器(IMM-IME)进行开发,对于微软发布的新型输入法技术―文本服务框架(TSF)的研究一直比较滞后,该文论述了 TSF 的基本构成、主要接口、输入法

逗比的输入法实现(二):基础概念和常用接口

为什么叫 Meow?因为这是给猫用的输入法。。。目录(一):基本情况(二):基础概念和常用接口(三):整体构架(四):编辑和候选(五):界面管理和无界面模式(六):词库和候选算法(七):皮肤的实现(八

输入法开发之:疑难杂症

套娃开始:当键盘事件被触发时,获得的参数是ITfContext。但是ITfContext并不能直接修改内容添加文字等,需要调用RequestEditSession,传入一个ITfEditSession

输入法tSF框架候选栏定位研究

在开发输入法应用的时候,我们需要让候选栏时刻跟踪输入光标的位置,来进行输入。但候选栏定位不准,一直会困扰输入法的开发者。windows老的输入法框架imm在部分场景下比如chrome应用中会定位不准,

获取输入法坐标

以下是微软TSF输入法取坐标的方法,从里面扒出来的int 取坐标(ITfContext *pContext, TfEditCookie ec, ITfComposition* pRangeCompos

软输入法tSF SampleIME 源码分析(转载)

类:CCandidateWindow,候选字窗口CCompositionPricessorEngine,拼写引擎CSampleIME,主程序CStringRange, 一个特殊的字符串类 见于 Sam

输入法的注册、安装和卸载

注册输入法输入法的安装和普通应用程序有一个大的区别是,除了复制文件到安装目录、做一些必要的设置外,还需要向Windows系统注册这个输入法。我们前期一篇博文 TSF(Text Service Fram

Windows IMM-IME汉字输入法

1.IMM与IMEIMM是Input Method Manage(输入法管理器)的缩写,IME是Input Method Editor(输入法编辑器)的缩写。微软公司在Windows 95/98/NT

tSF(text Services Framework)和输入法

1.TSF输入接口IMM-IME架构成熟,稳定,易于实现,在Windows中被广泛使用,甚至在Linux曾大量使用的中文输入接口SCIM中也可以看到IMM-IME的影子。但是由于IMM-IME在操作权

[输入法]tSF框架中预先上屏的字符管理和控制

在TSF框架中有时候我们需要通过框架上屏一些占位字符来进行定位,这时候我们就需要对预先上屏的字符进行管理,以微软的输入法为例图中红框框选出来的部分就是预先上屏的字符。在TSF框架中通过ITfRange