Shell扩展编程完全指南(2)如何编写一次操作多个文件对象的Shell

Shell扩展编程完全指南

copyright dedecms

linghuye copyright dedecms

第二节如何编写一次操作多个文件对象的Shell扩展

copyright dedecms

在第一节中, 我介绍了如何编写简单的Shell扩展, 并给出了一个简单的一次仅处理单个选择文件的上下文菜单扩展例子. 在本节中, 我将示范如何在一次操作中处理多个被选文件. 本扩展是一个用于注册和注销COM服务器的工具. 该例子也示范了如何使用ATL对话框类CDialogImpl. 在本节末尾我将讲述一些特殊的注册键,利用之你可以使你的扩展被所有的文件类型激活而不只是事先定好的文件类型.

织梦好,好织梦

第二节假设你已经读过第一节 ,你应该了解上下文菜单扩展的基本内容. 你也必须理解基础的COM, ATL, 还有STL 集合类的知识. 织梦好,好织梦

开始编写上下文菜单扩展– 它该作些什么?

这个Shell扩展可以让你注册以EXE, DLL, 和OCX文件形式提供的COM服务器. 不同于第一节 中的扩展, 该扩展将一次处理右击选择的所有文件. 内容来自dedecms

使用AppWizard 开始

运行AppWizard 并生成一个名为DllReg ATL工程. 保留所有的默认设置, 点击”完成”. 然后在ClassView树中右击DllReg classes项,在弹出的菜单中选择New ATL Object,添加一个COM 对象到DLL中. 内容来自dedecms

在ATL Object Wizard 中, 第一页已经选中了Simple Object, 因此单击Next. 在第二页中, 在Short Name编辑框中输入DllRegShlExt点击OK. (其余编辑框中的内容将自动填写.) 这样就创建了一个名为CdllRegShlExt 的已实现基本COM接口的新类. 我们将在该类中添加我们的代码.

本文来自织梦

初始化接口

我们的IShellExtInit::Initialize()实现与上一节大不相同, 有两方面的原因. 第一, 我们将列举所有被选的文件. 第二, 我们将检查所选择的文件是否输出了注册和注销的函数功能. 我们将只处理那些输出DllRegisterServer()DllUnregisterServer()函数的文件对象. 其余的文件将被忽略. dedecms.com

我们将使用列表视控件、STL 字符串和列表类, 所以你必须添加以下几行代码到stdafx.h文件中:

织梦好,好织梦

#include <commctrl.h> copyright dedecms 
#include <string> 

织梦好,好织梦

#include <list> copyright dedecms 
#include <atlwin.h> 
copyright dedecms
typedef std::list<std::basic_string<TCHAR> > string_list; 

dedecms.com

我们的CDllRegShlExt类需要一些成员变量:

本文来自织梦

protected: 

内容来自dedecms

HBITMAPm_hRegBmp; 
copyright dedecms
HBITMAPm_hUnregBmp; 
copyright dedecms
string_list m_lsFiles; 内容来自dedecms 
TCHARm_szDir [MAX_PATH]; 内容来自dedecms 

CDllRegShlExt构造器加载两个位图供上下文菜单使用:

copyright dedecms

CDLLRegShlExt::CDLLRegShlExt() 织梦内容管理系统 
{ dedecms.com 
m_hRegBmp = LoadBitmap ( _Module.GetModuleInstance(), 
dedecms.com
MAKEINTRESOURCE(IDB_REGISTERBMP) ); 本文来自织梦 
 
织梦内容管理系统
m_hUnregBmp = LoadBitmap ( _Module.GetModuleInstance(), 
本文来自织梦
MAKEINTRESOURCE(IDB_UNREGISTERBMP) ); 

织梦内容管理系统

} 织梦好,好织梦 

在你添加IShellExtInit接口到CDllRegShlExt接口列表中后(参看第一节 的说明), 我们开始编写Initialize()函数.Initialize()将执行这些步骤: copyright dedecms

  1. 改变当前工作目录为所查看的浏览器窗口目录。
  2. 列举所有被选择的文件.
  3. 对于每一个文件, 试着用LoadLibrary()加载.
  4. 如果LoadLibrary()成功了, 查看文件是否输出DllRegisterServer()DllUnregisterServer().
  5. 如果输出了这两个函数, 添加该文件名到我们的文件列表m_lsFiles中去.
HRESULT CDllRegShlExt::Initialize (  
织梦好,好织梦
LPCITEMIDLIST pidlFolder, 
内容来自dedecms
LPDATAOBJECT pDataObj, 
织梦内容管理系统
HKEY hProgID ) copyright dedecms 
{ 
织梦好,好织梦
TCHARszFile[MAX_PATH]; 本文来自织梦 
TCHARszFolder[MAX_PATH]; 织梦好,好织梦 
TCHARszCurrDir [MAX_PATH]; 

内容来自dedecms

TCHAR*pszLastBackslash; 

织梦内容管理系统

UINTuNumFiles; copyright dedecms 
HDROPhdrop; 

本文来自织梦

FORMATETC etc = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; 织梦好,好织梦 
STGMEDIUM stg = { TYMED_HGLOBAL }; 织梦好,好织梦 
HINSTANCE hinst; 
织梦内容管理系统
boolbChangedDir = false; 

织梦好,好织梦

HRESULT (STDAPICALLTYPE* pfn)(); 
织梦好,好织梦

一大堆繁琐的变量! 第一步是从传进来的pDataObj参数中获取HDROP 句柄. 这些与第一节 没什么区别. (实在太罗嗦了!)

织梦内容管理系统

// 从数据对象中读取文件列表.他们存储在HDROP 格式中 
dedecms.com
// 因此,取得 HDROP 句柄,并使用拖放API 织梦好,好织梦 
 

内容来自dedecms

if ( FAILED( pDO->GetData ( &etc, &stg ))) copyright dedecms 
return E_INVALIDARG; dedecms.com 
 

织梦好,好织梦

//取得HDROP 句柄. 

织梦内容管理系统

hdrop = (HDROP) GlobalLock ( stg.hGlobal ); 内容来自dedecms 
 

本文来自织梦

if ( NULL == hdrop ) 内容来自dedecms 
{ 

织梦好,好织梦

ReleaseStgMedium ( &stg ); 
内容来自dedecms
return E_INVALIDARG; 内容来自dedecms 
} 

copyright dedecms

 

dedecms.com

// 检查在该操作中有几个文件被选择. 内容来自dedecms 
uNumFiles = DragQueryFile ( hdrop, 0xFFFFFFFF, NULL, 0 ); 

本文来自织梦

接着是获取文件名的循环代码(使用DragQueryFile()) 并试用LoadLibrary()加载. 实际附带的例子中的代码事先改变了当前目录, 在这里我忽略它,因为它实在太冗长了. 织梦内容管理系统

for ( UINT uFile = 0; uFile < uNumFiles; uFile++ ) 
内容来自dedecms
{ 本文来自织梦 
//取得下一个文件名. 
织梦内容管理系统
if ( 0 == DragQueryFile ( hdrop, uFile, szFile, MAX_PATH )) 本文来自织梦 
continue; 

copyright dedecms

 copyright dedecms 
//试着加载文件. 

本文来自织梦

hinst = LoadLibrary ( szFile ); 
copyright dedecms
 
织梦内容管理系统
if ( NULL == hinst ) 

织梦好,好织梦

continue; copyright dedecms 

接着, 我们将查看该模块是否输出两个必须的函数.

copyright dedecms

// 获取 DllRegisterServer()函数的地址; 

内容来自dedecms

(FARPROC&) pfn = GetProcAddress ( hinst, "DllRegisterServer" ); 
内容来自dedecms
 
dedecms.com
// 如果没找着, 跳过该文件. dedecms.com 
if ( NULL == pfn ) 织梦好,好织梦 
{ 

本文来自织梦

FreeLibrary ( hinst ); 本文来自织梦 
continue; dedecms.com 
} 本文来自织梦 
 本文来自织梦 
// 获取DllUnregisterServer()函数的地址; 织梦内容管理系统 
(FARPROC&) pfn = GetProcAddress ( hinst, "DllUnregisterServer" ); dedecms.com 
 内容来自dedecms 
// 如果有,我们就可以处理该文件, 因此添加其到我们的文件名列表中去(m_lsFiles). 

copyright dedecms

if ( NULL != pfn ) 
织梦内容管理系统
{ 
内容来自dedecms
m_lsFiles.push_back ( szFile ); 内容来自dedecms 
} 内容来自dedecms 
 本文来自织梦 
FreeLibrary ( hinst ); 
内容来自dedecms
}// end for 
织梦内容管理系统

最后添加文件名到一个保存字符串的STL 列表集合变量m_lsFiles. 该列表在接下来将被使用, 我们将轮循所有的文件并进行注册或注销操作.

织梦内容管理系统

Initialize()中要做的最后一件事是释放所使用的资源并返回正确的值给浏览器. 本文来自织梦

// 释放资源. 织梦内容管理系统 
GlobalUnlock ( stg.hGlobal ); 内容来自dedecms 
ReleaseStgMedium ( &stg ); 
本文来自织梦
 
织梦好,好织梦
// 如果我们找到可以操作的文件, 返回 S_OK.否则,返回E_INVALIDARG,我们的代码就不会再被右击事件调用。 

织梦内容管理系统

 
copyright dedecms
return ( m_lsFiles.size() > 0 ) ? S_OK : E_INVALIDARG; 

织梦好,好织梦

} copyright dedecms 

如果你看一下例子代码, 你会看到我不得不通过检查文件名来判断当前查看的浏览器目录. 你可能奇怪为什么我不简单地使用pidlFolder参数,文档上说它是"包含所选择点击的文件对象的目录的标识符列表(Identifier List)" 嗯, 当我在Windows 98进行调试时, 该参数总是为空NULL, 所以它毫无用处. dedecms.com

添加我们的菜单项

接下来是IContextMenu的方法. 与先前一样, 你得加入ContextMenu接口到CDllRegShlExt实现的接口列表中. 而这些操作也正如第一节 中的步骤.

织梦好,好织梦

我们将添加两个菜单项, 一个用来注册所选文件, 另一个用来注销。添加的菜单项如下所示:

copyright dedecms

 [context menu - 5K] 内容来自dedecms

我们的QueryContextMenu()实现的开始如第一节. 我们检查uFlags, 如果CMF_DEFAULTONLY标志被设置就立即返回.

本文来自织梦

HRESULT CDLLRegShlExt::QueryContextMenu ( dedecms.com 
HMENU hmenu, 

本文来自织梦

UINTuMenuIndex, 
织梦好,好织梦
UINTuidFirstCmd, copyright dedecms 
UINTuidLastCmd, dedecms.com 
UINTuFlags ) 
织梦内容管理系统
{ 织梦好,好织梦 
UINT uCmdID = uidFirstCmd; 

织梦好,好织梦

 
织梦好,好织梦
// 如果CMF_DEFAULTONLY 标志被设置我们不作任何操作. 

copyright dedecms

if ( uFlags & CMF_DEFAULTONLY ) 织梦好,好织梦 
{ 
织梦内容管理系统
return MAKE_HRESULT ( SEVERITY_SUCCESS, FACILITY_NULL, 0 ); 

本文来自织梦

} 本文来自织梦 

接着, 我们添加"Register servers" 菜单项. 这儿有些新东西: 我们为每个菜单项设置一个位图. 这与WinZip 所作的一样,即放置一个图标在菜单左方.

copyright dedecms

// 添加register/unregister 项. 

织梦好,好织梦

InsertMenu ( hmenu, uMenuIndex, MF_STRING | MF_BYPOSITION, uCmdID++, 
dedecms.com
_T("Register server(s)") ); 
本文来自织梦
 
织梦内容管理系统
// 为register项设置位图. 
copyright dedecms
if ( NULL != m_hRegBmp ) 

织梦内容管理系统

{ 

内容来自dedecms

SetMenuItemBitmaps ( hmenu, uMenuIndex, MF_BYPOSITION, m_hRegBmp, NULL ); 

copyright dedecms

} 本文来自织梦 
 
内容来自dedecms
uMenuIndex++; 织梦好,好织梦 

SetMenuItemBitmaps()API用来在菜单项旁边显示小齿轮图标. 注意uCmdID被递增了, 所以下一次调用InsertMenu()添加的菜单项的命令ID比上一个大1. 最后, uMenuIndex也递增了,这样第二个菜单项将显示在第一个后.

copyright dedecms

对于第二个菜单项, 添加的代码与以上大致相同. 本文来自织梦

InsertMenu ( hmenu, uMenuIndex, MF_STRING | MF_BYPOSITION, uCmdID++, copyright dedecms 
_T("Unregister server(s)") ); 

织梦好,好织梦

 内容来自dedecms 
// 设置 unregister 项的位图. 织梦内容管理系统 
if ( NULL != m_hUnregBmp ) 内容来自dedecms 
{ dedecms.com 
SetMenuItemBitmaps ( hmenu, uMenuIndex, MF_BYPOSITION, m_hUnregBmp, NULL ); copyright dedecms 
} 
本文来自织梦

最后, 我们告诉浏览器我们添加了几个菜单项.

织梦内容管理系统

return MAKE_HRESULT ( SEVERITY_SUCCESS, FACILITY_NULL, 2 ); dedecms.com 

提供帮助提示和动作verb

正如先前, GetCommandString()方法在浏览器需要显示帮助信息或取得动作(verb)命令ID时被调用. 该扩展有两个菜单项, 所以我们需要检查uCmdID参数以确定哪一个菜单项被点击调用.

copyright dedecms

#include <atlconv.h> 织梦内容管理系统 
 

本文来自织梦

HRESULT CDLLRegShlExt::GetCommandString (  

copyright dedecms

UINTuCmdID, 

织梦内容管理系统

UINTuFlags,  本文来自织梦 
UINT* puReserved, 织梦好,好织梦 
LPSTR szName, 织梦好,好织梦 
UINTcchMax ) 
本文来自织梦
{ 

dedecms.com

LPCTSTR szPrompt; 

本文来自织梦

 织梦内容管理系统 
USES_CONVERSION; 织梦内容管理系统 
 
织梦好,好织梦
if ( uFlags & GCS_HELPTEXT ) 
织梦好,好织梦
{ 

本文来自织梦

switch ( uCmdID ) 织梦好,好织梦 
{ 
内容来自dedecms
case 0: 

copyright dedecms

szPrompt = _T("Register all selected COM servers"); 

织梦内容管理系统

break; 
dedecms.com
 dedecms.com 
case 1: 内容来自dedecms 
szPrompt = _T("Unregister all selected COM servers"); 
织梦好,好织梦
break; 内容来自dedecms 
 

copyright dedecms

default: 内容来自dedecms 
return E_INVALIDARG; 内容来自dedecms 
break; copyright dedecms 
} copyright dedecms 

如果uCmdID为0, 表示是第一个菜单项(register)被点击调用. 如果为1,是第二项(unregister)被调用. 我们在决定了帮助字符串后, 将之拷贝到提供的缓冲区中, 必要时转化为Unicode 格式.

内容来自dedecms

// 拷贝帮助字符串到提供的缓冲区中.如果Shell需要Unicode字符串,我们需要转化szName 到LPCWSTR. dedecms.com 
 
本文来自织梦
if ( uFlags & GCS_UNICODE ) 
copyright dedecms
{ 
本文来自织梦
lstrcpynW ( (LPWSTR) szName, T2CW(szPrompt), cchMax ); 内容来自dedecms 
} 织梦好,好织梦 
else 
织梦内容管理系统
{ 
织梦内容管理系统
lstrcpynA ( szName, T2CA(szPrompt), cchMax ); 

内容来自dedecms

} 

织梦内容管理系统

} 织梦好,好织梦 

在这个扩展中, 我也实现了一个动作(verb). 然而, 当在Windows 98上测试时, 浏览器从不会调用GetCommandString()来获取动作(verb). 我也写了一个测试程序,对一个DLL文件调用ShellExecute()并试着使用动作(verb), 但也不能工作. 我不知道NT上的行为是否不同. 我在这忽略了与此有关的代码, 如果你有兴趣可以去看例子代码. 织梦好,好织梦

执行用户选择

当用户点击菜单项, 浏览器调用我们的InvokeCommand()方法. InvokeCommand()函数首先检查lpVerb 高字. 如果非0, 其值就是要调用的动作(verb)名. 而我们知道动作不能正确地工作(至少在Win 98上), 所以不用处理. 否则, 如果lpVerb的低字为0 或1, 我们就知道有一个定制的菜单项被点击了.

本文来自织梦

HRESULT CDllRegShlExt::InvokeCommand ( LPCMINVOKECOMMANDINFO pCmdInfo ) dedecms.com 
{ 
copyright dedecms
// 如果 lpVerb 指向一字符串, 忽略此次调用. dedecms.com 
if ( 0 != HIWORD( pInfo->lpVerb )) 

本文来自织梦

return E_INVALIDARG; copyright dedecms 
 本文来自织梦 
// 检查lpVerb 是不是我们添加的命令(0 或 1) dedecms.com 
switch ( LOWORD( pInfo->lpVerb )) 
copyright dedecms
{ copyright dedecms 
case 0: 

内容来自dedecms

case 1: 织梦好,好织梦 
{ 
copyright dedecms
CProgressDlg dlg ( &m_lsFiles, pInfo ); 织梦内容管理系统 
 内容来自dedecms 
dlg.DoModal(); 

内容来自dedecms

return S_OK; 织梦好,好织梦 
} 

dedecms.com

break; 

dedecms.com

 
织梦内容管理系统
default: 内容来自dedecms 
return E_INVALIDARG; 织梦好,好织梦 
break; 内容来自dedecms 
} 
copyright dedecms
} 
内容来自dedecms

如果lpVerb为0 或1, 我们创建一个进度对话框(从ATL的CdialogImpl类派生), 并将文件名列表传给它. 本文来自织梦

所有实质的工作发生在CProgressDlg类中. 它的OnInitDialog()函数初始化列表控件, 并调用CProgressDlg::DoWork(). DoWork()轮循在CDllRegShlExt::Initialize()时创建的字符串列表, 并对每一个文件调用合适的函数. 基本的代码如下所示; 这里的代码并不完整,我忽略了错误检查代码, 以及填充列表控件的代码. 这里的代码刚好够说明如何轮循列表中的文件名并进行操作.

copyright dedecms

void CProgressDlg::DoWork() 织梦内容管理系统 
{ 内容来自dedecms 
HRESULT (STDAPICALLTYPE* pfn)(); 

织梦内容管理系统

string_list::const_iterator it, itEnd; 织梦好,好织梦 
HINSTANCE hinst; 本文来自织梦 
LPCSTRpszFnName; 内容来自dedecms 
HRESULThr; 
内容来自dedecms
WORDwCmd; 织梦内容管理系统 
 copyright dedecms 
wCmd = LOWORD ( m_pCmdInfo->lpVerb ); 

织梦内容管理系统

 

dedecms.com

// 我们只支持两个命令, 所以检查传进来的lpVerb值. dedecms.com 
if ( wCmd > 1 ) 

本文来自织梦

return; 

内容来自dedecms

 织梦内容管理系统 
// 决定我们该调用哪个命令.注意这些字符串没有使用 _T 宏, 因为 GetProcAddress() 只接收一个 
织梦好,好织梦
// ANSI 函数名字符串. copyright dedecms 
pszFnName = wCmd ? "DllUnregisterServer" : "DllRegisterServer"; 织梦内容管理系统 
 
织梦好,好织梦
for ( it = m_pFileList->begin(), itEnd = m_pFileList->end(); 

内容来自dedecms

it != itEnd; 
织梦好,好织梦
it++ ) copyright dedecms 
{ 
本文来自织梦
// 试着加载下一个文件. copyright dedecms 
hinst = LoadLibrary ( it->c_str() ); 

copyright dedecms

 dedecms.com 
if ( NULL == hinst ) 
dedecms.com
continue; 
copyright dedecms
 
织梦好,好织梦
// 取得 register/unregister函数地址. 

织梦好,好织梦

(FARPROC&) pfn = GetProcAddress ( hinst, pszFnName ); 
内容来自dedecms
 
织梦内容管理系统
// 如果没找到, 跳到下一个文件go on to the next file. 
dedecms.com
if ( NULL == pfn ) 
内容来自dedecms
continue; 本文来自织梦 
 
copyright dedecms
// 调用注册函数! 

织梦内容管理系统

hr = pfn(); 
copyright dedecms

我要解释一下for循环, 因为如果你没有使用过STL 集合类,它是有点古怪. m_pFileList是个指向m_lsFiles 变量的指针. (该指针通过CProgressDlg的构造函数被传递.) STL list集合有一种const_iterator类型, 它是一个类似于MFC中的POSITION类型的抽象实体. 一个const_iterator变量在行为上类似于一个列表中的const对象的指针, 所以这个迭代器可以使用->反引用以获取对象本身. 一个迭代器也可以使用++递增以在列表中移动指针.

织梦内容管理系统

所以, for循环的初始代码调用list::begin()取得指向列表中第一个字符串的迭代器, 而且调用list::end()取得指向列表末尾的迭代器, 即位于最后一个字符串之尾. (我将术语放在引号中以强调指向,开始,结束的概念,这些概念都被const_iterator类型所抽象化,而且必须通过const_iterator的相应方法来获取[如begin()] 或进行操作[如++].) 这两个迭代器分别赋值给ititEnd. 循环一直继续到it等于itEnd; 亦即, 当it还没到列表末尾. 迭代器it将每次向前递增, 每次它都前进一个字符串. dedecms.com

表达式it->c_str()使用->操作符. 因为it的行为正如一个string的指针(记住, m_pFileList是STL字符串列表的指针), it->c_str()调用string类的c_str()函数. c_str()返回一个C风格的字符串指针, 在这里其等同于LPCTSTR. 织梦好,好织梦

DoWork()的剩余部分是清理和错误处理. 你可以在例子中的ProgressDlg.cpp中找到完整的代码.

dedecms.com

(我刚意识到谈论一个名为”it”的变量是多奇怪. 抱歉.) :)

内容来自dedecms

注册Shell扩展

DllReg扩展对可执行文件进行操作, 所以其应该被注册到EXE, DLL,和OCX文件类型. 如第一节所说, 我们可以通过RGS脚本文件来完成, DllRegShlExt.rgs. 以下是注册我们的DLL为上下文菜单扩展所需的脚本. 内容来自dedecms

HKCR
{
    NoRemove dllfile
    {
        NoRemove shellex
        {
            NoRemove ContextMenuHandlers
            {
                ForceRemove DLLRegSvr = s '{8AB81E72-CB2F-11D3-8D3B-AC2F34F1FA3C}'
            }
        }
    }
    NoRemove exefile
    {
        NoRemove shellex
        {
            NoRemove ContextMenuHandlers
            {
                ForceRemove DLLRegSvr = s '{8AB81E72-CB2F-11D3-8D3B-AC2F34F1FA3C}'
            }
        }
    }
    NoRemove ocxfile
    {
        NoRemove shellex
        {
            NoRemove ContextMenuHandlers
            {
                ForceRemove DLLRegSvr = s '{8AB81E72-CB2F-11D3-8D3B-AC2F34F1FA3C}'
            }
        }
    }
} 

本文来自织梦

RGS文件的格式及关键字NoRemoveForceRemove的含义已在第一节中解释过.

copyright dedecms

正如上一节的例子一样,在NT/2000上我们需要添加我们的扩展到"approved" 扩展列表中去. 完成该工作的代码在DllRegisterServer()DllUnregisterServer()函数中.我不在这写出这些代码, 那只是简单的注册表获取, 你可以在例子工程代码中找到它. 内容来自dedecms

生成的扩展是什么样的?

当你点击定制的菜单项, 进度对话框将弹出并会显示操作的结果: 内容来自dedecms

 [progress dialog - 21K]

dedecms.com

列表控件显示每个文件名, 及函数调用的成败. 当你选中一个文件, 在列表下会显示一条详细信息, 以及失败的调用的错误描述.

dedecms.com

注册扩展的其它方法

到目前为止, 我们的扩展只被特定的文件类型调用. 但是我们可以通过在HKCR\*键下注册上下文菜单扩展使我们的扩展被所有文件类型调用:

内容来自dedecms

HKCR 
dedecms.com
{ 
内容来自dedecms
NoRemove * 

copyright dedecms

{ 

织梦好,好织梦

NoRemove shellex 

织梦内容管理系统

{ 内容来自dedecms 
NoRemove ContextMenuHandlers 本文来自织梦 
{ 
dedecms.com
ForceRemove DLLRegSvr = s '{8AB81E72-CB2F-11D3-8D3B-AC2F34F1FA3C}' 

内容来自dedecms

} dedecms.com 
} dedecms.com 
} 内容来自dedecms 
} copyright dedecms 

HKCR\*键下列出了适用于所有文件类型的Shell扩展. 注意,文档上说此类扩展将被所有Shell对象激发(如文件, 目录, 虚拟文件夹, 控制面板, 等等.), 但是在我调试时并不是这样的. 该扩展只被实际的物理文件所激发.

dedecms.com

当shell 版本为4.71+时, 这里还有个名为HKCR\AllFileSystemObjects的键. 如果我们在这个键下注册, 我们的扩展将被文件系统中的所有的文件和文件夹所激发, 除了根目录. (要被根目录激发应该在HKCR\Drive 下注册.) 然而, 这样做时,会出现一些反常的的行为. 因为发送到菜单也使用这个键, 所以DllReg菜单项与发送到菜单项交叉在一起: copyright dedecms

 [context menu - 7K ]

织梦内容管理系统

你也可以写一个操作目录的上下文菜单扩展. 关于这个例子,请参考拙著A Utility to Clean Up Compiler Temp Files.

本文来自织梦

最后,在shell版本4.71+中, 你可以让上下文菜单在用户右击浏览器窗口(包括桌面)的背景时激发. 要让你的扩展在这种情况下被激发,需要在HKCR\Directory\Background\shellex\ContextMenuHandlers键下进行注册. 使用该方法, 你可以添加定制菜单到桌面或任意目录上下文菜单. 这时传送到IShellExtInit::Initialize()的参数有些不同,所以我将在以后的文章中讲述这方面的内容.

dedecms.com

待续...

在接下来的第三节 里, 我们将学习一种新的扩展类型, QueryInfo 处理器, 它将为Shell对象显示弹出信息提示框. 我也将示范如何在Shell扩展中使用MFC. 内容来自dedecms