📜 ⬆️ ⬇️

We tame hotkeys with C # plugin system

Hello, dear habrazhitel!

In this article I want to show how I did a program to speed up and simplify some actions using hotkeys.

Introduction


About a month and a half ago, the user vvzvlad pushed me to the idea of ​​the program, to translate various text by pressing the hotkey. I used this program for a very long time, but a couple of days ago I had an idea to improve it.
I decided to add the ability to assign different hotkeys to different actions. For this, I used a plugin system.
The result was the HotKeyHelper program, which you can download here .
')
image

The project file is here .

Under the cut you will see the source code of the program and explanations to it.


Program



As I wrote above, I decided to use the plugin system.

To do this, we need to write the plugin interface:

public delegate void PluginHandler(string text); /// <summary> ///     /// </summary> public interface IPlugin { /// <summary> ///   /// </summary> string Name { get; } /// <summary> ///        /// </summary> bool NeedSelectedText { get;} /// <summary> ///    /// </summary> /// <param name="parametres"> </param> /// <param name="text">  </param> void MainMethod(string parametres,string text); event PluginHandler ShowBaloonHandler; event PluginHandler ShowFormHandler; event PluginHandler PasteTextHandler; } 


And write a method for loading plugins:

  private void LoadPlugins() { //    string sPath = System.IO.Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName); ; //       .dll foreach (string f in System.IO.Directory.GetFiles(sPath, "*.dll")) { System.Reflection.Assembly a = System.Reflection.Assembly.LoadFile(f); try { foreach (Type t in a.GetTypes()) { foreach (Type i in t.GetInterfaces()) { //     IPlugin if (i.Equals(Type.GetType("HotKeyHelper.IPlugin"))) { //          IPlugin p = (IPlugin)Activator.CreateInstance(t); p.ShowBaloonHandler += ShowBaloon; p.ShowFormHandler += ShowForm; p.PasteTextHandler += PasteText; comboBox1.Items.Add(p.Name); Plugins.Add(p); break; } } } } catch (Exception) { continue; } } } 


We will also need to call WinApi methods, for this we import them:

  [DllImport("User32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk); [DllImport("User32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool UnregisterHotKey(IntPtr hWnd, int id); [DllImport("user32.dll", SetLastError = true)] public static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, int dwExtraInfo); 


And we will write 2 methods necessary to perform the functionality of some plugins.

  /// <summary> ///   Ctrl+C /// </summary> void CtrlCEmul() { keybd_event(0x11, 0, 0, 0); keybd_event((byte)'C', 0, 0, 0); keybd_event((byte)'C', 0, 0x2, 0); keybd_event(0x11, 0, 0x2, 0); } /// <summary> ///   Ctrl+V /// </summary> void CtrlVEmul() { keybd_event(0x11, 0, 0, 0); keybd_event((byte)'V', 0, 0, 0); keybd_event((byte)'V', 0, 0x2, 0); keybd_event(0x11, 0, 0x2, 0); } 


We will also write a class for storing custom hotkeys:
 public class HotKey { /// <summary> ///   /// </summary> public string Plugin { get; set; } /// <summary> ///     /// </summary> public string Parametres { get; set; } /// <summary> ///   /// </summary> public string Key { get; set; } /// <summary> ///   /// </summary> public uint KeyCode { get; set; } /// <summary> ///   /// </summary> public string Modificators { get; set; } /// <summary> ///   /// </summary> public uint ModificatorsCode { get; set; } /// <summary> ///   /// </summary> /// <param name="path">   </param> /// <returns> </returns> public static List<HotKey> LoadHotKeys(string path) { List<HotKey> hotKeys = new List<HotKey>(); if (File.Exists(path)) foreach (string line in File.ReadAllLines(path)) { string[] parametres = line.Split(new string[] { "<!>" }, StringSplitOptions.None); hotKeys.Add(new HotKey() { Plugin = parametres[0], Parametres = parametres[1], Key = parametres[2], KeyCode = UInt32.Parse(parametres[3]), Modificators = parametres[4], ModificatorsCode = UInt32.Parse(parametres[5]) }); } return hotKeys; } /// <summary> ///   /// </summary> /// <param name="hotKeys"> </param> /// <param name="path">   </param> public static void SaveHotKeys(List<HotKey> hotKeys, string path) { List<string> parametres = new List<string>(); foreach (HotKey hotKey in hotKeys) { parametres.Add(String.Format("{0}<!>{1}<!>{2}<!>{3}<!>{4}<!>{5}", new object[]{hotKey.Plugin,hotKey.Parametres,hotKey.Key,hotKey.KeyCode, hotKey.Modificators,hotKey.ModificatorsCode})); } File.WriteAllLines(path, parametres); } } 


Now, after loading plugins, we can download custom hotkeys and subscribe to them:

  HotKeys = HotKey.LoadHotKeys("HotKeys.txt"); //       for (int i = 0; i < HotKeys.Count; i++) { if (!RegisterHotKey(this.Handle, i, HotKeys[i].ModificatorsCode, HotKeys[i].KeyCode)) { notifyIcon1.BalloonTipText = String.Format(" {0} {1}  ", HotKeys[i].Modificators, HotKeys[i].Key); notifyIcon1.ShowBalloonTip(10000); } } 


After we have subscribed to the hotkey, the WM_HOTKEY message will be sent to our window. To find out when the user clicked the required combination, we need to rewrite the method:
  protected override void WndProc(ref Message m) { //  WM_HOTKEY if (m.Msg == 0x0312) { //       int id = (int)m.WParam; IPlugin plugin = Plugins.SingleOrDefault(pl => pl.Name == HotKeys[id].Plugin); if (plugin == null) { notifyIcon1.Text = "   "; notifyIcon1.ShowBalloonTip(10000); return; } string text = ""; if(plugin.NeedSelectedText) { //  Ctrl+C CtrlCEmul(); //,       Thread.Sleep(150); //..       ,      for (int i = 0; i < 10; i++) { try { //    text = Clipboard.GetText(); break; } catch (ExternalException) { } Thread.Sleep(100); } } //   try { plugin.MainMethod(HotKeys[id].Parametres,text); } catch (Exception ex) { notifyIcon1.Text = "    "+ex.Message; notifyIcon1.ShowBalloonTip(10000); } return; } base.WndProc(ref m); } 


Now we have to add a more convenient interface for creating hotkeys. In this, I think, there is nothing interesting, however, I will show how I made the system for recording the pressed combination.
To do this, we need to subscribe to KeyDown and KeyUp form events:

  private void button1_Click(object sender, EventArgs e) { if (button1.Enabled) { button1.Enabled = false; isRecord = true; } } //     private void Form1_KeyDown(object sender, KeyEventArgs e) { //     "" if (isRecord) { //    modKeys = e.Modifiers; key = e.KeyCode; //     if (e.Alt) modifiers = 1; if (e.Control) modifiers = 2; if (e.Shift) modifiers = 4; if (e.Alt && e.Control) modifiers = 3; if (e.Alt && e.Shift) modifiers = 5; if (e.Control && e.Shift) modifiers = 6; if (e.Alt && e.Control && e.Shift) modifiers = 7; label1.Text = modKeys.ToString(); label2.Text = key.ToString(); } } //    private void Form1_KeyUp(object sender, KeyEventArgs e) { if (isRecord) { //    DialogResult result = MessageBox.Show(String.Format(" : {0} {1}",label1.Text,label2.Text), "", MessageBoxButtons.YesNo); if (result == DialogResult.Yes) isRecord = false; } } 


Well, that's it. The program is ready. It remains only to write any plugin to start using.

Plugin


In order for us to use the plugin in the program, we must inherit the main class from IPlugin.
For this we need to add a link to our program. Now we can write a simple plugin implementation. For example, I wrote a plugin that will place the selected text on pastebin.com :
  public class PastebinSender : IPlugin { public string Name { get { return "PastebinSender"; } } public bool NeedSelectedText { get { return true; } } public event PluginHandler ShowBaloonHandler; public event PluginHandler ShowFormHandler; public event PluginHandler PasteTextHandler; /// <summary> ///   /// </summary> /// <param name="parametres">   "     :  :  "</param> /// <param name="text">  </param> public void MainMethod(string parametres, string text) { string[] param = parametres.Split(':'); bool useMessage = param[0] == "1"; string format = param[1]; string expire = param[2]; using (HttpRequest request = new HttpRequest()) { MultipartDataCollection reqParams = new MultipartDataCollection(); //   - request.UserAgent = HttpHelper.RandomChromeUserAgent(); string cont = request.Get("http://pastebin.com").ToText(); string postKey = cont.Substring("post_key\" value=\"", "\""); reqParams.AddData("post_key", postKey); reqParams.AddData("submit_hidden", "submit_hidden"); reqParams.AddData("paste_code", text); reqParams.AddData("paste_format", format); reqParams.AddData("paste_expire_date", expire); reqParams.AddData("paste_private", "1"); reqParams.AddData("paste_name", ""); //     string link = request.Post("http://pastebin.com/post.php", reqParams).Address.AbsoluteUri; if (useMessage) { ShowFormHandler(link); } else { ShowBaloonHandler(link); } } } } 


In these plugins for http requests I used xNet

I also wrote a plugin to translate the selected text:
  public class GoogleTranslator : IPlugin { public string Name { get { return "GoogleTranslator"; } } public bool NeedSelectedText { get { return true; } } public event PluginHandler ShowBaloonHandler; public event PluginHandler ShowFormHandler; public event PluginHandler PasteTextHandler; /// <summary> ///   /// </summary> /// <param name="parametres">   "   :     "</param> /// <param name="text">  </param> public void MainMethod(string parametres, string text) { //   string[] param = parametres.Split(':'); string ToLang = param[0]; bool useMessage = param[1] == "1"; //     ::,       string[] message = text.Split(new string[] { "::" }, StringSplitOptions.None); if (message.Length != 2) { //     if (isRussian(text)) { //     string translate = GetTranslate(text, ToLang); PasteTextHandler(translate); } //      else { //    string translate = GetTranslate(text, "ru"); //     if (useMessage) { ShowFormHandler(translate); } else { ShowBaloonHandler(translate); } } } //    ::,       else { string mess = message[1]; string toLang = message[0]; //     string translate = GetTranslate(mess, toLang); PasteTextHandler(translate); } } private string GetTranslate(string message, string toLang) { //      - using (HttpRequest request = new HttpRequest()) { StringDictionary reqParams = new StringDictionary(); //   - request.UserAgent = HttpHelper.RandomChromeUserAgent(); reqParams["tl"] = toLang; reqParams["sl"] = "auto"; reqParams["client"] = "x"; string translate = ""; ShowBaloonHandler("..."); reqParams["text"] = message; //    string s = request.Get( "http://translate.google.ru/translate_a/t", reqParams).ToText(); //      string[] ts = s.Substrings("trans\":\"", "\","); foreach (string t in ts) { string tr = t; if (tr.Contains('\"')) { tr = tr.Replace("\\", ""); } //  if (translate != "") { translate = translate + " " + tr; } else { translate = tr; } } return translate; } } bool isRussian(string text) { foreach (char c in text) { if ((c >= '' && c <= '')) return true; } return false; } } 


After we have written and compiled the plugin, we have to put it in 1 folder with the program, and the next time you start you will be able to “bind” to the specific hotkey the execution of this plugin.

Source: https://habr.com/ru/post/143315/


All Articles