Привет, дорогой хабражитель!
В этой статье я хочу показать, как я делал программу для ускорения и упрощения некоторых действий с помощью хоткеев.
Примерно полтора месяца назад пользователь vvzvlad натолкнул меня на идею программы, для перевода различного текста по нажатию хоткея. Я очень долго пользовался этой программкой, но вот пару дней назад мне пришла в голову идея улучшить её.
Я решил добавить возможность назначать различные хоткеи на разные действия. Для этого я использовал систему плагинов.
В итоге получилась программа HotKeyHelper, которую вы можете скачать здесь.

Файл проекта находится здесь.
Под катом вы увидите исходный код программы и пояснения к нему.
Как я уже написал выше, я решил использовать систему плагинов.
Для этого нам необходимо написать интерфейс плагинов:
И написать метод для загрузки плагинов:
Также нам будет необходимо вызывать WinApi методы, для этого импортируем их:
И напишем 2 метода, необходимых для выполнения функционала некоторых плагинов
Также напишем класс для хранения пользовательских хоткеев:
Теперь после загрузки плагинов мы можем загрузить пользовательские хоткеи и подписаться на них:
После того, как мы подписались на хоткей, нашему окну будет посылаться сообщение WM_HOTKEY. Чтобы узнать, когда пользователь нажал необходимую комбинацию, мы должны переписать метод:
Теперь нам осталось добавить более-мение удобный интерфейс для создания хоткеев. В этом, я думаю, нет ничего интересного, однако я покажу, как я сделал систему для записи нажатой комбинации.
Для этого нам нужно подписаться на события формы KeyDown и KeyUp:
Ну всё. Программа готова. Осталось только написать какой-либо плагин, чтобы начать пользоваться.
Чтобы мы могли использовать плагин в программе, мы должны унаследовать основной класс от IPlugin.
Для этого нам нужно добавить ссылку на нашу программу. Теперь мы можем написать реализацию простого плагина. Для примера я написал плагин, который будет размещать выделенный текст на pastebin.com:
В этих плагинах для запросов по http я использовал xNet
Также я написал плагин для перевода выделенного текста:
После того, как мы написали и скомпилировали плагин, мы должны поместить его в 1 папку с программой, и при следующем запуске вы сможете «забиндить» на определённый хоткей выполнение данного плагина.
В этой статье я хочу показать, как я делал программу для ускорения и упрощения некоторых действий с помощью хоткеев.
Вступление
Примерно полтора месяца назад пользователь vvzvlad натолкнул меня на идею программы, для перевода различного текста по нажатию хоткея. Я очень долго пользовался этой программкой, но вот пару дней назад мне пришла в голову идея улучшить её.
Я решил добавить возможность назначать различные хоткеи на разные действия. Для этого я использовал систему плагинов.
В итоге получилась программа HotKeyHelper, которую вы можете скачать здесь.

Файл проекта находится здесь.
Под катом вы увидите исходный код программы и пояснения к нему.
Программа
Как я уже написал выше, я решил использовать систему плагинов.
Для этого нам необходимо написать интерфейс плагинов:
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; }
И написать метод для загрузки плагинов:
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; } } }
Также нам будет необходимо вызывать WinApi методы, для этого импортируем их:
[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);
И напишем 2 метода, необходимых для выполнения функционала некоторых плагинов
/// <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); }
Также напишем класс для хранения пользовательских хоткеев:
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); } }
Теперь после загрузки плагинов мы можем загрузить пользовательские хоткеи и подписаться на них:
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); } }
После того, как мы подписались на хоткей, нашему окну будет посылаться сообщение WM_HOTKEY. Чтобы узнать, когда пользователь нажал необходимую комбинацию, мы должны переписать метод:
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); }
Теперь нам осталось добавить более-мение удобный интерфейс для создания хоткеев. В этом, я думаю, нет ничего интересного, однако я покажу, как я сделал систему для записи нажатой комбинации.
Для этого нам нужно подписаться на события формы KeyDown и KeyUp:
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; } }
Ну всё. Программа готова. Осталось только написать какой-либо плагин, чтобы начать пользоваться.
Плагин
Чтобы мы могли использовать плагин в программе, мы должны унаследовать основной класс от IPlugin.
Для этого нам нужно добавить ссылку на нашу программу. Теперь мы можем написать реализацию простого плагина. Для примера я написал плагин, который будет размещать выделенный текст на 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); } } } }
В этих плагинах для запросов по http я использовал xNet
Также я написал плагин для перевода выделенного текста:
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; } }
После того, как мы написали и скомпилировали плагин, мы должны поместить его в 1 папку с программой, и при следующем запуске вы сможете «забиндить» на определённый хоткей выполнение данного плагина.