图 1.5
图 1.6
图 1.7
然后一切默认即可,这样ATL就为我们生成了一个组件框架,我们以下的讨论都基于此框架。
2.添加代码
图1.8 组件类继承关系
图1.8 中红色方框是我们自己要实现的 Shell 扩展接口,它不是向导自动生成代码,需要我们手工输入。
我们从该框架中可以获得很多好处,首先通过 ATL 的模板类 CcomCoClass 我们就可以省去反复再三的 QueryInterface 接口的实现,而我们只需要绑定组件和接口的映射关系(如下图1.9 所示)以及实现所继承接口的全部虚函数即可,以及组件的注册等它基本上都为我们做好了一切,好处大家就慢慢体会吧。下面我们首先介绍继承的各接口和其虚成员函数的作用,它们的声明包含在
图1.9 建立组件和接口的映射关系
图1.9 红色方框为 IShellExtInit 和 IContextMenu 接口和组件的接口映射关系,它不是向导自动生成代码,需要我们手工输入。
IShellExtInit接口:IShellExtInit 接口为 Shell 扩展编程必须要实现的接口。该接口主要用来初始化 Shell 扩展处理器(表一所列的处理器),它仅有一个虚成员函数Initialize,用户所有的 Shell 扩展初始化动作都由该函数完成。该函数的原型如下:
HRESULT Initialize( LPCITEMIDLIST pidlFolder, LPDATAOBJECT lpdobj, HKEY hkeyProgID ); |
在 Initialize 函数中,我们要做的事情就是获取用户鼠标右键点击的文件名称,但是有可能用户选择了多个文件,这里为了简单起见我们仅获取文件列表中的第一个文件。在这里我们得补充一点内容:当用户在一个拥有 WS_EX_ACCEPTFILES 风格的窗体中Drag/Drop 文件时这些文件名会以同一种格式存储,而且文件完整路径的获取也都以DragQueryFile API函数来实现。但是 DragQueryFile 需要传入一个 HDROP 句柄,该句柄即为 Drag/Drop 文件名称列表数据句柄(开始存放数据的内存区域首指针)。而 HDROP 句柄的可以通过接口 " DATAOBJECT lpdobj" 的成员函数" GetData" 来获取。以下为获取第一个 Drag/Drop 文件的完整文件路径的具体代码:
//数据存储格式 FORMATETC fmt = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; //数据存储内存句柄(常用于IDataObject和IAdviseSink接口的数据传输操作) STGMEDIUM stg = { TYMED_HGLOBAL }; if(FAILED(pDataObj->GetData(&fmt, &stg))) { //如果获取数据内存句柄失败则返回E_INVALIDARG, //返回E_INVALIDARG则Explorer不会再调用我们的Shell扩展接口 return E_INVALIDARG; } //获取实际数据内存句柄 HDROP hDrop = (HDROP)GlobalLock(stg.hGlobal); if(NULL==hDrop) { //在COM程序中养成良好的检错习惯是很重要的!!! return E_INVALIDARG; } //获取用户Drag/Drop的文件数目 int nDropCount = ::DragQueryFile((HDROP)stg.hGlobal, 0xFFFFFFFF, NULL, 0); //本示例程序仅获取第一个Drag/Drop文件完整路径 //以下注释代码为获取所有文件完整路径的实现代码: //for(int i = 0; i < nDropCount; ++i){ //循环获取每个Drag/Drop文件的完整文件名 //::DragQueryFile((HDROP)stg.hGlobal, i, m_pzDropFile, MAX_PATH); //} //如果用户Drag/Drop的文件数目不为一个则不予处理 if(1==nDropCount) { //pzDropFile为组件类内部的private变量 //它用来保存用户Drag/Drop的文件完整文件名 memset(m_pzDropFile, 0x0, MAX_PATH*sizeof(TCHAR)); ::DragQueryFile((HDROP)stg.hGlobal, 0, m_pzDropFile, MAX_PATH); } //释放内存句柄 ::ReleaseStgMedium(&mdmSTG); |
至