正确使用AddClipboardFormatListener并订阅WM_CLIPBOARDUPDATE消息

时间:2019-12-23 19:53:04

标签: 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接口,但在这种情况下,我认为这两件事都不与我的问题有关(图我以防万一)。

1 个答案:

答案 0 :(得分:1)

您不应使用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;
}