当Windows Shell将要为某文件对象显示其上下文相关菜单(或显示菜单条上的File菜单)时,系统将调用上下文相关菜单处理程序中IContextMenu接口的QueryContexMenu成员函数。此时上下文相关菜单处理程序可通过调用InsertMenu函数按位置(MF_POSITION)直接将想要加入的菜单选项加入列上下文相关菜单中。加入的菜单项可以是通常的字符串(MF_STRING),也可以是位图(MF_BITMAP)。当用户选中某个由上下文相关菜单处理程序维护的菜单选项时,Shell将调用此处理程序IContextMenu接口的InvokeCommand成员函数以使处理程序有机会处理用户选择的命令。若对某类文件登记有多个上下文相关菜单处理程序,则各命令的顺序是由文件类下的ContextMenuHandlers关键字决定。
上下文相关菜单处理程序的具体的编写过程如下:
(1)创建一个空的DLL项目
上下文相关菜单处理程序仍将以进程内COM服务器的形态存在,因此需要用为上下文相关菜单处理程序创建应该空的DLL项目,本文将项目命名为ContextMenuExt;
(2)为项目添加一个类CContextMenuExt
首先还是来考虑DLL需要实现的功能,上下文相关菜单处理程序需要一个类来实现COM接口IContextMenu和IShellExtInit。为此,笔者给项目添加一个类CContextMenuExt。添加了CContextMenuExt类之后,从项目的文件视图可以看出项目中多了两个文件,它们分别是类CContextMenuExt的头文件(CContextMenuExt.h)和源代码文件(CcontextMenuExt.cpp)。接下来修改CContextMenuExt的类声明,使之继承IContextMenu和IShellExtInit接口,并添加3个成员变量m_cRef、m_pDataObj、m_szFileName和一个受保护的成员函数。在CContextMenuExt类的公有声明部分,定义了类的构造函数、析构函数以及为了实现IUnknown、IContextMenu和IShellExtInit接口而设的成员函数。之所以要实现IUnknown接口,是因为IUnknown接口是一切接口的祖先。除继承了IUnknown接口的3个基本方法之外,IContextMenu接口还定义了真正实现菜单扩展的QueryContextMenu、InvokeCommand和GetCommandString方法,关于这些方法的作用,前面已经做了简要的说明,这里不在赘述。
在CContextMenuEx类的受保护部分,声明了一个成员函数ClickSample,这个函数正是为了处理添加的菜单命令而设的。定义了一个数组m_szFileName用以保存选中文件的完整路径;
(3)为类CContextMenuExt添加实现代码
为了清楚起见,笔者将逐步给出其各个成员函数的说明。首先来看看CContextMenuExt类的构造函数。在构造函数中对两个成员变量进行了初始化,其中m_cRef表示对该类的引用计数,m_pDataObj则表示系统传送过来的IDataObject接口的指针,将在后面使用。g_nDllRefCount是DLL中需要定义的一个全局变量,用来记录整个DLL被引用的次数,并将以此决定是否运行Shell对DLL服务器进行卸载。当用户选中了某个AC3文件类的上下文菜单DLL服务器维护的菜单选项时,Shell将调用CContextMenuExt类中IContextMenu接口的InvokeCommand成员函数来处理用户的选择。此处CContextMenuExt的做法是通过参数lpcmi的lpVerb找到用户选择的菜单的标志符,并调用相应的成员函数进行处理。在本文中这个成员函数启动AC3播放器播放存放在m_szFileName数组中的文件;
(4)为项目添加另一个类CcontextMenuExtFactory。Dll中有了CContextMenuExt类之后,还需要一种手段来创建DLL中的这个功能类。于是便需要添加另一个类CContextMenuExtFactory来作为类CContextMenuExt类工厂。所谓"类工厂",是专门用来生产别的功能类的COM对象。它需要实现IClassfactory接口,当然也需要实现IUnknown接口。其中的CreateInstance和LockServer两个成员函数是IClassFactory接口要求其实现类必须实现的方法;
(5)为服务器实现一些框架性代码
需要为整个DLL定义一个引用计数变量g_nDllRefCount和一个用来记录DLL的模块句柄的变量g_hThisDll。作为Win32的动态链接库,需要为DLL实现几个标准函数,如DLLMain、DLLCanUnloadNow、DLLGet ClassObject等,首先为这些函数添加原型。函数DllMain是所有Win32动态链接库的入口函数。在DLL的全局变量g_hThisDll中保存进程的实例标识hInstance以备后用。当系统的进程或线程初始化或者被终止时,Dll中的DllMain函数即被系统以相应的参数调用。此外,当别的进程或者线程调用LoadLibrary函数装载Dll或者调用FreddLibray函数卸载Dll时,DllMain函数也会被系统调用。函数DllCanUnloadNow通过全局变量g_nDllRefCount来决定实现该函数的DLL是否正在使用之中,如果不是,调用者就可以将该DLL安全地从内层中卸载。DllGetClassObject函数被用来获取DLL中定义的类的对象实例。DllGetClassObject函数通常在CoGetClassObject函数中被调用。
(6)编辑上下文菜单处理程序的注册文件制作一个 .reg文件,按照一定的格式书写完后,在操作系统中双击这个文件,文件表的注册条目被合并到注册表中,这里需要强调的是该处理程序的类和接口的全局唯一标志符是使用Visual C++6.0提供的UUIDGEN实用工具生成的。还需要说明的,由于笔者使用的操作系统是Windows2000,所以编译连接好的ContextMenuExt.dll文件应放到C:\WINNT\System下。
查看本文来源