Подтвердить что ты не робот

Как получить выделенный текст из сфокусированного окна с помощью собственного API Win32?

Мое приложение. будет работать в системе, попробуйте следить за горячей клавишей; когда пользователь выбирает какой-либо текст в любом окне и нажимает горячую клавишу, как мне получить выделенный текст, когда я получаю сообщение WM_HOTKEY?

Чтобы зафиксировать текст в буфер обмена, я попытался отправить Ctrl + C с помощью keybd_event() и SendInput() в активное окно (GetActiveWindow()) и пробное окно (GetForegroundWindow()); пробовали комбинации между ними; Все напрасно. Могу ли я получить выделенный текст сфокусированного окна в Windows с помощью простых системных API Win32?

4b9b3361

Ответ 1

TL; DR: Да, есть способ сделать это, используя простые API-интерфейсы win32, но это трудно реализовать правильно.

WM_COPY и WM_GETTEXT могут работать, но не во всех случаях. Они зависят от того, что окно приема правильно обрабатывает запрос, а во многих случаях оно не будет. Позвольте мне пробежать один из возможных способов сделать это. Это может быть не так просто, как вы надеялись, но что в приключенческом мире наполнено программированием win32? Готов? ОК. Отпустите.

Сначала нам нужно получить идентификатор HWND целевого окна. Есть много способов сделать это. Одним из таких подходов является тот, который вы упомянули выше: получите окно переднего плана, а затем окно с фокусом и т.д. Однако есть одна огромная, которую многие забывают. После того, как вы получите окно переднего плана, вы должны AttachThreadInput, чтобы получить окно с фокусом. В противном случае GetFocus() просто вернет NULL.

Существует намного более простой способ. Просто (пропустите) используйте функции GUITREADINFO. Это намного безопаснее, поскольку оно позволяет избежать всех скрытых опасностей, связанных с присоединением вашего входного потока к другой программе.

LPGUITHREADINFO lpgui = NULL;
HWND target_window = NULL;

if( GetGUIThreadInfo( NULL, lpgui ) )
    target_window = lpgui->hwndFocus;
else
{
    // You can get more information on why the function failed by calling
    // the win32 function, GetLastError().
}

Отправка нажатий клавиш для копирования текста немного более сложна...

Мы будем использовать SendInput вместо keybd_event, потому что это быстрее и, что самое главное, не может быть перепутано с помощью одновременного ввода пользователем или других программ, имитирующих нажатия клавиш.

Это означает, что программа должна будет запускаться в Windows XP или более поздней версии, однако, извините, если ваш запуск 98!

// We're sending two keys CONTROL and 'V'. Since keydown and keyup are two
// seperate messages, we multiply that number by two.
int key_count = 4;

INPUT* input = new INPUT[key_count];
for( int i = 0; i < key_count; i++ )
{
    input[i].dwFlags = 0;
    input[i].type = INPUT_KEYBOARD;
}

input[0].wVK = VK_CONTROL;
input[0].wScan = MapVirtualKey( VK_CONTROL, MAPVK_VK_TO_VSC );
input[1].wVK = 0x56 // Virtual key code for 'v'
input[1].wScan = MapVirtualKey( 0x56, MAPVK_VK_TO_VSC );
input[2].dwFlags = KEYEVENTF_KEYUP;
input[2].wVK = input[0].wVK;
input[2].wScan = input[0].wScan;
input[3].dwFlags = KEYEVENTF_KEYUP;
input[3].wVK = input[1].wVK;
input[3].wScan = input[1].wScan;

if( !SendInput( key_count, (LPINPUT)input, sizeof(INPUT) ) )
{
    // You can get more information on why this function failed by calling
    // the win32 function, GetLastError().
}

Там. Это было не так плохо, не так ли?

Теперь нам просто нужно взглянуть на то, что в буфере обмена. Это не так просто, как вы сначала подумали. "Буфер обмена" может фактически содержать несколько представлений одной и той же вещи. Приложение, которое активно при копировании в буфер обмена, контролирует, что именно помещать в буфер обмена.

При копировании текста из Microsoft Office, например, он помещает данные RTF в буфер обмена вместе с текстовым представлением одного и того же текста. Таким образом вы можете вставить его в блокнот и блокнот. Wordpad будет использовать формат rich-text, в то время как блокнот будет использовать формат текстового текста.

Однако для этого простого примера предположим, что нас интересует только открытый текст.

if( OpenClipboard(NULL) )
{
    // Optionally you may want to change CF_TEXT below to CF_UNICODE.
    // Play around with it, and check out all the standard formats at:
    // http://msdn.microsoft.com/en-us/library/ms649013(VS.85).aspx
    HGLOBAL hglb = GetClipboardData( CF_TEXT );
    LPSTR lpstr = GlobalLock(hglb);

    // Copy lpstr, then do whatever you want with the copy.

    GlobalUnlock(hglb);
    CloseClipboard();
}
else
{
    // You know the drill by now. Check GetLastError() to find out what
    // went wrong. :)
}

И вот оно! Просто убедитесь, что вы скопируете lpstr в какую-то переменную, которую хотите использовать, не используйте lpstr напрямую, так как мы должны уступить контроль над содержимым буфера обмена, прежде чем закрыть его.

Программирование Win32 может быть довольно сложным сначала, но через некоторое время... это все еще сложно.

Ура!

Ответ 2

Попробуйте добавить Sleep() после каждого SendInput(). Некоторые приложения просто не так быстро поймают ввод на клавиатуре.

Ответ 3

Попробуйте SendMessage (WM_COPY и т.д.).