Windows注册鼠标钩子,获取用户选中的文本

打印 上一主题 下一主题

主题 1368|帖子 1368|积分 4104

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
注册鼠标钩子

  1. // 注册鼠标钩子
  2. HHOOK hMouseHook;
  3. hMouseHook = SetWindowsHookEx(WH_MOUSE_LL,
  4.     MouseProc,
  5.     GetModuleHandle(NULL),
  6.     0);
  7. // 取消鼠标钩子
  8. UnhookWindowsHookEx(hMouseHook);
  9. hMouseHook = nullptr;
复制代码
上述代码中MouseProc方法用于处理系统的鼠标消息
处理鼠标消息

  1. LRESULT MouseHook::MouseProc(int nCode, WPARAM wParam, LPARAM lParam)
  2. {
  3.     static QPoint pos;
  4.     static qint64 lastTriggerTime = 0;
  5.     if (nCode >= 0) {
  6.         if (wParam == WM_LBUTTONDOWN) {
  7.             pos = QCursor::pos();           
  8.         }
  9.         else if (wParam == WM_LBUTTONUP) {
  10.             if (pos != QCursor::pos()) {
  11.                 timer->start(280);  //拖拽
  12.             }
  13.             qint64 currentTime = QDateTime::currentMSecsSinceEpoch();
  14.             if (currentTime - lastTriggerTime <= 500) {
  15.                 timer->start(280); //双击
  16.             }
  17.             lastTriggerTime = currentTime;
  18.         }
  19.     }
  20.     return CallNextHookEx(hMouseHook, nCode, wParam, lParam);
  21. }
复制代码
这段代码中,我们使用一个QTimer来处理双击或拖拽选中文本的操作。
文本选中后的处理方法

  1. void MouseHook::processMouseUp()
  2. {
  3.     auto textGetter = new TextGetter(this);
  4.     connect(textGetter,
  5.         &TextGetter::resultReady,
  6.         this,
  7.         &MouseHook::onTextReady,
  8.         Qt::ConnectionType::QueuedConnection);
  9.     textGetter->start();
  10. }
复制代码
TextGetter是一个继承自QThread的类,我们将在一个新线程中去获取用户选中的文本。
获取选中文本的第一种情况

  1. QString TextGetter::getSelectedTextByUIAutomation() {
  2.     try {
  3.         auto hr = CoInitialize(NULL);
  4.         if (FAILED(hr))
  5.         {
  6.             CoUninitialize();
  7.             return "";
  8.         }
  9.         CComPtr<IUIAutomation> automation;
  10.         hr = CoCreateInstance(CLSID_CUIAutomation, nullptr, CLSCTX_INPROC_SERVER, IID_IUIAutomation,(void**)(&automation));
  11.         if (FAILED(hr))
  12.         {
  13.             CoUninitialize();
  14.             return "";
  15.         }
  16.         CComPtr<IUIAutomationElement> focusedElement;
  17.         hr = automation->GetFocusedElement(&focusedElement);
  18.         if (FAILED(hr) || !focusedElement)
  19.         {
  20.             CoUninitialize();
  21.             return "";
  22.         }
  23.         CComPtr<IUIAutomationTextPattern> textPattern;
  24.         hr = focusedElement->GetCurrentPatternAs(UIA_TextPatternId, IID_PPV_ARGS(&textPattern));
  25.         if (FAILED(hr) || !textPattern)
  26.         {
  27.             CoUninitialize();
  28.             return "";
  29.         }
  30.         CComPtr<IUIAutomationTextRangeArray> selection;
  31.         hr = textPattern->GetSelection(&selection);
  32.         if (FAILED(hr) || !selection)
  33.         {
  34.             CoUninitialize();
  35.             return "";
  36.         }
  37.         CComPtr<IUIAutomationTextRange> range;
  38.         hr = selection->GetElement(0, &range);
  39.         if (FAILED(hr) || !range)
  40.         {
  41.             CoUninitialize();
  42.             return "";
  43.         }
  44.         CComBSTR text;
  45.         range->GetText(-1, &text);
  46.         std::wstring ws(text, SysStringLen(text));
  47.         CoUninitialize();
  48.         return QString::fromStdWString(ws);
  49.     }
  50.     catch (std::exception& e) {
  51.         return "";
  52.     }      
  53. }
复制代码
这段代码使用 Microsoft UI Automation (UIA) API 从当前具有焦点的 UI 元素中获取选中文本的方法。但有的时候这种方法获取不到想要的文本(老式窗口中的文本)
获取选中文本的第二种情况

当第一种情况获取到的文本是空时,就要尝试第二种情况
  1. auto hwnd = getCurrentHwnd();
  2. if (!hwnd) {
  3.     return "";
  4. }
  5. auto cache = cacheClipboard();
  6. sendCtrlC();
  7. str = getClipboardText();
  8. if (str.isEmpty()) {
  9.     CloseClipboard();
  10.     return "";
  11. }
  12. restoreClipboard(cache);
  13. CloseClipboard();
复制代码
这种情况,先获取系统当前聚焦的窗口,然后缓存当前剪切板,然后发送Ctrl+C复制此窗口选中的文本,然后获取剪切板内的文本,然后把之前缓存的内容存入剪切板。
下面我们看看这些实当代码:
获取系统当前聚焦的窗口

  1. HWND TextGetter::getCurrentHwnd()
  2. {
  3.     HWND hwnd = GetForegroundWindow();
  4.     DWORD threadId = GetWindowThreadProcessId(hwnd, NULL);
  5.     AttachThreadInput(GetCurrentThreadId(), threadId, TRUE);
  6.     hwnd = GetFocus();
  7.     AttachThreadInput(GetCurrentThreadId(), threadId, FALSE);
  8.     POINT pt;
  9.     GetCursorPos(&pt);
  10.     RECT rect;
  11.     GetWindowRect(hwnd, &rect);
  12.     if (pt.x<rect.left || pt.y<rect.top || pt.x>rect.right || pt.y>rect.bottom) {
  13.         return nullptr;
  14.     }
  15.     return hwnd;
  16. }
复制代码
假如聚焦的窗口与鼠标所在位置的窗口不是一个窗口,那么我们取消任务。
缓存剪切板的内容

  1. ClipboardData TextGetter::cacheClipboard()
  2. {
  3.     OpenClipboard(nullptr);
  4.     ClipboardData cache;
  5.     UINT format = 0;
  6.     while ((format = EnumClipboardFormats(format)) != 0) {
  7.         HANDLE hData = GetClipboardData(format);
  8.         if (hData) {
  9.             SIZE_T size = GlobalSize(hData);
  10.             HGLOBAL hCopy = GlobalAlloc(GMEM_MOVEABLE, size);
  11.             if (hCopy) {
  12.                 void* pDest = GlobalLock(hCopy);
  13.                 void* pSource = GlobalLock(hData);
  14.                 if (pDest && pSource) {
  15.                     memcpy(pDest, pSource, size);
  16.                 }
  17.                 GlobalUnlock(hData);
  18.                 GlobalUnlock(hCopy);
  19.                 cache.push_back({ format, hCopy });
  20.             }
  21.         }
  22.     }
  23.     EmptyClipboard();
  24.     CloseClipboard();
  25.     return cache;
  26. }
复制代码
发送Ctrl+C按键消息

  1. void TextGetter::sendCtrlC()
  2. {
  3.     INPUT inputs[4] = { 0 };
  4.     inputs[0].type = INPUT_KEYBOARD;
  5.     inputs[0].ki.wVk = VK_CONTROL;
  6.     inputs[1].type = INPUT_KEYBOARD;
  7.     inputs[1].ki.wVk = 'C';
  8.     inputs[2].type = INPUT_KEYBOARD;
  9.     inputs[2].ki.wVk = 'C';
  10.     inputs[2].ki.dwFlags = KEYEVENTF_KEYUP;
  11.     inputs[3].type = INPUT_KEYBOARD;
  12.     inputs[3].ki.wVk = VK_CONTROL;
  13.     inputs[3].ki.dwFlags = KEYEVENTF_KEYUP;
  14.     SendInput(ARRAYSIZE(inputs), inputs, sizeof(INPUT));
  15.     QThread::msleep(360);
  16. }
复制代码
按键发送乐成后须要等待360毫秒
获取剪切板的内容

  1. QString TextGetter::getClipboardText()
  2. {
  3.     OpenClipboard(nullptr);
  4.     if (!IsClipboardFormatAvailable(CF_UNICODETEXT)) {
  5.         CloseClipboard();
  6.         return "";
  7.     }
  8.     HANDLE hData = GetClipboardData(CF_UNICODETEXT);
  9.     if (hData == nullptr) {
  10.         CloseClipboard();
  11.         return "";
  12.     }
  13.     LPCWSTR pText = static_cast<LPCWSTR>(GlobalLock(hData));
  14.     if (pText == nullptr) {
  15.         CloseClipboard();
  16.         return "";
  17.     }
  18.     QString result = QString::fromWCharArray(pText);
  19.     GlobalUnlock(hData);
  20.     CloseClipboard();
  21.     return result;
  22. }
复制代码
不要猜疑发送按键Ctrl+C这个方案是否可行,有道词典就是这么干的。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

光之使者

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表