利用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百度雲鏈接

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