Windows资源管理器拖放文件实现

在Windows操作系统中,资源管理器(Explorer)是用户与文件系统交互的主要界面。实现与资源管理器的拖放(Drag and Drop)功能可以极大地提高应用程序的用户体验。本文将介绍如何在MFC应用程序中实现拖放文件到资源管理器,以及从资源管理器拖放文件回应用程序的技术。

基本技术

要实现拖放文件到资源管理器,通常会使用COleDataSource类。这个类提供了处理OLE数据源的功能,允许定义拖放操作的数据格式。以下是实现拖放文件到资源管理器的基本步骤:

  1. COleDataSource派生一个类。
  2. 分配全局内存,并创建CFSTR_FILEDESCRIPTOR格式的数据,然后使用CacheGlobalData方法。
  3. 重写派生类中的OnRenderFileData方法。
  4. OnRenderFileData方法中处理CFSTR_FILECONTENTS,并直接写入数据。

创建演示应用程序

首先,使用Visual Studio 2005(或更早版本)生成一个基于对话框的MFC应用程序。然后,使用资源编辑器向对话框中添加一个列表控件,并将其与类型为CListCtrl的DDX控件变量关联,命名为m_fileList。接下来,在OnInitDialog方法中添加以下代码来设置列表控件:

BOOL CExplorerDelayDropDlg::OnInitDialog() { CDialog::OnInitDialog(); AfxOleInit(); m_fileList.SetExtendedStyle(LVS_EX_FULLROWSELECT); m_fileList.InsertColumn(0, _T("文件"), LVCFMT_LEFT, 75); m_fileList.InsertColumn(1, _T("详情"), LVCFMT_LEFT, 175); for (TCHAR c = _T('A'); c < _T('F'); c++) { CString text1, text2; text1.Format(_T("%c.txt"), c); text2.Format(_T("文件满是%cs"), c); m_fileList.SetItemText(m_fileList.InsertItem(c - _T('A'), text1), 1, text2); } return TRUE; }

上述代码仅用一些虚拟的文件名填充了列表控件。注意,调用了AfxOleInit方法(如果应用程序已经支持OLE,则不需要这样做)。

COleDataSource派生一个类

由于使用的是延迟数据渲染,需要从COleDataSource派生一个类,以便可以重写OnRenderFileData方法(默认版本仅返回FALSE)。首先,向项目中添加一个名为CMyOleDataSource的类(派生自COleDataSource)。

class CMyOleDataSource : public COleDataSource { DECLARE_DYNAMIC(CMyOleDataSource) }

然后,需要添加一个OnRenderFileData的重写,如下所示:

BOOL CMyOleDataSource::OnRenderFileData(LPFORMATETC lpFormatEtc, CFile* pFile) { 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) { memset(pBytes, (int)m_Files.GetAt(lpFormatEtc->lindex)[0], buffSize); pFile->Write(pBytes, buffSize); } GlobalUnlock(hGlob); } GlobalFree(hGlob); return TRUE; } return COleDataSource::OnRenderFileData(lpFormatEtc, pFile); }

在上述代码中,通过填充512字节的特定字符来创建虚拟文件。在更实际的场景中,需要根据lindex参数检索特定文件,然后从远程源或可能的存档中检索该文件。

处理LVN_BEGINDRAG通知

需要处理LVN_BEGINDRAG通知,如下所示。可以使用属性框添加一个处理程序,或者在对话框类中手动添加一个ON_NOTIFY处理程序。

void CExplorerDelayDropDlg::OnBeginDrag(NMHDR *pNMHDR, LRESULT *pResult) { UINT uFileCount = m_fileList.GetSelectedCount(); UINT uBuffSize = sizeof(FILEGROUPDESCRIPTOR) + (uFileCount-1) * sizeof(FILEDESCRIPTOR); HGLOBAL hFileDescriptor = GlobalAlloc(GHND | GMEM_SHARE, uBuffSize); if (hFileDescriptor) { FILEGROUPDESCRIPTOR* pGroupDescriptor = (FILEGROUPDESCRIPTOR*)GlobalLock(hFileDescriptor); if (pGroupDescriptor) { 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); FORMATETC etcDescriptor = { RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR), NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; m_DataSrc.CacheGlobalData(RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR), hFileDescriptor, &etcDescriptor); 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); if (dwEffect == DROPEFFECT_NONE) { GlobalFree(hFileDescriptor); } *pResult = 0; }

这就是基本的技术。显然,这只是展示了基本的技术。需要编写额外的代码来使整个过程更加流畅。例如,如果从远程设备拉取文件,文件写入之前会有延迟,在这种情况下,可能需要显示一个进度条,或者确保主应用程序不会完全冻结。但基本技术将保持不变。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485