Valerii Rusakov
Valerii Rusakov

Reputation: 1091

Monitoring clipboard

I am writting dll with global hooks. One of the task is viewing clipboard and deleting all data from it when someone perform copy operation. Here is my callback function for window:

string test("my data");

LRESULT CALLBACK WndHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    switch(msg) {
        case WM_CREATE:
            nextClipboardViewer = SetClipboardViewer(windowHandler);
            MessageBeep(MB_ICONINFORMATION);
            break;
        case WM_CHANGECBCHAIN:
            if((HWND) wParam == nextClipboardViewer) 
                nextClipboardViewer == (HWND) lParam;
            else if(nextClipboardViewer != NULL)
                SendMessage(nextClipboardViewer, msg, wParam, lParam);
            break;
        case WM_DRAWCLIPBOARD:
            if(OpenClipboard(windowHandler)) {
                EmptyClipboard();
                HGLOBAL hClipboardData;
                hClipboardData = GlobalAlloc(GMEM_MOVEABLE, test.size() + 1);
                char * pchData;
                pchData = (char*)GlobalLock(hClipboardData);
                memcpy(pchData, test.c_str(), test.size() + 1);
                GlobalUnlock(hClipboardData);
                SetClipboardData(CF_TEXT, hClipboardData);
                CloseClipboard();
            }
            SendMessage(nextClipboardViewer, msg, wParam, lParam);
            break;
        case WM_DESTROY:
            ChangeClipboardChain(windowHandler, nextClipboardViewer);
            PostQuitMessage(0);
            break;
        default:
            return DefWindowProc(hwnd, msg, wParam, lParam);
            break;
    }
    return 0;
}

I am just trying replace information in clipboard, but this code doesn't work.

Updated: Now i am using invisible window and SetClipboardViewer for monitoring changes. But data in clipboard doesn't change.

Upvotes: 0

Views: 3556

Answers (1)

Jonathan Potter
Jonathan Potter

Reputation: 37132

I doubt it's really safe to change the contents of the clipboard while processing a WM_DRAWCLIPBOARD message - at the very least I'm surprised you don't trigger an infinite loop (since your calls to EmptyClipboard() and SetClipboardData() could be triggering another WM_DRAWCLIPBOARD message). Possibly the system has protection against that - I've never tried to find out - but it still feels wrong :)

Try this version, which:

  1. Moves the clipboard update to a separate message that the window posts to itself (moving it outside of the clipboard change the notification code)
  2. Uses a global flag to ignore changes it makes itself.

Note: I think the actual bug with your code was that when you're processing WM_CREATE, windowHandler is not yet assigned. You are presumably setting that to the value CreateWindowEx returns, but when WM_CREATE is being processed CreateWindowEx hasn't actually returned yet. This means the clipboard viewer is never actually established correctly. I've changed the references to use hwnd to fix this.

string test("my data");

#define MSG_UPDATECLIPBOARD     (WM_APP + 1)
static bool g_fIgnoreClipboardChange = false;

LRESULT CALLBACK WndHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    switch(msg) {
    case WM_CREATE:
        nextClipboardViewer = SetClipboardViewer(hwnd);
        MessageBeep(MB_ICONINFORMATION);
        break;
    case WM_CHANGECBCHAIN:
        if((HWND) wParam == nextClipboardViewer) 
            nextClipboardViewer = (HWND) lParam;
        else if(nextClipboardViewer != NULL)
            SendMessage(nextClipboardViewer, msg, wParam, lParam);
        break;
    case WM_DRAWCLIPBOARD:
        if (!g_fIgnoreClipboardChange)
            PostMessage(hwnd, MSG_UPDATECLIPBOARD, 0, 0);
        if(nextClipboardViewer != NULL)
            SendMessage(nextClipboardViewer, msg, wParam, lParam);
        break;
    case MSG_UPDATECLIPBOARD:
        g_fIgnoreClipboardChange = true;
        if(OpenClipboard(hwnd)) {
            HGLOBAL hClipboardData;
            hClipboardData = GlobalAlloc(GMEM_MOVEABLE, test.size() + 1);
            char * pchData;
            pchData = (char*)GlobalLock(hClipboardData);
            memcpy(pchData, test.c_str(), test.size() + 1);
            GlobalUnlock(hClipboardData);
            SetClipboardData(CF_TEXT, hClipboardData);
            CloseClipboard();
        }
        g_fIgnoreClipboardChange = false;
        break;      
    case WM_DESTROY:
        ChangeClipboardChain(hwnd, nextClipboardViewer);
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hwnd, msg, wParam, lParam);
        break;
    }
    return 0;
}

Upvotes: 1

Related Questions