ck_
ck_

Reputation: 3829

How do I save a copy of the clipboard and then revert back to it?

I'm reading words in a text box by simulating the key presses required to highligh and copy the text. When I'm done, I want the clipboard to be exactly how I found it.

I was hoping I'd be able to do something like this:

IDataObject clipboardBackup = Clipboard.GetDataObject();
Clipboard.Clear();
//Save other things into the clipboard here, etc//
Clipboard.SetDataObject(clipboardBackup);

But that doesn't seem to work. It looks like you can go the route of specifically trying for text, audio, pictures, etc. and then saving them accordingly. (I guess 'data object' is specialized like that in my example too, I was hoping it was generic.)

I'd prefer not to use cases for every possible type of clipboard data, both to be more concise and to make sure I never lose the data regardless of format.

Any tips for grabbing any and all of the clipboard and then restoring it?

Upvotes: 4

Views: 3665

Answers (5)

Phoeson
Phoeson

Reputation: 211

I've implement one sample with c++.

#include "stdafx.h"
#include "TestClip.h"
#include <Windows.h>
#include <queue>

using namespace std;

typedef struct
{
UINT format;
LPVOID content;
SIZE_T size;
} ClipBoardItem;

void InspectClipboard()
{
UINT uFormat = 0;
HGLOBAL hglb;
LPVOID hMem;
float totalSize = 0;

if (!OpenClipboard(NULL))
{
    cout << "Open clipboard failed!" << endl;
    system("pause");
    return;
}

int countFormat = CountClipboardFormats();
cout << "Clipboard formats count: " << countFormat << endl;

uFormat = EnumClipboardFormats(uFormat);
while (uFormat)
{
    cout << "Clipboard format:" << uFormat;
    hglb = GetClipboardData(uFormat);
    if (hglb != NULL)
    {
        hMem = GlobalLock(hglb);
        SIZE_T size = GlobalSize(hMem);
        cout << ", size:" << size << endl;
        totalSize += size;
        if (hMem != NULL)
        {
            GlobalUnlock(hglb);
        }
    }
    else
    {
        cout << " data is NULL" << endl;
    }
    uFormat = EnumClipboardFormats(uFormat);
}
CloseClipboard();

string unit = "bytes";
if (totalSize >= 1024)
{
    totalSize /= 1024;
    unit = "KB";
}
if (totalSize >= 1024)
{
    totalSize /= 1024;
    unit = "MB";
}
if (totalSize >= 1024)
{
    totalSize /= 1024;
    unit = "GB";
}
cout << "Total size is: " << totalSize << " " << unit.data() << endl;
}

queue<ClipBoardItem> BackupClipboard()
{
queue<ClipBoardItem> clipQueue;
UINT uFormat = 0;
HGLOBAL hglb;
LPTSTR lptstr;
LPVOID hMem;
if (!OpenClipboard(NULL))
{
    cout << "Open clipboard failed" << endl;
    return clipQueue;
}

uFormat = EnumClipboardFormats(uFormat);
while (uFormat)
{
    cout << "Backup clipboard format:" << uFormat << endl;
    hglb = GetClipboardData(uFormat);
    if (hglb != NULL)
    {
        hMem = GlobalLock(hglb);
        SIZE_T size = GlobalSize(hMem);
        if (size > 0)
        {
            ClipBoardItem clipitem;
            clipitem.format = uFormat;
            clipitem.content = malloc(size);
            clipitem.size = size;
            memcpy(clipitem.content, hMem, size);
            clipQueue.push(clipitem);
        }

        if (hMem != NULL)
        {
            GlobalUnlock(hglb);
        }
    }
    uFormat = EnumClipboardFormats(uFormat);
}
EmptyClipboard();
CloseClipboard();
cout << "Clipboard has been cleaned" << endl;
return clipQueue;
}

void RestoreClipboard(queue<ClipBoardItem> clipQueue)
{
if (!OpenClipboard(NULL)) return;

while (!clipQueue.empty())
{
    ClipBoardItem clipitem = clipQueue.front();
    HGLOBAL hResult = GlobalAlloc(GMEM_MOVEABLE, clipitem.size);
    if (hResult == NULL)
    {
        cout << "GlobalAlloc failed" << endl;
        clipQueue.pop();
        continue;
    }
    memcpy(GlobalLock(hResult), clipitem.content, clipitem.size);
    GlobalUnlock(hResult);
    if (SetClipboardData(clipitem.format, hResult) == NULL) {
        cout << "Set clipboard data failed" << endl;
    }
    cout << "Resotred clipboard format:" << clipitem.format << endl;
    GlobalFree(hResult);
    free(clipitem.content);
    clipQueue.pop();
}
CloseClipboard();
}

int _tmain(int argc, TCHAR* argv [], TCHAR* envp [])
{
InspectClipboard();
cout << "Press any key to backup and empty clipboard" << endl;
system("pause");

queue<ClipBoardItem> clipQueue = BackupClipboard();
InspectClipboard();
cout << "Press any key to restore clipboard" << endl;
system("pause");
RestoreClipboard(clipQueue);
cout << "Clipboard has been restored" << endl;
system("pause");
InspectClipboard();
system("pause");
return 0;
}

Hope this helps!

Upvotes: 1

Chris Thornton
Chris Thornton

Reputation: 15817

It is impossible to fully restore the clipboard in cases where delayed rendering is used, or where the clipboard data actually contains pointers back into the local program that are not intended for other programs to use them (dumb, but I've seen it). Consider the classic example of Excel, using delayed rendering to offer the same selection in dozens of different formats, including hazardous ones like Bitmap and HTML (could consume hundreds of MB and several minutes of clock time for rendering, if thousands of cells are copied). And then there is the whole situation of other clipboard viewers reacting to your clipboard manipulations. They'll get duplicate data, altered data, or notification that the clipboard has been erased.
I think this sums it up best:

“Programs should not transfer data into our out of the clipboard without an explicit instruction from the user.”
— Charles Petzold, Programming Windows 3.1, Microsoft Press, 1992

Upvotes: 2

Winston Smith
Winston Smith

Reputation: 21882

The following article presents some code to backup and restore the Clipboard. There's more to it than one might imagine, so I won't repost the code here.

Clipboard Backup In C#

Upvotes: 3

AakashM
AakashM

Reputation: 63338

Do not use the clipboard unless directly instructed to do so by the logged-on user. It's not for general application storage - it's for the user to use for what they want to clip.

If you want to get text from an arbitrary textbox, locate the window (in the Win32) sense and send an EM_GETTEXT Win32 message.

Upvotes: 3

ZombieSheep
ZombieSheep

Reputation: 29953

I'd consider the requirement for restoring the clipboard at all, if I were in your position. More often than not, the amount of data on the clipboard is going to be relatively small (a block of text, an image from a website, something like that), but every now and then you'll come across a situation where the user has placed a large amount of data on the clipboard, possibly some time ago, and possibly no longer required. Your app will have to be robust enough to cope with temporarily storing all that data during the lifetime of your operation. I personally don't think it is likely to be worth the effort, although you know the situation better than I could.

In short, don't. Just clear the clipboard with Clipboard.Clear(); after you're done.

Upvotes: -1

Related Questions