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

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

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.

Collapse
BOOL CMyOleDataSource::OnRenderFileData(
    LPFORMATETC lpFormatEtc,CFile* pFile)
{
// We only need to handle CFSTR_FILECONTENTS
if(lpFormatEtc->cfFormat == 
        RegisterClipboardFormat(CFSTR_FILECONTENTS))
    {   
        HGLOBAL hGlob = NULL;
constint 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.

Collapse
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.


注意:如果使用AfxOleInit的话MFC拖放文件容易出现Server Busy的情况,此时应该换成OleInitialize(NULL);来初始化。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
To create a PAL decoder block in GNU Radio Companion (GRC), you can follow these steps: 1. Open GNU Radio Companion and create a new flow graph. 2. Drag a "QT GUI Frequency Sink" block onto the flow graph canvas and connect it to the output of your PAL decoder block. This will allow you to visualize the output of your decoder. 3. Drag a "Feedforward AGC" block onto the flow graph canvas and connect it to the output of your PAL decoder block. This block will adjust the gain of the signal to a constant level. 4. Drag a "Low Pass Filter" block onto the flow graph canvas and connect it to the output of the AGC block. This block will remove high-frequency noise from the signal. 5. Drag a "Throttle" block onto the flow graph canvas and connect it to the output of the low pass filter block. This block limits the rate at which the signal is processed to prevent overloading the CPU. 6. Drag a "QT GUI Time Sink" block onto the flow graph canvas and connect it to the output of the throttle block. This block will allow you to visualize the output of the decoder in the time domain. 7. Finally, add a "PAL Decoder" block to the flow graph. You can find this block in the "Digital" section of the block library. Connect the input of the decoder block to your PAL signal source and the output to the input of the AGC block. 8. Configure the PAL Decoder block according to your signal's characteristics, such as the frequency offset and the type of PAL signal being used. 9. Save the flow graph and run it to see the output of your PAL decoder block. Note that the specific steps may vary depending on your signal source and the requirements of your PAL decoder.

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值