Click here to Skip to main content
16,015,518 members
Articles / Desktop Programming / MFC
Article

How to Implement Drag and Drop Between Your Program and Explorer

Rate me:
Please Sign up or sign in to vote.
4.91/5 (105 votes)
13 Dec 2002 900.5K   11.5K   278   188
A step-by-step description of how to drag and drop files between your application and Explorer windows.

Introduction

I've seen several questions around the CodeProject boards lately asking about doing drag and drop between a program and Explorer windows. Like many things in Windows, it seems easy once you know how it's done, but finding the answer can be quite a chore. In this article, I demonstrate how to hook up drag and drop so your program can accept drops from an Explorer window, and be a drag source so your users can drag files into an Explorer window.

The sample project is an MFC app, and the article assumes you're familiar with C++, MFC, and using COM objects and interfaces. If you need help with COM objects and interfaces, check out my Intro to COM article. The program is MultiFiler, a little utility that acts like a drag and drop "staging area". You can drag any number of files into MultiFiler, and it shows them all in a list. You can then drag files back out to Explorer windows, using the Shift or Ctrl keys to tell Explorer to move or copy the original files, respectively.

Drag and Drop with Explorer

As you know, Explorer lets you drag files among Explorer windows and the desktop. When you begin a drag operation, the Explorer window you drag from (the drop source), creates a COM object implementing the IDataObject interface, and puts some data into the object. The window you drag into (the drop target), then reads that data using IDataObject methods; that's how it knows what files are being dropped.

If you check out the data contained in the IDataObject with a viewer like ClipSpy, you'll see that Explorer puts several data formats in the data object:

Image 1

The important format is CF_HDROP. The other formats are custom formats registered by Explorer for its own use. If we write an app that registers its window as a drop target, and if we know how to read CF_HDROP data, we'll be able to accept dropped files. Similarly, if we can fill a data object with CF_HDROP data, Explorer will let our app be a drag source. So, what's contained in that CF_HDROP format? Read on...

The DROPFILES data structure

So what exactly is the CF_HDROP format? It turns out that it's just a DROPFILES struct. There's also the HDROP type, which is simply a pointer to a DROPFILES struct.

DROPFILES isn't a very complex structure. Here is its definition:

struct DROPFILES
{
    DWORD pFiles;    // offset of file list
    POINT pt;        // drop point (client coords)
    BOOL fNC;        // is it on NonClient area and pt is in screen coords
    BOOL fWide;      // wide character flag
};

The one thing that isn't listed in the struct definition is the list of filenames. The list is formatted as a double-null terminated list of strings. But where is it stored? It is actually stored right after the fWide member, and pFiles holds the offset (relative to the beginning of the struct) where the list is located in memory. The only other member that's used in drag and drop is fWide, which indicates whether the filenames are in ANSI or Unicode characters.

Accepting a drag and drop from Explorer

Accepting a drag and drop is much easier than initiating one, so I'll cover accepting first.

There are two ways for your window to accept drag and drop. The first way is a holdover from Windows 3.1 and uses the WM_DROPFILES message. The other way is to register your window as an OLE drop target.

The old way - WM_DROPFILES

To use the old method, you first set the "accept files" style in your window. For dialogs, this is on the "Extended Styles" page, as shown here:

Image 2

If you want to set this style at runtime, call the DragAcceptFiles() API, which takes two parameters. The first is your main window handle, and the second is TRUE to indicate you can accept drag and drop. If your main window is a CView instead of a dialog, you'll need to set this style at runtime.

No matter which of the two methods you use, your window becomes a drop target. When you drag files or folders from an Explorer window and drop them in your window, the window receives a WM_DROPFILES message. The WPARAM of a WM_DROPFILES message is an HDROP that lists what files are being dropped. There are three APIs you use to get the file list out of the HDROP: DragQueryFile(), DragQueryPoint(), and DragFinish().

  • DragQueryFile() does two things: returns the number of files being dragged, and enumerates through the list of files. DragQueryPoint() returns the pt member of the DROPFILES struct. DragFinish() frees up memory allocated during the drag and drop process.
  • DragQueryFile() takes four parameters: The HDROP, the index of the filename to return, a buffer allocated by the caller to hold the name, and the size of the buffer in characters. If you pass -1 as the index, DragQueryFile() returns the number of files in the list. Otherwise, it returns the number of characters in the filename. You can test this return against 0 to tell if the call succeeded.
  • DragQueryPoint() takes two parameters, the HDROP and a pointer to a POINT struct that receives the value in the pt member of the DROPFILES struct. DragFinish() just takes one parameter, the HDROP.

A typical WM_DROPFILES handler would look like this:

void CMyDlg::OnDropFiles ( HDROP hdrop ) 
{
UINT  uNumFiles;
TCHAR szNextFile [MAX_PATH];

    // Get the # of files being dropped.
    uNumFiles = DragQueryFile ( hdrop, -1, NULL, 0 );

    for ( UINT uFile = 0; uFile < uNumFiles; uFile++ )
        {
        // Get the next filename from the HDROP info.
        if ( DragQueryFile ( hdrop, uFile, szNextFile, MAX_PATH ) > 0 )
            {
            // ***
            // Do whatever you want with the filename in szNextFile.
            // ***
            }
        }

    // Free up memory.
    DragFinish ( hdrop );
}

DragQueryPoint() isn't necessary if all you want is the list of files. (Actually, I've never had to use it myself.)

The new way - using an OLE drop target

The other method of accepting drag and drop is to register your window as an OLE drop target. Normally, doing so would require that you write a C++ class that implements the IDropTarget interface. However, MFC has a COleDropTarget class that takes care of that for us. The process is a bit different depending on whether your main window is a dialog or a CView, so I'll cover both below.

Making a CView a drop target

CView already has some drag and drop support built-in, however it's not normally activated. To activate it, you add a COleDropTarget member variable to the view, and then call its Register() function in your view's OnInitialUpdate() to make the view a drop target, as shown below:

void CMyView::OnInitialUpdate()
{
    CView::OnInitialUpdate();

    // Register our view as a drop target.
    // m_droptarget is a COleDropTarget member of CMyView.
    m_droptarget.Register ( this );
}

Once that's done, you then override four virtual functions that are called when the user drags over your view:

  • OnDragEnter(): Called when the cursor enters your window.
  • OnDragOver(): Called when the cursor moves inside your window.
  • OnDragLeave(): Called when the cursor leaves your window.
  • OnDrop(): Called when the user drops in your window.
OnDragEnter()

OnDragEnter() is the first function called. Its prototype is:

DROPEFFECT CView::OnDragEnter( COleDataObject* pDataObject, 
               DWORD dwKeyState, CPoint point );

The parameters are:

  • pDataObject: Pointer to a COleDataObject that contains the data being dragged.
  • dwKeyState: A set of flags indicating which mouse button is clicked and which shift keys (if any) are pressed. The flags are MK_CONTROL, MK_SHIFT, MK_ALT, MK_LBUTTON, MK_MBUTTON, and MK_RBUTTON.
  • point: The cursor position, expressed in the view's client coordinates.

OnDragEnter() returns a DROPEFFECT value, which tells OLE whether the drop will be accepted, and if so, what cursor should be displayed. The values and their meanings are:

  • DROPEFFECT_NONE: The drop will not be accepted. The cursor changes to: Image 3.
  • DROPEFFECT_MOVE: The data will be moved by the drop target. The cursor changes to: Image 4.
  • DROPEFFECT_COPY: The data will be copied by the drop target. The cursor changes to: Image 5
  • DROPEFFECT_LINK: The data will be linked to by the drop target. The cursor changes to: Image 6.

Normally, in OnDragEnter() you examine the data being dragged and see if it meets your criteria. If not, you return DROPEFFECT_NONE to reject the drag and drop. Otherwise, you can return one of the other values depending on what you intend to do with the data.

OnDragOver()

If you return a value other than DROPEFFECT_NONE from OnDragEnter(), OnDragOver() is called whenever the mouse cursor moves within your window. The prototype of OnDragOver() is:

DROPEFFECT CView::OnDragOver ( COleDataObject* pDataObject, 
               DWORD dwKeyState, CPoint point );

The parameters and return value are identical to OnDragEnter(). OnDragOver() lets you return different DROPEFFECT values depending on the cursor position and the shift key state. For instance, if your main view window has several areas, displaying different lists of information, and you want to allow drops only in one part, you'd check the cursor position in the point parameter, and return DROPEFFECT_NONE if the cursor is not in that area.

As for the shift keys, you normally react to them as described below:

  • SHIFT pressed (MK_SHIFT in dwKeyState): Return DROPEFFECT_MOVE.
  • CONTROL pressed (MK_CONTROL): Return DROPEFFECT_COPY.
  • Both pressed (MK_SHIFT | MK_CONTROL): Return DROPEFFECT_LINK.

These are only guidelines, although it's best to adhere to them, since they are what Explorer uses. But if some of the actions (copy, move, or link) doesn't make sense for your app, you don't have to return the corresponding DROPEFFECT. For instance, in MultiFiler (I'll get to it, I promise!) OnDragOver() always returns DROPEFFECT_COPY. Just be sure to return the right value, so that the cursor accurately indicates to the user, what will happen if he drops in your window.

OnDragLeave()

OnDragLeave() is called if the user drags out of your window without dropping. The prototype is:

void CView::OnDragLeave();

It has no parameters or return value - its purpose is to let you clean up any memory you allocated during OnDragEnter() and OnDragOver().

OnDrop()

If the user drops over your window (and you didn't return DROPEFFECT_NONE from the most recent call to OnDragOver()), then OnDrop() is called so you can act on the drag and drop. The prototype of OnDrop() is:

BOOL CView::OnDrop ( COleDataObject* pDataObject, 
         DROPEFFECT dropEffect, CPoint point );

The dropEffect parameter is equal to the last return value from OnDragOver(), and the others are the same as OnDragEnter(). The return value is TRUE if the drop is completed successfully (it's up to you to define what a "successful" completion is), or FALSE if not.

OnDrop() is where all the action happens - you can act on the dropped data in whatever way makes sense for your app. In MultiFiler, the dropped files are added to the main window's list control.

Making a dialog a drop target

Things are a bit more difficult if your main window is a dialog (or anything not derived from CView). Since the base COleDropTarget implementation is designed to work only with a CView-derived window, you need to derive a new class from COleDropTarget and override the four methods outlined above.

A typical COleDropTarget-derived class declaration looks like this:

class CMyDropTarget : public COleDropTarget  
{
public:
    DROPEFFECT OnDragEnter ( CWnd* pWnd, COleDataObject* pDataObject,
                             DWORD dwKeyState, CPoint point );

    DROPEFFECT OnDragOver ( CWnd* pWnd, COleDataObject* pDataObject,
                            DWORD dwKeyState, CPoint point );

    BOOL OnDrop ( CWnd* pWnd, COleDataObject* pDataObject,
                  DROPEFFECT dropEffect, CPoint point );

    void OnDragLeave ( CWnd* pWnd );

    CMyDropTarget ( CMyDialog* pMainWnd );
    virtual ~CMyDropTarget();

protected:
    CMyDialog* m_pParentDlg;  // initialized in constructor</FONT>
};

In this example, the constructor is passed a pointer to the main window, so the drop target methods can send messages and do other stuff in the dialog. You can change this to whatever best suits your needs. You then implement the four drag and drop methods as described in the previous section. The only difference is the additional CWnd* parameter, which is a pointer to the window that the cursor is over at the time of the call.

Once you have this new class, you add a drop target member variable to your dialog and call its Register() function in OnInitDialog():

BOOL CMyDialog::OnInitDialog()
{
    // Register our dialog as a drop target.
    // m_droptarget is a CMyDropTarget member of CMyDialog.
    m_droptarget.Register ( this );
}

Accessing the HDROP data in a CDataObject

If you use an OLE drop target, your drag and drop functions receive a pointer to a COleDataObject. This is an MFC class that implements IDataObject and contains all of the data that the drag source created when the drag began. You'll need a bit of code to look for CF_HDROP data in the data object and get an HDROP handle. Once you have an HDROP, you can use DragQueryFile(), as shown earlier, to read the list of dropped files.

Here is the code to get an HDROP from a COleDataObject:

BOOL CMyDropTarget::OnDrop ( CWnd* pWnd, COleDataObject* pDataObject,
                             DROPEFFECT dropEffect, CPoint point )
{
HGLOBAL hg;
HDROP   hdrop;

    // Get the HDROP data from the data object.
    hg = pDataObject->GetGlobalData ( CF_HDROP );
    
    if ( NULL == hg )
        return FALSE;

    hdrop = (HDROP) GlobalLock ( hg );

    if ( NULL == hdrop )
        {
        GlobalUnlock ( hg );
        return FALSE;
        }

    // Read in the list of files here...

    GlobalUnlock ( hg );

    // Return TRUE/FALSE to indicate success/failure 
}

Summary of the two methods

Handling WM_DROPFILES:

  • Holdover from Windows 3.1; it could conceivably be removed in the future.
  • No customization of the drag and drop process possible - you can only act after the drop occurs.
  • Doesn't let you inspect the raw data being dropped.
  • If you don't need any fancy customization, this method is much easier to code.

Using an OLE drop target:

  • Uses a COM interface, which is a modern and better-supported mechanism.
  • Good MFC support through CView and COleDropTarget.
  • Allows full control over the drag and drop operation.
  • Gives you access to the raw IDataObject so you can access any data format.
  • Requires a bit more code, but once you write it once, you can cut and paste it to new projects.

How MultiFiler accepts drag and drop

The demo project ZIP file actually contains three MultiFiler projects. Each uses one of the techniques I've described to accept drag and drop from Explorer windows. When the user drops over the MultiFiler window, all of the dropped files are added to a list control, as shown here:

Image 7

MultiFiler automatically eliminates duplicate files, so any file will only appear once in the list.

If you are using Windows 2000 and run a MultiFiler that uses an OLE drop target, you'll notice that the drag image is the new-fangled faded style:

Image 8

This doesn't come for free, but it's a nice example of the customization you can do when you use an OLE drop target. I'll explain how to do this at the end of the article.

Initiating a drag and drop

To have Explorer accept dragged files, all we have to do is create some CF_HDROP data and put it in a data object. Of course, if it were that simple, I wouldn't have anything to write about. The DROPFILES struct is a bit tricky to create (since it's not always the same size), but again, after you write the code once (or, after I write it once!) you can reuse it everywhere.

MultiFiler initiates a drag and drop when you select files in the list control and drag them. The control sends an LVN_BEGINDRAG notification message when this happens, so that's when MultiFiler creates the data object and hands it off to OLE to begin the drag and drop operation.

The steps in creating a DROPFILES are:

  1. Enumerate all of the selected items in the list control, putting them in a string list.
  2. Keep track of the length of each string as it's added to the string list.
  3. Allocate memory for the DROPFILES itself and the list of filenames.
  4. Fill in the DROPFILES members.
  5. Copy the list of filenames into the allocated memory.

I'll go over the MultiFiler code now, so you can see exactly how to set up a DROPFILES. If you feel like checking out the source in the MultiFiler projects, all three do it the same way, so you can look at any of the three.

The first step is to put all of the selected filenames in a list, and keep track of the memory needed to hold all of the strings.

void CMultiFilerDlg::OnBegindragFilelist(NMHDR* pNMHDR, LRESULT* pResult) 
{
CStringList    lsDraggedFiles;
POSITION       pos;
CString        sFile;
UINT           uBuffSize = 0;

    // For every selected item in the list,
    // put the filename into lsDraggedFiles.
    // c_FileList is our dialog's CListCtrl.
    pos = c_FileList.GetFirstSelectedItemPosition();

    while ( NULL != pos )
        {
        nSelItem = c_FileList.GetNextSelectedItem ( pos );
        // put the filename in sFile
        sFile = c_FileList.GetItemText ( nSelItem, 0 );    

        lsDraggedFiles.AddTail ( sFile );

        // Calculate the # of chars required to hold this string.
        uBuffSize += lstrlen ( sFile ) + 1;
        }

At this point, uBuffSize holds the total length of all the strings, including nulls, in characters. We add 1 for the final null to terminate the list, then multiply that by sizeof(TCHAR) to convert from characters to bytes. We then add sizeof(DROPFILES) to get the final required buffer size.

// Add 1 extra for the final null char,
// and the size of the DROPFILES struct.
uBuffSize = sizeof(DROPFILES) + sizeof(TCHAR) * (uBuffSize + 1);

Now that we know how much memory we need, we can allocate it. When doing drag and drop, you allocate memory from the heap with GlobalAlloc():

HGLOBAL    hgDrop;
DROPFILES* pDrop;

    // Allocate memory from the heap for the DROPFILES struct.
    hgDrop = GlobalAlloc ( GHND | GMEM_SHARE, uBuffSize );

    if ( NULL == hgDrop )
        return;

We then get direct access to the memory with GlobalLock():

pDrop = (DROPFILES*) GlobalLock ( hgDrop );

if ( NULL == pDrop )
    {
    GlobalFree ( hgDrop );
    return;
    }

Now we can start filling in the DROPFILES. The GHND flag in the GlobalAlloc() call initializes the memory to zero, so we only have to set a couple of members:

    // Fill in the DROPFILES struct.
    pDrop->pFiles = sizeof(DROPFILES);

#ifdef _UNICODE
    // If we're compiling for Unicode, set the Unicode flag in the struct to
    // indicate it contains Unicode strings.
    pDrop->fWide = TRUE;
#endif

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;

    // Copy all the filenames into memory after
    // the end of the DROPFILES struct.
    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 };

    // Put the data in the data source.
    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.

// The global g_uCustomClipbrdFormat is set like this:
// UINT g_uCustomClipbrdFormat = RegisterClipboardFormat 
// ( _T("MultiFiler_3BCFE9D1_6D61_4cb6_9D0B_3BB3F643CA82") );

    // Add our own custom data, so we know that
    // the drag originated from our window.
    // The data will just be a dummy bool.
HGLOBAL hgBool;

    hgBool = GlobalAlloc ( GHND | GMEM_SHARE, sizeof(bool) );

    if ( NULL == hgBool )
        {
        GlobalFree ( hgDrop );
        return;
        }

    // Put the data in the data source.
    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 DROPEFFECT_LINK does not make Explorer create shortcuts, so that's why I didn't include DROPEFFECT_LINK in the call above.

Once DoDragDrop() returns, we check the return value. If it's DROPEFFECT_MOVE or DROPEFFECT_COPY, the drag and drop completed successfully, so we remove all of the selected files from the main window's list control. If it's DROPEFFECT_NONE, things are a bit tricky. On Windows 9x, it means the operation was canceled. However, on NT/2000, the shell returns DROPEFFECT_NONE for move operations as well. (This is by design! See KB article Q182219 for details.) So on NT, we have to manually check whether the files were moved, and if so, we remove them from the list control. The code is a bit long, so I won't repeat it here. Check out the MultiFiler source if you're interested in how it works.

The last thing we do is free the allocated memory if the drag and drop was canceled. If it completed, then the drop target owns the memory, and we must not free it. Below is the code to check the return value from DoDragDrop(), just without the NT/2000 code I just mentioned.

    switch ( dwEffect )
        {
        case DROPEFFECT_COPY:
        case DROPEFFECT_MOVE:
            {
            // The files were copied or moved.
            // Note: Don't call GlobalFree() because
            // the data will be freed by the drop target.

            // ** Omitted code to remove list control items. **
            }
        break;

        case DROPEFFECT_NONE:
            {
            // ** Omitted code for NT/2000 that checks
            // if the operation actually succeeded. **

            // The drag operation wasn't accepted, or was canceled, so we 
            // should call GlobalFree() to clean up.

            GlobalFree ( hgDrop );
            GlobalFree ( hgBool );
            }
        break;
        }
}

Other details

You can also right-click the MultiFiler list control to get a context menu with four commands. They are pretty simple, and deal with managing the selection in the list and clearing the list.

Image 9

Oh, and I promised to explain how to get the cool drag image on Windows 2000! It's actually pretty simple. There is a new coclass supported by the shell called CLSID_DragDropHelper with two interfaces, IDragSourceHelper and IDropTargetHelper. IDropTargetHelper is the one that draws the drag image. It has four methods whose names should be familiar: DragEnter(), DragOver(), DragLeave(), and Drop(). All you have to do is do your normal drag and drop processing, determine what DROPEFFECT you will return, and then call the IDropTargetHelper method that corresponds to the COleDropTarget method. The IDropTargetHelper methods need the DROPEFFECT to properly draw the drag image, so that's why you need to determine it first.

If you look at the MultiFiler sample that uses a CView, you'll see two member variables:

IDropTargetHelper* m_piDropHelper;
bool               m_bUseDnDHelper;

In the view's constructor, the code creates the drop helper COM object and gets an IDropTargetHelper interface. Based on whether this succeeds, m_bUseDnDHelper is set so that other functions will know whether the COM object is available.

CMultiFilerView::CMultiFilerView() : m_bUseDnDHelper(false),
                                     m_piDropHelper(NULL)
{
    // Create an instance of the shell drag and drop helper object.
    if ( SUCCEEDED( CoCreateInstance ( CLSID_DragDropHelper, NULL, 
                                       CLSCTX_INPROC_SERVER,
                                       IID_IDropTargetHelper, 
                                       (void**) &m_piDropHelper ) ))
        {
        m_bUseDnDHelper = true;
        }
}

Then the four drag and drop functions call IDropTargetHelper methods. Here's an example:

DROPEFFECT CMultiFilerView::OnDragEnter(COleDataObject* pDataObject, 
                          DWORD dwKeyState, CPoint point) 
{
DROPEFFECT dwEffect = DROPEFFECT_NONE;

    // ** Omitted - code to determine dwEffect. **

    // Call the drag and drop helper.
    if ( m_bUseDnDHelper )
        {
        // The DnD helper needs an IDataObject interface, so get one from
        // the COleDataObject.  Note that the FALSE param means that
        // GetIDataObject will not AddRef() the returned interface, so 
        // we do not Release() it.
        IDataObject* piDataObj = pDataObject->GetIDataObject ( FALSE ); 

        m_piDropHelper->DragEnter ( GetSafeHwnd(), piDataObj, 
                                    &point, dwEffect );
        }

    return dwEffect;
}

GetIDataObject() is an undocumented function in COleDataObject that returns an IDataObject interface. (You can find some useful stuff by looking through the MFC header files sometimes!)

And finally, the view's destructor releases the COM object.

CMultiFilerView::~CMultiFilerView()
{
    if ( NULL != m_piDropHelper )
        m_piDropHelper->Release();
}

By the way, if you don't have a recent Platform SDK installed, you may not have the definition of the IDropTargetHelper interface and the associated GUIDs. I've included the necessary definitions in each of the MultiFiler samples; just uncomment them and you should be good to go.

If you're wondering about going the other way - using IDragSourceHelper to draw the neat drag image when MultiFiler is the drag source - the documentation is less clear on this topic, and it certainly looks harder than using IDropTargetHelper, so I haven't worked on it yet.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Software Developer (Senior) VMware
United States United States
Michael lives in sunny Mountain View, California. He started programming with an Apple //e in 4th grade, graduated from UCLA with a math degree in 1994, and immediately landed a job as a QA engineer at Symantec, working on the Norton AntiVirus team. He pretty much taught himself Windows and MFC programming, and in 1999 he designed and coded a new interface for Norton AntiVirus 2000.
Mike has been a a developer at Napster and at his own lil' startup, Zabersoft, a development company he co-founded with offices in Los Angeles and Odense, Denmark. Mike is now a senior engineer at VMware.

He also enjoys his hobbies of playing pinball, bike riding, photography, and Domion on Friday nights (current favorite combo: Village + double Pirate Ship). He would get his own snooker table too if they weren't so darn big! He is also sad that he's forgotten the languages he's studied: French, Mandarin Chinese, and Japanese.

Mike was a VC MVP from 2005 to 2009.

Comments and Discussions

 
AnswerRe: .... And the Clipboard? Pin
Michael Dunn28-Jul-07 10:13
sitebuilderMichael Dunn28-Jul-07 10:13 
GeneralRe: .... And the Clipboard? Pin
DaProgramma28-Jul-07 11:00
DaProgramma28-Jul-07 11:00 
GeneralRe: .... And the Clipboard? Pin
Michael Dunn28-Jul-07 11:04
sitebuilderMichael Dunn28-Jul-07 11:04 
GeneralRe: .... And the Clipboard? Pin
DaProgramma28-Jul-07 12:43
DaProgramma28-Jul-07 12:43 
GeneralRe: .... And the Clipboard? Pin
Raptorikus7-Sep-07 6:56
Raptorikus7-Sep-07 6:56 
GeneralRe: .... And the Clipboard? Pin
DaProgramma7-Sep-07 20:39
DaProgramma7-Sep-07 20:39 
GeneralRe: .... And the Clipboard? Pin
Raptorikus10-Sep-07 5:23
Raptorikus10-Sep-07 5:23 
GeneralRe: .... And the Clipboard? Pin
DaProgramma10-Sep-07 5:37
DaProgramma10-Sep-07 5:37 
AFAIK, I did it right. (I use .NET, not C++ and MFC).

Well, I don't care any longer, it seems to work good enough, so I'll leave it alone.




GeneralRe: .... And the Clipboard? Pin
Raptorikus13-Sep-07 6:15
Raptorikus13-Sep-07 6:15 
GeneralExtremely useful - Plus many other bonus items Pin
Yogesh Dhakad13-Jul-07 21:46
Yogesh Dhakad13-Jul-07 21:46 
QuestionHow to transfer file from listctrl to Explorer? Pin
whiteclouds12-Jun-07 23:53
whiteclouds12-Jun-07 23:53 
AnswerRe: How to transfer file from listctrl to Explorer? Pin
Michael Dunn13-Jun-07 14:51
sitebuilderMichael Dunn13-Jun-07 14:51 
QuestionHow to get Explorer current path before drop files ? Pin
GlobalFree15-Apr-07 17:31
GlobalFree15-Apr-07 17:31 
AnswerRe: How to get Explorer current path before drop files ? Pin
Codemonkey856-Jul-08 7:07
Codemonkey856-Jul-08 7:07 
GeneralVista Pin
MyRock23-Mar-07 0:38
MyRock23-Mar-07 0:38 
GeneralRe: Vista Pin
Michael Dunn27-Mar-07 20:48
sitebuilderMichael Dunn27-Mar-07 20:48 
GeneralRe: Vista Pin
cmumford16-Oct-07 10:49
cmumford16-Oct-07 10:49 
GeneralRe: Vista Pin
munkeez20-May-08 14:24
munkeez20-May-08 14:24 
Questionhaii Pin
ajay singh manhas20-Mar-07 3:27
ajay singh manhas20-Mar-07 3:27 
QuestionDrap from folder blocks windows explorer on drag Pin
Rui Frazao15-Mar-07 1:07
Rui Frazao15-Mar-07 1:07 
AnswerRe: Drap from folder blocks windows explorer on drag Pin
Michael Dunn16-Mar-07 15:14
sitebuilderMichael Dunn16-Mar-07 15:14 
AnswerRe: Drap from folder blocks windows explorer on drag Pin
pASkuda1-Feb-08 2:09
pASkuda1-Feb-08 2:09 
QuestionHow to get order selected? Pin
An Phung Nguyen11-Mar-07 22:44
An Phung Nguyen11-Mar-07 22:44 
AnswerRe: How to get order selected? Pin
Michael Dunn16-Mar-07 15:13
sitebuilderMichael Dunn16-Mar-07 15:13 
Generalone more question Pin
Anxnahlia25-Jan-07 21:08
Anxnahlia25-Jan-07 21:08 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.