0

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.

Valerii Rusakov
  • 1,091
  • 2
  • 11
  • 22
  • 1
    Even if this could be made to work, all you would trap is instances where the `WM_COPY` message is used to place data on the clipboard - there's no guarantee it will be. Creating a window and using `SetClipboardViewer` or `AddClipboardFormatListener` is a much better solution. – Jonathan Potter Aug 19 '13 at 10:51
  • 3
    There's no point in emptying the clipboard *before* the app copies data to it. Wrong hook, use WH_CALLWNDPROCRET. And you really *do* want to use SetClipboardViewer. If you don't have a HWND then just create one, it doesn't have to be visible. Make sure your uninstaller is flawless as well, it will get a good workout. – Hans Passant Aug 19 '13 at 11:59

1 Answers1

1

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;
}
Burak
  • 2,251
  • 1
  • 16
  • 33
Jonathan Potter
  • 36,172
  • 4
  • 64
  • 79
  • Yes, you are right. My head pain was wrong window handler in SetClipboardViewer() function. Now it works perfectly. – Valerii Rusakov Aug 20 '13 at 07:50
  • I am just learning C++ and have a very simple question for you: I am trying to use this code to create a new thread in my console win32 application. I am getting an error on "nextClipboardViewer" is undefined. What library do I include to solve this problem? I googled it but found nothing on this specific function and library imports; I'm assuming that's my issue. – nomaam Jul 04 '16 at 04:42
  • 1
    That code came from the OP and was just a fragment, it wasn't a full program. If you add `HWND nextClipboardViewer = 0;` **outside** a function (to make it a global) it should get you past that error, but I'm not sure what other errors you'll encounter using just a fragment of someone else's code. – Jonathan Potter Jul 04 '16 at 04:59
  • Thank you. I find it hard to tell in C++ when there's an error because of a missing library or something completely different. (VS 2015) – nomaam Jul 05 '16 at 02:38