c++ - 正确使用 AddClipboardFormatListener 并订阅 WM_CLIPBOARDUPDATE 消息

标签 c++ winapi clipboard wndproc window-messages

我目前正在尝试在我的应用程序中使用 Windows 剪贴板及其通知。具体来说,我正在尝试订阅 WM_CLIPBOARDUPDATE 使用 AddClipboardFormatListener() 的窗口消息功能。以前,我一直在使用 SetClipboardViewer() 功能以便将我的窗口直接添加到剪贴板查看器链中。这工作得很好,我收到了相关消息WM_DRAWCLIPBOARDWM_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 接口(interface),但我认为在这种情况下,这两件事中的任何一个都与我的问题无关(我想以防万一)。

最佳答案

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

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;
}

关于c++ - 正确使用 AddClipboardFormatListener 并订阅 WM_CLIPBOARDUPDATE 消息,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59460658/

相关文章:

c++ - C++20 中两种模块文件(接口(interface)和实现)的意义何在?

c++ - 正确处理静态 HTREEITEM 的清理

javascript - 将文本从剪贴板粘贴到 Firefox 和 Chrome

angular - 类型 'copy' 上不存在属性 'Clipboard'

python - 自动复制网页到剪贴板

c++ - 测量内存的潜伏期

c++ - C++中的对象实例化

c++ - 使用 boost 库从文件创建图形

c++ - 如何在 RichEdit 控件中更改选择颜色?

c++ - UAC 提示提升 - 它是如何工作的?