C# 输入法

虽说输入法不是什么新事物,各种语言版本都有,不过在C#不常见;这就会给人一种误会:C#不能做!其实C#能不能做呢,答案是肯定的——三种方式都行:IMM、TSF以及外挂式。IMM这种就是调windows的一些底层api,不过在新版本的windows中基本上已经不能用了,属于一种过时的操作方式。TSF是微软推荐的一种新方式,不过相对C#资料太少;线上主要的一些都是针对C++的版本资料,当然可以作为借鉴来实现C#版的。我这里主要介绍一种外挂式的(天啦撸,C#可以写外挂?),对于高手来说肯定不值一提,不过也算是实现了外挂及输入法!题外话——C#可以做外挂么?答案是可以的,C#针对windows的api编程资料还是很多的,下面就简单的介绍一下面可能要使用到的api:

安装了一个钩子,截取鼠标键盘等信号

public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);

停止使用钩子

public static extern bool UnhookWindowsHookEx(int idHook);

通过信息钩子继续下一个钩子

public static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam);

线程钩子需要用到

static extern int GetCurrentThreadId();

使用WINDOWS API函数代替获取当前实例的函数,防止钩子失效

public static extern IntPtr GetModuleHandle(string name);

转换指定的虚拟键码和键盘状态的相应字符或字符

public static extern int ToAscii(int uVirtKey, //[in] 指定虚拟关键代码进行翻译。
int uScanCode, // [in] 指定的硬件扫描码的关键须翻译成英文。高阶位的这个值设定的关键,如果是(不压)
byte[] lpbKeyState, // [in] 指针,以256字节数组,包含当前键盘的状态。每个元素(字节)的数组包含状态的一个关键。如果高阶位的字节是一套,关键是下跌(按下)。在低比特,如果设置表明,关键是对切换。在此功能,只有肘位的CAPS LOCK键是相关的。在切换状态的NUM个锁和滚动锁定键被忽略。
byte[] lpwTransKey, // [out] 指针的缓冲区收到翻译字符或字符。
int fuState);

1.有了以上的这些api基本上就可能实现鼠标键盘的监控或者锁定等;那么首先要安装钩子:

   // 安装键盘钩子
public void Start()
{ if (hKeyboardHook == )
{
KeyboardHookProcedure = new HookProc(KeyboardHookProc); hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookProcedure, GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName), ); //如果SetWindowsHookEx失败
if (hKeyboardHook == )
{
Stop();
throw new Exception("安装键盘钩子失败");
}
}
}

2.安装完后就要对获取到钩子进行处理:

private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)
{
// 侦听键盘事件
if (nCode >= && wParam == 0x0100)
{
KeyboardHookStruct MyKeyboardHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct)); #region 开关
if (MyKeyboardHookStruct.vkCode == || MyKeyboardHookStruct.vkCode == || MyKeyboardHookStruct.vkCode == )
{
isLocked = isLocked ? false : true;
}
#endregion #region
if (isLocked)
{
if (isStarted && MyKeyboardHookStruct.vkCode >= && MyKeyboardHookStruct.vkCode <= )
{
var c = int.Parse(((char)MyKeyboardHookStruct.vkCode).ToString());
OnSpaced(c);
isStarted = false;
return ;
}
if (isStarted && MyKeyboardHookStruct.vkCode == )
{
OnBacked();
return ;
}
if ((MyKeyboardHookStruct.vkCode >= && MyKeyboardHookStruct.vkCode <= ) || MyKeyboardHookStruct.vkCode == )
{
if (MyKeyboardHookStruct.vkCode >= && MyKeyboardHookStruct.vkCode <= )
{
Keys keyData = (Keys)MyKeyboardHookStruct.vkCode;
KeyEventArgs e = new KeyEventArgs(keyData);
KeyUpEvent(this, e);
isStarted = true;
}
if (MyKeyboardHookStruct.vkCode == )
{
OnSpaced();
isStarted = false;
}
return ;
}
else
return ;
}
#endregion
}
return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);
}

上面一些数字,对于刚入门的同学来说也不是什么问题,一看就明白是对哪些键做的操作。

3.停止钩子

 public void Stop()
{
bool retKeyboard = true; if (hKeyboardHook != )
{
retKeyboard = UnhookWindowsHookEx(hKeyboardHook);
hKeyboardHook = ;
} if (!(retKeyboard))
throw new Exception("卸载钩子失败!");
}

4.注册事件

 private void WordBoard_Load(object sender, EventArgs e)
{
Program.keyBordHook.KeyUpEvent += KeyBordHook_KeyUpEvent;
Program.keyBordHook.OnSpaced += KeyBordHook_OnSpaced;
Program.keyBordHook.OnBacked += KeyBordHook_OnBacked;
}

5.根据输入内容显示并进行转换

 private void ShowCharatar()
{
this.listView1.BeginInvoke(new Action(() =>
{
label1.Text = keys; try
{
this.listView1.Items.Clear();
var arr = CacheHelper.Get(keys);
if (arr != null)
for (int i = ; i < (arr.Length > ? : arr.Length); i++)
{
this.listView1.Items.Add((i + ) + "、" + arr[i]);
}
}
catch
{
label1.Text = keys = "";
}
}));
}

6.显示输入

 private void KeyBordHook_KeyUpEvent(object sender, KeyEventArgs e)
{
keys += e.KeyCode.ToString().ToLower();
this.ShowCharatar();
}

7.空格上屏

 private void KeyBordHook_OnSpaced(int choose)
{
try
{
if (CacheHelper.ContainsKey(keys))
{
if (choose > )
{
choose = choose - ;
} Program.keyBordHook.Send(CacheHelper.Get(keys)[choose]);
label1.Text = "";
this.listView1.Clear();
}
}
catch
{ }
keys = "";
}

8.将数据发送到激活的输入框中

 public void Send(string msg)
{
if (!string.IsNullOrEmpty(msg))
{
Stop();
SendKeys.Send("{RIGHT}" + msg);
Start();
}
}

9.back键回退

 private void KeyBordHook_OnBacked()
{
if (!string.IsNullOrEmpty(keys))
{
keys = keys.Substring(, keys.Length - );
}
this.ShowCharatar();
}

当然这里还可以使其他键来完善更多的功能,例如拼音的分页处理等

至于什么五笔、拼音就要使用词库来解决了;其中五笔比较简单,拼音就非常复杂了,各种分词、联想等...这里以五笔为主,拼音为单拼来实现基本的输入功能;所以不需要什么高深算法,简单使用MemoryCache就轻松高效搞定(有兴趣的可以来https://github.com/yswenli/Wenli.IEM 上完善)

10.键词转换

 /*****************************************************************************************************
* 本代码版权归@wenli所有,All Rights Reserved (C) 2015-2017
*****************************************************************************************************
* CLR版本:4.0.30319.42000
* 唯一标识:8ebc884b-ee5f-45de-8638-c054b832e0ce
* 机器名称:WENLI-PC
* 联系人邮箱:wenguoli_520@qq.com
*****************************************************************************************************
* 项目名称:$projectname$
* 命名空间:Wenli.IEM
* 类名称:CacheHelper
* 创建时间:2017/3/3 16:18:14
* 创建人:wenli
* 创建说明:
*****************************************************************************************************/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Caching;
using System.Text;
using System.Windows.Forms; namespace Wenli.IEM.Helper
{
public static class CacheHelper
{
static MemoryCache _wubiCache = new MemoryCache("wubi"); static MemoryCache _pinyinCache = new MemoryCache("pinyin"); static CacheHelper()
{
var path = Application.StartupPath + "\\Win32\\world.dll";
var arr = File.ReadAllLines(path);
foreach (string item in arr)
{
var key = item.Substring(, item.IndexOf(" "));
var value = item.Substring(item.IndexOf(" ") + );
_wubiCache.Add(key, (object)value, DateTimeOffset.MaxValue);
} // path = Application.StartupPath + "\\Win32\\pinyin.dll";
arr = File.ReadAllLines(path);
foreach (string item in arr)
{
var key = item.Substring(, item.IndexOf(" "));
var value = item.Substring(item.IndexOf(" ") + );
_pinyinCache.Add(key, (object)value, DateTimeOffset.MaxValue);
}
} public static string[] Get(string key)
{
if (!string.IsNullOrEmpty(key))
{
var str = string.Empty; try
{
if (_wubiCache.Contains(key))
str = _wubiCache[key].ToString();
}
catch { }
try
{
if (_pinyinCache.Contains(key))
str += " " + _pinyinCache[key].ToString();
}
catch { } if (!string.IsNullOrEmpty(str))
{
var arr = str.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries);
for (int i = ; i < arr.Length; i++)
{
if (arr[i].IndexOf("*") > -)
{
arr[i] = arr[i].Substring(, arr[i].IndexOf("*"));
}
}
return arr;
}
} return null;
} public static bool ContainsKey(string key)
{
if (_wubiCache.Contains(key))
return true;
if (_pinyinCache.Contains(key))
return true;
return false;
} public static void Clear()
{
_wubiCache.Dispose();
GC.Collect(-);
}
}
}

到此一个基本型的C#版外挂输入法就成功完成了,源码地址:https://github.com/yswenli/Wenli.IEM

转载请标明本文来源:http://www.cnblogs.com/yswenli/p/6528447.html
更多内容欢迎star作者的github:https://github.com/yswenli/Wenli.IEM 
如果发现本文有什么问题和任何建议,也随时欢迎交流~

C# 输入法的更多相关文章

  1. 隐马尔科夫模型python实现简单拼音输入法

    在网上看到一篇关于隐马尔科夫模型的介绍,觉得简直不能再神奇,又在网上找到大神的一篇关于如何用隐马尔可夫模型实现中文拼音输入的博客,无奈大神没给可以运行的代码,只能纯手动网上找到了结巴分词的词库,根据此 ...

  2. 在 Linux 中使用搜狗拼音输入法以及搞定 Flash 和支付宝

    在 Ubuntu 中安装搜狗输入法 在 Ubuntu Kylin 系统中,默认安装搜狗拼音输入法,但是在原生 Ubuntu 系统中则不是.这可以理解,毕竟搜狗输入法的 Linux 版有 Kylin 团 ...

  3. Atitit 输入法原理与概论ati use

    Atitit 输入法原理与概论ati use 1.1. 输入法技术点1 1.2. 参考多多输入法设置2 1.3. Attilax博客集合知识点2 1.4. 输入法的书籍当当几乎没有..都是打字的.2 ...

  4. Win10 字体模糊解决(DPI缩放禁用),设置默认输入法英文

    电脑坏了 , 换了新电脑, 但是新电脑,死活不能装win7, 装都不能装!!!郁闷了 好多地方字体模糊了,百般设置都不好看, 后来远程桌面到win2008server, 发现,在远程桌面里面居然很清晰 ...

  5. 3.Kali 1.0 / 2.0 安装中文输入法(谷歌pinyin + 其他)

    1.kali默认是没有中午输入法的,需要自己安装一下 2.首先我们先获取root权限 dnt@HackerKali:~$ su密码: 3.安装中文输入法(apt-get 指令不会的同学可以学习一下基础 ...

  6. Ubuntu 14.04 LTS中怎样安装fcitx中文输入法

    轉載: http://jingyan.baidu.com/article/4b07be3c60da3f48b380f3f0.html 一,安装fcitx,这么好的软件,ubuntu软件中心肯定是找得到 ...

  7. CentOS 6.7 如何启用中文输入法

    安装CentOS系统后,如何启用中文输入法呢?这个问题看起来简单,但对于Linux初学者来说,也可能不是一件容易的事.本文笔者和大家分享一下"CentOS 6.6 如何启用中文输入法&quo ...

  8. Ubuntu 下ibus拼音输入法启用 (ubuntu 16.04

    Ubuntu 下ibus拼音输入法启用 我安装的是英文版的ubuntu 16.04,打开只带英文,并没有中文. 设置输入法为iBus 从system settings 进入language suppo ...

  9. 移动前端手机输入法自带emoji表情字符处理

    今天,测试给我提了一个BUG,说移动端输入emoji表情无法提交.很早以前就有思考过,手机输入法里自带的emoji表情,应该是某些特殊字符.既然是字符,那应该都能提交才对,可是为啥会被卡住呢?搜了一下 ...

  10. Ubuntu在wps-office等qt5程序下不能切换中文fcitx输入法的问题

    经检查,是缺了fcitx-qt的包.比如qt5的程序,需要一个叫fcitx-libs-qt5的包. 如果您在基于qt的程序下不能使用基于fcitx的中文输入法,请检查以下包是否已安装: sudo ap ...

随机推荐

  1. js原生之函数

    1.函数作为参数传给其他函数:    data.sort(function(a,b){return a-b})    //关于数组的sort函数,其回调函数返回负值,a在b之前    //正值,b在a ...

  2. UGUI batch 规则和性能优化

    UGUI batch 规则和性能优化 (基础) Unity 绘图性能优化 - Draw Call Batching : http://docs.unity3d.com/Manual/DrawCallB ...

  3. UED大全

    http://www.baiduux.com/  百度UFOhttp://ued.sohu.com/  搜狐UEDhttp://ued.taobao.com/  淘宝UEDhttp://www.ued ...

  4. C++中定义使用受限的类

    1.只能在堆上使用的类 栈上对象通过自动调用析构函数释放空间,将该类的析构函数定义为private就可以阻止其被自动调用,从而阻止在栈上使用该类的对象,为了避免资源泄漏问题,在堆上使用该类的对象时我们 ...

  5. Redis缓存数据之简单逻辑

    并发不高的情况: 读: 读redis->没有,读DataBase->把DB数据写回redis,有的话直接从redis中取: 写: 写DataBase->成功,再写redis: 并发高 ...

  6. The certificate used to sign "" has either expired or has been revoked.

    这句话的意思就是"签名"的证书已过期或已被吊销. 说白了就是有人删除了你的证书,解决的办法就是在创建一个,创建证书有两种办法,. 第一: 在苹果开发者中心,进入自己的账号,请求一个 ...

  7. 只为粗暴看一下ES6的字符串模板的性能

    网上查找"ES6 字符串模板 +性能"5分钟无果遂写了一个暴力测试. 测试对象: +=方式,字符串累加计算方式 +s1+s2...+sn方式,即传统连加拼接字符串方式 s.push ...

  8. iOS 获取公历、农历日期的年月日

    iOS 获取公历.农历日期的年月日 介绍三种方法获取 Date (NSDate) 的年月日. 用 date 表示当前日期.测试日期为公历 2017 年 2 月 5 日,农历丁酉年,鸡年,正月初九. l ...

  9. [html5] 学习笔记-表单新增元素与属性

    本节讲的是表单元素的form,formaction属性,frommethod,formenctype属性,formtarget,autofocus属性,required,labels属性. 1.for ...

  10. GIS制图人员的自我修养(1)--制图误区

    GIS制图人员的自我修养 by 李远祥 最近一直坚持写GIS制图的技术专题,并不是为了要介绍有什么好的技术和方法去制图,而是要告诉所有从事这一方向的人员一个铁铮铮的实现--要做好GIS制图,必须加强自 ...