【问题标题】:SendInput to Ctrl+C text Doesn't Work When Launched from Hotkey从热键启动时,SendInput 到 Ctrl+C 文本不起作用
【发布时间】:2018-01-07 15:52:33
【问题描述】:

当前使用此代码在 Windows 10 中当前打开的窗口中复制所选文本。如果我在目标程序(记事本)获得焦点时自行运行此代码,则该代码工作正常。记事本中选中的文本被复制到数据变量OK中。

wchar_t title[MAX_PATH];
HWND target_window = GetForegroundWindow();
GetWindowText(target_window, title, MAX_PATH);
std::wcout << "Target window is '" << title << "'" << std::endl;

// Send Control + C
int key_count = 4;

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

input[0].ki.wVk = VK_CONTROL;
input[0].ki.wScan = MapVirtualKey(VK_CONTROL, MAPVK_VK_TO_VSC);
input[1].ki.wVk = 0x43; // Virtual key code for 'c'
input[1].ki.wScan = MapVirtualKey(0x43, MAPVK_VK_TO_VSC);
input[2].ki.dwFlags = KEYEVENTF_KEYUP;
input[2].ki.wVk = input[0].ki.wVk;
input[2].ki.wScan = input[0].ki.wScan;

input[3].ki.dwFlags = KEYEVENTF_KEYUP;
input[3].ki.wVk = input[1].ki.wVk;
input[3].ki.wScan = input[1].ki.wScan;

if (!SendInput(key_count, (LPINPUT)input, sizeof(INPUT)))
{
    // TODO: error handling
}
else
{
    // not ideal but not sure of another way to wait for SendInput to complete
    Sleep(100); 
    if (OpenClipboard(NULL))
    {
        HGLOBAL hglb = GetClipboardData(CF_UNICODETEXT);
        LPWSTR lpwstr = (LPWSTR)(GlobalLock(hglb));
        std::wstring data(lpwstr);
        GlobalUnlock(hglb);
        CloseClipboard();
        // do something with selected text in data
    }
    else
    {
        // TODO: error handling
    }
}

但是,如果我通过热键启动完全相同的代码,它就不起作用:

if (RegisterHotKey(
    NULL,
    1,
    MOD_CONTROL | MOD_ALT | MOD_NOREPEAT,
    VK_OEM_2))  // back slash question mark key
{
    std::cout << "Hotkey 'Ctrl+Alt+/' registered, using MOD_NOREPEAT flag\n";
}

MSG msg = { 0 };
while (GetMessage(&msg, NULL, 0, 0) != 0)
{
    if (msg.message == WM_HOTKEY)
    {
        std::cout << "WM_HOTKEY received\n";

        // Call function to COPY TEXT here

        if (RegisterHotKey(
            NULL,
            1,
            MOD_CONTROL | MOD_ALT | MOD_NOREPEAT,
            VK_OEM_2))  // back slash question mark key
        {
            std::cout << "Hotkey 'Ctrl+Alt+/' registered, using MOD_NOREPEAT flag\n";
        }
    }
}

现在,在这两种情况下,GetWindowText() 都显示了我要从中复制文本的程序的标题。

另外,我写了一个简单的测试工具来检查 Ctrl+C 是否被传递给 Window,它就是。似乎 Ctrl+C 正在传递,但没有发生复制。

【问题讨论】:

  • 第一个代码 sn-p 中的注释表明,您要处理刚刚复制到剪贴板的数据。如果是这种情况,那么将数据复制到剪贴板是不合适的。用户可能不希望甚至不知道程序中的某些操作会破坏 他们的剪贴板数据。
  • 我很清楚这一点;剪贴板将在最终程序中备份和恢复。如果有另一种方法可以在不使用剪贴板的情况下从任何程序中获取文本,那么这将是可取的。但是,据我发现,其他直接抓取文本的方法并不能在所有程序中始终如一地工作。
  • 为什么不直接将WM_GETTEXT 发送到前台窗口的活动输入控件?查看GetGUIThreadInfo() 以找到该控件。
  • 据我所知(但我不是 win32 编程方面的专家)只能使用标准 Windows 控件或响应该消息的控件,我试图满足的要求是获取用户在各种应用程序中选择的文本,其中一些不是基于标准的 Windows 控件。我不确定这是否还会告诉我用户选择了哪些文本,或者只是控件中的文本,但我会进一步研究。

标签: c++ winapi sendinput


【解决方案1】:

是否有可能 Alt 由于热键而仍然关闭,而您实际上正在发送 Ctrl+Alt+C? SendInput 将输入直接插入到全局输入队列中。

您可以尝试设置一个计时器来响应热键,并在计时器处理程序中调用GetAsyncKeyState,直到所有修饰键都已启动,然后再生成输入。

更好的选择是使用UI Automation 而不是像这样的hack。

【讨论】:

  • GetAsyncKeyState 检查所有修饰键是否已修复问题。我还将研究 UI 自动化框架,但担心它可能不适用于我需要集成的某些 3rd 方应用程序。
猜你喜欢
  • 2019-04-29
  • 2014-06-04
  • 2014-08-23
  • 2014-07-14
  • 1970-01-01
  • 2013-03-20
  • 2018-05-13
  • 2023-04-03
  • 1970-01-01
相关资源
最近更新 更多