• Программинг

Нужны источники бесперебойного питания?

Источники бесперебойного питания от дизельстор

C# Вставка текста в «чужое» приложение. Вариант 1

Давно хотел написать программу, которая бы хранила заготовки и шаблоны текста, для быстрой вставки в другие приложения. Стандартными средствами .NET это сделать не удалось. В интернете тоже нет конкретной информации, которая бы четко и ясно ответила бы на этот вопрос. По крупицам собрав и разобрав различную информацию, опробовав кучу способов, я смог написать кое-что. Сегодня разберем приложение, которое будет вставлять определенный текст в «чужую» программу.
Реализация

Как я писал выше, одними возможностями .NET такой фокус сделать нельзя. Берем наши старые функции WINAPI. Нам понадобится 7 таких функций. Ниже они перечислены. Каждую описывать не буду. Ищите в MSDN.

1 using System.Runtime.InteropServices;
2 ...
3 [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
4 public static extern IntPtr GetFocus();
5  
6 [DllImport("user32.dll")]
7 public static extern IntPtr GetForegroundWindow();
8  
9 [DllImport("user32.dll")]
10 public static extern IntPtr SetForegroundWindow(IntPtr hWnd);
11  
12 [DllImport("user32.dll", CharSet = CharSet.Auto)]
13 static extern bool PostMessage(IntPtr hWnd, int Msg, char wParam, int lParam);
14  
15 [DllImport("user32")]
16 public static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
17  
18 [DllImport("user32")]
19 public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
20  
21 [DllImport("kernel32.dll", SetLastError = true)]
22 static extern uint GetCurrentThreadId();

Первое дело сделано – описали нужные функции.

Остально написать две функции. Обе важные, хоть и короткие. Первая получает дескриптор(хэндл) окна, в котором содержится фокус ввода.

1 void GetFocusedControl()
2 {
3     IntPtr hFocus;
4     IntPtr hFore;
5     uint id = 0;
6     //узнаем в каком окне находится фокус ввода
7     hFore = GetForegroundWindow();
8     //подключаемся к процессу
9     AttachThreadInput(GetWindowThreadProcessId(hFore, out id), GetCurrentThreadId(), true);
10     //получаем хэндл фокуса
11     hFocus = GetFocus();
12     //отключаемся от процесса
13     AttachThreadInput(GetWindowThreadProcessId(hFore, out id), GetCurrentThreadId(), false);
14     hControl = hFocus;
15 }

В переменной hControl содержится наш дескриптор окна, в которое мы будем вставлять текст.

Вторая функции принимает какой-то текст, разбирает его и посимвольно передает его в какое-то окно.

1 void pasteText(string text)
2 {
3     try
4     {
5         //активизируем окно, которое имело фокус
6         SetForegroundWindow(hControl);
7         int WM_CHAR = 0x0102;
8         //передаем ему текст посимвольно
9         foreach (char ch in text)
10         {
11             PostMessage(hControl, WM_CHAR, ch, 1);
12         }
13     }
14     catch (Exception error)
15     {
16         MessageBox.Show(error.Message);
17     }
18 }

Перед передачей функцией SetForegroundWindow мы активизируем окно, в которое передаем текст.

Описаный выше способ передачи использует функцию PostMessage. У этого способа есть определенные минусы:
- нельзя передать форматированный-rtf текст;
- теоретически нельзя будет передать некоторым приложениям (каким, пока не знаю, но теоретически такое возможно);
- неизвестно, как будет работать в новых операционных системах Microsoft.

Из плюсов хочу отметить важную особенность – не используется буфер обмена (clipboard). То, что находится в буфере обмена не затрагивается.
Заключение

Как всегда, я представляю тестовый проект, чтобы вы могли посмотреть как это все работает.

Важно! Как запускать:
- создайте ярлык на рабочем столе на exe-файл проекта;
- в свойствах ярлыка назначьте горячие клавиши для запуска;
- поставьте курсор в какое-то поле ввода и нажмите заранее заданную комбинацию клавиш ярлыка;
- введите текст и нажмите вставить.

Все это необходимо, чтобы приложение до запуска получило дескриптор окна с фокусом. Иначе приложение получит само себя.