分享
 
 
 

外壳扩展编写完全傻瓜指南(三)(Michael Dunn)

王朝vc·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

Download demo project - 11 Kb

在指南的第一部分第二部分,我向大家演示了如何编写上下文菜单扩展。在第三部分,我将燕是一种新的扩展类型,向大家解释如何共享外壳的内存,并且演示如何在ATL之外使用MFC。

第三部分假设你已经知道了外壳扩展的基本知识(在第一部分中解释了),而且你对MFC很熟悉。要注意的是这儿的扩展需要4.71或者更高版本的扩展,所以你必须是运行Windows 98 或 2000,或者在95/NT4 上装有活动桌面(Active Desktop)。

查询信息扩展(The QueryInfo extension)

活动桌面(Active Desktop)引进了一个新特征,如果你的鼠标在特定的对象上悬停的话,工具条提示会显示对象的描述。比如说,在“我的电脑”上悬停,就会出现如下的工具提示:

其它一些对象比如说“网上邻居”和“控制面板”也有相似的提示。我们也可以通过查询信息扩展(QueryInfo extension)为其他一些对象提供我们自己的工具提示。

关于查询信息扩展(QueryInfo extension)的说明是:这是我命名的;我这么称呼他是因为他用了这么一个借口:IQueryInfo 。到现在我可以说,它还没有一个官方名字。我快速的查看了一下1999年十月的MSDN,甚至没有提到这个扩展。 很明确它是一个被支持的扩展,因为微软的Office也为它的文件类型安装了QueryInfo扩展,如下所示:

WinZip 版本8也有一个为压缩文件安装的QueryInfo扩展:

我已经找到的最佳的文档是Dino Esposito的在2000年3月的MSDN杂志上的文章“Enhance Your User's Experience with New Infotip and Icon Overlay Shell Extensions(使用新的信息提示和图标覆盖外壳扩展来增强你的用户的体验)”。

QueryInfo扩展的开始?它能做什么?

这个外壳扩展将是一个快速文本文件查看器——它将显示文件大小和文件的第一行的内容。当用户在一个TXT文件上悬停鼠标的时候,我们的信息将在工具提示(tooltip)上显示。

用AppWizard 开始

运行AppWizard ,做一个新的ATL COM wizard app。我们叫它TxtInfo 。因为我们这次要使用MFC,所以请选中Support MFC 复选框,然后单击完成。我们现在就有了一个空的将会生成DLL的ATL项目,但是我们必须添加自己的外壳扩展COM对象。在ClassView树中,右键单击 TxtInfo classes项,选择New ATL Object。

在ATL Object 向导,第一面板已经选择了Simple Object ,只要单击下一步就行了。在第二面板中,在Short Name 编辑控件中输入TxtInfoShlExt ,然后单击确定(面板中的其它的编辑框将会自动完成)。这就创建了一个类名为CTxtInfoShlExt 的类,它包含了实现一个COM对象的基本代码。我们将向这个类添加我们的代码。

如果你看一下ClassView树的的时候,你将会发现我们有一个从CWinApp派生的CTxtInfoApp类。这个类和全局变量theApp的出现使我们使用MFC成为可能,正如我们编写一个没有ATL的普通MFC DLL一样。

接口实现

以前,在我们的上下文菜单扩展中(context menu extensions),我们实现Explorer实现我们对象的IShellExtInit接口。对外壳扩展来说,还有另一个实现接口,IPersistFile这是一个QueryInfo 扩展使用的接口。为什么会不同?如果你还记得的话,IShellExtInit::Initialize()接收一个IDataObject 指针,通过该指针我们可以枚举被选中的文件。通过IPersistFile扩展只能对单个文件进行操作。因为鼠标不可能在同一时间在两个对象上悬停,所以QueryInfo扩展一次只在一个文件上工作,所以它使用IPersistFile 。

所以我们需要添加IPersistFile到CTxtInfoShlExt 实现的接口列表中。打开TxtInfoShlExt.h,然后添加下面红色的行:

#include <comdef.h>

#include <shlobj.h>

class ATL_NO_VTABLE CTxtInfoShlExt :

public CComObjectRootEx<CComSingleThreadModel>,

public CComCoClass<CTxtInfoShlExt, &CLSID_TxtInfoShlExt>,

public IDispatchImpl<ITxtInfoShlExt, &IID_ITxtInfoShlExt, &LIBID_TXTINFOLib>,

public IPersistFile

{

BEGIN_COM_MAP(CTxtInfoShlExt)

COM_INTERFACE_ENTRY(ITxtInfoShlExt)

COM_INTERFACE_ENTRY(IDispatch)

COM_INTERFACE_ENTRY(IPersistFile)

END_COM_MAP()

我们还需要一个变量来存储Explorer在我们实现过程中的文件名:

protected:

// ITxtInfoShlExt

CString m_sFilename;

注意的是我们现在可以在任何地方使用一个MFC对象。

如果你查看IPersistFile的文档,你会发现有很多的方法。幸运的是,对本文的扩展,我们仅仅需要实现Load()而忽略其他的。下面是IPersistFile的方法的原型。

public:

// IPersistFile

STDMETHOD(GetClassID)(LPCLSID) { return E_NOTIMPL; }

STDMETHOD(IsDirty)() { return E_NOTIMPL; }

STDMETHOD(Load)(LPCOLESTR, DWORD);

STDMETHOD(Save)(LPCOLESTR, BOOL) { return E_NOTIMPL; }

STDMETHOD(SaveCompleted)(LPCOLESTR) { return E_NOTIMPL; }

STDMETHOD(GetCurFile)(LPOLESTR*) { return E_NOTIMPL; }

除了Load()的任何方法都仅仅返回E_NOTIMPL,表示我们并不实现他。

更加漂亮的是我们的Load()方法非常简单。我们仅需要存储Explorer传给我们的文件的名称。这是鼠标悬停的那个文件。

HRESULT CTxtInfoShlExt::Load ( LPCOLESTR wszFilename, DWORD dwMode )

{

AFX_MANAGE_STATE(AfxGetStaticModuleState()); // init MFC

// Let CString convert the filename to ANSI if necessary.

m_sFilename = wszFilename;

return S_OK;

}

注意函数的第一行。要使MFC工作正常,这行是必需的。因为我们的DLL被非MFC程序装载,每个使用MFC的出口函数必须人工初始化MFC。如果不包括这一行,很多的MFC函数(大多是和资源相关的)将会中断或产生断言。

文件名被保存在m_sFilename以备后用。注意,我使用了CString赋值操作符转换字符串到ANSI的优点,如果这个DLL是作为ANSI建立的话。

创建工具提示文本

在Explorer调用我们的Load()方法之后,它调用QueryInterface()来获得另一个接口:IQueryInfo 。IQueryInfo是一个相当简单的接口,仅有两个方法(实际上我们只是用了其中一个)。再次打开TxtInfoShlExt.h ,添加如下红颜色的行:

class ATL_NO_VTABLE CTxtInfoShlExt :

public CComObjectRootEx<CComSingleThreadModel>,

public CComCoClass<CTxtInfoShlExt, &CLSID_TxtInfoShlExt>,

public IDispatchImpl<ITxtInfoShlExt, &IID_ITxtInfoShlExt, &LIBID_TXTINFOLib>,

public IPersistFile,

public IQueryInfo

{

BEGIN_COM_MAP(CTxtInfoShlExt)

COM_INTERFACE_ENTRY(ITxtInfoShlExt)

COM_INTERFACE_ENTRY(IDispatch)

COM_INTERFACE_ENTRY(IPersistFile)

COM_INTERFACE_ENTRY(IQueryInfo)

END_COM_MAP()

然后添加IQueryInfo的方法:

// IQueryInfo

STDMETHOD(GetInfoFlags)(DWORD*) { return E_NOTIMPL; }

STDMETHOD(GetInfoTip)(DWORD, LPWSTR*);

GetInfoFlags()方法目前不被应用,我们仅仅返回E_NOTIMPL。 GetInfoTip()是我们返回给Explorer并让它显示在工具条提示上的实现之处。首先是讨厌的模块:

HRESULT CTxtInfoShlExt::GetInfoTip (

DWORD dwFlags,

LPWSTR* ppwszTip )

{

AFX_MANAGE_STATE(AfxGetStaticModuleState()); // init MFC

LPMALLOC pMalloc;

CStdioFile file;

DWORD dwFileSize;

CString sFirstLine;

BOOL bReadLine;

CString sTooltip;

USES_CONVERSION;

再次的,为了初始化MFC,AFX_MANAGE_STATE 首先被调用。这必须是在函数开始就被做,甚至在变量定义之前,因为很多的构造器是调用了MFC函数。

dwFlags 目前未被应用。ppwszTip 是一个指向一个LPWSTR 的指针(Unicode字符串指针),我们设置它指向我们必须分配的缓存。

第一步,我们将试图打开文件。我们知道它的文件名,因为我们早先在Load()函数中将它存储了。

if ( !file.Open ( m_sFilename , CFile::modeRead | CFile::shareDenyWrite ))

return E_FAIL;

现在,由于我们需要使用外壳内存分配器来分配一个缓存,我们需要一个IMalloc 接口指针。通过调用SHGetMalloc()来获得这个指针:

if ( FAILED( SHGetMalloc ( &pMalloc )))

return E_FAIL;

稍候,关于IMalloc 我有更多要说的东西。下一步是获得文件的大小,并读出文件的第一行:

// Get the size of the file.

dwFileSize = file.GetLength();

// Read in the first line from the file.

bReadLine = file.ReadString ( sFirstLine );

bReadLine 通常都是TRUE,除非这个文件是不可访问的或者只有0字节长。下一步是创建工具提示(tooltip)的第一部分,列出了文件的大小:

sTooltip.Format ( _T("File size: %lu"), dwFileSize );

现在,如果我们能够阅读文件的第一行,把它添加到工具提示(tooltip)中。

if ( bReadLine )

{

sTooltip += _T("\n");

sTooltip += sFirstLine;

}

现在,我们已经完成了工具提示。我们需要分配一个缓存。这儿我们使用IMalloc 。由SHGetMalloc()返回的指针是外壳IMalloc接口的一个拷贝。任何使用那个接口分配的内存都存在于外壳的进程空间中,因此外壳可以使用它。更重要的,外壳也可以释放它。所以我们要做的是分配缓存,然后就忘了它吧。外壳在使用完了之后会释放它的。

另外一件需要意思到的是我们返回给外壳的字符串必须是Unicode的。这就是为什么我们在下面的Alloc()调用中计算时乘以sizeof(wchar_t);仅仅分配lstrlen(sToolTip)长度的内存只有必需的内存的一半。

*ppwszTip = (LPWSTR) pMalloc->Alloc ( (1 + lstrlen(sTooltip)) * sizeof(wchar_t) );

if ( NULL == *ppwszTip )

{

pMalloc->Release();

return E_OUTOFMEMORY;

}

// Use the Unicode string copy function to put the tooltip text in the buffer.

wcscpy ( *ppwszTip, T2COLE((LPCTSTR) sTooltip) );

最后一件要做的事是释放我们早先的到的IMalloc 接口。

pMalloc->Release();

return S_OK;

}

这就是所有我们要做的。Explorer获得在*ppwszTip中的字符串,并把它显示在工具提示(tooltip)中。

注册外壳扩展

QueryInfo扩展的注册和上下文菜单扩展稍有不同。我们的扩展在HKEY_CLASSES_ROOT 的子键下注册,它的名字是我们想处理的文件扩展名。在这篇文章里,它是HKCR\.txt 。不过等一等,好像有点奇怪!你可能认为ShellEx 的子键看起来应该像"TooltipHandlers"之类的样子。但是不是!这个键被叫做 "{00021500-0000-0000-C000-000000000046}"。

在这儿,我想微软可能试图越过我们偷偷摸摸做些外壳扩展。如果你看看注册表的话,你会发现其它的ShellEx 的子键的名称也是GUID。上面的GUID正好是IQueryInfo的GUID。

不管怎样,这是是我们的扩展被.TXT文件调用的必须脚本:

HKCR

{

NoRemove .txt

{

NoRemove shellex

{

NoRemove {00021500-0000-0000-C000-000000000046} = s '{F4D78AE1-05AB-11D4-8D3B-444553540000}'

}

}

}

你可以通过复制上面的这段代码,然后改变".txt"为任何你想要的扩展名来使扩展被其他扩展名的文件调用。不幸的是,你不能在*或者 AllFileSystemObjects下注册达到能被所用文件调用的目的。

正如我们在前面的扩展中所讲的一样,在NT/2000下,我们需要添加我们的扩展到“认证的(approved)”扩展列表当中。实现这个过程的代码在 DllRegisterServer()和DllUnregisterServer()函数中。

待续...

在第四部分中,我们将回到上下文菜单中去看看一种新的扩展,拖和扔(drag and drop)处理。我们也将看到更多的MFC的使用。

你可以从下面的网址获得这个和其他文章的最新版本:http://home.inreach.com/mdunn/code/

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有