正确使用AddClipboardFormatListener并订阅WM_CLIPBOARDUPDATE消息

2023-12-19

我当前正在尝试在我的应用程序中使用 Windows 剪贴板及其通知。具体来说,我正在尝试订阅WM_CLIPBOARDUPDATE https://learn.microsoft.com/en-us/windows/win32/dataxchg/wm-clipboardupdate窗口消息通过使用AddClipboardFormatListener() https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-addclipboardformatlistener功能。之前,我一直在使用SetClipboardViewer() https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setclipboardviewer函数以便将我的窗口直接添加到剪贴板查看器链中。这一切都很好,我已经收到了相关消息WM_DRAWCLIPBOARD and WM_DESTROYCLIPBOARD当预期时。但是,我想避免继续使用剪贴板链,因为它非常不稳定。

我的理解是我完全能够收到WM_CLIPBOARDUPDATE打电话后AddClipboardFormatListener()。我还缺少另一个步骤吗?我需要做什么才能确保正确收到此消息?就目前情况而言,我在执行复制操作时没有收到它。

这是我的代码的简短示例:

WNDPROC 覆盖:

LRESULT CALLBACK ClipboardService::CallWndProc(int nCode, WPARAM wParam, LPARAM lParam)
  {
    switch ( pMsg->message )
    {
    case WM_DRAWCLIPBOARD:
        // Handle clipboard available event and forward message
        break;
    case WM_CLIPBOARDUPDATE:
        // This is never triggered
        break;
    case WM_DESTROYCLIPBOARD:
        // Handle clipboard cleared event and forward message
        break;
    }
    return ::CallNextHookEx( g_Hook, nCode, wParam, lParam );
} 

由构造函数调用:

HRESULT ClipboardService::SetOrRefreshWindowsHook()
{
    HRESULT hr = S_OK;
    try
    {
        if (!m_bHookSet)
        {
            g_hwndCurrent = ::CreateWindowEx(0, "Message", "ClipboardMessageWindow", 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);
            m_dwThreadID = ::GetWindowThreadProcessId(g_hwndCurrent, &m_dwProcessID);
            _Module.Lock();
            SetLastError(0);

            g_Hook = ::SetWindowsHookEx(WH_CALLWNDPROC, CallWndProc, 0, m_dwThreadID);
            //g_hwndNext = ::SetClipboardViewer(g_hwndCurrent); old way to subscribe

            // This is what I expect should subscribe me to WM_CLIPBOARDUPDATE messages
            if (!::AddClipboardFormatListener(g_hwndCurrent))
                hr_exit(E_UNEXPECTED); 

            DWORD dwLastError = ::GetLastError();
            g_This = this;
            m_bHookSet = true;
        }
    }
    catch (...)
    {
        hr_exit(E_UNEXPECTED);
    }
wrapup:
    return hr;
} 

这是一个由 .NET 包装器调用的 COM 接口,但我认为这两件事与本例中的问题无关(我想添加以防万一)。


你不应该使用SetWindowsHookEx(WH_CALLWNDPROC)接收消息到您自己的窗口。使用RegisterClass/Ex()而不是注册您自己的自定义窗口类,该类具有您的WndProc分配给它,然后CreateWindowEx()可以创建该窗口类的实例。无需挂钩。

HINSTANCE g_hThisInst = NULL;
HWND g_hwndCurrent = NULL; 
//HWND g_hwndNext = NULL;
bool g_AddedListener = false;

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
    g_hThisInst = hinstDLL;
    return TRUE;
}


LRESULT CALLBACK ClipboardService::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_CREATE:
        //g_hwndNext = ::SetClipboardViewer(hwnd);
        g_AddedListener = ::AddClipboardFormatListener(hwnd);
        return g_AddedListener ? 0 : -1;

    case WM_DESTROY:
        /*
        ChangeClipboardChain(hwnd, g_hwndNext);
        g_hwndNext = NULL;
        */
        if (g_AddedListener)
        {
            RemoveClipboardFormatListener(hwnd);
            g_AddedListener = false;
        }
        return 0;

    /*
    case WM_CHANGECBCHAIN:
        if (g_hwndNext == (HWND)wParam)
            g_hwndNext = (HWND)lParam;
        else if (g_hwndNext)
            SendMessage(g_hwndNext, uMsg, wParam, lParam);
        break;

    case WM_DRAWCLIPBOARD:
        // Handle clipboard available event
        if (g_hwndNext)
            SendMessage(g_hwndNext, uMsg, wParam, lParam);
        break;
    */

    case WM_CLIPBOARDUPDATE:
        // Handle clipboard updated event
        return 0;

    case WM_DESTROYCLIPBOARD:
        // Handle clipboard cleared event and forward message
        break;
    }

    return ::DefWindowProc(hwnd, uMsg, wParam, lParam);
} 

HRESULT ClipboardService::SetOrRefreshWindowsHook()
{
    try
    {
        if (!g_hwndCurrent)
        {
            WNDCLASS wndClass = {};
            wndClass.lpfnWndProc = &ClipboardService::WndProc;
            wndClass.hInstance = g_hThisInst;
            wndClass.lpszClassName = TEXT("ClipboardMessageWindow");

            if (!::RegisterClass(&wndClass))
            {
                DWORD dwLastError = ::GetLastError();
                if (dwLastError != ERROR_CLASS_ALREADY_EXISTS)
                    return HRESULT_FROM_WIN32(dwLastError);
            }

            g_hwndCurrent = ::CreateWindowEx(0, wndClass.lpszClassName, "", 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, g_hThisInst, NULL);
            if (!g_hwndCurrent)
            {
                DWORD dwLastError = ::GetLastError();
                return HRESULT_FROM_WIN32(dwLastError);
            }

            g_This = this;
        }
    }
    catch (...)
    {
        return E_UNEXPECTED;
    }

    return S_OK;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

正确使用AddClipboardFormatListener并订阅WM_CLIPBOARDUPDATE消息 的相关文章

随机推荐

  • 与逻辑语句匹配的 Rcpp 矩阵的子集

    在 R 中 如果我们有一个数据矩阵 比如一个 100 x 10 矩阵 X 以及一个具有可能值 0 1 2 3 的 100 元素向量 t 我们可以使用简单的方法轻松找到 X 的子矩阵 y句法 y X t 1 但是 问题是 我如何使用 Rcpp
  • “d3.translateExtent”坐标如何工作?

    jsfiddle演示 https jsfiddle net sarathsaleem 76t03kbu 我正在尝试向圆圈添加拖动并尝试应用translateExtent 那么如何将拖动边界限制在矩形范围内呢 var height 500 v
  • Python3 globals() 和 locals() 内容

    在我的Python3学习中 在尝试globals 和locals 时 我做了一个非常基本的 独立的Python程序并收到了结果为此我请求专家解释 然后 我在我的 Python 程序 任何 中执行了相同的 2 个函数 并在我的程序中收到了许多
  • 在 T-SQL 中将分钟格式化为小时,将分钟格式化为十进制数

    有没有一种干净而简单的方法将整数格式化为小时和分钟的十进制表示形式 非常遗憾的是 T SQL 中没有 Timespan 这样的东西来支持这一点 需要明确的是 如果我有 70 分钟 我想将其转换为 1 小时 10 分钟 即1 10 我还想将其
  • 为什么编译器不能在不使用 const 注释的情况下检测到函数是常量?

    在铁锈中 const函数中可以放入的代码非常有限 例如for不允许循环 也不允许任何非循环const函数调用 我知道堆分配存在问题const函数 但为什么下面的代码无效 fn add a u8 b u8 gt u8 a b const A
  • Angular 服务 - GET 返回未定义

    我正在尝试使用角度服务从数据库中获取用户 在服务中执行 GET 请求时 我可以console log res 并得到回应 但是 当我尝试从另一个组件获取数据时 它总是出现undefined 请帮忙 用户 service ts import
  • 将工作表链接到主工作表并在主工作表不存在时保留值? Excel 2010

    我有很多工作表 目前 每三个月我必须一张一张地浏览这些工作表 以更新几个单元格中的信息 如果我可以创建一个包含这几个更新的单元格的主源文件 我的所有工作表都链接到该文件 那就太好了 这样我就可以更新这个文件 然后我的所有文件都会更新 我唯一
  • Angular 6 中 ng add 与 npm install 之间的区别

    随着 Angular6 的发布 他们添加了一个新命令ng add https github com angular material2 blob master src lib schematics collection json 谁能告诉我
  • jquery ajax 和完整服务器路径

    当使用 jquery 通过 ajax 提交表单时 有没有办法使用完整的服务器路径而不是 url 下面的示例不起作用 但它会让您了解我正在尝试做什么 我知道你不能执行跨域 ajax 请求 但这都在同一台物理服务器上 我不想设置代理或任何太花哨
  • Go gin 获取请求体 json

    我使用邮递员发布数据并在正文中放置一些简单的 json 请求正文 order 1 Name ts1 我需要将数据传输到 json 我尝试如下 我无法获取 json 不知道缺少什么 router POST user func c gin Co
  • CSS 动画 - 为一个又一个元素设置动画

    我想要为一个元素设置动画 然后再为另一个元素设置动画 这可以用 CSS 实现吗 我似乎无法让它工作here http jsbin com licapepose 1 edit html css output到目前为止我的尝试 我有两个主要问题
  • PHP中如何检查字符串是否可以用作变量名?

    在 PHP 中可以使用可变变量 例如 class obj fieldName Surname object new obj object gt Name John object gt fieldName Doe echo object gt
  • 尝试使用 maven 编译 JavaFX 项目时断言失败

    我正在开发一个 javaFX 项目 我想使用 maven 在终端中编译它 但我无法让它工作 它在 IntelliJ 中编译得很好 但当我在终端中时却编译不好 我正在使用编译脚本来运行它 我使用的是 M2 MacBook Air 这是我的 J
  • 奇怪的 Linq 错误

    我正在使用 Linq 将任何对象的数组转换为 CSV 列表 String Join From item In objectArray Select item ToString ToArray 这给了我一个奇怪的错误 范围变量名称不能与 Ob
  • 如何在IOS中制作慢动作视频

    我要做 慢动作 在视频文件和音频中 在一些帧之间 需要store将渐变视频作为新视频 Ref http www youtube com watch v BJ3 xMGzauk http www youtube com watch v BJ3
  • 如何使用 jquery 在 bootstrap 中激活导航栏

    我想让导航栏在我单击时处于活动状态 这是我使用的jquery document ready function navbar li click function e navbar li removeClass active var this
  • 如何使用 Python 和 OpenCV 从左到右、从上到下对轮廓进行排序

    我正在寻找带有数字和字符的图像的轮廓 用于 OCR 因此 我需要将轮廓从左到右排序 同时逐行排序 即从上到下 现在 轮廓不是这样排序的 例如 上面图像的轮廓是随机排序的 我需要的是排序为 D o y o u k n o w s o m e
  • android - FileProvider - 名称不能为空

    我的清单中有以下 FileProvider
  • 使用 BATCH 脚本检查目录中是否存在任何类型的文件

    您好 我正在编写一个批处理文件来检查给定文件夹内是否有任何类型的文件 到目前为止我已经尝试过以下方法 if EXIST FOLDERNAME echo Files Exist ELSE echo Empty 如果我知道文件扩展名 例如带有以
  • 正确使用AddClipboardFormatListener并订阅WM_CLIPBOARDUPDATE消息

    我当前正在尝试在我的应用程序中使用 Windows 剪贴板及其通知 具体来说 我正在尝试订阅WM CLIPBOARDUPDATE https learn microsoft com en us windows win32 dataxchg