views:

26

answers:

1

Hi all,

I'm trying to create a backup of the Windows clipboard. Basically what I'm doing is using EnumClipboardFormats() to get all of the formats that exist on the clipboard currently, and then for each format, I'm calling GetClipboardData(format).

Part of backing up the data obviously involves duplicating it. I do that by calling GlobalLock() (which "Locks a global memory object and returns a pointer to the first byte of the object's memory block.") on the data returned by GetClipboardData(), then I fetch the size of the data by calling GlobalSize(), and then finally I do a memcpy() to duplicate the data. I then of course call GlobalUnlock() when I'm done.

Well, this works... most of the time. My program crashes at the GlobalLock() if the clipboard contains data with the format CF_BITMAP or CF_METAFILEPICT. After reading this Old New Thing blog post (http://blogs.msdn.com/b/oldnewthing/archive/2007/10/26/5681471.aspx) I found out why the crash occurs: apparently not all data on the clipboard is allocated using GlobalAlloc() (such as CF_BITMAP data), and so calling GlobalLock() on that data causes a crash.

I came across this MSDN article ( http://msdn.microsoft.com/en-us/library/ms649014#_win32_Memory_and_the_Clipboard ) and it gives a list of clipboard formats and how they are freed by the system. So what I did was hard-code into my program all of the clipboard formats (CF_*) that are not freed by the GlobalFree() function by the system, and I simply don't back up those formats; I skip them.

This workaround seems to work well, actually. Even if a bitmap is on the clipboard, or "special" data (such as rows copied from Excel to the clipboard), my clipboard backup function works well and I haven't experienced any crashes. Also, even if there's a bitmap on the clipboard and I skip some formats during the backup (like CF_BITMAP), I can still Ctrl+V paste the originally-copied bitmap from the clipboard after restoring my clipboard backup, as the bitmap is represented by other formats on the clipboard as well that don't cause my program to crash (CF_DIB).

However, it's a workaround at best. My fear is that one of these times some weird format (perhaps a private one, i.e one between CF_PRIVATEFIRST and CF_PRIVATELAST, or maybe some other type) will be on the clipboard and my program, after calling GlobalLock(), will crash again. But since there doesn't seem to be much documentation explaining the best way to back up the clipboard, and it's clear that GlobalLock() does not work properly for all datatypes (unfortunately), I'm not sure how to handle these situations. Is it safe to assume that all other formats -- besides the formats listed in the previous URL that aren't freed by GlobalFree() -- can be "grabbed" using GlobalLock()?

Any ideas?

+1  A: 

This is folly, as you cannot 100% backup/restore the clipboard. Lots of apps used delayed rendering, and the data is not actually on the clipboard. When you request to paste, they get notified and produce the data. This would take several minutes and hundreds of MB for large amounts of data from apps like Excel. Take a look at the number of formats listed on the clipboard when you copy from Excel. There will be more than two dozen, including Bitmap, Metafile, HTML. What do you think will happen if you select 255x25000 cells in Excel and copy that? How large would that bitmap be? Tip: save any open documents before attempting this, as you're likely going to have to reboot.

Chris Thornton
1) With delayed rendering, the app will produce the data after my app calls GetClipboardData(), correct? 2) Let's assume a best case scenario, and there's only a small amount of data on the clipboard. How do I safely backup all of this data? (i.e, without crashing) 3) Finally, let's assume a worst case scenario and there's a huge amount of not-yet-rendered data on the clipboard, like you talked about. Is there any way I can know there's a huge amount of data so I can then "skip" backing up this data?
YouCanSam
Also: If I selected 255x25000 cells in Excel, copied it to the clipboard, and then my program backed it up, wouldn't this be the same as just pressing Ctrl+V after I copied all of those cells in Excel to the clipboard, since both operations (backup/Ctrl+V paste) presumably would cause Excel to render the data?
YouCanSam
#1 correct. #2, yes you can definitely paste bitmaps, etc., without crashing. I Find a working example in the language of your choice. #3 You should be able to do that, yes. And you can pick and choose which formats to capture, and which to abandon.
Chris Thornton
Your assumptions about the Excel test are flawed. When you press Ctrl+V, it is NOT the same as backing up the entire clipboard. When you press Ctrl+V in an application, that application examines the clipboard formats and picks one. It may decide to paste as OLE data, HTML, Metafile, DIB, or yes, Bitmap. But it won't paste ALL of them. It'll pick its favorite, possibly after considering the "priority" format as presented. Then it'll paste one. To be clear, some complex formats such as OLE, may involve several different formats. But regardless, it won't paste all two dozen or whatever..
Chris Thornton
..continued.. So when you paste the Bitmap, Excel is notified with a WM_RenderFormat message, requesting CF_BITMAP. Excel then produces that format, and your app pastes the data. But in your clipboard/backup scenario, you would ask for ALL of the formats, thus generating about 24 WM_RenderFormat requests. It will take time, it will take lots of RAM, and the result won't be pretty.
Chris Thornton
In short, this strategy is doomed.
Chris Thornton
Thanks. 2) Okay. But if I call GlobalLock(GetClipboardData(CF_BITMAP)), it crashes. Is CF_BITMAP (and the other formats listed in the URL I included in my original post) the only clipboard format that shouldn't be passed to GlobalLock()? Can I safely call GlobalLock() on private clipboard formats and all of the other registered/non-registered clipboard formats? 3) Could you elaborate on how I might detect if there is a large amount of data on the clipboard? Should I just pick and choose which formats to backup, or is there a better way?
YouCanSam
In Delphi, all I need to do is: Bitmap := TBitmap.Create; Bitmap.Assign(Clipboard);While I do use GlobalLock for other formats, apparently I let Delphi handle it for Bitmaps. Not sure what goes on in there. I'm not sure if it's a case of the Delphi bitmap object being smart, or the Delphi clipboard object being smart. I know I used to do it differently, as this code goes back before Delphi even existed (it was TPW, circa 1992).
Chris Thornton
Thanks for your insight Chris.
YouCanSam