一.适用于拖放一般文件的WM_DROPFILES消息

WM_DROPFILES消息支持我们拖动文件资源管理器下面的文件夹和文件到自己的窗口,为此我们首先要在接受文件拖放窗口的初始化函数中添加如下代码,表示该窗口支持文件拖放:

this->DragAcceptFiles();

利用类向导或手动添加消息映射处理WM_DROPFILES消息,其中映射函数
_afx_msg void OnDropFiles(HDROP hDropInfo)_中参数hDropInfo保存着拖放到窗口的文件信息,可以通过下面的代码获取到拖放到当前窗口的文件路径,

TCHAR szFileName[MAX_PATH + 1] = {};
UINT nFiles = DragQueryFile(hDrop, 0xFFFFFFFF, szFileName, MAX_PATH);  //获取拖放文件的个数
for (int i = 0; i < nFiles; ++i)
{DragQueryFile(hDrop, i, szFileName, MAX_PATH);if (PathIsDirectory(szFileName)){//处理目录程序}
}

DragFileName函数的详细用法可以参考:https://docs.microsoft.com/zh-cn/windows/win32/api/shellapi/nf-shellapi-dragqueryfilea?redirectedfrom=MSDN.

处理WM_DROPFILES消息非常简单,但是局限性也很大,他不支持显示拖放的缩略图并且不支持拖放文件以外的数据类型例如从outlook中直接拖放邮件到我们的程序中,要实现这样的效果我们需要使用更高级的OLE拖放。

二.ole拖放

本节我会详细介绍如何使用ole支持多个outlook邮件拖放,并在自己的窗口显示拖放缩略图,并在结尾附上源代码

在MFC程序中要使用ole相关接口,需要在程序实例初始化函数InitInstance()中调用

AfxOleInit();

ole拖放的关键是COleDropTarget类,这是一个利用嵌套类实现的COM对象,有关COM的知识非常难理解,但是好在我们并不需要知道他的原理就可以使用他。

为了使自己的窗口(演示使用的是派生于CDialogEx的对话框类型窗口)支持ole拖放我们首先要做的是实现一个派生于的COleDropTarget的myOleDropTarget类,并重写下面几个虚函数:

virtual DROPEFFECT OnDragEnter(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point);
virtual BOOL OnDrop(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point);
virtual void OnDragLeave(CWnd* pWnd);
virtual DROPEFFECT OnDragOver(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point);

这四个函数分别是拖放进入窗口,执行拖放,离开窗口和结束拖放时会调用的函数。为了使我们的窗口在拖放时能通知到这些函数我们需要以下两步操作如果窗口派生于CView的话可以直接重写这四个虚函数,CView中提供了这些虚函数):**

1.在窗口类中添加成员变量:myOleDrop m_oleDrop;

2. 在窗口OnCreate函数中调用下面代码j注册窗口:

m_oleDrop.Register(this);

OnDragEnter和OnDragOver的返回值DROPEFFECT决定了拖放鼠标进入窗口时显示的情况,
一般会用到下面三种:

DROPEFFECT_COPY:复制拖放,

DROPEFFECT_NONE:禁止拖放,

DROPEFFECT_MOVE:移动拖放,

当我们发现拖放的数据是我们想要处理的数据类型时我们可以返回DROPEFFECT_MOVE或DROPEFFECT_COPY,如果数据不是我们感兴趣的可以返回DROPEFFECT_NONE

拖放操作中我们最关心的是怎么获取拖放数据,一般情况下我们在OnDrop函数中获取拖放数据最合理,OnDrop函数有四个参数:

CWnd* pWnd 窗口类
COleDataObject* pDataObject 保存IDataObject接口的类对象
DROPEFFECT dropEffect 前面提到的拖放类型
CPoint point 保存执行时鼠标的位置

最值得我们注意的是pDataObject,这是一个COleDataObject类型的指针,COleDataObject其实是MFC对IDataObject接口的一个包装,可以让我们方便的操作IDataObject接口,IDataObject是一个com接口,它保存了拖放到窗口的数据,下面我给出在OnDrop中取出拖动数据是普通文件的代码

BOOL myOleDrop::OnDrop(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point)
{FORMATETC fc;                                                  //拖放数据的类型STGMEDIUM stgm;                                                    //数据的传输方式和实际数据pDataObject->BeginEnumFormats();                               //开始枚举所有数据类型while (pDataObject->GetNextFormat(&fc)){if (pDataObject->IsDataAvailable(fc.cfFormat))                //数据可以获取{pDataObject->GetData(fc.cfFormat, &stgm);               //获取数据//普通文件的拖放if (fc.cfFormat == CF_HDROP&& stgm.tymed==TYMED_HGLOBAL){HDROP hDrop= (HDROP)GlobalLock(stgm.hGlobal);TCHAR szFileName[MAX_PATH + 1] = {};UINT nFiles = DragQueryFile(hDrop, 0xFFFFFFFF, szFileName, MAX_PATH);  //获取拖放文件的个数for (int i = 0; i < nFiles; ++i){DragQueryFile(hDrop, i, szFileName, MAX_PATH);if (PathIsDirectory(szFileName)){//处理目录}}GlobalUnlock(stgm.hGlobal);}}}return true;
}

上面的代码简单演示了如何获取ole拖放的数据,其中FORMATETC和STGMEDIUM是比较重要的两个结构体,FORMATETC中关键的字段,cfFormat 保存了拖放的数据类型,tymed保存了数据的传输方式。STGMEDIUM中tymed保存了数据的存储方式,指引我们该以那种方式获取数据,有下面几种数据传输方式:

enum tagTYMED
{
TYMED_HGLOBAL = 1 全局类型,
TYMED_FILE = 2 文件类型,
TYMED_ISTREAM = 4 流类型,
TYMED_ISTORAGE = 8, 复合文档,有层次结构的多个流和Storage集合
TYMED_GDI = 16, gdi
TYMED_MFPICT = 32, 没用过,是一种图片类型
TYMED_ENHMF = 64, 没有过,图元文件
TYMED_NULL = 0 空,不传输任何数据
} TYMED;

例如拖动普通文件时cfFormat 是CF_HDROP类型,_pDataObject->GetData(fc.cfFormat, &stgm)_获取STGMEDIUM信息,stgm.tymed=TYMED_HGLOBAL表示数据保存在全局,此时,stgm.hGlobal是有效的,表明我们需要通过stgm.hGlobal获取数据,此外我们还可以处理任何我们感兴趣的数据。

但是很遗憾我们并不能通过以上代码获取到从outlook拖动到我们窗口的邮件,因为它并不是以CF_HDROP类型来传递数据,下面我给出OnDrop函数如何取得拖动到我们窗口的多个outlook邮件的信息,并把它们保存为msg文件的代码(代码为了简洁我没有做太多执行成功的判断):

BOOL myOleDrop::OnDrop(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point)
{// TODO: Add your specialized code here and/or call the base classFORMATETC fc;STGMEDIUM stgm;HRESULT hr;FORMATETC file_desc = { RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR) ,0,DVASPECT_CONTENT,-1,TYMED_HGLOBAL };                                                                                    //注册剪贴板数据类型if (pDataObject->GetData(file_desc.cfFormat, &stgm)){LPFILEGROUPDESCRIPTOR p = (LPFILEGROUPDESCRIPTOR)GlobalLock(stgm.hGlobal);for (int n = 0; n < p->cItems; ++n){FORMATETC file_content= { RegisterClipboardFormat(CFSTR_FILECONTENTS) ,0,DVASPECT_CONTENT, n , TYMED_HGLOBAL | TYMED_ISTREAM | TYMED_ISTORAGE };IDataObject *poj = pDataObject->GetIDataObject(FALSE);              //获取IDataObject接口hr = poj->GetData(&file_content, &stgm);if(stgm.tymed==TYMED_ISTREAM){}if (stgm.tymed == TYMED_ISTORAGE){TCHAR szTempPath[MAX_PATH + 1] = {};::GetTempPath(MAX_PATH, szTempPath);CString strFileName = szTempPath;strFileName += (p->fgd[n]).cFileName;IStorage * pp;hr = ::StgCreateDocfile(strFileName.GetString(), STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE,0,&pp);                     //创建复合文档hr=stgm.pstg->CopyTo(NULL, NULL, NULL, pp);     //给文件中填入数据if (pp)pp->Release();}if(stgm.tymed==TYMED_HGLOBAL){}}GlobalUnlock(stgm.hGlobal);}
}

上面的代码可以实现将从outlook中拖放到窗口的邮件保存成msg文件放到用户的temp文件夹下

outlook邮件拖放采用的数据格式是非常规的LPFILEGROUPDESCRIPTOR格式,需要我们提前注册这种数据格式,具体方法参考上面的代码,LPFILEGROUPDESCRIPTOR中有两项cItemsfgd数组,分别保存着文件个数和文件信息。
接着我们注册 file_content类型(方法参考上面的代码),他保存着LPFILEGROUPDESCRIPTOR中文件的数据,FORMATETC中lindex表示第几个数据(如果有两个邮件有两个数据可以生成两个msg文件),它采用的传输方法是TYMED_ISTORAGE,这是一种结构化的文件存储方式,我们可以用它创建复合文档,outlook文件也是一种复合文档,关于IStorage接口不做多介绍,感兴趣可以在https://docs.microsoft.com/zh-cn/windows/win32/api/objidl/nn-objidl-istorage参考用法

现在还剩一个问题就是我们的程序不支持拖放时显示缩略图,首先我们在myoleDrag类中添加一个IDropTargetHelper 的指针,这是一个com接口指针,

IDropTargetHelper * pDrgHelper;

接下来我们在构造函数中初始化这个接口指针,这会牵扯到一些com的知识,不过我们不需要了解,因为这些代码是固定的:

HRESULT hr = ::CoCreateInstance(CLSID_DragDropHelper, NULL, CLSCTX_INPROC_SERVER, IID_IDropTargetHelper, (LPVOID*)&pDrgHelper);

最后我们只需分别在OnDragEnter函数结尾添加

if (pDrgHelper)pDrgHelper->DragEnter(pWnd->GetSafeHwnd(), pDataObject->GetIDataObject(FALSE), &point, DROPEFFECT_COPY);

在OnDrop函数结尾添加

if (pDrgHelper)pDrgHelper->Drop( pDataObject->GetIDataObject(FALSE), &point, DROPEFFECT_COPY);

** OnDragLeave结尾添加**

if (pDrgHelper)pDrgHelper->DragLeave();

OnDragOver结尾添加

if (pDrgHelper)pDrgHelper->DragOver(&point, DROPEFFECT_COPY);

这样我们就实现了当拖放在我们的窗口时,会显示拖放的缩略图。

本文演示了拖放普通文件和outlook邮件的处理方法,实际开发时可以选择处理自己感兴趣的数据,包括自己定义注册的剪贴板数据格式,方法类似

下面给出完整的myOleDrop.h和myOleDrop.cpp处理邮件拖放和普通拖放文件,并将拖放到窗口的邮件保存在temp文件夹下,

myOleDrop.h

#pragma once
// myOleDrop command targetclass myOleDrop : public COleDropTarget
{DECLARE_DYNAMIC(myOleDrop)public:myOleDrop();virtual ~myOleDrop();
private:IDropTargetHelper * pDrgHelper;DROPEFFECT m_dpEffect;
protected:DECLARE_MESSAGE_MAP()
public:virtual DROPEFFECT OnDragEnter(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point);virtual BOOL OnDrop(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point);virtual void OnDragLeave(CWnd* pWnd);virtual DROPEFFECT OnDragOver(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point);
};

myOleDrop.cpp


#include "stdafx.h"
#include "myOleDrop.h"// myOleDropIMPLEMENT_DYNAMIC(myOleDrop, COleDropTarget)myOleDrop::myOleDrop():m_dpEffect(DROPEFFECT_NONE)
{//获取IDragTargetHelper接口HRESULT hr = ::CoCreateInstance(CLSID_DragDropHelper, NULL, CLSCTX_INPROC_SERVER, IID_IDropTargetHelper, (LPVOID*)&pDrgHelper);
}myOleDrop::~myOleDrop()
{if (pDrgHelper)pDrgHelper->Release();
}BEGIN_MESSAGE_MAP(myOleDrop, COleDropTarget)
END_MESSAGE_MAP()// myOleDrop message handlersDROPEFFECT myOleDrop::OnDragEnter(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point)
{// TODO: Add your specialized code here and/or call the base classm_dpEffect = DROPEFFECT_NONE;FORMATETC file_desc = { RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR) ,0,DVASPECT_CONTENT,-1,TYMED_HGLOBAL };FORMATETC fc;pDataObject->BeginEnumFormats();while (pDataObject->GetNextFormat(&fc)){if (fc.cfFormat == file_desc.cfFormat||fc.cfFormat==CF_HDROP){m_dpEffect = DROPEFFECT_COPY;break;}}if (pDrgHelper)pDrgHelper->DragEnter(pWnd->GetSafeHwnd(), pDataObject->GetIDataObject(FALSE), &point, m_dpEffect);return m_dpEffect;
}BOOL myOleDrop::OnDrop(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point)
{// TODO: Add your specialized code here and/or call the base classHDROP hDrop=NULL;FORMATETC fc;STGMEDIUM stgm;HRESULT hr;FORMATETC file_desc = { RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR) ,0,DVASPECT_CONTENT,-1,TYMED_HGLOBAL };if (pDataObject->GetData(file_desc.cfFormat, &stgm)){LPFILEGROUPDESCRIPTOR p = (LPFILEGROUPDESCRIPTOR)GlobalLock(stgm.hGlobal);for (int n = 0; n < p->cItems; ++n){FORMATETC file_content= { RegisterClipboardFormat(CFSTR_FILECONTENTS) ,0,DVASPECT_CONTENT, n , TYMED_HGLOBAL | TYMED_ISTREAM | TYMED_ISTORAGE };IDataObject *poj = pDataObject->GetIDataObject(FALSE);hr = poj->GetData(&file_content, &stgm);if(stgm.tymed==TYMED_ISTREAM){}if (stgm.tymed == TYMED_HGLOBAL){}if (stgm.tymed == TYMED_ISTORAGE){TCHAR szTempPath[MAX_PATH + 1] = {};::GetTempPath(MAX_PATH, szTempPath);CString strFileName = szTempPath;strFileName += (p->fgd[n]).cFileName;IStorage * pp;hr = ::StgCreateDocfile(strFileName.GetString(), STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE,0,&pp);hr=stgm.pstg->CopyTo(NULL, NULL, NULL, pp);if (pp)pp->Release();}}GlobalUnlock(stgm.hGlobal);}else if (pDataObject->GetData(CF_HDROP,&stgm)){hDrop = (HDROP)::GlobalLock(stgm.hGlobal);TCHAR szFileName[MAX_PATH + 1] = {};UINT nFiles = DragQueryFile(hDrop, -1, szFileName, MAX_PATH);for (int i = 0; i < nFiles; ++i){DragQueryFile(hDrop, i, szFileName, MAX_PATH);if (PathIsDirectory(szFileName)){//处理目录}}::GlobalUnlock(stgm.hGlobal);}if (pDrgHelper)pDrgHelper->Drop( pDataObject->GetIDataObject(FALSE), &point, dropEffect);return COleDropTarget::OnDrop(pWnd, pDataObject, dropEffect, point);
}void myOleDrop::OnDragLeave(CWnd* pWnd)
{// TODO: Add your specialized code here and/or call the base classif (pDrgHelper)pDrgHelper->DragLeave();COleDropTarget::OnDragLeave(pWnd);
}DROPEFFECT myOleDrop::OnDragOver(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point)
{// TODO: Add your specialized code here and/or call the base classif (pDrgHelper)pDrgHelper->DragOver(&point, m_dpEffect);return m_dpEffect;
}

由于本人水平有限,代码中难免有错误和写的不好的地方,希望路过各位批评指正,感谢!

c++,MFC实现拖放目标Drop(以拖放Outlook邮件和普通文件作为示例)相关推荐

  1. html拖放数据库字段,HTML5 拖放(Drag 和 Drop)

    拖放是一种常见的特性,即抓取对象以后拖到另一个位置. 在 HTML5 中,拖放是标准的一部分,任何元素都能够拖放. #div1 {width:350px;height:70px;padding:10p ...

  2. h5物体拖动_HTML5原生拖拽/拖放(drag drop)详解

    前言 拖放(drap && drop)在我们平时的工作中,经常遇到.它表示:抓取对象以后拖放到另一个位置.目前,它是HTML5标准的一部分.我从几个方面学习并实践这个功能. 拖放的流程 ...

  3. html5拖放详解,HTML5拖拽/拖放(drag drop)详解

    H5中拖拽属性: draggable: auto | true | false 拖动事件: - dragstart 在元素开始被拖动时触发 - dragend 在拖动操作完成时触发 - drag 在元 ...

  4. java 拖放文字_Java实现拖放效果

    一.代码 import java.awt.Dimension; import java.awt.Image; import java.awt.datatransfer.DataFlavor; impo ...

  5. Silverlight Blend动画设计系列八:拖放(Drag-Drop)操作与拖放行为(DragBehavior)

    Silverlight & Blend动画设计系列八:拖放(Drag-Drop)操作与拖放行为(DragBehavior) 原文:Silverlight & Blend动画设计系列八: ...

  6. HTML5原生拖拽/拖放 Drag Drop 详解

    转载自:juejin.im/post/5a169d- 前言 拖放(drap && drop)在我们平时的工作中,经常遇到.它表示:抓取对象以后拖放到另一个位置.目前,它是HTML5标准 ...

  7. HTML5原生拖拽/拖放(drag drop)详解

    前言 拖放(drap && drop)在我们平时的工作中,经常遇到.它表示:抓取对象以后拖放到另一个位置.目前,它是HTML5标准的一部分.我从几个方面学习并实践这个功能. 拖放的流程 ...

  8. html5怎么设置drop,HTML5 拖放(Drag 和 Drop)

    拖放(Drag 和 drop)是 HTML5 标准的组成部分. 将 RUNOOB.COM 图标拖动到矩形框中. 拖放 拖放是一种常见的特性,即抓取对象以后拖到另一个位置. 在 HTML5 中,拖放是标 ...

  9. MFC单文档程序加载web网站和html文件

    使用CHtmlView类,CHtmlView类的主要功能是访问Web网站和HTML文档:该类可说是对webbrowser控件的封装: 新建一个单文档项目:选择 CHtmlView 类作为视类的基类:项 ...

最新文章

  1. 读取Excel的文本框,除了解析xml还可以用python调用VBA
  2. mybatis学习(4):工具类和实体类的创建
  3. ArcGIS Flex API 中的 Flex 技术(一)--事件
  4. 鸿蒙WLAN模组联网+解决在Visual Studio Code不能更改Linux文件的问题
  5. dubbo学习之事件通知实践
  6. linux安装.AppImage后缀安装包
  7. (转)Managed DirectX +C# 开发(入门篇)(一)
  8. IPTV码流分析指标
  9. java.io.IOException: Could not find status of job:job_1534233312603_0002
  10. ‘numeric_limits’ is not a member of ‘std‘解决方法
  11. 单设施重心法选址matlab编程
  12. c语言flag,[求助]int flag的意思
  13. 百度地图API之绘制折线及点击事件
  14. sim800a指令_sim900a和sim800a的区别是什么
  15. php中x22是什么意思,我想问一下:联想s22e和x22区别哪款好用点??真实反馈一下!!...
  16. xlsx表格怎么做汇总统计_表格进行分类汇总怎么做
  17. 计算机成绩分数段,用FreQuency函数统计学生成绩分数段的技巧-excel技巧-电脑技巧收藏家...
  18. 推荐一款快速生成海报的微信小插件
  19. XCTF mobile新手区解题记录(WP)以及一些总结和思考
  20. 2021年深圳南山区工业企业租金补贴申报时间及条件,补贴300万

热门文章

  1. 荣耀手机计算机设置功能,华为荣耀路由器设置(手机和电脑设置)的方法(图文详解)...
  2. linux驱动开发 - 07_pinctrl 和 gpio 子系统实战
  3. 西部广播电视杂志《西部广播电视》杂志社《西部广播电视》编辑部2022年第21期目录
  4. c语言输出空格问题。
  5. 国鼎代理极海APM32F030x8系列MCU手持式激光测距仪应用方案
  6. 单级倒立摆matlab仿真程序,单级倒立摆控制系统设计及MATLAB中的仿真..doc
  7. 论文研究 | 基于机器视觉的汽车精密零件表面缺陷自动检测方法
  8. 从李飞飞、吴恩达、安德鲁的年度总结中,我们发现了三条2018年AI行业发展趋势...
  9. ie如何导入html文件类型,Magicodes.IE: 导入导出通用库,支持Dto导入导出以及动态导出,支持Excel、Word、Pdf和Html。...
  10. Worthington丨Worthington 磷酸酶,碱性