开源项目CuteSqlite开发笔记(八):Windows 64位/32位使用GetWindowLongPtr钩子函数

需求描述

在开发CuteSqlite的时候, 有一个功能需要实现,鼠标移到WTL::CStatic上后,发送消息通知CToolTipCtrl弹出。

遇到问题

WTL::CStatic控件没有相应 WM_MOUSEMOVE 消息,需要返回一个HTCLIENT消息来让窗口处理函数执行 WM_MOUSEMOVE消息,因为控件实例的鼠标消息,比如WM_MOUSEMOVE不会发送到父窗口上,只会发送到它本身,所以我们不能在父窗口的消息映射里添加处理CStatic的WM_MOUSEMOVE消息处理函数。针对这种情况,我们可以通过钩子的方式hook掉CStatic的原窗口处理内部函数。

如果是开发win 32位的应用程序,这个时候需要用到两个32位的勾子函数:GetWindowLong和SetWindowLong。

如果是开发win 64位的应用程序,这个时候需要用到两个64位的勾子函数:GetWindowLongPtr和SetWindowLongPtr。

处理64位的上述两个函数(GetWindowLongPtr和SetWindowLongPtr)兼容32位的应用程序,微软的MSDN解释如下:

GetWindowLongPtr检索有关指定窗口的信息。 该函数还会将指定偏移量的值检索到额外的窗口内存中。

注意 若要编写与 32 位和 64 位版本的 Windows 兼容的代码,请使用  GetWindowLongPtr。 为 32 位 Windows 编译时,  GetWindowLongPtr 定义为对  GetWindowLong 函数的调用。

编译错误

**特别注意的是:**如果在64位开发环境中,还使用32位的函数GetWindowLong和SetWindowLong,会发生编译错误,类似错误如下:

1>QParamElem.cpp
1>f:\project\wtlproject\cutesqlite\cutesqlite\ui\common\param\qparamelem.cpp(288): error C2065: “GWL_WNDPROC”: 未声明的标识符
1>f:\project\wtlproject\cutesqlite\cutesqlite\ui\common\param\qparamelem.cpp(291): error C2065: “GWL_USERDATA”: 未声明的标识符
1>f:\project\wtlproject\cutesqlite\cutesqlite\ui\common\param\qparamelem.cpp(292): error C2065: “GWL_WNDPROC”: 未声明的标识符

问题原因

原因是在64位开发环境中,头文件WinUser.h对宏GWL_WNDPROC,GWL_USERDATA等的声明改成了GWLP_WNDPROC,GWLP_USERDATA

解决方案

而解决的方法,64位开发环境使用函数GetWindowLongPtr和SetWindowLongPtr,替换32位的函数GetWindowLong和SetWindowLong。

实例解释

针对上述的需求:鼠标移到WTL::CStatic上后,发送消息通知CToolTipCtrl弹出。下面我们通过代码来解释这两个函数的使用。

头文件QParamElem.h声明的变量和钩子函数:

class QParamElem: public CWindowImpl<QParamElem> {
   
...
    CStatic desLabel; // 需要显示tooltip的文本框

    CToolTipCtrl tooltipCtrl; // tooltip提示控件
    std::pair<WNDPROC, HWND> procWndPair; // 钩子使用的变量,保存原来CStatic消息处理函数的地址和控件句柄HWND
    WNDPROC m_pWndProc; // 原来CStatic消息处理函数的地址
...
    // 初始化和绑定tooltipCtrl提示控件
    void createAndBindToolTip();
...
    // CStatic消息替换函数
    static LRESULT funcLabelProcWnd(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam);
...
}

QParamElem.cpp实现的函数如下:

// 初始化和绑定tooltipCtrl
void QParamElem::createAndBindToolTip()
{
   
	if (tooltipCtrl.IsWindow() || !desLabel.IsWindow() ) {
   
		return;
	}
	tooltipCtrl.Create(desLabel.m_hWnd, NULL, NULL, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP);
	tooltipCtrl.AddTool(desLabel.m_hWnd, data.description.c_str());
	tooltipCtrl.Activate(TRUE);		
	
	// 拦截鼠标消息.
	m_pWndProc = (WNDPROC)::GetWindowLongPtr(desLabel.m_hWnd, GWLP_WNDPROC); // 获取原窗口处理函数
	procWndPair.first = m_pWndProc; // 这个是用户定义的类型1,不重要
	procWndPair.second = tooltipCtrl.m_hWnd; // 这个是用户定义的类型2,不重要
	::SetWindowLongPtr(desLabel.m_hWnd, GWLP_USERDATA, (LONG_PTR)&procWndPair); // 设置窗口的自定义数据,用于存储原处理函数和ToolTip句柄
	::SetWindowLongPtr(desLabel.m_hWnd, GWLP_WNDPROC, (LONG_PTR)QParamElem::funcLabelProcWnd); // 自定义一个窗口处理函数,对鼠标消息预先过滤.
}

// 钩子替换的消息处理函数
LRESULT QParamElem::funcLabelProcWnd(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
   
	auto pp = (std::pair<WNDPROC, HWND> *)::GetWindowLongPtr(hWnd, GWLP_USERDATA);
	WNDPROC funcOld = pp->first;
	auto tooltip_hwnd = pp->second;
	if(nMsg == WM_NCHITTEST){
   
		// 1.static 控件没有相应 WM_MOUSEMOVE 消息,需要返回一个HTCLIENT来让窗口处理函数执行 WM_MOUSEMOVE 消息.
		// 2.就是把 WM_NCHITTEST 消息转换为 WM_MOUSEMOVE消息.
		return HTCLIENT;
	} else if(nMsg == WM_MOUSEMOVE){
   
		// WM_MOUSEMOVE
		// WM_NCHITTEST
		// 1.发送一格WM_MOUSEMOVE消息给tooltip控件处理.这样tooltip才会在指定位置显示.
		MSG msg = {
    hWnd, nMsg, wParam, lParam };
		CToolTipCtrl tip;
		tip.Attach(tooltip_hwnd);
		tip.RelayEvent(&msg);
	}
	return CallWindowProc(funcOld, hWnd, nMsg, wParam, lParam);
}

OK, 这样子就可以实现了对 CStatic消息的转换,从而实现tooltip的显示。

完整的实例源码:

GitHub:   QParamElem.h    QParamElem.cpp

Gitee:      QParamElem.h    QParamElem.cpp

相关推荐

  1. windows驱动开发-3264

    2024-01-23 06:42:02       26 阅读
  2. 32QT连接64MySQL

    2024-01-23 06:42:02       37 阅读
  3. windows 下 MinGW,TDM-GCC 编译6432应用

    2024-01-23 06:42:02       33 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-01-23 06:42:02       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-01-23 06:42:02       106 阅读
  3. 在Django里面运行非项目文件

    2024-01-23 06:42:02       87 阅读
  4. Python语言-面向对象

    2024-01-23 06:42:02       96 阅读

热门阅读

  1. cocoapods 常用命令

    2024-01-23 06:42:02       52 阅读
  2. yarn的安装及使用教程

    2024-01-23 06:42:02       64 阅读
  3. WEB前端IDE的使用以及CSS的应用

    2024-01-23 06:42:02       48 阅读
  4. 如何使用Pycharm社区免费版创建Django项目

    2024-01-23 06:42:02       52 阅读
  5. npm install vue3-print-nb --legacy-peer-deps npm ERR!

    2024-01-23 06:42:02       49 阅读
  6. bash 5.2中文修订2

    2024-01-23 06:42:02       51 阅读
  7. kafka(二)——常用命令

    2024-01-23 06:42:02       61 阅读