Jacobr365
Jacobr365

Reputation: 846

C# Form Application Getting a ClipboardFormatListener to only receive messages when a user pastes content

I am working on a project that tracks how often a user has to copy and past data is so if a user is constantly just pasting from one form to another we can automate parts so forms auto-fill. This is being used in a corporate call center for the the quality assurance people. The Form does a bunch of other stuff but that its not really important for the question.

As it stands the form application uses a keyboard hook to catch when a user does control+v. When it catches the control+v combo it grabs the contents of the clipboard and sends it off to the processing part of the program. This all works well and good, but it can not pick up when a user does a right click copy/paste I have to go a different route for that.

In order to try and catch when a user uses the right click copy/paste message I figured it would be just as easy to listen to the clipboard for changes. I accomplished this with AddClipboardFormatListener(this.Handle); and with a override for the WndProc method like this:

protected override void WndProc(ref Message m)
    {
        base.WndProc(ref m);

        if (m.Msg == WM_CLIPBOARDUPDATE)
        {
            IDataObject iData = Clipboard.GetDataObject();      // Clipboard's data.
            uint processID = 0;
            IntPtr ForeGroundWindowHandle = GetForegroundWindow();
            GetWindowThreadProcessId(ForeGroundWindowHandle, out processID);

            if (iData.GetDataPresent(DataFormats.Text))
            {
                string text = (string)iData.GetData(DataFormats.Text);
                ClipboardEventQueue.Enqueue(new ClipboardDataStorage(text, Convert.ToInt32(processID)));
                LogWrite(text); 
            }
            else if (iData.GetDataPresent(DataFormats.Bitmap))
            {
                Bitmap image = (Bitmap)iData.GetData(DataFormats.Bitmap);
                ClipboardEventQueue.Enqueue(new ClipboardDataStorage("Clipboard copied an image", Convert.ToInt32(processID)));
                LogWrite("Clipboard copied an image");
            }
        }
    }

The issue is the the WM_CLIPBOARDUPDATE message is only sent when the user copies to the clipboard. And I only care about what they actually paste. Is there a way to get a message when the users pastes something or am I going to have to use a mouse hook to accomplish this by tracking right clicks?

Upvotes: 0

Views: 493

Answers (1)

Andrew Williamson
Andrew Williamson

Reputation: 8671

I want to catch when any paste is done at all. So if they open a web page in chrome and copy a street address, and then paste that into some other program, I want to catch that paste as well.

Unfortunately, the clipboard doesn't quite allow this kind of functionality. There are two main events that the clipboard is based on - advertising, and requesting. Let's say we have applications 'X' and 'Y' running. All other running applications can see when X advertises new data available on the clipboard. The WM_CLIPBOARDUPDATE message goes to every application. But when Y requests that data, only X knows about it. The WM_RENDERFORMAT message goes straight from X to Y.

As a very hacky workaround, you could listen for the advertisements. Whenever someone advertises new data, you request all of it from them. Then, you take control of the clipboard, and advertise the data again. This way, you steal ownership of the data, so you can work out when anyone is requesting from the clipboard. Follow the tutorial here to set your window up as a clipboard viewer. In their WndProc loop, try this:

protected override void WndProc(ref Message m)
{
    ...
    if (m.Msg == WM_DRAWCLIPBOARD)
    {
        IDataObject iData = Clipboard.GetDataObject();
        ClipBoard.SetDataObject(iData);
    }
    if (m.Msg == WM_RENDERFORMAT)
        Console.WriteLine("Clipboard data requested");
}

Upvotes: 2

Related Questions