What a drag: Dragging a virtual file (IStorage edition)


There are three storage media that can be used for virtual file transfer. We've already seen HGLOBAL and IStream; the last one is IStorage. I doubt anybody will ever need to do virtual file transfer with structured storage, but here it is anyway. Remember that the theme of this series is "It's the least you can do", so I'm going to try to get away with as little as possible.

Starting with our stream-based sample from last time, we need only make a few changes. First, of course, we have to declare that we provide an IStorage as our file contents.

CTinyDataObject::CTinyDataObject() : m_cRef(1)
{
  SetFORMATETC(&m_rgfe[DATA_FILEGROUPDESCRIPTOR],
               RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR));
  SetFORMATETC(&m_rgfe[DATA_FILECONTENTS],
               RegisterClipboardFormat(CFSTR_FILECONTENTS),
               TYMED_ISTORAGE, /* lindex */ 0);
}

Next, we need to produce that storage in our IDataObject::GetData handler:

HRESULT CTinyDataObject::GetData(FORMATETC *pfe, STGMEDIUM *pmed)
{
  ZeroMemory(pmed, sizeof(*pmed));

  switch (GetDataIndex(pfe)) {
  case DATA_FILEGROUPDESCRIPTOR:
  {
    FILEGROUPDESCRIPTOR fgd;
    ZeroMemory(&fgd, sizeof(fgd));
    fgd.cItems = 1;
    StringCchCopy(fgd.fgd[0].cFileName,
                  ARRAYSIZE(fgd.fgd[0].cFileName),
                  TEXT("Dummy"));
    pmed->tymed = TYMED_HGLOBAL;
    return CreateHGlobalFromBlob(&fgd, sizeof(fgd),
                              GMEM_MOVEABLE, &pmed->hGlobal);
  }

  case DATA_FILECONTENTS: //  Create an empty storage
  {
    pmed->tymed = TYMED_ISTORAGE;
    ILockBytes *plb;
    HRESULT hr = CreateILockBytesOnHGlobal(NULL, TRUE, &plb);
    if (SUCCEEDED(hr)) {
        hr = StgCreateDocfileOnILockBytes(plb,
                STGM_READWRITE | STGM_SHARE_EXCLUSIVE |
                STGM_CREATE | STGM_DIRECT,
                0, &pmed->pstg);
        plb->Release();
    }
    return hr;
  }

  return DV_E_FORMATETC;
}

The hardest part was creating the empty storage object! The bookkeeping you're by now well-familiar with. and, as noted when we made the HGLOBAL-based data object, there are additional attributes you can set in the FILEGROUPDESCRIPTOR to make the experience a bit smoother.

That pretty much covers "The least you can do" for virtual file transfer in the shell. You can think of these little sample programs as "scratch data objects"—you can use them as the basis for more complicated virtual file transfer scenarios. We'll see more about this in future articles.

Comments (10)
  1. Anonymous says:

    I may be missing the obvious but doesn’t outlook use structured storage when dragging messages around.

  2. JamesNT says:

    Mr. Chen,

    Thank you for this series.  It has been one of the most fun so far.

    JamesNT

  3. Anonymous says:

    Steve: Are you nitpicking?  I don’t think those are virtual files (or real files; doesn’t outlook use one large file for all your e-mail?)

  4. Anonymous says:

    god dammit it’s so boring, what’s the purpose raymond?

  5. Anonymous says:

    @gb When you need it, you’ll be able to use it.

  6. Anonymous says:

    @gb

    You can’t fool me. I’ve seen lots of movies where esoteric computer knowledge saves the world. When the aliens attack, a knowledge of COM and OLE may save the world. It’s certainly more likely than them using Macs like the aliens in Independence Day. Watch the skys but make sure you read Dale Rogerson!

    More to the point, I could hack up an explorer style application with a treeview/splitter/listview/menu bar that lets me drag and drop files from my PTP device. Or anything else for that matter.

  7. Anonymous says:

    @Anon

    I am a computer science student and I wonder if it still a good idea to learn COM!

    What do you think?

  8. Anonymous says:

    If you want to integrate with Windows, you will probably have to learn it. If you’re not aiming to do that, then learn the concepts behind it and why it developed the way it did.

    I know the basics and how to use it in a pinch, but I probably couldn’t write a COM component easily, and I consider myself a fairly competent Windows developer. Maybe I’m not if I try to avoid COM… ;)

  9. Anonymous says:

    Now all I need to do is figure out how to implement the other end of the drop target so that I can register file associations.

    MSDN says this:

    On Microsoft Windows XP systems, enhanced support is added for applications that implement drag-and-drop features and expose their IDropTarget interface. With this method, the Shell communicates with the application through the Component Object Model (COM) subsystem. This method offers a significant performance gain over both the command line method and DDE. To use this method, add a DropTarget subkey under a verb’s key. Under the DropTarget, add a registry string named Clsid, and set the value of this string to a valid CLSID Key.

    MSDN online also adds this helpful advice:

    Implementations of IDropTarget::Drop need to return quickly to the caller avoid causing an unresponsive state. There are two ways to accomplish this:

    Marshall the input IDropTarget to a new thread.

    Take a reference to the data object, set a short timer, and process the drop against that timer. If you use this method, you must ensure that you do not begin processing a second drop while the first is still being processed.

    http://msdn2.microsoft.com/en-us/library/bb776883.aspx

    What it doesn’t say is: Does this work on Vista?

    I assume all I need to do is expose on object on my exe with the specified CLSID that supports an IDropTarget interface.

  10. Anonymous says:

    I see from msdn online this works on XP and later systems.  oops.

Comments are closed.