正确使用 AddClipboardFormatListener 并订阅 WM_CLIPBOARDUPDATE 消息
Properly using AddClipboardFormatListener and subscribing to WM_CLIPBOARDUPDATE message
我目前正在尝试在我的应用程序中使用 Windows 剪贴板及其通知。具体来说,我正在尝试订阅 WM_CLIPBOARDUPDATE
window message by using the AddClipboardFormatListener()
function. Previously, I had been using the SetClipboardViewer()
函数,以便将我的 window 直接添加到剪贴板查看器链中。这工作得很好,我在预期的时候收到了相关消息 WM_DRAWCLIPBOARD
和 WM_DESTROYCLIPBOARD
。但是,我想避免继续使用剪贴板链,因为它的易变性。
我的理解是,在调用 AddClipboardFormatListener()
后,我将完全能够收到 WM_CLIPBOARDUPDATE
。我还缺少另一个步骤吗?我需要做什么才能确保我正确收到此消息?目前,我在执行复制操作时没有收到它。
这里是我的代码的简化示例:
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)
来接收发给您自己 window 的消息。使用 RegisterClass/Ex()
来注册您自己的自定义 window class 并分配了您的 WndProc
,然后 CreateWindowEx()
可以创建该 [=19] 的实例=] class。无需挂钩。
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;
}
我目前正在尝试在我的应用程序中使用 Windows 剪贴板及其通知。具体来说,我正在尝试订阅 WM_CLIPBOARDUPDATE
window message by using the AddClipboardFormatListener()
function. Previously, I had been using the SetClipboardViewer()
函数,以便将我的 window 直接添加到剪贴板查看器链中。这工作得很好,我在预期的时候收到了相关消息 WM_DRAWCLIPBOARD
和 WM_DESTROYCLIPBOARD
。但是,我想避免继续使用剪贴板链,因为它的易变性。
我的理解是,在调用 AddClipboardFormatListener()
后,我将完全能够收到 WM_CLIPBOARDUPDATE
。我还缺少另一个步骤吗?我需要做什么才能确保我正确收到此消息?目前,我在执行复制操作时没有收到它。
这里是我的代码的简化示例:
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)
来接收发给您自己 window 的消息。使用 RegisterClass/Ex()
来注册您自己的自定义 window class 并分配了您的 WndProc
,然后 CreateWindowEx()
可以创建该 [=19] 的实例=] class。无需挂钩。
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;
}