Click here to Skip to main content
6,295,667 members and growing! (15,185 online)
Email Password   helpLost your password?
General Programming » Programming Tips » General     Intermediate

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

By Nishant Sivakumar

Shows how to use CFSTR_FILEDESCRIPTOR and CFSTR_FILECONTENTS to perform drag/drop with virtual files
VC7.1, VC8.0Win2K, WinXPVS.NET2003, VS2005, Dev
Posted:13 Sep 2006
Views:51,617
Bookmarked:65 times
Announcements
Loading...
 
Search    
Advanced Search
Prize winner in Competition "MFC/C++ Aug 2006"
printPrint   Broken Article?Report       add Share
  Discuss Discuss   Recommend Article Email
35 votes for this article.
Popularity: 7.38 Rating: 4.78 out of 5
2 votes, 5.7%
1

2

3
1 vote, 2.9%
4
32 votes, 91.4%
5

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 lindex parameter, 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 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

About the Author

Nishant Sivakumar


Member
Nish is a real nice guy living in Atlanta, who has been coding since 1990, when he was 13 years old. Originally from sunny Trivandrum in India, he recently moved to Atlanta from Toronto and is a little sad that he won't be able to play in snow anymore.

Nish has been a Microsoft Visual C++ MVP since October, 2002 - awfully nice of Microsoft, he thinks. He maintains an MVP tips and tricks web site - www.voidnish.com where you can find a consolidated list of his articles, writings and ideas on VC++, MFC, .NET and C++/CLI. Oh, and you might want to check out his blog on C++/CLI, MFC, .NET and a lot of other stuff - blog.voidnish.com

Nish loves reading Science Fiction, P G Wodehouse and Agatha Christie, and also fancies himself to be a decent writer of sorts. He has authored a romantic comedy Summer Love and Some more Cricket as well as a programming book – Extending MFC applications with the .NET Framework.

Nish's latest book C++/CLI in Action published by Manning Publications is now available for purchase. You can read more about the book on his blog.

Despite his wife's attempts to get him into cooking, his best effort so far has been a badly done omelette. Some day, he hopes to be a good cook, and to cook a tasty dinner for his wife.
Location: United States United States

Other popular Programming Tips articles:

Article Top
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
 Msgs 1 to 25 of 43 (Total in Forum: 43) (Refresh)FirstPrevNext
QuestionWhat can I do if I want to move a folder? PinmemberYu Tist4:26 12 May '09  
QuestionHow would I do this without MFC? PinmemberNTDLS9:09 4 Mar '08  
GeneralRe: How would I do this without MFC? PinmvpMichael Dunn9:49 18 Mar '08  
GeneralHow do I drag a 4GB file? Pinmembercmumford12:05 9 Aug '07  
GeneralRe: How do I drag a 4GB file? Pinmemberrfalck16:40 21 Aug '07  
GeneralRe: How do I drag a 4GB file? Pinmemberrfalck17:41 22 Aug '07  
GeneralRe: How do I drag a 4GB file? Pinmemberdrichard283013:35 17 Sep '07  
GeneralRe: How do I drag a 4GB file? Pinmemberrfalck14:54 17 Sep '07  
GeneralRe: How do I drag a 4GB file? Pinmembercmumford4:51 25 Sep '07  
GeneralRe: How do I drag a 4GB file? PinmemberBrian Pence10:57 27 Sep '07  
GeneralRe: How do I drag a 4GB file? Pinmembercmumford13:24 27 Sep '07  
GeneralRe: How do I drag a 4GB file? Pinmemberfabioglauser14:34 9 Oct '07  
GeneralRe: How do I drag a 4GB file? Pinmemberdrichard28307:17 7 Nov '07  
QuestionBuild Fails if "Use of MFC" changed to "Static Library" from "Shared DLL" PinmemberRaptorikus8:15 19 Jul '07  
AnswerRe: Build Fails if "Use of MFC" changed to "Static Library" from "Shared DLL" Pinmembercmumford11:12 8 Aug '07  
QuestionWhere was the file dropped? Pinmemberrfalck16:04 1 May '07  
AnswerRe: Where was the file dropped? Pinmembertimothy_russell16:29 3 May '07  
AnswerRe: Where was the file dropped? Pinmembercmumford12:57 29 Oct '07  
QuestionUnable to complie using MSVC Express w PlatformSDK Pinmembercgrusin1211:30 7 Feb '07  
GeneralNot enough storage is available to complete this operation. Pinmemberlallous4:40 4 Dec '06  
GeneralRe: Not enough storage is available to complete this operation. Pinmembercmumford11:17 8 Aug '07  
GeneralRe: Not enough storage is available to complete this operation. PinmvpGiorgi Dalakishvili22:30 24 Nov '08  
GeneralThe code's crashing Pinmemberlallous3:17 4 Dec '06  
GeneralRe: The code's crashing Pinmemberrfalck15:59 1 May '07  
GeneralAny idea to implement this in C# Pinmemberjohn Pure8:28 23 Oct '06  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 13 Sep 2006
Editor: Nishant Sivakumar
Copyright 2006 by Nishant Sivakumar
Everything else Copyright © CodeProject, 1999-2009
Web12 | Advertise on the Code Project