feat: 添加监听系统聚焦窗口变化

This commit is contained in:
sleepwithoutbz
2025-09-24 12:31:49 +08:00
parent 9e5421bd05
commit acc2b20e5e
4 changed files with 138 additions and 9 deletions

View File

@@ -32,12 +32,15 @@ add_subdirectory(ElaWidgetTools)
# 导出src下的cpp文件 # 导出src下的cpp文件
file(GLOB_RECURSE SRC_FILES CONFIGURE_DEPENDS src/*.cpp src/UI/*.cpp) file(GLOB_RECURSE SRC_FILES CONFIGURE_DEPENDS src/*.cpp src/UI/*.cpp)
qt_add_executable(cbh qt_add_executable(${PROJECT_NAME}
MANUAL_FINALIZATION MANUAL_FINALIZATION
${SRC_FILES} ${SRC_FILES}
) )
target_link_libraries(cbh PRIVATE # 添加宏定义,输出日志所处文件位置
target_compile_definitions(${PROJECT_NAME} PRIVATE QT_MESSAGELOGCONTEXT)
target_link_libraries(${PROJECT_NAME} PRIVATE
Qt6::Widgets Qt6::Widgets
Qt6::Core Qt6::Core
Qt6::Gui Qt6::Gui
@@ -50,7 +53,7 @@ set(cbh_resource_files
"assets/icon.png" "assets/icon.png"
) )
qt_add_resources(cbh "cbh" qt_add_resources(${PROJECT_NAME} "cbh"
PREFIX PREFIX
"/" "/"
FILES FILES
@@ -63,7 +66,7 @@ qt_add_resources(cbh "cbh"
if(${QT_VERSION} VERSION_LESS 6.1.0) if(${QT_VERSION} VERSION_LESS 6.1.0)
set(BUNDLE_ID_OPTION MACOSX_BUNDLE_GUI_IDENTIFIER com.example.cbh) set(BUNDLE_ID_OPTION MACOSX_BUNDLE_GUI_IDENTIFIER com.example.cbh)
endif() endif()
set_target_properties(cbh PROPERTIES set_target_properties(${PROJECT_NAME} PROPERTIES
${BUNDLE_ID_OPTION} ${BUNDLE_ID_OPTION}
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION} MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR} MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
@@ -72,10 +75,10 @@ set_target_properties(cbh PROPERTIES
) )
include(GNUInstallDirs) include(GNUInstallDirs)
install(TARGETS cbh install(TARGETS ${PROJECT_NAME}
BUNDLE DESTINATION . BUNDLE DESTINATION .
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
) )
qt_finalize_executable(cbh) qt_finalize_executable(${PROJECT_NAME})

View File

@@ -1,15 +1,25 @@
#include "ElaApplication.h" #include "ElaApplication.h"
#include <QScreen>
#include <QApplication>
#include "window.h"
#include "clipboard.h" #include "clipboard.h"
#include "winSpy.h"
#include "window.h"
#include <QApplication>
#include <QScreen>
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
#include <Windows.h> #include <Windows.h>
#endif #endif
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
QApplication a(argc, argv); QApplication a(argc, argv);
// 设置消息模式:输出时间、类型、文件、行号和消息本身
#ifdef QT_MESSAGELOGCONTEXT
qSetMessagePattern("[%{time yyyy-MM-dd HH:mm:ss.zzz}] [%{type}] %{file}:%{line} - %{message}");
#else
qSetMessagePattern("[%{time yyyy-MM-dd HH:mm:ss.zzz}] [%{type}] - %{message}");
#endif
// 设置监听剪贴板
ClipboardMonitor cbm(&a); ClipboardMonitor cbm(&a);
// 设置监听系统窗口焦点变化
WinSpy spy;
eApp->init(); eApp->init();
MainWindow w; MainWindow w;
w.show(); w.show();

103
src/winSpy.cpp Normal file
View File

@@ -0,0 +1,103 @@
#include <QDebug.h>
#include <QString>
#include <windows.h>
#include "winSpy.h"
// 全局变量,保存钩子句柄
static HWINEVENTHOOK hook = NULL;
// 获取窗口标题的辅助函数
QString GetWindowTitle(HWND hwnd) {
char title[256];
if (GetWindowTextA(hwnd, title, sizeof(title))) {
return QString(title);
}
return "Untitled";
}
// 获取窗口所属进程名的辅助函数
QString GetWindowProcessName(HWND hwnd) {
DWORD processId;
GetWindowThreadProcessId(hwnd, &processId);
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processId);
if (hProcess) {
char processName[MAX_PATH];
DWORD size = sizeof(processName);
if (QueryFullProcessImageNameA(hProcess, 0, processName, &size)) {
CloseHandle(hProcess);
QString fullPath(processName);
// 只返回可执行文件名称
qsizetype lastSlash = fullPath.lastIndexOf("\\");
if (lastSlash != -1) {
return fullPath.mid(lastSlash + 1);
}
return fullPath;
}
CloseHandle(hProcess);
}
return "Unknown";
}
// 事件回调函数
void CALLBACK WinEventProc(HWINEVENTHOOK hWinEventHook, DWORD event, HWND hwnd, LONG idObject, LONG idChild, DWORD dwEventThread,
DWORD dwmsEventTime) {
// 确保事件对象是窗口且事件是前台窗口改变
if (idObject != OBJID_WINDOW || idChild != CHILDID_SELF) {
return;
}
if (event == EVENT_SYSTEM_FOREGROUND) {
HWND foregroundWindow = GetForegroundWindow(); // 获取当前前景窗口
if (foregroundWindow) {
QString title = GetWindowTitle(foregroundWindow);
QString processName = GetWindowProcessName(foregroundWindow);
// 记录信息,这里示例为输出到控制台
qDebug() << "Foreground Window Changed:";
qDebug() << " Handle: " << foregroundWindow;
qDebug() << " Title: " << title;
qDebug() << " Process: " << processName;
qDebug() << "----------------------------------------";
// TODO: 处理窗口数据保存
}
}
}
void WinSpy::setHook() {
if (hook) {
qDebug() << "Already monitoring.";
return;
}
// 设置事件钩子,监听前台窗口变化事件
hook = SetWinEventHook(EVENT_SYSTEM_FOREGROUND, // 最小事件值
EVENT_SYSTEM_FOREGROUND, // 最大事件值
NULL, // 钩子函数所在的DLL句柄NULL表示当前进程
WinEventProc, // 事件回调函数指针
0, // 感兴趣的进程ID0表示所有进程
0, // 感兴趣的线程ID0表示所有线程
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS // 标志:上下文外,跳过自身进程事件
);
if (!hook) {
qWarning() << "Failed to set win event hook!";
return;
}
qDebug() << "Start monitoring foreground window changes";
// // 消息循环是必须的,否则回调函数不会被调用
// MSG msg;
// while (GetMessage(&msg, NULL, 0, 0)) {
// TranslateMessage(&msg);
// DispatchMessage(&msg);
// }
}
void WinSpy::freeHook() {
// 程序退出前卸载钩子
if (hook) {
UnhookWinEvent(hook);
}
}
WinSpy::WinSpy() { setHook(); }
WinSpy::~WinSpy() { freeHook(); }

13
src/winSpy.h Normal file
View File

@@ -0,0 +1,13 @@
#pragma once
#include <windows.h>
class WinSpy {
public:
WinSpy();
~WinSpy();
private:
void setHook();
void freeHook();
};