Помощь - Поиск - Пользователи - Календарь
Полная версия: работа с клавиатурой
Форум «Всё о Паскале» > Delphi, Assembler и другие языки. > Другие языки
Unknown
Подскажите, пожалуйста, как научить программу отлавливать факт нажатия кнопок клавиатуры в окне другого приложения?
В общем нужно что-то типа клавиатурного шпиона написать...
volvo
Глобальный хук на клавиатуру?

Вот тут есть пример на C#: Processing Global Mouse and Keyboard Hooks in C#
Unknown
Спасибо! Теперь другой вопрос: как в окно другого приложения (например, Word'а) записать какой-то текст, который был отловлен моей программой?
volvo
А оно тебе надо писать это в окно Word-а? Создать DOC-файл и открыть его Word-ом будет недостаточно?
Unknown
да, мне это нужно smile.gif
Мне нужно написать прогу наподобие PuntoSwitcher.
volvo
Ну, тогда смотри, как работать с Word-ом: Word Automation, больше помочь ничем не могу, .NET - это не мое smile.gif
Unknown
Word - это я просто в качестве примера привел. А вообще мне нужно: отловить ввод текста, определить последнее окно, в котором клавиши нажимались, стереть введенный текст и заменить транслитированным.
Сейчас я хочу узнать, как обратиться к окну другого приложения. Или хотя бы как это правильно сформулировать, чтобы поискать можно было. Я правильно понимаю, что мне нужен хэндл окна? еще нужно определить текстовое поле на этом окне...
В общем, если кто подскажет, будет здорово smile.gif
Unknown
определять хэндл окна по его названию научился - findwindow, но это не совсем то, что нужно...
volvo
Цитата
Я правильно понимаю, что мне нужен хэндл окна? еще нужно определить текстовое поле на этом окне...
Ты правильно понимаешь... На WinAPI это делается через FindWindow/FindWindowEx, в Шарпе - насколько я знаю - через вызов этих же функций с использованием механизма pInvoke. Последнее окно - это то, которое имеет фокус ввода? API-шная GetFocus Function тебе поможет (вызывать - через тот же pInvoke)... Вот и все собственно...
Unknown
Вот спасибо! оказывается есть полно полезных функций! smile.gif
Unknown
Гмм... GetFocus все время возвращает 0... в чем может быть дело?

Код
if (winH != GetFocus())
            {
                buf = new List<int>();
                winH = GetFocus();
            }
            buf.Add(e.KeyValue);
            textBox1.Text += e.KeyCode;


Добавлено через 18 мин.
Вроде бы GetForegroundWindow - то, что нужно! )
volvo
Ну и чего ты творишь? Я ж тебе дал ссылку на MSDN, там явно сказано:
Цитата
Return Value
The return value is the handle to the window with the keyboard focus. If the calling thread's message queue does not have an associated window with the keyboard focus, the return value is NULL.
...
Use the GetForegroundWindow function to retrieve the handle to the window with which the user is currently working. You can associate your thread's message queue with the windows owned by another thread by using the AttachThreadInput function.


Вот так это приблизительно делается на WinAPI:
// Получаем хэндл активного приложения
HWND hWnd = ::GetForegroundWindow();
DWORD myProcessId, otherProcessId;
DWORD otherThread=::GetWindowThreadProcessId(hWnd, &otherProcessId);
DWORD myThread = ::GetWindowThreadProcessId(mуWnd, &myProcessId);
// Подключаемся к другому потоку
::AttachThreadInput(myThread, otherThread, true);
// Получаем в его контексте дочернее окно с фокусом, обрабатываем его как нужно
HWND hWndOfFocused = ::GetFocus();

// И отсоединяемся от чужого процесса
::AttachThreadInput(myThread, otherThread, false);
Возможно, .NET позволяет сделать это же самое и проще...
Unknown
спасибо, попробовал сделать по шагам - споткнулся на GetFocus - возвращает ноль.
winH = GetForegroundWindow();
int otherThread = GetWindowThreadProcessId(winH, out otherProcessId);
int myThread = GetWindowThreadProcessId(this.Handle, out myProcessId);
AttachThreadInput(myThread, otherThread, true);
IntPtr n = GetFocus();

В чем может быть дело?

Пробовал сделать по-другому: через SendKeys.Send, но возникла проблема с раскладками - чтобы вывести транслитированный текст с помощью Send, нужно сменить раскладку в окне активного приложения - никак не могу разобраться, как это сделать!
for (int i = 0; i < buf.Count; i++)
SendKeys.Send("{BACKSPACE}");
LoadKeyboardLayout(LANG_EN_US, KLF_ACTIVATE);
foreach (int i in buf)
SendKeys.Send(abc[1, i - 'а']);
LoadKeyboardLayout(LANG_Ru_RU, KLF_ACTIVATE);

Но, видимо, LoadKeyboardLayout меняет раскладку только в моей программе... пробовал подключиться к потоку другого приложения - не помогло.

Обойтись без раскладок, в принципе, можно - используя SendMessage:
SendMessage(txtbox, WM_SETFOCUS, nul, nul);
SendMessage(txtbox, WM_KEYDOWN, (IntPtr) 'х', nul);
SendMessage(txtbox, WM_CHAR, (IntPtr) 'х', nul);
SendMessage(txtbox, WM_KEYUP, (IntPtr) 'х', nul);

Но тут надо определить хэндл поля ввода txtbox.
txtbox = FindWindowEx(winH, IntPtr.Zero, "Edit", null);

Это работает для NotePad'а, а универсальный способ есть?
volvo
Цитата
попробовал сделать по шагам - споткнулся на GetFocus - возвращает ноль.
Хм... Надо будет установить себе хотя бы SharpDevelop, посмотреть, что творится в C#, потому как приведенный мной код в C++ отрабатывает прекрасно, GetFocus получает дескриптор активного контрола, и посылка в него, скажем,
::SendMessage(hWndOfFocused, WM_SETTEXT, 0,(long int)"Just a test\0");
, приводит к появлению этого текста в чужом приложении... НО!!! Не везде, естественно. Универсального способа нет и вряд ли он будет, потому что это сработает только тогда, когда контрол является оконным, то есть, если у него вообще есть HWND. А если нет? А если чужое приложение рисует на канве (как это делал ICQ, например, в форме быстрого ответа. Не знаю, может сейчас уже изменили, и там тоже используются оконные компоненты?), что тогда делать будешь?

Кстати, еще один способ (опять же, только для оконных контролов) - получить активное приложение через GetForegroundWindow, а потом пройтись по каждому из его дочерних окон, то есть, перебрать все суб-контролы этого приложения (удобно делается через EnumChildWindows), для каждого получать GetWindowInfo, и проверять в полученной структуре поле dwWindowStatus. Если оно == WS_ACTIVECAPTION, значит, нашел контрол, на котором фокус ввода.
volvo
Update: ответ на вопрос
Цитата
GetFocus - возвращает ноль. <...> В чем может быть дело?

WinForms FAQ - "// Note that if the focused Control is not a .Net control, then this will return null."
Гость
Цитата
WinForms FAQ - "// Note that if the focused Control is not a .Net control, then this will return null."

т.е., к примеру, поле мемо программы, написанной в билдере, я не смогу определить как выделенное?
volvo
Установил себе наконец-то SharpDevelop, написал так:

      void getActControl(IntPtr myWnd)
{
while(myWnd != IntPtr.Zero)
{
uint otherPID = 0;
uint otherTID = NativeMethods.GetWindowThreadProcessId(myWnd, out otherPID);

NativeMethods.AttachThreadInput(NativeMethods.GetCurrentThreadId(), otherTID, true);
IntPtr myFocused = NativeMethods.GetFocus();
NativeMethods.AttachThreadInput(NativeMethods.GetCurrentThreadId(), otherTID, false);

st += "Handle = " + myWnd.ToString() + " (Focused: " + myFocused.ToString() + ") \n";
getActControl(NativeMethods.GetWindow(myWnd, 5)); // 5 = GW_CHILD

myWnd = NativeMethods.GetWindow(myWnd, 2); // 2 = GW_HWNDNEXT
}
}

// Вызываю так:
string st = "";
IntPtr actWin = NativeMethods.GetForegroundWindow();
st += "** Handle: " + actWin.ToString() + "\n";
getActControl(NativeMethods.GetWindow(actWin, 5)); // 5 = GW_CHILD

foreach(string ss in st.Split('\n')) {
listBox1.Items.Add(ss);
}
, ни в случае активного NotePad-а, ни в случае активного Word-а не получаю нулей в myFocused...
Unknown
Если я не ошибаюсь, я пробовал на ICQ...
Спасибо за помощь, программу написал - с помощью SendKeys )
Это текстовая версия — только основной контент. Для просмотра полной версии этой страницы, пожалуйста, нажмите сюда.