如何使用本地Win32 API从焦点窗口中获取选定的文本?

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

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

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

WM_COPY和WM_GETTEXT可能工作,但不是在所有情况下。 它们依赖于正确处理请求的接收窗口 – 在很多情况下它不会。 让我通过一个可能的方式来执行此操作。 它可能不像你期望的那么简单,但是冒险充满了win32编程的世界是什么? 准备? 好。 我们走吧。

首先我们需要获得目标窗口的HWND ID。 有很多方法可以做到这一点。 其中一种方法就是上面提到的方法:获得前景窗口,然后获得焦点窗口等等。但是,有一个很多人忘记的巨大问题。 获得前景窗口后, 必须使用 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数据放入剪贴板,同时还会显示同一文本的纯文本格式。 这样你可以把它粘贴到记事本和记事本中。 写字板将使用富文本格式,而记事本将使用纯文本格式。

对于这个简单的例子,假设我们只对纯文本感兴趣。

 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编程起初可能相当艰巨,但过了一段时间……仍然令人望而生畏。

干杯!

尝试在每个SendInput()之后添加一个Sleep() ()。 有些应用程序在捕捉键盘输入方面并不是那么快。

尝试SendMessage(WM_COPY等)。