如何使用本机Win32 API从焦点窗口中获取所选文本?

leg*_*s2k 18 c windows clipboard winapi selection

我的应用.将在系统上运行,尝试监控热键; 当用户在任何窗口中选择一些文本并按下热键时,当我收到WM_HOTKEY消息时,如何获取所选文本?

为了将文本捕获到剪贴板,我尝试使用和向活动窗口()和forground window()发送Ctrl + C ; 尝试过这些组合; 一切都是徒劳.我可以使用普通的Win32系统API在Windows中获取焦点窗口的选定文本吗?keybd_event()SendInput()GetActiveWindow()GetForegroundWindow()

kur*_*ige 20

TL; DR:是的,有一种方法可以使用普通的win32系统API来实现这一点,但是很难正确实现.

WM_COPY和WM_GETTEXT可能有效,但并非在所有情况下都有效.它们依赖于正确处理请求的接收窗口 - 在许多情况下它不会.让我通过一种可能的方式来做这件事.它可能不像你希望的那么简单,但冒险中充满了win32编程的世界?准备?好.我们走吧.

首先,我们需要获取目标窗口的HWND id.有很多方法可以做到这一点.一种这样的方法是你上面提到的那个:得到前景窗口然后是带焦点的窗口等等.然而,有一个很多人忘记的巨大陷阱.获得前景窗口后,您必须 AttachThreadInput获得具有焦点的窗口.否则GetFocus()只会回来NULL.

有一种更简单的方法.Simply(miss)使用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().
}
Run Code Online (Sandbox Code Playgroud)

发送键击来复制文本更复杂一点......

我们将使用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().
}
Run Code Online (Sandbox Code Playgroud)

那里.那不是那么糟糕,是吗?

现在我们只需要看看剪贴板中的内容.这并不像你想象的那么简单."剪贴板"实际上可以包含同一事物的多个表示.复制到剪贴板时处于活动状态的应用程序可以控制放置剪贴板的确切位置.

例如,当您从Microsoft Office复制文本时,它会将RTF数据放在剪贴板中,同时将相同文本的纯文本表示形式放在一起.这样你就可以将它粘贴到wordpad和记事本中.写字板将使用富文本格式,而记事本将使用纯文本格式.

但是,对于这个简单的例子,我们假设我们只对明文感兴趣.

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. :)
}
Run Code Online (Sandbox Code Playgroud)

你有它!只需确保将lpstr复制到要使用的某个变量,不要直接使用lpstr,因为我们必须在关闭之前放弃对剪贴板内容的控制.

Win32编程起初可能非常令人生畏,但过了一段时间......它仍然令人生畏.

干杯!

  • 有效!哦,谢谢,折腾了好久,终于成功了!早些时候,当我尝试使用 SendInput 时,我也做了同样的事情,但问题是我注册的热键。我注册了`Ctrl + Alt + S`,并在_WM_HOTKEY_中,我使用`Ctrl + C`调用了SendInput。但是当用户按下`Ctrl + Alt + S`时,当我虚拟通过`Ctrl + C`时,`Alt`仍然会按下;当我将热键更改为“Windows + S”时,它工作得很好。在与“Alt”组合的热键中,当我实际上取消按下“Alt”(KEYEVENTF_KEYUP)时,它也可以工作。 (2认同)
  • @OliverWeichhold - 我相信(这是很久以前的事了)我只是简单地解释“发送 WM_COPY”将不起作用,除非你 **A)** 使用 `attachThreadInput`,并且 **b)** 获取窗口键盘焦点。然后我继续解释不相关的“SendInput”方法,该方法不使用任何 HWND id。这两个想法并没有像应有的那样清晰地分开。 (2认同)