﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>IT博客-孤独的夜-文章分类-Windows</title><link>http://www.cnitblog.com/wangk/category/4991.html</link><description>&lt;table align=center&gt;
&lt;span id="dict_daily"&gt;
&lt;a href="http://dict.cn/" target="_blank"&gt;英语学习&lt;/a&gt;
&lt;/span&gt; 
&lt;script language="JavaScript" src="http://dict.cn/daily.php" defer="defer"&gt;
&lt;/script&gt;
&lt;/table&gt;</description><language>zh-cn</language><lastBuildDate>Wed, 28 Sep 2011 10:47:54 GMT</lastBuildDate><pubDate>Wed, 28 Sep 2011 10:47:54 GMT</pubDate><ttl>60</ttl><item><title>[转载] How to drag a virtual file from your app into Windows Explorer</title><link>http://www.cnitblog.com/wangk/articles/24418.html</link><dc:creator>孤独的夜</dc:creator><author>孤独的夜</author><pubDate>Thu, 22 Mar 2007 06:40:00 GMT</pubDate><guid>http://www.cnitblog.com/wangk/articles/24418.html</guid><description><![CDATA[&nbsp;&nbsp;&nbsp; <a title="How to drag a virtual file from your app into Windows Explorer" href="http://www.codeproject.com/tips/ExplorerDelayDrop.asp" target=_blank>How to drag a virtual file from your app into Windows Explorer </a><br><br><span id=intelliTXT>
<h2>Introduction</h2>
<p>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 <code>CF_HDROP</code> 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 <code>CFSTR_FILEDESCRIPTOR</code> and <code>CFSTR_FILECONTENTS</code>, 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.</p>
<h2>Basic Technique</h2>
<ul>
    <li>Derive a class from <code>COleDataSource</code>
    <li>Allocate global memory and create data in <code>CFSTR_FILEDESCRIPTOR</code> format, and then use <code>CacheGlobalData</code> on the <code>COleDataSource</code> derived class
    <li>Override <code>OnRenderFileData</code> in this derived class
    <li>In the <code>OnRenderFileData</code> override, handle <code>CFSTR_FILECONTENTS</code>, and write directly to the <code>CFSTR_FILECONTENTS</code></li>
</ul>
<h2>Creating the demo app</h2>
<p>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 <code>CListCtrl</code> and name it <code>m_fileList</code>. Now add the following code to the <code>OnInitDialog</code> to setup the List control.</p>
<pre>BOOL CExplorerDelayDropDlg::OnInitDialog()<br>{<br>    CDialog::OnInitDialog();<br><br>    . . .       <br><br>    AfxOleInit();<br><br>    m_fileList.SetExtendedStyle(LVS_EX_FULLROWSELECT);<br>    m_fileList.InsertColumn(<span class=cpp-literal>0</span>, _T(<span class=cpp-string>"File"</span>), LVCFMT_LEFT,<span class=cpp-literal>75</span>);<br>    m_fileList.InsertColumn(<span class=cpp-literal>1</span>, _T(<span class=cpp-string>"Details"</span>), LVCFMT_LEFT,<span class=cpp-literal>175</span>);<br><br><span class=cpp-keyword>for</span>(TCHAR c = _T('A'); c &lt; _T('F'); c++)<br>    {<br>        CString text1, text2;<br>        text1.Format(_T(<span class=cpp-string>"%c.txt"</span>),c);<br>        text2.Format(_T(<span class=cpp-string>"File full of %cs"</span>),c);<br>        m_fileList.SetItemText(<br>            m_fileList.InsertItem(c - _T('A'),text1),<span class=cpp-literal>1</span>,text2);<br>    }<br><br><span class=cpp-keyword>return</span> TRUE;  <br>}</pre>
<p>The code merely fills up the List control with some dummy file names. Note how I have made a call to <code>AfxOleInit </code>(you don't need to do this if your app already supports OLE).</p>
<h2>Deriving a class from <code>COleDataSource</code></h2>
<p>Since we are using delayed data rendering, we need to derive a class from <code>COleDataSource</code> so that we can override <code>OnRenderFileData</code> (the default version merely returns <code>FALSE</code>). So, the first step is to add a class named <code>CMyOleDataSource</code> (derived from <code>COleDataSource</code>) to the project.</p>
<pre><span class=cpp-keyword>class</span> CMyOleDataSource : <span class=cpp-keyword>public</span> COleDataSource<br>{<br>    DECLARE_DYNAMIC(CMyOleDataSource)</pre>
<p>Now, we need to add an override for <code>OnRenderFileData</code> as shown below.</p>
<div class=smallText id=premain2 style="WIDTH: 100%"><img id=preimg2 height=9 src="http://www.codeproject.com/images/minus.gif" width=9 preid="2"><span id=precollapse2 style="MARGIN-BOTTOM: 0pt" preid="2"> Collapse</span></div>
<pre id=pre2 style="MARGIN-TOP: 0pt">BOOL CMyOleDataSource::OnRenderFileData(<br>    LPFORMATETC lpFormatEtc,CFile* pFile)<br>{<br><span class=cpp-comment>// We only need to handle CFSTR_FILECONTENTS</span><br><span class=cpp-keyword>if</span>(lpFormatEtc-&gt;cfFormat == <br>        RegisterClipboardFormat(CFSTR_FILECONTENTS))<br>    {   <br>        HGLOBAL hGlob = NULL;<br><span class=cpp-keyword>const</span><span class=cpp-keyword>int</span> buffSize = <span class=cpp-literal>512</span>;<br>        hGlob = GlobalAlloc(GMEM_FIXED, buffSize);<br><span class=cpp-keyword>if</span>(hGlob)<br>        {<br>            LPBYTE pBytes = (LPBYTE)GlobalLock(hGlob);          <br><span class=cpp-keyword>if</span>(pBytes)<br>            {<br><span class=cpp-comment>// lpFormatEtc-&gt;lindex can be used to identify</span><br><span class=cpp-comment>// the file that's being copied</span><br>                memset(pBytes, (<span class=cpp-keyword>int</span>) m_Files.GetAt(<br>                    lpFormatEtc-&gt;lindex)[<span class=cpp-literal>0</span>], buffSize);<br>                pFile-&gt;Write(pBytes,buffSize);              <br>            }<br>            GlobalUnlock(hGlob);<br>        }<br>        GlobalFree(hGlob);<br><span class=cpp-comment>// Need to return TRUE to indicate success to Explorer</span><br><span class=cpp-keyword>return</span> TRUE;<br>    }<br><span class=cpp-keyword>return</span> COleDataSource::OnRenderFileData(<br>        lpFormatEtc, pFile);<br>}</pre>
<p>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 <code>lindex </code>parameter, and then retrieve that file from a remote source or perhaps from an archive.</p>
<p>Now, we just need to handle the <code>LVN_BEGINDRAG </code>notification as shown below. You can either use the Properties box to add a handler, or manually add an <code>ON_NOTIFY </code>handler in the dialog class.</p>
<div class=smallText id=premain3 style="WIDTH: 100%"><img id=preimg3 height=9 src="http://www.codeproject.com/images/minus.gif" width=9 preid="3"><span id=precollapse3 style="MARGIN-BOTTOM: 0pt" preid="3"> Collapse</span></div>
<pre id=pre3 style="MARGIN-TOP: 0pt"><span class=cpp-keyword>void</span> CExplorerDelayDropDlg::OnBeginDrag(NMHDR *pNMHDR, LRESULT *pResult)<br>{<br>    UINT uFileCount = m_fileList.GetSelectedCount();    <br><br><span class=cpp-comment>// The CFSTR_FILEDESCRIPTOR format expects a </span><br><span class=cpp-comment>// FILEGROUPDESCRIPTOR structure followed by an</span><br><span class=cpp-comment>// array of FILEDESCRIPTOR structures, one for</span><br><span class=cpp-comment>// each file being dropped</span><br>    UINT uBuffSize = <span class=cpp-keyword>sizeof</span>(FILEGROUPDESCRIPTOR) + <br>        (uFileCount-<span class=cpp-literal>1</span>) * <span class=cpp-keyword>sizeof</span>(FILEDESCRIPTOR);<br>    HGLOBAL hFileDescriptor = GlobalAlloc ( <br>        GHND | GMEM_SHARE, uBuffSize );        <br><br><span class=cpp-keyword>if</span>(hFileDescriptor)<br>    {<br>        FILEGROUPDESCRIPTOR* pGroupDescriptor = <br>            (FILEGROUPDESCRIPTOR*) GlobalLock ( hFileDescriptor );<br><span class=cpp-keyword>if</span>(pGroupDescriptor)<br>        {<br><span class=cpp-comment>// Need a pointer to the FILEDESCRIPTOR array</span><br>            FILEDESCRIPTOR* pFileDescriptorArray = <br>                (FILEDESCRIPTOR*)((LPBYTE)pGroupDescriptor + <span class=cpp-keyword>sizeof</span>(UINT));<br>            pGroupDescriptor-&gt;cItems = uFileCount;            <br><br>            POSITION pos = m_fileList.GetFirstSelectedItemPosition();<br><span class=cpp-keyword>int</span> index = <span class=cpp-literal>0</span>;<br>            m_DataSrc.m_Files.RemoveAll();<br><span class=cpp-keyword>while</span>( NULL != pos )<br>            {   <br><span class=cpp-keyword>int</span> nSelItem = m_fileList.GetNextSelectedItem( pos );<br>                ZeroMemory(&amp;pFileDescriptorArray[index], <br><span class=cpp-keyword>sizeof</span>(FILEDESCRIPTOR));<br>                lstrcpy ( pFileDescriptorArray[index].cFileName, <br>                    m_fileList.GetItemText( nSelItem, <span class=cpp-literal>0</span> ) );<br>                m_DataSrc.m_Files.Add(<br>                    pFileDescriptorArray[index].cFileName);<br>                pFileDescriptorArray[index].dwFlags = <br>                    FD_FILESIZE|FD_ATTRIBUTES;<br>                pFileDescriptorArray[index].nFileSizeLow = <span class=cpp-literal>512</span>;<br>                pFileDescriptorArray[index].nFileSizeHigh = <span class=cpp-literal>0</span>;<br>                pFileDescriptorArray[index].dwFileAttributes = <br>                    FILE_ATTRIBUTE_NORMAL;<br>                index++;<br>            }<br>        }<br><span class=cpp-keyword>else</span><br>        {<br>            GlobalFree ( hFileDescriptor );<br>        }<br>    }<br>    GlobalUnlock ( hFileDescriptor );       <br><br><span class=cpp-comment>// For the CFSTR_FILEDESCRIPTOR format, we use</span><br><span class=cpp-comment>// CacheGlobalData since we make that data available </span><br><span class=cpp-comment>// immediately</span><br>    FORMATETC etcDescriptor = { <br>        RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR), <br>        NULL, DVASPECT_CONTENT, -<span class=cpp-literal>1</span>, TYMED_HGLOBAL };<br>    m_DataSrc.CacheGlobalData ( RegisterClipboardFormat(<br>        CFSTR_FILEDESCRIPTOR), hFileDescriptor, &amp;etcDescriptor );<br><br><span class=cpp-comment>// For CFSTR_FILECONTENTS, we use DelayRenderFileData</span><br><span class=cpp-comment>// as this data will have to come from a non-physical</span><br><span class=cpp-comment>// device, like an FTP site, an add-on device, or an archive</span><br>    FORMATETC etcContents = { <br>        RegisterClipboardFormat(CFSTR_FILECONTENTS), <br>        NULL, DVASPECT_CONTENT, -<span class=cpp-literal>1</span>, TYMED_HGLOBAL };<br>    m_DataSrc.DelayRenderFileData(RegisterClipboardFormat(<br>        CFSTR_FILECONTENTS), &amp;etcContents);<br><br>    DROPEFFECT dwEffect = m_DataSrc.DoDragDrop ( <br>        DROPEFFECT_COPY | DROPEFFECT_MOVE );<br><br><span class=cpp-comment>// Free memory in case of failure</span><br><span class=cpp-keyword>if</span>(dwEffect == DROPEFFECT_NONE )<br>    {<br>        GlobalFree( hFileDescriptor );<br>    } <br>    *pResult = <span class=cpp-literal>0</span>;<br>}</pre>
<h2>Conclusion</h2>
<p>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.</p>
<h2>Reference</h2>
<p>For more on Drag/Drop with Explorer, read Mike Dunn's excellent article : <a href="http://www.codeproject.com/shell/explorerdragdrop.asp">How to Implement Drag and Drop Between Your Program and Explorer</a> which explains how to use <code>CF_HDROP </code>to transfer existing files to Explorer.</p>
</span><br>注意：如果使用AfxOleInit的话MFC拖放文件容易出现Server Busy的情况，此时应该换成OleInitialize(NULL);来初始化。<br>
<img src ="http://www.cnitblog.com/wangk/aggbug/24418.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/wangk/" target="_blank">孤独的夜</a> 2007-03-22 14:40 <a href="http://www.cnitblog.com/wangk/articles/24418.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>