Click here to Skip to main content
11,704,315 members (57,261 online)
Click here to Skip to main content

How to drag a virtual file from your app into Windows Explorer

, 13 Sep 2006 CPOL 141.8K 1.4K 95
Rate this:
Please Sign up or sign in to vote.
Shows how to use CFSTR_FILEDESCRIPTOR and CFSTR_FILECONTENTS to perform drag/drop with virtual files

Introduction

I was working on an FTP client application where I needed to support drag/drop with Windows Explorer. Dropping files from Explorer into the client was trivial and didn't cause me too much trouble, but dropping files back into Explorer was not as easy. Using CF_HDROP was ruled out because the source file would not physically exist, as it would have to be downloaded from the FTP server before Explorer can get at it. After playing around with various stuff, I finally hit upon using CFSTR_FILEDESCRIPTOR and CFSTR_FILECONTENTS, which suited my purposes. In this article, I'll demonstrate a simple dialog app that will let you drag non-existent files from your app into Explorer.

Basic Technique

  • Derive a class from COleDataSource
  • Allocate global memory and create data in CFSTR_FILEDESCRIPTOR format, and then use CacheGlobalData on the COleDataSource derived class
  • Override OnRenderFileData in this derived class
  • In the OnRenderFileData override, handle CFSTR_FILECONTENTS, and write directly to the CFSTR_FILECONTENTS

Creating the demo app

Generate a default Dialog based MFC application with Visual Studio 2005 (or an earlier version if you don't have 2005). Use the resource editor to add a List control to the dialog, and associate a DDX control variable with it of type CListCtrl and name it m_fileList. Now add the following code to the OnInitDialog to setup the List control.

BOOL CExplorerDelayDropDlg::OnInitDialog()
{
    CDialog::OnInitDialog();

    . . .       

    AfxOleInit();

    m_fileList.SetExtendedStyle(LVS_EX_FULLROWSELECT);
    m_fileList.InsertColumn(0, _T("File"), LVCFMT_LEFT,75);
    m_fileList.InsertColumn(1, _T("Details"), LVCFMT_LEFT,175);

    for(TCHAR c = _T('A'); c < _T('F'); c++)
    {
        CString text1, text2;
        text1.Format(_T("%c.txt"),c);
        text2.Format(_T("File full of %cs"),c);
        m_fileList.SetItemText(
            m_fileList.InsertItem(c - _T('A'),text1),1,text2);
    }

    return TRUE;  
}

The code merely fills up the List control with some dummy file names. Note how I have made a call to AfxOleInit (you don't need to do this if your app already supports OLE).

Deriving a class from COleDataSource

Since we are using delayed data rendering, we need to derive a class from COleDataSource so that we can override OnRenderFileData (the default version merely returns FALSE). So, the first step is to add a class named CMyOleDataSource (derived from COleDataSource) to the project.

class CMyOleDataSource : public COleDataSource
{
    DECLARE_DYNAMIC(CMyOleDataSource)

Now, we need to add an override for OnRenderFileData as shown below.

BOOL CMyOleDataSource::OnRenderFileData(
    LPFORMATETC lpFormatEtc,CFile* pFile)
{
    // We only need to handle CFSTR_FILECONTENTS
    if(lpFormatEtc->cfFormat == 
        RegisterClipboardFormat(CFSTR_FILECONTENTS))
    {   
        HGLOBAL hGlob = NULL;
        const int buffSize = 512;
        hGlob = GlobalAlloc(GMEM_FIXED, buffSize);
        if(hGlob)
        {
            LPBYTE pBytes = (LPBYTE)GlobalLock(hGlob);          
            if(pBytes)
            {
                // lpFormatEtc->lindex can be used to identify
                // the file that's being copied
                memset(pBytes, (int) m_Files.GetAt(
                    lpFormatEtc->lindex)[0], buffSize);
                pFile->Write(pBytes,buffSize);              
            }
            GlobalUnlock(hGlob);
        }
        GlobalFree(hGlob);
        // Need to return TRUE to indicate success to Explorer
        return TRUE;
    }
    return COleDataSource::OnRenderFileData(
        lpFormatEtc, pFile);
}

Note how in the above code, I create dummy files by filling up 512 bytes with a specific character that identifies the file. In a more real life scenario, you'd have to retrieve a specific file, identified by the lindexparameter, and then retrieve that file from a remote source or perhaps from an archive.

Now, we just need to handle the LVN_BEGINDRAG notification as shown below. You can either use the Properties box to add a handler, or manually add an ON_NOTIFY handler in the dialog class.

void CExplorerDelayDropDlg::OnBeginDrag(NMHDR *pNMHDR, LRESULT *pResult)
{
    UINT uFileCount = m_fileList.GetSelectedCount();    

    // The CFSTR_FILEDESCRIPTOR format expects a 
    // FILEGROUPDESCRIPTOR structure followed by an
    // array of FILEDESCRIPTOR structures, one for
    // each file being dropped
    UINT uBuffSize = sizeof(FILEGROUPDESCRIPTOR) + 
        (uFileCount-1) * sizeof(FILEDESCRIPTOR);
    HGLOBAL hFileDescriptor = GlobalAlloc ( 
        GHND | GMEM_SHARE, uBuffSize );        

    if(hFileDescriptor)
    {
        FILEGROUPDESCRIPTOR* pGroupDescriptor = 
            (FILEGROUPDESCRIPTOR*) GlobalLock ( hFileDescriptor );
        if(pGroupDescriptor)
        {
            // Need a pointer to the FILEDESCRIPTOR array
            FILEDESCRIPTOR* pFileDescriptorArray = 
                (FILEDESCRIPTOR*)((LPBYTE)pGroupDescriptor + sizeof(UINT));
            pGroupDescriptor->cItems = uFileCount;            

            POSITION pos = m_fileList.GetFirstSelectedItemPosition();
            int index = 0;
            m_DataSrc.m_Files.RemoveAll();
            while( NULL != pos )
            {   
                int nSelItem = m_fileList.GetNextSelectedItem( pos );
                ZeroMemory(&pFileDescriptorArray[index], 
                    sizeof(FILEDESCRIPTOR));
                lstrcpy ( pFileDescriptorArray[index].cFileName, 
                    m_fileList.GetItemText( nSelItem, 0 ) );
                m_DataSrc.m_Files.Add(
                    pFileDescriptorArray[index].cFileName);
                pFileDescriptorArray[index].dwFlags = 
                    FD_FILESIZE|FD_ATTRIBUTES;
                pFileDescriptorArray[index].nFileSizeLow = 512;
                pFileDescriptorArray[index].nFileSizeHigh = 0;
                pFileDescriptorArray[index].dwFileAttributes = 
                    FILE_ATTRIBUTE_NORMAL;
                index++;
            }
        }
        else
        {
            GlobalFree ( hFileDescriptor );
        }
    }
    GlobalUnlock ( hFileDescriptor );       

    // For the CFSTR_FILEDESCRIPTOR format, we use
    // CacheGlobalData since we make that data available 
    // immediately
    FORMATETC etcDescriptor = { 
        RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR), 
        NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
    m_DataSrc.CacheGlobalData ( RegisterClipboardFormat(
        CFSTR_FILEDESCRIPTOR), hFileDescriptor, &etcDescriptor );

    // For CFSTR_FILECONTENTS, we use DelayRenderFileData
    // as this data will have to come from a non-physical
    // device, like an FTP site, an add-on device, or an archive
    FORMATETC etcContents = { 
        RegisterClipboardFormat(CFSTR_FILECONTENTS), 
        NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
    m_DataSrc.DelayRenderFileData(RegisterClipboardFormat(
        CFSTR_FILECONTENTS), &etcContents);

    DROPEFFECT dwEffect = m_DataSrc.DoDragDrop ( 
        DROPEFFECT_COPY | DROPEFFECT_MOVE );

    // Free memory in case of failure
    if(dwEffect == DROPEFFECT_NONE )
    {
        GlobalFree( hFileDescriptor );
    } 
    *pResult = 0;
}

Conclusion

That's it. Obviously, this just shows the bare techniques. You'd need to write extra code to make the whole process smooth. For example, if you are pulling the file from a remote device, there'd be a delay before the file gets written, in which case, you'd need to show a progress bar, or ensure that your main app does not freeze up entirely. But the basic technique will remain the same.

Reference

For more on Drag/Drop with Explorer, read Mike Dunn's excellent article : How to Implement Drag and Drop Between Your Program and Explorer which explains how to use CF_HDROP to transfer existing files to Explorer.

History

  • Sep/13/2006 - Article first published

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Nish Nishant
United States United States
Nish Nishant is a Software Architect/Consultant based out of Columbus, Ohio. He has over 15 years of software industry experience in various roles including Lead Software Architect, Principal Software Engineer, and Product Manager. Nish is a recipient of the annual Microsoft Visual C++ MVP Award since 2002 (13 consecutive awards as of 2014).

Nish is an industry acknowledged expert in the Microsoft technology stack. He authored
C++/CLI in Action for Manning Publications in 2005, and had previously co-authored
Extending MFC Applications with the .NET Framework for Addison Wesley in 2003. In addition, he has over 140 published technology articles on CodeProject.com and another 250+ blog articles on his
WordPress blog. Nish is vastly experienced in team management, mentoring teams, and directing all stages of software development.

Contact Nish : You can reach Nish on his google email id voidnish.

Website and Blog

You may also be interested in...

Comments and Discussions

 
Questionhi!How get to the destination address? Pin
forcj92817-Nov-13 14:53
memberforcj92817-Nov-13 14:53 
Questionhow can I implement this in .Net or C# Pin
Edgar10527-May-12 8:39
memberEdgar10527-May-12 8:39 
QuestionConnecting to a real file or delay loading a CF_HDROP Pin
Rick Pingry12-Nov-10 7:50
memberRick Pingry12-Nov-10 7:50 
AnswerRe: Connecting to a real file or delay loading a CF_HDROP Pin
Rick Pingry17-Nov-10 6:57
memberRick Pingry17-Nov-10 6:57 
GeneralRe: Connecting to a real file or delay loading a CF_HDROP Pin
Nishant Sivakumar17-Nov-10 7:18
mvpNishant Sivakumar17-Nov-10 7:18 
GeneralMy vote of 5 Pin
Rick Pingry2-Nov-10 9:35
memberRick Pingry2-Nov-10 9:35 
QuestionWhat can I do if I want to move a folder? Pin
Yu Tist12-May-09 3:26
memberYu Tist12-May-09 3:26 
AnswerRe: What can I do if I want to move a folder? [modified] Pin
jerry_wangjh4-Dec-10 15:48
memberjerry_wangjh4-Dec-10 15:48 
GeneralRe: What can I do if I want to move a folder? [modified] Pin
Lester_200811-Aug-12 23:16
memberLester_200811-Aug-12 23:16 
AnswerRe: What can I do if I want to move a folder? Pin
Lester_200810-Aug-12 23:31
memberLester_200810-Aug-12 23:31 
Recently I also meet this problem. Is there anyone who can solve it? Thanks in advance! Smile | :)
QuestionHow would I do this without MFC? Pin
NTDLS4-Mar-08 8:09
memberNTDLS4-Mar-08 8:09 
GeneralRe: How would I do this without MFC? Pin
Michael Dunn18-Mar-08 8:49
mvpMichael Dunn18-Mar-08 8:49 
QuestionHow do I drag a 4GB file? Pin
cmumford9-Aug-07 11:05
membercmumford9-Aug-07 11:05 
AnswerRe: How do I drag a 4GB file? Pin
rfalck21-Aug-07 15:40
memberrfalck21-Aug-07 15:40 
GeneralRe: How do I drag a 4GB file? Pin
rfalck22-Aug-07 16:41
memberrfalck22-Aug-07 16:41 
GeneralRe: How do I drag a 4GB file? Pin
drichard283017-Sep-07 12:35
memberdrichard283017-Sep-07 12:35 
GeneralRe: How do I drag a 4GB file? Pin
rfalck17-Sep-07 13:54
memberrfalck17-Sep-07 13:54 
GeneralRe: How do I drag a 4GB file? Pin
cmumford25-Sep-07 3:51
membercmumford25-Sep-07 3:51 
GeneralRe: How do I drag a 4GB file? Pin
Brian Pence27-Sep-07 9:57
memberBrian Pence27-Sep-07 9:57 
GeneralRe: How do I drag a 4GB file? Pin
cmumford27-Sep-07 12:24
membercmumford27-Sep-07 12:24 
GeneralRe: How do I drag a 4GB file? Pin
fabioglauser9-Oct-07 13:34
memberfabioglauser9-Oct-07 13:34 
GeneralRe: How do I drag a 4GB file? Pin
drichard28307-Nov-07 6:17
memberdrichard28307-Nov-07 6:17 
QuestionBuild Fails if "Use of MFC" changed to "Static Library" from "Shared DLL" Pin
Raptorikus19-Jul-07 7:15
memberRaptorikus19-Jul-07 7:15 
AnswerRe: Build Fails if "Use of MFC" changed to "Static Library" from "Shared DLL" Pin
cmumford8-Aug-07 10:12
membercmumford8-Aug-07 10:12 
QuestionWhere was the file dropped? Pin
rfalck1-May-07 15:04
memberrfalck1-May-07 15:04 
AnswerRe: Where was the file dropped? Pin
timothy_russell3-May-07 15:29
membertimothy_russell3-May-07 15:29 
AnswerRe: Where was the file dropped? Pin
cmumford29-Oct-07 11:57
membercmumford29-Oct-07 11:57 
QuestionUnable to complie using MSVC Express w PlatformSDK Pin
cgrusin127-Feb-07 10:30
membercgrusin127-Feb-07 10:30 
GeneralNot enough storage is available to complete this operation. Pin
lallous4-Dec-06 3:40
memberlallous4-Dec-06 3:40 
GeneralRe: Not enough storage is available to complete this operation. Pin
cmumford8-Aug-07 10:17
membercmumford8-Aug-07 10:17 
GeneralRe: Not enough storage is available to complete this operation. Pin
Giorgi Dalakishvili24-Nov-08 21:30
mvpGiorgi Dalakishvili24-Nov-08 21:30 
GeneralThe code's crashing Pin
lallous4-Dec-06 2:17
memberlallous4-Dec-06 2:17 
GeneralRe: The code's crashing Pin
rfalck1-May-07 14:59
memberrfalck1-May-07 14:59 
GeneralRe: The code's crashing Pin
Xijie Zhang5-Dec-11 16:19
memberXijie Zhang5-Dec-11 16:19 
GeneralRe: The code's crashing Pin
paopao_lin13-Aug-12 20:43
memberpaopao_lin13-Aug-12 20:43 
GeneralAny idea to implement this in C# Pin
john Pure23-Oct-06 7:28
memberjohn Pure23-Oct-06 7:28 
GeneralRe: Any idea to implement this in C# Pin
Meski23-Oct-06 15:31
memberMeski23-Oct-06 15:31 
GeneralRe: Any idea to implement this in C# Pin
timothy_russell3-May-07 15:41
membertimothy_russell3-May-07 15:41 
QuestionDrag to Notepad? Pin
Michael A. Rusakov26-Sep-06 18:27
memberMichael A. Rusakov26-Sep-06 18:27 
AnswerRe: Drag to Notepad? Pin
Nishant Sivakumar11-Oct-06 6:22
staffNishant Sivakumar11-Oct-06 6:22 
GeneralRe: Drag to Notepad? Pin
David C Elliott11-Jan-07 5:14
memberDavid C Elliott11-Jan-07 5:14 
GeneralVC2003 doesn't open the project Pin
Michael A. Rusakov26-Sep-06 18:20
memberMichael A. Rusakov26-Sep-06 18:20 
GeneralRe: VC2003 doesn't open the project Pin
Nishant Sivakumar11-Oct-06 6:20
staffNishant Sivakumar11-Oct-06 6:20 
GeneralRe: VC2003 doesn't open the project Pin
indrekm28-Oct-06 3:41
memberindrekm28-Oct-06 3:41 
QuestionWhen it is a big file, does it become an error why? Pin
giura21-Sep-06 20:32
membergiura21-Sep-06 20:32 
AnswerRe: When it is a big file, does it become an error why? Pin
Nishant Sivakumar11-Oct-06 6:17
staffNishant Sivakumar11-Oct-06 6:17 
QuestionRe: When it is a big file, does it become an error why? [modified] Pin
giura12-Oct-06 21:19
membergiura12-Oct-06 21:19 
GeneralHandy Pin
Shog918-Sep-06 7:36
sitebuilderShog918-Sep-06 7:36 
GeneralRe: Handy Pin
Nishant Sivakumar18-Sep-06 7:49
staffNishant Sivakumar18-Sep-06 7:49 
GeneralRe: Handy Pin
Shog918-Sep-06 8:10
sitebuilderShog918-Sep-06 8:10 

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

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

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.150819.1 | Last Updated 13 Sep 2006
Article Copyright 2006 by Nish Nishant
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid