Note that the pFiles member doesn't indicate the size of the DROPFILES struct; it's the offset of the file list. But since the file list is located right after the end of the struct, its offset is the same as the size of the struct.
Now we can copy all of the filenames into memory, and then unlock the buffer.
TCHAR* pszBuff;
pos = lsDraggedFiles.GetHeadPosition();
pszBuff = (TCHAR*) (LPBYTE(pDrop) + sizeof(DROPFILES));
while ( NULL != pos )
{
lstrcpy ( pszBuff, (LPCTSTR) lsDraggedFiles.GetNext ( pos ) );
pszBuff = 1 + _tcschr ( pszBuff, '\0' );
}
GlobalUnlock ( hgDrop );
The next step is to construct a COleDataSource object and put our data into it. We also need a FORMATETC struct that describes the clipboard format (CF_HDROP) and how the data is stored (an HGLOBAL).
COleDataSource datasrc;
FORMATETC etc = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
datasrc.CacheGlobalData ( CF_HDROP, hgDrop, &etc );
Now, this would be sufficient to initiate a drag and drop, but there's one more detail to take care of. Since MultiFiler accepts drag and drop, it will happily accept the drag and drop we are about to initiate ourselves. While that's not disastrous, it's not very neat either. So we will add another bit of data to the data source, in a custom clipboard format that we register. Our OnDragEnter() and OnDragOver() functions will check for this format, and if it's present, they will not accept the drop.
HGLOBAL hgBool;
hgBool = GlobalAlloc ( GHND | GMEM_SHARE, sizeof(bool) );
if ( NULL == hgBool )
{
GlobalFree ( hgDrop );
return;
}
etc.cfFormat = g_uCustomClipbrdFormat;
datasrc.CacheGlobalData ( g_uCustomClipbrdFormat, hgBool, &etc );
Note that we don't have to set the data to any particular value - the fact that the data is in the data source is the important part.
Now that we've put together the data, we can start the drag and drop operation! We call the DoDragDrop() method of COleDataSource, which does not return until the drag and drop is completed. The only parameter is one or more DROPEFFECT values that indicate what operations we will allow the user to do. It returns a DROPEFFECT value indicating what the user wants to do with the data, or DROPEFFECT_NONE if the drop was aborted or not accepted by the target.
DROPEFFECT dwEffect;
dwEffect = datasrc.DoDragDrop ( DROPEFFECT_COPY | DROPEFFECT_MOVE );
In our case, we only allow copying and moving. During the drag and drop, the user can hold down the Control or Shift key to change the operation. For some reason, passing
|