利用C#实现的外挂式甲骨文拼音输入法

利用C#实现外挂式甲骨文拼音输入法

最近一直做甲骨文的拼音输入法研究,以下为在此次学习中积累所得,如有理解错误,欢迎指正,部分资料查询来源网友分享,在此致以感谢。
  1. 首先对于输入法来说,可分为早期的外挂式输入法(早期的五笔)、IME(微软提供的输入法编程规范)、TSF(文本服务框架)提高了输入法的安全性和规范性,对于IME和TSF暂且不谈,本文为利用C#进行开发,众所周知,C#可以用于开发各种外挂,这个甲骨文输入法也是基于外挂式进行开发。
  2. 进行开发之前首先进行字库的构造,笔者用的软件为FontCreator,构造字库是一件麻烦而又费时的事情,对此本软件只对293个甲骨字进行了构造,由于目前没有标准的甲骨字编码,本软件采用了Unicode编码,通过Unicode编码查找出对应的汉字,然后再利用FontCreator软件进行对应甲骨文的设计,如下图甲骨字型重复此过程,把已知甲骨字全部做出来(费时的活,慢慢干),因为甲骨文字不像楷体那样方方正正,做的时候注意前后上下的距离,否则会直接影响打出来字体的美观,针对于刚才所说采用的Unicode编码,当系统字库未安装甲骨文字库时会显示出对应的汉字,这里牵扯到字体内码,内码即确定该字体的唯一编码,对于甲骨文当前未有具体编码,故采用这种方式编码方式,这里多说一点,关于字体的内码和外码,内码确定了汉字的存储,与输入法无关,外码像拼音,五笔,郑码等,通过输入法的码表映射来查找内码,从而确定汉字。由于在vs中不能使用非TrueType字体(原因不知),所以制作出的应是Truetype字体。
  3. 字库构造好之后,先把字库保存,开始写输入法程序,利用C#进行输入法程序开发,需要利用到HOOK技术,即Windows消息处理机制首先进行勾子安装
// 安装键盘钩子 
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("安装键盘钩子失败");
                }
            }
        }

安装成功后即可监听键盘消息,进行处理

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);
        }

停止勾子的使用

public void Stop()
        {
            bool retKeyboard = true;


            if (hKeyboardHook != 0)
            {
                retKeyboard = UnhookWindowsHookEx(hKeyboardHook);
                hKeyboardHook = 0;
            }

            if (!(retKeyboard))
                throw new Exception("卸载钩子失败!");
        }

以上为勾子技术的实现,接下来注册事件

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

输入内容的识别

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 = "";
                }
            }));
        }

输入上屏

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 = "";
        }

发送数据

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

词库转换

 public static class CacheHelper
    {
        static MemoryCache _cikuCache = new MemoryCache("ciku");

        static MemoryCache _duoyinCache = new MemoryCache("duoyin");

        static CacheHelper()
        {
            var path = Application.StartupPath + "\\Win32\\ciku.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);
                _cikuCache.Add(key, (object)value, DateTimeOffset.MaxValue);
            }

            //

             path = Application.StartupPath + "\\Win32\\duoyin.dll";
             arr = File.ReadAllLines(path);
            foreach (string item in arr)
            {
                var key = item.Substring(0, item.IndexOf(" "));
                var value = item.Substring(item.IndexOf(" ") + 1);
                _duoyinCache.Add(key, (object)value, DateTimeOffset.MaxValue);
            }
        }

        public static string[] Get(string key)
        {
            if (!string.IsNullOrEmpty(key))
            {
                var str = string.Empty;

                try
                {
                    if (_cikuCache.Contains(key))
                        str = _cikuCache[key].ToString();
                }
                catch { }
                try
                {
                    if (_duoyinCache.Contains(key))
                        str += " " + _duoyinCache[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 (_cikuCache.Contains(key))
                return true;
            if (_duoyinCache.Contains(key))
                return true;
            return false;
        }

        public static void Clear()
        {
            _cikuCache.Dispose();
            GC.Collect(-1);
        }
 }

以上输入法完成了大部分,具体可参考yswenli的输入法研究
4 接下来完成输入法的其他功能
实现换肤

 bool skinflag = true;
        private void skin_Click(object sender, EventArgs e)
        {
            if(skinflag || listBox1.Visible == false)
            {
                listBox1.Visible = true;
            } 
            else
            {
                listBox1.Visible = false;
            }
            skinflag = !skinflag;
        }
         private void listBox1_SelectedValueChanged(object sender, EventArgs e)
        {
            if (listBox1.SelectedIndex != -1)
           {
              if(listBox1.SelectedItem.ToString().Equals("少女粉"))
              {
                  pinkset();
              }
              else if(listBox1.SelectedItem.ToString().Equals("海洋蓝"))
              {
                  seaset();
              }
              else if (listBox1.SelectedItem.ToString().Equals("护眼绿"))
              {
                  greenset();
              }
              else
              {
                  morenset();
              }
           }
        }

隐藏程序功能,同时需要注意,隐藏程序后需要停止勾子的监控,否则电脑键盘导致不能用

private void hide_MouseClick(object sender, MouseEventArgs e)
        {
            this.Hide();
            this.notifyIcon1.Visible = true;
            Program.keyBordHook.Stop();
            label1.Text = "";
            listView1.Clear();
        }

更改窗体的透明度

bool toumingflag = true;
        private void touming_Click(object sender, EventArgs e)
        {
        
            if(toumingflag || trackBar1.Visible==false)
            {
                trackBar1.Visible = true;     
            }
            else 
            {
                trackBar1.Visible = false;
            }
            toumingflag = !toumingflag;             
        }

设置勾子的使用状态

bool stopflag = true;//1为使用,0为暂停 
        private void stop_Click(object sender, EventArgs e)
        {
            if(stopflag)
            {
                stop.BackgroundImage = Properties.Resources.play_24px;
                Program.keyBordHook.Stop();
                // 创建the ToolTip 
                ToolTip toolTip1 = new ToolTip();
                // 设置显示样式
                toolTip1.AutoPopDelay = 5000;//提示信息的可见时间
                toolTip1.InitialDelay = 200;//事件触发多久后出现提示
                toolTip1.ReshowDelay = 500;//指针从一个控件移向另一个控件时,经过多久才会显示下一个提示框
                toolTip1.ShowAlways = true;//是否显示提示框
                //  设置伴随的对象.
                toolTip1.SetToolTip(stop, "继续监控");//设置提示按钮和提示内容
                
                label1.Text = "";
                listView1.Clear();
            }
            else
            {
                stop.BackgroundImage = Properties.Resources.pause_24px;
                Program.keyBordHook.Start();
                // 创建the ToolTip 
                ToolTip toolTip1 = new ToolTip();
                // 设置显示样式
                toolTip1.AutoPopDelay = 5000;//提示信息的可见时间
                toolTip1.InitialDelay = 200;//事件触发多久后出现提示
                toolTip1.ReshowDelay = 500;//指针从一个控件移向另一个控件时,经过多久才会显示下一个提示框
                toolTip1.ShowAlways = true;//是否显示提示框
                //  设置伴随的对象.
                toolTip1.SetToolTip(stop, "停止监控");//设置提示按钮和提示内容
               
            }
            stopflag = !stopflag;
        }

防止程序多次运行,利用线程限制程序只能运行一次

[STAThread]
        static void Main()
        {
            #region 防止程序多次运行
            string MName = Process.GetCurrentProcess().MainModule.ModuleName;
            string PName = Path.GetFileNameWithoutExtension(MName);
            Process[] myProcess = Process.GetProcessesByName(PName);
            if (myProcess.Length > 1)
            {
                MessageBox.Show("本程序已在运行,请勿多次运行!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            #endregion
            else
            {
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                keyBordHook.Start();
                Application.Run(new MainForm());
                keyBordHook.Stop();
            }
        }

5 以上所有基本完成输入法的开发设置,接下来是程序的打包,笔者采用installshiled2018打包,具体打包教程可进行百度,这里进行提示一下字体文件怎么打包进去,首先选择Application Files,然后在Destination Computer上右键选择show predefinedible Folder下的FontsFolder, 把字库文件添加进去即可,以上即为所有开发流程,效果图如下所示
在这里插入图片描述
下面是笔者制作好的链接:https://pan.baidu.com/s/1V4eLdUEdy-2Z7vNPyuOCyg
提取码:cz3u百度云链接

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章