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

原创 2001年10月08日 17:48:00

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

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

查询信息扩展(The QueryInfo extension)

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

[My Computer tooltip - 6K]

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

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

[Word doc tooltip - 5K]

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

[WinZip tooltip - 5K]

    我已经找到的最佳的文档是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 复选框,然后单击完成。我们现在就有了一个空的将会生成DLLATL项目,但是我们必须添加自己的外壳扩展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

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

class ATL_NO_VTABLE CTxtInfoShlExt : 
    public CComObjectRootEx<CComSingleThreadModel>,
    public CComCoClass<CTxtInfoShlExt, &CLSID_TxtInfoShlExt>,
    public IDispatchImpl<ITxtInfoShlExt, &IID_ITxtInfoShlExt, &LIBID_TXTINFOLib>,
    {
BEGIN_COM_MAP(CTxtInfoShlExt)
 COM_INTERFACE_ENTRY(ITxtInfoShlExt)
 COM_INTERFACE_ENTRY(IDispatch)
 
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,
    
{
BEGIN_COM_MAP(CTxtInfoShlExt)
 COM_INTERFACE_ENTRY(ITxtInfoShlExt)
 COM_INTERFACE_ENTRY(IDispatch)
 COM_INTERFACE_ENTRY(IPersistFile)
 
END_COM_MAP()

    然后添加IQueryInfo的方法:

    // IQueryInfo
    STDMETHOD(GetInfoFlags)(DWORD*)     { return E_NOTIMPL; }
    STDMETHOD(GetInfoTip)(DWORD, LPWSTR*);

    GetInfoFlags()方法目前不被应用,我们仅仅返回E_NOTIMPLGetInfoTip()是我们返回给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)中。

[text file tooltip - 6K]

注册外壳扩展

    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/

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

  BODY{ COLOR: black; FONT-FAMILY: Verdana, Helvetica, Arial, sans-serif; FONT-SIZE: 10pt}P...
  • mefish
  • mefish
  • 2001年09月26日 10:37
  • 1173

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

    外壳扩展(Shell Extention)是一个能向Windows外壳(资源管理器)添加一些功能的COM对象。这有很多的内容,但是却很少有关于它们的易懂的文档告诉我们如何去编写这些外壳(She...
  • mefish
  • mefish
  • 2001年09月21日 15:41
  • 2002

Webpack傻瓜入门指南

Webpack傻瓜式指南 这个写的好啊,师傅推荐的,安利安利! 试着敲一下:var webpack = require('webpack'); module.exports = { ent...
  • yCharlee
  • yCharlee
  • 2016年09月23日 22:47
  • 420

Ogre嵌入MFC傻瓜完全教程(三)

经过前两两篇博文的讲解,我们已经完成了渲染工作,但只是渲染而没有交互性,本篇博文我们就来加上事件的处理方法。 首先我们需要为项目添加一个帧监听类:CMyFrameListener,为了直观,在这直接...
  • guoyk1990
  • guoyk1990
  • 2014年05月17日 13:52
  • 2666

Windows shell 扩展编程教程详解

所谓的Shell扩展就是能够添加某种功能到Windows Shell的COM对象。Windows里有着各种各样的扩展,但关于Shell扩展的原理以及如何编写Shell扩展的文档却很少。如果你想深入地了...
  • zhazhaq
  • zhazhaq
  • 2017年08月05日 17:56
  • 204

VC++ 外壳扩展编程之windows右键菜单

http://www.programbbs.com/doc/show.asp?ID=473文档作者:李晓飞所属类别:VC++推荐指数:★★★☆文档人气:418本周人气:13发布日期:2006-12-1...
  • sworder_001
  • sworder_001
  • 2007年03月21日 22:32
  • 4661

NDK: NativeActivity原理

在AndroidManifest.xml文件里面指定入口activity为nativeactivity, 这样应用程序一启动,java虚拟机这边就开一个主线程,主线程创建一个活动,就是nat...
  • zhou191954
  • zhou191954
  • 2014年04月04日 14:44
  • 898

华三交换机光口级联傻瓜交换机光口

华三交换机光口级联傻瓜交换机光口   port link-type trunk port trunk permit vlan all speed 1000...
  • weixin_38200154
  • weixin_38200154
  • 2017年06月04日 22:18
  • 207

通向实在之路:宇宙法则的完全指南 笔记

通向实在之路:宇宙法则的完全指南 目录 1 科学的根源2 古代定理和现代问题3 物理世界里数的种类4 奇幻的复数5 对数、幂和根的几何6 实数微积分7 复...
  • cteng
  • cteng
  • 2014年10月07日 23:00
  • 1241

读《完全写作指南》

写作书
  • wide288
  • wide288
  • 2017年07月11日 08:58
  • 189
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:外壳扩展编写完全傻瓜指南(三)(Michael Dunn)
举报原因:
原因补充:

(最多只允许输入30个字)