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

发布于 2024-08-21 11:22:18 字数 325 浏览 7 评论 0原文

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

为了将文本捕获到剪贴板,我尝试使用 keybd_event()SendInput()Ctrl + C 发送到活动窗口 (< code>GetActiveWindow()) 和前景窗口 (GetForegroundWindow());尝试了其中的组合;一切都是徒劳的。我可以使用普通的 Win32 系统 API 获取 Windows 中聚焦窗口的选定文本吗?

My app. will be running on the system try monitoring for a hotkey; when the user selects some text in any window and presses a hotkey, how do I obtain the selected text, when I get the WM_HOTKEY message?

To capture the text on to the clipboard, I tried sending Ctrl + C using keybd_event() and SendInput() to the active window (GetActiveWindow()) and forground window (GetForegroundWindow()); tried combinations amongst these; all in vain. Can I get the selected text of the focused window in Windows with plain Win32 system APIs?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

狼性发作 2024-08-28 11:22:18

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 编程一开始可能会让人望而生畏,但过了一段时间……它仍然让人望而生畏。

干杯!

TL;DR: Yes, there is a way to do this using plain win32 system APIs, but it's difficult to implement correctly.

WM_COPY and WM_GETTEXT may work, but not in all cases. They depend on the receiving window handling the request correctly - and in many cases it will not. Let me run through one possible way of doing this. It may not be as simple as you were hoping, but what is in the adventure filled world of win32 programming? Ready? Ok. Let's go.

First we need to get the HWND id of the target window. There are many ways of doing this. One such approach is the one you mentioned above: get the foreground window and then the window with focus, etc. However, there is one huge gotcha that many people forget. After you get the foreground window you must AttachThreadInput to get the window with focus. Otherwise GetFocus() will simply return NULL.

There is a much easier way. Simply (miss)use the GUITREADINFO functions. It's much safer, as it avoids all the hidden dangers associated with attaching your input thread with another program.

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().
}

Sending the keystrokes to copy the text is a bit more involved...

We're going to use SendInput instead of keybd_event because it's faster, and, most importantly, cannot be messed up by concurrent user input, or other programs simulating keystrokes.

This does mean that the program will be required to run on Windows XP or later, though, so, sorry if your running 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().
}

There. That wasn't so bad, was it?

Now we just have to take a peek at what's in the clipboard. This isn't as simple as you would first think. The "clipboard" can actually hold multiple representations of the same thing. The application that is active when you copy to the clipboard has control over what exactly to place in the clipboard.

When you copy text from Microsoft Office, for example, it places RTF data into the clipboard, alongside a plain-text representation of the same text. That way you can paste it into wordpad and notepad. Wordpad would use the rich-text format, while notepad would use the plain-text format.

For this simple example, though, let's assume we're only interested in plaintext.

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. :)
}

And there you have it! Just make sure you copy lpstr to some variable you want to use, don't use lpstr directly, since we have to cede control of the contents of the clipboard before we close it.

Win32 programming can be quite daunting at first, but after a while... it's still daunting.

Cheers!

疑心病 2024-08-28 11:22:18

尝试在每个 SendInput() 之后添加一个 Sleep()。有些应用程序捕获键盘输入的速度并不快。

Try adding a Sleep() after each SendInput(). Some apps just aren't that fast in catching keyboard input.

眼泪淡了忧伤 2024-08-28 11:22:18

尝试SendMessage(WM_COPY等)。

Try SendMessage(WM_COPY, etc. ).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文