Copying a file to the clipboard so you can paste it into Explorer or an email message or whatever


Today's Little Program takes a fully-qualified file name from the command line and puts that file onto the clipboard. Once there, you can paste it into an Explorer window, or into an email message, or a word processing document, or anybody else who understands shell data objects.

#include <windows.h>
#include <shlobj.h>
#include <atlbase.h>
#include <shlobj.h>

class COleInitialize {
public:
 COleInitialize() : m_hr(OleInitialize(NULL)) { }
 ~COleInitialize() { if (SUCCEEDED(m_hr)) OleUninitialize(); }
 operator HRESULT() const { return m_hr; }
 HRESULT m_hr;
};

// GetUIObjectOfFile incorporated by reference

int __cdecl wmain(int argc, PWSTR argv[])
{
 COleInitialize init;
 CComPtr<IDataObject> spdto;

 if (SUCCEEDED(init) &&
     argc == 2 &&
     SUCCEEDED(GetUIObjectOfFile(nullptr, argv[1], IID_PPV_ARGS(&spdto))) &&
     SUCCEEDED(OleSetClipboard(spdto)) &&
     SUCCEEDED(OleFlushClipboard())) {
  // success
 }

 return 0;
}

The COle­Initialize class is just the OLE counterpart to the CCo­Initialize class we saw some time ago.

All the program does is take the file name on the command line, asks the shell for the corresponding data object, then puts that object onto the clipboard, erasing what was there before.

Once the data is on the clipboard, our job is done so we exit.

No, wait! If you exit while your application has data on the clipboard, that clipboard data may be lost. The documentation for Ole­Set­Clipboard notes:

If you need to leave the data on the clipboard after your application is closed, you should call Ole­Flush­Clipboard rather than calling Ole­Set­Clipboard with a NULL parameter value.

Therefore, we stick in a call to Ole­Flush­Clipboard before exiting. This forces any delay-rendered content to be rendered immediately, because we ain't gonna be around to delay-render it no more.

Note that the file on the command line must be fully-qualified, because we pass it straight to Get­UI­Object­Of­File, which expects a fully-qualified path. Fixing the program to allow relative paths (and to actually print error messages and stuff) is left as an exercise, because Little Programs don't deal with annoying details like error checking and reporting.

Comments (10)
  1. Nitpick:

    COleInitialize() : m_hr(OleInitialize(NULL)) { }

    ~COleInitialize() { if (SUCCEEDED(m_hr)) OleUninitialize(); }

    It is just possible that the runtime will succeed creation of all the members but then decide that for some reason it can't create the containing object. In such a scenario OleInitialize(NULL) will be called (and may succeed), but OleUninitialize() will not be called. Fix would be to change the constructor as follows:

    COleInitialize() { m_hr = OleInitialize(NULL); }

  2. Danny says:

    Another nitpick:

    #include <shlobj.h>

    #include <atlbase.h>

    #include <shlobj.h>

  3. Paul Parks says:

    @Maurits: What you say is technically true, but it's rather irrelevant to the example. In Raymond's code there are no intervening initializations that could throw, so the constructor will complete and the destructor will get called at scope exit. If your OLE initializer is esoteric enough to trigger the behavior you describe, then you're doing RAII wrong. Besides, Raymond's intializer is much more likely to do the right thing than putting the calls directly into wmain. So, no, there's nothing really wrong with using the initializer list there.

  4. Joshua says:

    I'd suspect the fact the process terminates almost immediately on failure means the error-cleanup is irrelevant.

  5. GregM says:

    Maurits, in what situation do you think that it would be possible for the creation of that object, exactly as it is defined, to fail after the OleInitialize function has returned when it's in the member initialization list, but not when it's in the body of the constructor?

  6. Could that be a setup to the really really complicated way to delay render a virtual file?

    (such as needed when you write a compressor GUI and want to drag files outside which then will be uncompressed on the fly)

    Is there a full example of something like that documented somewhere? Back when I needed that I had to combine a few samples from various web sites and it felt horribly complex.

  7. Francis Gagne says:

    When exiting Microsoft Word (and maybe other Office applications), if you're copied some fancy OLE object from Word to the clipboard, Word will ask whether you want to keep that data on the clipboard or not. Reading this article, I figured Word is probably asking me whether it should call OleFlushClipboard or clear the clipboard. (I never worked directly with the clipboard API functions, so I didn't know about OleFlushClipboard).

  8. I'll just say one thing:  clip.exe

    Check it out.  One of the most useful programs in System32.

  9. 640k says:

    You're doing it wrong. Please don't teach people these hacks.

    No need to use bloated COM for this, Winapi can do this stuff just fine. With less, and more readable, code.

  10. 640k says:

    OpenClipboard

    SetClipboardData

    CloseClipboard

    …are more than enough.

    [Sure, if you want to manually set a bunch of different clipboard formats. (CFSTR_SHELLIDLIST, CFSTR_SHELLIDLISTOFFSET, CFSTR_FILEDESCRIPTOR, CFSTR_FILECONTENTS, CFSTR_FILENAME, CF_HDROP, CFSTR_PREFERREDDROPEFFECT, plus the drag image formats, plus other stuff I'm probably forgetting. Why not just let the shell do all the heavy lifting?) -Raymond]

Comments are closed.