HotKey,SendMessage,PostMessage基础

本文介绍如何在MFC应用程序中实现热键注册与处理,包括单文档界面中使用OnChar与OnHotKey处理函数来响应特定的键盘输入,并展示了如何在对话框中使用热键进行窗口显示与隐藏的操作。
/****** 单文档中使用HotKey和OnChar ******/
/****** testView.h ******/
public:
 virtual ~CTestView();
 virtual void OnInitialUpdate();

protected:
 
//{{AFX_MSG(CTestView)
 afx_msg void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags);
 
//}}AFX_MSG
 afx_msg LONG OnHotKey(WPARAM wParam,LPARAM lParam);
 DECLARE_MESSAGE_MAP()

/****** testView.cpp ******/
BEGIN_MESSAGE_MAP(CTestView, CView)
//{{AFX_MSG_MAP(CTestView)
ON_WM_CHAR() 
//}}AFX_MSG_MAP
ON_MESSAGE(WM_HOTKEY,OnHotKey)
// Standard printing commands
//.......
END_MESSAGE_MAP()

/*注册热键*/
void CTestView::OnInitialUpdate()
{
    ASSERT(NULL != GetSafeHwnd());   
    
    
//Register热键   
#ifdef _DEBUG 
//debug版本   
    int nRet = RegisterHotKey(GetSafeHwnd(),199,MOD_CONTROL,'I'); 
//热键 ctrl + i  
    if(!nRet)   
        AfxMessageBox(_T("RegisterHotKey 0 false"));   
    nRet = RegisterHotKey(GetSafeHwnd(),201,MOD_ALT,'B'); 
//热键 alt + b  
    nRet = RegisterHotKey(GetSafeHwnd(),203,MOD_ALT,'E'); 
//热键 alt + e   
    if(!nRet)   
        AfxMessageBox(_T("RegisterHotKey 1 false"));   
#else 
//release版本   
    RegisterHotKey(GetSafeHwnd(),199,MOD_CONTROL,'I'); 
//热键 ctrl + i   
    RegisterHotKey(GetSafeHwnd(),201,MOD_ALT,'B'); 
//热键 alt + b  
    RegisterHotKey(GetSafeHwnd(),203,MOD_ALT,'E'); 
//热键 alt + e 
#endif 
}

/*处理键盘消息*/
void CTestView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
    if(GetKeyState(VK_CONTROL) & 0x8000)
    {
        if(nChar == 2)  /*Ctrl+b*/
        {  AfxMessageBox("您按了组合键:Ctrl+b");
        }
        else if(nChar == 5/*Ctrl+e*/
        {  AfxMessageBox("您按了组合键:Ctrl+e");
        } 
        else if (nChar == 9/*Ctrl+i*/
        {
        AfxMessageBox("您按了组合键:Ctrl+i");
        }   
    }   
    CView::OnChar(nChar, nRepCnt, nFlags);
}

/*接收组合键消息*/
LONG CTestView::OnHotKey(WPARAM wParam,LPARAM lParam)            
{  
    UINT fuModifiers = (UINT) LOWORD(lParam);  
// key-modifier flags    
    UINT uVirtKey = (UINT) HIWORD(lParam);     
// virtual-key code    
    
    
//判断响应了什么热键   
    if( MOD_CONTROL == fuModifiers && 'I' == uVirtKey )   
    {   
        AfxMessageBox(_T("你按下了组合键 ctrl + i"));     
    }   
    else if( MOD_ALT == fuModifiers && 'B' == uVirtKey )   
    {   
        AfxMessageBox(_T("你按下了组合键 alt + b"));     
    }   
    else if( MOD_ALT == fuModifiers && 'E' == uVirtKey )   
    {   
        AfxMessageBox(_T("你按下了组合键 alt + e"));     
    } 
    else  
    {
        AfxMessageBox(_T("你按下了未知热键"));
    }
    
    return 0;           
} 
////////////////////////////////////////////////////////////////////////////
/****** 对话框中使用HotKey ******/
/****** FDiskDlg.h ******/
    
//{{AFX_VIRTUAL(CFDiskDlg)
    public:
    virtual BOOL DestroyWindow();
    protected:
    virtual void DoDataExchange(CDataExchange* pDX); 
// DDX/DDV support
    
//}}AFX_VIRTUAL

    
//{{AFX_MSG(CFDiskDlg)
    virtual BOOL OnInitDialog();
    afx_msg void OnHotKey(WPARAM wp,LPARAM lp);
    afx_msg void OnTimer(UINT nIDEvent);
    
//}}AFX_MSG
/****** FDiskDlg.cpp ******/
BEGIN_MESSAGE_MAP(CFDiskDlg, CDialog)
//{{AFX_MSG_MAP(CFDiskDlg)
ON_MESSAGE(WM_HOTKEY,OnHotKey)
ON_WM_TIMER()
    
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

BOOL CFDiskDlg::OnInitDialog()
{   
  ::RegisterHotKey(m_hWnd,199,MOD_ALT,'X');
  ::RegisterHotKey(m_hWnd,201,MOD_ALT,'Z');
  SetTimer(0,80,NULL);
return TRUE;
}

void CFDiskDlg::OnHotKey(WPARAM wp,LPARAM lp)
//热键
{
    if(wp==199)
    {
        
//if(IsWindowVisible())
            ShowWindow(SW_HIDE);
        
//else
            
//ShowWindow(SW_SHOWNORMAL);
            
//ShowWindow(SW_SHOW);
    }
    if (wp ==201)
    {
        ShowWindow(SW_SHOW);
    }
}

BOOL CFDiskDlg::DestroyWindow() 
{
    
//::UnregisterHotKey(m_hWnd,199);//释放注册的热键
    
//::UnregisterHotKey(m_hWnd,201);//释放注册的热键
    return CDialog::DestroyWindow();
}

void CFDiskDlg::OnTimer(UINT nIDEvent) 
{
        
//定时执行的任务  
    CDialog::OnTimer(nIDEvent);
}
////////////////////////////////////////////////////////////////////////////
/*向记事本文件发消息*/
HWND hwnd=::FindWindow(NULL,"无标题 - 记事本");  
//HWND hedit = ::GetDlgItem(hwnd, 0x0000000F);
HWND hedit = FindWindowEx(hwnd, NULL, "edit", NULL);
::PostMessage(hedit,WM_KEYUP,'1',0); /*向记事本发消息*/
::SendMessage(hwnd,WM_CLOSE,0,0);    /*关闭记事本文件*/
你修改一下,现在只能是二维,如果多维的也要处理,import sys import win32api import win32gui import win32clipboard import win32con import time import threading import keyboard # 需要安装:pip install keyboard from datetime import datetime, date from PyQt5.QtWidgets import (QApplication, QMainWindow, QMessageBox, QVBoxLayout, QLabel, QPushButton, QLineEdit, QWidget, QTableWidget, QTableWidgetItem, QHBoxLayout, QGroupBox, QDialog, QFormLayout, QDialogButtonBox) from PyQt5.QtCore import Qt, QThread, pyqtSignal, QTimer # 检查日期限制 EXPIRY_DATE = datetime(2026, 6, 1) if datetime.now() > EXPIRY_DATE: QMessageBox.critical(None, "过期提示", "此软件已过期,无法使用。") sys.exit(1) def show_error_message(message): """显示错误信息对话框。""" msg = QMessageBox() msg.setIcon(QMessageBox.Critical) msg.setText(message) msg.setWindowTitle("错误") msg.setStandardButtons(QMessageBox.Ok) msg.exec_() def show_success_message(message): """显示成功信息对话框。""" msg = QMessageBox() msg.setIcon(QMessageBox.Information) msg.setText(message) msg.setWindowTitle("成功") msg.setStandardButtons(QMessageBox.Ok) msg.exec_() def get_window_handle_at_mouse_cursor(): """获取当前鼠标光标位置下的窗口句柄。""" cursor_pos = win32api.GetCursorPos() hwnd = win32gui.WindowFromPoint(cursor_pos) return hwnd def clear_window_content(hwnd): """清空窗口内容""" # 检查窗口是否有效 if not win32gui.IsWindow(hwnd): return False # 尝试获取窗口类名 try: class_name = win32gui.GetClassName(hwnd) # 对于编辑框控件 if class_name in ["Edit", "RichEdit", "RichEdit20W"]: win32gui.SendMessage(hwnd, win32con.EM_SETSEL, 0, -1) # 全选 win32gui.SendMessage(hwnd, win32con.WM_CLEAR, 0, 0) # 清除 return True except: pass # 通用方法 try: win32gui.SendMessage(hwnd, win32con.WM_SETTEXT, 0, "") return True except: return False def send_text_directly(hwnd, text): """直接向窗口发送文本""" # 检查窗口是否有效 if not win32gui.IsWindow(hwnd): return False # 尝试获取窗口类名 try: class_name = win32gui.GetClassName(hwnd) # 对于编辑框控件 if class_name in ["Edit", "RichEdit", "RichEdit20W"]: win32gui.SendMessage(hwnd, win32con.WM_SETTEXT, 0, text) # 发送回车 win32gui.PostMessage(hwnd, win32con.WM_KEYDOWN, win32con.VK_RETURN, 0) win32gui.PostMessage(hwnd, win32con.WM_KEYUP, win32con.VK_RETURN, 0) return True except: pass # 通用方法 try: # 先清空内容 win32gui.SendMessage(hwnd, win32con.WM_SETTEXT, 0, "") # 发送文本 win32gui.SendMessage(hwnd, win32con.WM_SETTEXT, 0, text) # 发送回车 win32gui.PostMessage(hwnd, win32con.WM_KEYDOWN, win32con.VK_RETURN, 0) win32gui.PostMessage(hwnd, win32con.WM_KEYUP, win32con.VK_RETURN, 0) return True except: return False def get_clipboard_data_as_list(): """获取剪贴板内容并返回一维或二维列表。""" for _ in range(3): # 重试3次 try: win32clipboard.OpenClipboard(None) clipboard_data = win32clipboard.GetClipboardData(win32con.CF_UNICODETEXT) win32clipboard.CloseClipboard() lines = [line.strip() for line in clipboard_data.split('\n') if line.strip()] if any('\t' in line for line in lines): return [line.split('\t') for line in lines] else: return lines except Exception: try: win32clipboard.CloseClipboard() except: pass time.sleep(0.1) return [] class SenderThread(QThread): """发送线程,支持停止功能""" progress = pyqtSignal(int, int) # 当前进度,总数量 finished = pyqtSignal(bool, str) # 是否成功,消息 error = pyqtSignal(str) # 错误消息 def __init__(self, parent, hwnds, data, delay): super().__init__(parent) self.hwnds = hwnds # 窗口句柄列表 self.data = data # 要发送的数据 self.delay = delay # 延时 self._stop_flag = False # 停止标志 def stop(self): """停止发送""" self._stop_flag = True def run(self): """执行发送操作""" try: total = len(self.data) for i, item in enumerate(self.data): # 检查停止标志 if self._stop_flag: self.finished.emit(False, "用户停止发送") return # 检查窗口句柄是否有效 for hwnd in self.hwnds: if not win32gui.IsWindow(hwnd): self.error.emit(f"目标窗口已关闭或句柄无效: {hwnd}") self.finished.emit(False, "窗口已关闭") return # 发送数据 if isinstance(item, list): # 二维数据 if len(item) != len(self.hwnds): self.error.emit(f"数据维度与窗口数量不匹配: {len(item)} != {len(self.hwnds)}") continue for j, text in enumerate(item): if not send_text_directly(self.hwnds[j], text): self.error.emit(f"向窗口 {self.hwnds[j]} 发送数据失败") else: # 一维数据 if not send_text_directly(self.hwnds[0], item): self.error.emit(f"向窗口 {self.hwnds[0]} 发送数据失败") # 更新进度 self.progress.emit(i + 1, total) # 延时 time.sleep(self.delay) self.finished.emit(True, "发送完成") except Exception as e: self.finished.emit(False, f"发送过程中发生错误: {str(e)}") class ClipboardApp(QMainWindow): def __init__(self): super().__init__() self.sender_thread = None self.stop_requested = False self.initUI() self.setWindowTitle(f"剪贴板自动发送工具 (按F8停止)") # 设置热键处理 self.hotkey_timer = QTimer(self) self.hotkey_timer.timeout.connect(self.check_hotkey) self.hotkey_timer.start(100) # 每100毫秒检查一次热键 def initUI(self): self.setGeometry(300, 300, 800, 600) central_widget = QWidget() self.setCentralWidget(central_widget) layout = QVBoxLayout() # 添加预览区域 preview_layout = QHBoxLayout() preview_btn = QPushButton("预览剪贴板", self) preview_btn.clicked.connect(self.preview_clipboard) preview_layout.addWidget(preview_btn) layout.addLayout(preview_layout) # 添加表格预览 self.table = QTableWidget(self) self.table.setMinimumHeight(300) layout.addWidget(self.table) # 进度标签 self.progress_label = QLabel("准备就绪", self) layout.addWidget(self.progress_label) # 延时设置 delay_layout = QHBoxLayout() delay_label = QLabel("发送延时(秒):", self) self.delay_input = QLineEdit(self) self.delay_input.setPlaceholderText("输入延时") self.delay_input.setText("1.0") # 默认值 delay_layout.addWidget(delay_label) delay_layout.addWidget(self.delay_input) layout.addLayout(delay_layout) # 说明标签 self.label = QLabel( "使用说明:\n" "1. 复制要发送的内容\n" "2. 点击预览按钮查看内容\n" "3. 设置发送延时\n" "4. 点击开始后将鼠标移动到目标窗口\n" "5. 按F8键可随时停止发送" ) layout.addWidget(self.label) # 按钮布局 button_layout = QHBoxLayout() # 开始按钮 self.start_button = QPushButton('开始发送', self) self.start_button.clicked.connect(self.start) button_layout.addWidget(self.start_button) # 停止按钮 self.stop_button = QPushButton('停止发送', self) self.stop_button.clicked.connect(self.stop_sending) self.stop_button.setEnabled(False) button_layout.addWidget(self.stop_button) layout.addLayout(button_layout) # 添加有效期提示 expiry_label = QLabel(f"如有问题请联系我", self) expiry_label.setStyleSheet("color: red; font-weight: bold;") layout.addWidget(expiry_label) central_widget.setLayout(layout) def check_hotkey(self): """检查热键是否被按下""" if keyboard.is_pressed('f8'): self.stop_sending() # 等待按键释放,避免连续触发 while keyboard.is_pressed('f8'): time.sleep(0.1) def preview_clipboard(self): """预览剪贴板内容""" try: data = get_clipboard_data_as_list() if not data: show_error_message("剪贴板为空") return # 判断是一维还是二维数据 if isinstance(data[0], list): # 二维数据 rows = len(data) cols = max(len(row) for row in data) self.table.setRowCount(rows) self.table.setColumnCount(cols) # 填充数据 for i, row in enumerate(data): for j, value in enumerate(row): item = QTableWidgetItem(str(value)) self.table.setItem(i, j, item) else: # 一维数据 self.table.setRowCount(len(data)) self.table.setColumnCount(1) # 填充数据 for i, value in enumerate(data): item = QTableWidgetItem(str(value)) self.table.setItem(i, 0, item) # 自动调整列宽 self.table.resizeColumnsToContents() self.progress_label.setText(f"已加载 {len(data)} 行数据") show_success_message("预览已更新") except Exception as e: show_error_message(f"预览失败: {str(e)}") def start(self): """开始处理剪贴板内容""" try: self.delay = float(self.delay_input.text()) except ValueError: show_error_message("请输入有效的延时!") return # 获取当前表格中的数据 rows = self.table.rowCount() cols = self.table.columnCount() if rows == 0: show_error_message("没有数据可发送!") return self.progress_label.setText("请在5秒内移动鼠标到目标窗口...") QApplication.processEvents() # 更新UI time.sleep(5) hwnd = get_window_handle_at_mouse_cursor() # 检查窗口是否有效 if not win32gui.IsWindow(hwnd): show_error_message("目标窗口无效或已关闭!") return if cols == 1: # 一维数据 data = [ self.table.item(row, 0).text() for row in range(rows) if self.table.item(row, 0) ] hwnds = [hwnd] else: # 二维数据 self.progress_label.setText("请在5秒内移动鼠标到第二个目标窗口...") QApplication.processEvents() # 更新UI time.sleep(5) hwnd1 = get_window_handle_at_mouse_cursor() # 检查窗口是否有效 if not win32gui.IsWindow(hwnd1): show_error_message("第二个目标窗口无效或已关闭!") return # 构建二维数据 data = [] for row in range(rows): row_data = [] for col in range(cols): item = self.table.item(row, col) row_data.append(item.text() if item else "") data.append(row_data) hwnds = [hwnd, hwnd1] # 创建并启动发送线程 self.sender_thread = SenderThread(self, hwnds, data, self.delay) self.sender_thread.progress.connect(self.update_progress) self.sender_thread.finished.connect(self.sending_finished) self.sender_thread.error.connect(self.show_error) # 更新UI状态 self.start_button.setEnabled(False) self.stop_button.setEnabled(True) self.progress_label.setText("开始发送...按F8键停止") self.sender_thread.start() def stop_sending(self): """停止发送操作""" if self.sender_thread and self.sender_thread.isRunning(): self.sender_thread.stop() self.stop_button.setEnabled(False) self.progress_label.setText("正在停止...") def update_progress(self, current, total): """更新进度显示""" self.progress_label.setText(f"发送进度: {current}/{total} ({current / total * 100:.1f}%)") def sending_finished(self, success, message): """发送完成处理""" self.start_button.setEnabled(True) self.stop_button.setEnabled(False) if success: self.progress_label.setText(message) show_success_message(message) else: self.progress_label.setText(f"发送失败: {message}") show_error_message(message) def show_error(self, message): """显示错误消息""" self.progress_label.setText(f"错误: {message}") show_error_message(message) def closeEvent(self, event): """窗口关闭事件处理""" if self.sender_thread and self.sender_thread.isRunning(): self.sender_thread.stop() self.sender_thread.wait(2000) # 等待2秒 # 停止热键检查计时器 self.hotkey_timer.stop() event.accept() if __name__ == '__main__': app = QApplication(sys.argv) # 再次检查日期限制(双保险) if datetime.now() > EXPIRY_DATE: QMessageBox.critical(None, "过期提示", "此软件已过期,无法使用。") sys.exit(1) main_window = ClipboardApp() main_window.show() sys.exit(app.exec_())
最新发布
08-23
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值