Dragging a shell object, part 3: Detecting an optimized move

We were considering how to detect that the drag/drop operation resulted in a conceptual Move even if the DROPEFFECT_MOVE was optimized away.

If the drop target is the shell, you can query the data object for CFSTR_PERFORMEDDROPEFFECT to see what the performed effect was.

void OnLButtonDown(HWND hwnd, BOOL fDoubleClick,
                   int x, int y, UINT keyFlags)
        if (dwEffect & DROPEFFECT_MOVE) {
        CheckPerformedEffect(hwnd, pdto);

Of course, we need that CheckPerformedEffect function too.

void CheckPerformedEffect(HWND hwnd, IDataObject *pdto)
  FORMATETC fe = {
  if (SUCCEEDED(pdto->GetData(&fe, &stgm))) {
    if ((stgm.tymed & TYMED_HGLOBAL) &&
        GlobalSize(stgm.hGlobal) >= sizeof(DWORD)) {
       DWORD *pdw = (DWORD*)GlobalLock(stgm.hGlobal);
       if (pdw) {
         if (*pdw == DROPEFFECT_MOVE) {
            MessageBox(hwnd, TEXT("Moved"), TEXT("Scratch"), MB_OK);

If the item is dropped on a shell window, the drop target will set data into the data object under the clipboard format name CFSTR_PERFORMEDDROPEFFECT. The data takes the form of a DWORD in an HGLOBAL, and the value is the actual drop effect before any optimizations kicked in.

Here, we check whether it was a DROPEFFECT_MOVE and display a special message if so.

Comments (4)
  1. Anonymous says:

    Ahh now this is a gem.

    However after doing some additional reading I find myself confused by MSDN documentation (and no this isn’t the first time).



    The CFSTR_PERFORMEDDROPEFFECT format identifier was intended to allow the target to indicate to the data object what operation actually took place. However, the Shell uses optimized moves for file system objects whenever possible. In that case, the Shell normally sets the CFSTR_PERFORMEDDROPEFFECT value to DROPEFFECT_NONE, to indicate to the data object that the original data has been deleted. Thus, the source cannot use the CFSTR_PERFORMEDDROPEFFECT value to determine which operation has taken place. While most sources do not need this information, there are some exceptions. For instance, even though optimized moves eliminate the need for a source to delete any data, the source might still need to update a related database to indicate that the files have been moved or copied.


    Of course that claims to be for Version 5..

    a little below we then get some more documentation which fails to clarify anything



    This format identifier is used by the target to inform the data object through its IDataObject::SetData method of the outcome of a data transfer. The data is an STGMEDIUM structure that contains a global memory object. The structure’s hGlobal member points to a DWORD set to the appropriate DROPEFFECT value, normally DROPEFFECT_MOVE or DROPEFFECT_COPY.

    This format is normally used when the outcome of an operation can be either move or copy, such as in anoptimized move or delete-on-paste operation. It provides a reliable way for the target to tell the data object what actually happened. It was introduced because the value of pdwEffect returned by DoDragDrop did not reliably indicate which operation had taken place. The CFSTR_PERFORMEDDROPEFFECT format is the reliable way to indicate that an unoptimized move has taken place.


    So should I use CFSTR_LOGICALPERFORMEDDROPEFFECT or CFSTR_PERFORMEDDROPEFFECT ? After all I think it’s pretty safe to assume that people all have IE 5? Or maybe not (maybe assumptions are never safe, like assuming people will have enough sense to recognize bad coding practices as simple conveniences for demonstrative purposes)? Perhaps I should be doing DllGetVersion on the Shell dll?

  2. Anonymous says:

    I can’t believe you hard-coded the strings in the MessageBox() call. This example needs a full local-language independant implementation.


  3. Anonymous says:

    i hope there’s a series #4 in drag-drop lectures that explains what happens when IAsyncOperation gets into the picture


  4. Anonymous says:

    I can’t believe you hard-coded the strings in the MessageBox() call. This example needs a full local-language independant implementation.

    At least the grumpiest of them all won’t be able to complain about unicode correctness. ;)

Comments are closed.