好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

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 == 0)

    {

     keyboardhookprocedure = new hookproc(keyboardhookproc);

     hkeyboardhook = setwindowshookex(wh_keyboard_ll, keyboardhookprocedure, getmodulehandle(process.getcurrentprocess().mainmodule.modulename), 0);

     //如果setwindowshookex失败

     if (hkeyboardhook == 0)

     {

      stop();

      throw new exception( "安装键盘钩子失败" );

     }

    }

   }

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

?

private int keyboardhookproc( int ncode, int32 wparam, intptr lparam)

   {

    // 侦听键盘事件

    if (ncode >= 0 && wparam == 0x0100)

    {

     keyboardhookstruct mykeyboardhookstruct = (keyboardhookstruct)marshal.ptrtostructure(lparam, typeof (keyboardhookstruct));

     #region 开关

     if (mykeyboardhookstruct.vkcode == 20 || mykeyboardhookstruct.vkcode == 160 || mykeyboardhookstruct.vkcode == 161)

     {

      islocked = islocked ? false : true ;

     }

     #endregion

     #region

     if (islocked)

     {

      if (isstarted && mykeyboardhookstruct.vkcode >= 48 && mykeyboardhookstruct.vkcode <= 57)

      {

       var c = int .parse((( char )mykeyboardhookstruct.vkcode).tostring());

       onspaced(c);

       isstarted = false ;

       return 1;

      }

      if (isstarted && mykeyboardhookstruct.vkcode == 8)

      {

       onbacked();

       return 1;

      }

      if ((mykeyboardhookstruct.vkcode >= 65 && mykeyboardhookstruct.vkcode <= 90) || mykeyboardhookstruct.vkcode == 32)

      {

       if (mykeyboardhookstruct.vkcode >= 65 && mykeyboardhookstruct.vkcode <= 90)

       {

        keys keydata = (keys)mykeyboardhookstruct.vkcode;

        keyeventargs e = new keyeventargs(keydata);

        keyupevent( this , e);

        isstarted = true ;

       }

       if (mykeyboardhookstruct.vkcode == 32)

       {

        onspaced(0);

        isstarted = false ;

       }

       return 1;

      }

      else

       return 0;

     }

     #endregion

    }

    return callnexthookex(hkeyboardhook, ncode, wparam, lparam);

   }

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

3.停止钩子

?

public void stop()

   {

    bool retkeyboard = true ;

    if (hkeyboardhook != 0)

    {

     retkeyboard = unhookwindowshookex(hkeyboardhook);

     hkeyboardhook = 0;

    }

    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 = 0; i < (arr.length > 10 ? 9 : arr.length); i++)

       {

        this .listview1.items.add((i + 1) + "、" + 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 > 0)

      {

       choose = choose - 1;

      }

      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(0, keys.length - 1);

    }

    this .showcharatar();

   }

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

至于什么五笔、拼音就要使用词库来解决了;其中五笔比较简单,拼音就非常复杂了,各种分词、联想等...这里以五笔为主,拼音为单拼来实现基本的输入功能;所以不需要什么高深算法,简单使用memorycache就轻松高效搞定(有兴趣的可以来https://github测试数据/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测试数据

*****************************************************************************************************

  * 项目名称:$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(0, item.indexof( " " ));

     var value = item.substring(item.indexof( " " ) + 1);

     _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(0, item.indexof( " " ));

     var value = item.substring(item.indexof( " " ) + 1);

     _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 = 0; i < arr.length; i++)

      {

       if (arr[i].indexof( "*" ) > -1)

       {

        arr[i] = arr[i].substring(0, 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(-1);

   }

  }

}

到此一个基本型的c#版外挂输入法就成功完成了,源码地址:https://github测试数据/yswenli/wenli.iem

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持服务器之家!

原文链接:http://HdhCmsTestcnblogs测试数据/yswenli/p/6528447.html

dy("nrwz");

查看更多关于C#实现输入法功能详解的详细内容...

  阅读:52次