Версия для печати темы

Нажмите сюда для просмотра этой темы в обычном формате

Форум «Всё о Паскале» _ Ада и другие языки _ работа с клавиатурой

Автор: Unknown 16.04.2009 23:58

Подскажите, пожалуйста, как научить программу отлавливать факт нажатия кнопок клавиатуры в окне другого приложения?
В общем нужно что-то типа клавиатурного шпиона написать...

Автор: volvo 17.04.2009 1:12

Глобальный хук на клавиатуру?

Вот тут есть пример на C#: http://www.codeproject.com/KB/cs/globalhook.aspx

Автор: Unknown 17.04.2009 2:39

Спасибо! Теперь другой вопрос: как в окно другого приложения (например, Word'а) записать какой-то текст, который был отловлен моей программой?

Автор: volvo 17.04.2009 2:48

А оно тебе надо писать это в окно Word-а? Создать DOC-файл и открыть его Word-ом будет недостаточно?

Автор: Unknown 17.04.2009 3:24

да, мне это нужно smile.gif
Мне нужно написать прогу наподобие PuntoSwitcher.

Автор: volvo 17.04.2009 4:10

Ну, тогда смотри, как работать с Word-ом: http://www.codeproject.com/KB/cs/Word_Automation.aspx?display=Print, больше помочь ничем не могу, .NET - это не мое smile.gif

Автор: Unknown 17.04.2009 15:21

Word - это я просто в качестве примера привел. А вообще мне нужно: отловить ввод текста, определить последнее окно, в котором клавиши нажимались, стереть введенный текст и заменить транслитированным.
Сейчас я хочу узнать, как обратиться к окну другого приложения. Или хотя бы как это правильно сформулировать, чтобы поискать можно было. Я правильно понимаю, что мне нужен хэндл окна? еще нужно определить текстовое поле на этом окне...
В общем, если кто подскажет, будет здорово smile.gif

Автор: Unknown 17.04.2009 16:31

определять хэндл окна по его названию научился - findwindow, но это не совсем то, что нужно...

Автор: volvo 17.04.2009 16:36

Цитата
Я правильно понимаю, что мне нужен хэндл окна? еще нужно определить текстовое поле на этом окне...
Ты правильно понимаешь... На WinAPI это делается через FindWindow/FindWindowEx, в Шарпе - насколько я знаю - через вызов этих же функций с использованием механизма pInvoke. Последнее окно - это то, которое имеет фокус ввода? API-шная http://msdn.microsoft.com/en-us/library/ms646294(VS.85).aspx тебе поможет (вызывать - через тот же pInvoke)... Вот и все собственно...

Автор: Unknown 17.04.2009 16:46

Вот спасибо! оказывается есть полно полезных функций! smile.gif

Автор: Unknown 20.04.2009 17:08

Гмм... GetFocus все время возвращает 0... в чем может быть дело?

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


Добавлено через 18 мин.
Вроде бы GetForegroundWindow - то, что нужно! )

Автор: volvo 20.04.2009 17:28

Ну и чего ты творишь? Я ж тебе дал ссылку на 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 21.04.2009 6:12

спасибо, попробовал сделать по шагам - споткнулся на 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 21.04.2009 13:11

Цитата
попробовал сделать по шагам - споткнулся на 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 21.04.2009 14:25

Update: ответ на вопрос

Цитата
GetFocus - возвращает ноль. <...> В чем может быть дело?

http://www.syncfusion.com/FAQ/windowsforms/faq_c41c.aspx#q1021q - "// Note that if the focused Control is not a .Net control, then this will return null."

Автор: Гость 24.04.2009 8:02

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

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

Автор: volvo 25.04.2009 18:08

Установил себе наконец-то 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 27.04.2009 7:48

Если я не ошибаюсь, я пробовал на ICQ...
Спасибо за помощь, программу написал - с помощью SendKeys )