一步步教你如何编写Shell扩展

转载 2006年05月22日 13:08:00
所谓的Shell扩展就是能够添加某种功能到Windows Shell的COM对象。
Windows里有着各种各样的扩展,但关于Shell扩展的原理以及如何编写Shell扩展的文档却很少。
如果你想深入地了解Shell各方面的细节,我特别推荐Dino Esposito的著作《Visual C++ Windows Shell Programming》。
但对于那些没有这本书的,或只对Shell扩展本身感兴趣的朋友,我写了这个编程指南希望能够帮助你理解怎样编写Shell扩展。
该指南假设你理解COM和ATL的基本原理及应用。

第一节对Shell扩展进行了概括性的介绍, 并给出了一个上下文菜单的扩展以引起你对以后各章的兴趣.

但Shell扩展到底是什么玩意呢?
"Shell扩展"从字面上分两个部分,Shell与Extension。
Shell指Windows Explorer, 而Extension则指由你编写的当某一预先约定好的事件(如在以.doc为后缀的文件图标上单击右键)发生时由Explorer调用执行的代码。
因此一个Shell扩展就是一个为Explorer添加功能的COM对象。

Shell扩展是个进程内服务器(运行在Explorer进程内),它实现了一些接口来处理与 Explorer 的通信。
ATL在我看来是设计Shell扩展最简单最快捷的方法, 如果没有它,你就不得不一遍又一遍地编写繁琐的 QueryInterface() 及AddRef()代码.
另外,在Windows NT 和 2000上调试Shell扩展相对比较容易一些,这我以后会讲到的。

Shell
扩展有很多种类型,每种类型都在各自不同的事件发生时被调用运行,但也有一些扩展的类型和调用情形是非常相似的。

类型 何时被调用 应该作些什么
Context menu
扩展处理器
用户右键单击文件或文件夹对象时,
或在一个文件夹窗口中的背景处单击右键时(要求
shell版本为4.71+
添加菜单项到上下文菜单中
Property sheet
扩展处理器
要显示一个文件对象的属性框时 添加定制属性页到属性表中
Drag and drop
扩展处理器
用户用右键拖放文件对象到文件夹窗口或桌面时 添加菜单项到上下文菜单中
Drop 扩展处理器 用户拖动Shell对象并将它放到一个文件对象上时 任何想要的操作
QueryInfo扩展处理器 (需要shell版本 4.71+) 用户将鼠标盘旋于文件或其他Shell对象的图标上时 返回一个浏览器用于显示在提示框中的字符串

现在你可能想知道Shell扩展到底是什么样的. 如果你安装了 WinZip (有谁没装的吗?), 它就包含了多种的Shell扩展,其中也就有上下文菜单扩展.
下图是WinZip 8 为压缩文件对象添加的定制菜单项:


WinZip 编写了添加菜单项的代码, 提供了浏览器状态栏上的菜单项帮助提示, 并在用户选择一个菜单命令时执行相应的操作。

WinZip 还包括一个拖放目标扩展处理器. 该类型与上下文菜单十分类似, 但它是在用户用右键拖放文件时被触发的.
下图是 WinZip 定制的拖放菜单:


Shell扩展的类型很多,而且微软也正不断地在每一新版本的Windows中加入更多的扩展类型.
现在让我们把注意力放在上下文菜单上, 因为它们易于编写,效果也很明显(这能马上满足你).

在我们编写代码之前, 先说一下一些简化编码及调试工作的技巧.
当shell扩展被 Explorer调用后, 它会在内存中呆上一段时间, 这会使你无法重新编译并生成Shell扩展DLL文件.

要让 Explorer 更迅速地卸载Shell扩展执行文件,需要创建如下注册表项:
HKLM/Software/Microsoft/Windows/CurrentVersion/Explorer/AlwaysUnloadDLL
并将其值设为 "1". 对于Win9x, 这是你能做的最好的方法。

而在Win NT/2000上, 你可以找到如下键:
HKCU/Software/Microsoft/Windows/CurrentVersion/Explorer
并创建一个名为DesktopProcess的DWORD值 1. 这会使桌面和任务栏运行在同一个进程中, 而其他每一个 Explorer 窗口都运行在它自己的每一个进程内. 也就是说,你可以在单个的Explorer 窗口内进行调试, 而后只要你关闭该窗口,你的DLL就会被马上卸载, 这就避免了因为DLL正被Windows使用而无法替换更新. 而如果不幸出现这种情况,你就不得不注销登录后再重新登录进Windows从而强制卸载使用中的Shell扩展DLL.

我将在稍后解释如何在Win9x中进行调试的细节.

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

开头先让我们做简单一些, 只弹出一个对话框以表明当前的扩展能够正常地工作.
我们把扩展关联到 .TXT 文件, 因此当用户右键单击文本文件对象时扩展就会被调用.

使用 AppWizard 开始
好吧, 让我们开始吧! 什么? 我还没告诉你怎样使用那些神秘的 shell 扩展接口?
别着急, 我会边进行边解释的。
我觉得先解释一下一个概念再紧接着说明示例代码,对理解例子程序会更简单一些. 当然我也可以把所有的东西都先解释完,然后再解释代码, 但我觉得这样做不能吸引人的注意力。不管怎么样, 向 VC开火,开始!

运行AppWizard,生成一个名为SimpleExt 的 ATL COM 工程. 保留所有默认的设置选项,点击”完成”.
现在我们已经有了一个空的 ATL工程,它可以编译并生成一个 DLL, 但我们还需要添加Shell扩展的 COM 对象.
在 ClassView 中, 右击 SimpleExt classes 条目, 选择 New ATL Object.

在ATL Object Wizard里, 第一页默认已经选择了 Simple Object , 所以单击 Next 即可.
在第二页中, 在Short Name 文本框里输入 SimpleShlExt ,点击 OK. (其余的文本框会自动填充完.)
这样就创建了一个名为 CSimpleShlExt 的类,其包含了实现COM对象最基本的代码. 我们将在这个类中加入我们自己的代码.

初始化接口
当我们的shell扩展被加载时, Explorer 将调用我们所实现的COM对象的 QueryInterface() 函数以取得一个 IShellExtInit 接口指针.
该接口仅有一个方法 Initialize(), 其函数原型为:

HRESULT IShellExtInit::Initialize ( LPCITEMIDLIST pidlFolder, LPDATAOBJECT pDataObj, HKEY hProgID );

Explorer 使用该方法传递给我们各种各样的信息.
PidlFolder是用户所选择操作的文件所在的文件夹的 PIDL 变量. (一个 PIDL [指向ID 列表的指针] 是一个数据结构,它唯一地标识了在Shell命名空间的任何对象, 一个Shell命名空间中的对象可以是也可以不是真实的文件系统中的对象.)
pDataObj 是一个 IDataObject 接口指针,通过它我们可以获取用户所选择操作的文件名。
hProgID 是一个HKEY 注册表键变量,可以用它获取我们的DLL的注册数据.
在这个简单的扩展例子中, 我们将只使用到 pDataObj 参数.

要添加这个接口进 COM 对象, 先打开SimpleShlExt.h 文件, 然后加入下列标红的代码:

#include "shlobj.h"
#include "comdef.h"

class ATL_NO_VTABLE CSimpleShlExt :
public CComObjectRootEx,
public CComCoClass,
public IDispatchImpl,
public IShellExtInit

BEGIN_COM_MAP(CSimpleShlExt)
COM_INTERFACE_ENTRY(ISimpleShlExt)
COM_INTERFACE_ENTRY(IDispatch)
COM_INTERFACE_ENTRY(IShellExtInit)
END_COM_MAP()

COM_MAP是ATL实现 QueryInterface()机制的宏,它包含的列表告诉ATL其它外部程序用QueryInterface()能从我们的 COM对象获取哪些接口.
接着,在类声明里, 加入Initialize()的函数原型.
另外我们需要一个变量来保存文件名:

protected:
TCHAR m_szFile [MAX_PATH];
public:
// IShellExtInit
STDMETHOD(Initialize)(LPCITEMIDLIST, LPDATAOBJECT, HKEY);

然后, 在 SimpleShlExt.cpp 文件中, 加入该函数方法的实现定义:

HRESULT CSimpleShlExt::Initialize ( LPCITEMIDLIST pidlFolder, LPDATAOBJECT pDataObj, HKEY hProgID )

我们要做的是取得被右击选择的文件名,再把该文件名显示在弹出消息框中。
可能会有多个文件同时被选择右击, 你可以用pDataObj 接口指针获取所有的文件名, 但现在为简单起见, 我们只获取第一个文件名.

文件名的存放格式与你拖放文件到带WS_EX_ACCEPTFILES风格的窗口时使用的文件名格式是一样的。
这就是说我们可以使用同样的API来获取文件名: DragQueryFile().
首先我们先获取包含在IdataObject中的数据句柄:

{
FORMATETC fmt = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
STGMEDIUM stg = { TYMED_HGLOBAL };
HDROP hDrop;

// 在数据对象内查找 CF_HDROP 型数据.
if ( FAILED( pDataObj->GetData ( &fmt, &stg )))
{
// Nope! Return an "invalid argument" error back to Explorer.
return E_INVALIDARG;
}

// 获得指向实际数据的指针
hDrop = (HDROP) GlobalLock ( stg.hGlobal );

// 检查非NULL.
if ( NULL == hDrop )
{
return E_INVALIDARG;
}

请注意错误检查,特别是指针的检查。
由于我们的扩展运行在 Explorer 进程内, 要是我们的代码崩溃了, Explorer也会随之崩溃. 在Win 9x上, 这样的一个崩溃可能导致需要重启系统.

所以, 现在我们有了一个 HDROP 句柄, 我们就可以获取我们需要的文件名了:

// 有效性检查 – 保证最少有一个文件名.
UINT uNumFiles = DragQueryFile ( hDrop, 0xFFFFFFFF, NULL, 0 );
if ( 0 == uNumFiles )
{
GlobalUnlock ( stg.hGlobal );
ReleaseStgMedium ( &stg );
return E_INVALIDARG;
}
HRESULT hr = S_OK;

// 取得第一个文件名并把它保存在类成员m_szFile 中.
if ( 0 == DragQueryFile ( hDrop, 0, m_szFile, MAX_PATH ))
{
hr = E_INVALIDARG;
}
GlobalUnlock ( stg.hGlobal );
ReleaseStgMedium ( &stg );
return hr;
}

要是我们返回 E_INVALIDARG, Explorer 将不会继续调用以后的扩展代码.
要是返回 S_OK, Explorer 将再一次调用QueryInterface() 获取另一个我们下面就要添加的接口指针: IContextMenu.

与上下文菜单交互的接口

一旦 Explorer 初始化了扩展,它就会接着调用 IContextMenu 的方法让我们添加菜单项, 提供状态栏上的提示, 并响应执行用户的选择.

添加IContextMenu 接口到Shell扩展类似于上面IshellExtInit接口的添加 .打开 SimpleShlExt.h,添加下列标红的代码:

class ATL_NO_VTABLE CSimpleShlExt :
public CComObjectRootEx,
public CComCoClass,
public IDispatchImpl,
public IShellExtInit,

public IContextMenu

{
BEGIN_COM_MAP(CSimpleShlExt)
COM_INTERFACE_ENTRY(ISimpleShlExt)
COM_INTERFACE_ENTRY(IDispatch)
COM_INTERFACE_ENTRY(IShellExtInit)

COM_INTERFACE_ENTRY(IContextMenu)

END_COM_MAP()

添加 IContextMenu 方法的函数原型:
public:
// IContextMenu
STDMETHOD(GetCommandString)(UINT, UINT, UINT*, LPSTR, UINT);
STDMETHOD(InvokeCommand)(LPCMINVOKECOMMANDINFO);
STDMETHOD(QueryContextMenu)(HMENU, UINT, UINT, UINT, UINT);

修改上下文菜单 IContextMenu 有三个方法.
第一个是 QueryContextMenu(), 它让我们可以修改上下文菜单. 其原型为:

HRESULT IContextMenu::QueryContextMenu ( HMENU hmenu, UINT uMenuIndex, UINT uidFirstCmd, UINT uidLastCmd, UINT uFlags );

hmenu 上下文菜单句柄.
uMenuIndex 是我们应该添加菜单项的起始位置.
uidFirstCmduidLastCmd 是我们可以使用的菜单命令ID值的范围.
uFlags 标识了Explorer 调用QueryContextMenu()的原因,
这我以后会说到的.

而返回值根据你所查阅的文档的不同而不同.
Dino Esposito 的书中说返回值是你所添加的菜单项的个数.
而 VC6.0所带的MSDN 又说它是我们添加的最后一个菜单项的命令ID加上 1.
而最新的 MSDN 又说:
将返回值设为你为各菜单项分配的命令ID的最大差值,加上1.
例如, 假设 idCmdFirst 设为5,而你添加了三个菜单项 ,命令ID分别为 5, 7, 和 8.
这时返回值就应该是: MAKE_HRESULT(SEVERITY_SUCCESS, 0, 8 - 5 + 1).

我是一直按 Dino 的解释来做的, 而且工作得很好.
实际上, 他的方法与最新的 MSDN 是一致的, 只要你严格地使用 uidFirstCmd作为第一个菜单项的ID,再对接续的菜单项ID每次加1.

我们暂时的扩展仅加入一个菜单项,所以 QueryContextMenu() 非常简单:

HRESULT CSimpleShlExt::QueryContextMenu ( HMENU hmenu,UINT uMenuIndex, 
UINT uidFirstCmd, UINT uidLastCmd, UINT uFlags ) { // 如果标志包含 CMF_DEFAULTONLY 我们不作任何事情. if ( uFlags & CMF_DEFAULTONLY ) { return MAKE_HRESULT ( SEVERITY_SUCCESS, FACILITY_NULL, 0 ); } InsertMenu ( hmenu, uMenuIndex, MF_BYPOSITION, uidFirstCmd, _T("SimpleShlExt Test Item") ); return MAKE_HRESULT ( SEVERITY_SUCCESS, FACILITY_NULL, 1 ); }

首先我们检查 uFlags.
你可以在 MSDN中找到所有标志的解释, 但对于上下文菜单扩展而言, 只有一个值是重要的: CMF_DEFAULTONLY.
该标志告诉Shell命名空间扩展保留默认的菜单项,这时我们的Shell扩展就不应该加入任何定制的菜单项,这也是为什么此时我们要返回 0 的原因.
如果该标志没有被设置, 我们就可以修改菜单了 (使用 hmenu 句柄), 并返回 1 告诉Shell我们添加了一个菜单项.

在状态栏上显示提示帮助

下一个要被调用的IContextMenu 方法是 GetCommandString(). 如果用户是在浏览器窗口中右击文本文件,或选中一个文本文件后单击文件菜单时,状态栏会显示提示帮助.
我们的 GetCommandString() 函数将返回一个帮助字符串供浏览器显示.

GetCommandString() 的原型是:

HRESULT IContextMenu::GetCommandString ( UINT idCmd, UINT uFlags, UINT *pwReserved, LPSTR pszName, UINT cchMax );

idCmd 是一个以0为基数的计数器,标识了哪个菜单项被选择.
因为我们只有一个菜单项, 所以idCmd 总是0. 但如果我们添加了3个菜单项, idCmd 可能是 0, 1, 或 2.
uFlags 是另一组标志(我以后会讨论到的).
PwReserved 可以被忽略.
pszName 指向一个由Shell拥有的缓冲区,我们将把帮助字符串拷贝进该缓冲区.
cchMax 是该缓冲区的大小.
返回值是S_OKE_FAIL.

GetCommandString() 也可以被调用以获取菜单项的动作( "verb") .
verb 是个语言无关性字符串,它标识一个可以加于文件对象的操作。
ShellExecute()的文档中有详细的解释, 而有关verb的内容足以再写一篇文章, 简单的解释是:verb 可以直接列在注册表中(如 "open" 和 "print"等字符串), 也可以由上下文菜单扩展创建. 这样就可以通过调用ShellExecute()执行实现在Shell扩展中的代码.

不管怎样, 我说了这多只是为了解释清楚GetCommandString() 的作用.
如果 Explorer 要求一个帮助字符串,我们就提供给它. 如果 Explorer 要求一个verb, 我们就忽略它. 这就是 uFlags 参数的作用.
如果 uFlags 设置了GCS_HELPTEXT 位, 则 Explorer 是在要求帮助字符串. 而且如果 GCS_UNICODE 被设置, 我们就必须返回一个Unicode字符串.

我们的 GetCommandString() 如下:

#include "atlconv.h"
// 为使用 ATL 字符串转换宏而包含的头文件 
             
HRESULT CSimpleShlExt::GetCommandString( UINT idCmd, UINT uFlags,
UINT* pwReserved, LPSTR pszName, UINT cchMax ) { USES_CONVERSION; //检查 idCmd, 它必须是0,因为我们仅有一个添加的菜单项. if ( 0 != idCmd ) return E_INVALIDARG; // 如果 Explorer 要求帮助字符串,就将它拷贝到提供的缓冲区中. if ( uFlags & GCS_HELPTEXT ) { LPCTSTR szText = _T("This is the simple shell extension's help"); if ( uFlags & GCS_UNICODE ) { // 我们需要将 pszName 转化为一个 Unicode 字符串, 接着使用Unicode字符串拷贝 API. lstrcpynW ( (LPWSTR) pszName, T2CW(szText), cchMax ); } else { // 使用 ANSI 字符串拷贝API 来返回帮助字符串. lstrcpynA ( pszName, T2CA(szText), cchMax ); } return S_OK; } return E_INVALIDARG; }

这里没有什么特别的代码; 我用了硬编码的字符串并把它转换为相应的字符集.
如果你从未使用过ATL字符串转化宏,你一定要学一下,因为当你传递Unicode字符串到COM和OLE函数时,使用转化宏会很有帮助的.
我在上面的代码中使用了T2CW 和 T2CA 将TCHAR 字符串分别转化为Unicode 和 ANSI字符串.
函数开头处的USES_CONVERSION 宏其实声明了一个将被转化宏使用的局部变量.

要注意的一个问题是: lstrcpyn() 保证了目标字符串将以null为结束符.
这与C运行时(CRT)函 数strncpy()不同. 当要拷贝的源字符串的长度大于或等于cchMax 时 strncpy()不会添加一个 null 结束符.
我建议总使用lstrcpyn(), 这样你就不必在每一个strncpy()后加入检查保证字符 串以 null为结束符的代码.

执行用户的选择

IContextMenu 接口的最后一个方法是 InvokeCommand(). 当用户点击我们添加的菜单项时该方法将被调用. 其函数原型是:

HRESULT IContextMenu::InvokeCommand ( LPCMINVOKECOMMANDINFO pCmdInfo );

CMINVOKECOMMANDINFO 结构带有大量的信息, 但我们只关心 lpVerbhwnd 这两个成员.
lpVerb参数有两个作用 – 它或是可被激发的verb(动作)名, 或是被点击的菜单项的索引值.
hwnd 是用户激活我们的菜单扩展时所在的浏览器窗口的句柄.

因为我们只有一个扩展的菜单项, 我们只要检查lpVerb 参数, 如果其值为0, 我们可以认定我们的菜单项被点击了.
我能想到的最简单的代码就是弹出一个信息框, 这里的代码也就做了这么多. 信息框显示所选的文件的文件名以证实代码正确地工作.

HRESULT CSimpleShlExt::InvokeCommand ( LPCMINVOKECOMMANDINFO pCmdInfo )
{
    // 如果lpVerb 实际指向一个字符串, 忽略此次调用并退出.
    if ( 0 != HIWORD( pCmdInfo->lpVerb ))
        return E_INVALIDARG;
    // 点击的命令索引 – 在这里,唯一合法的索引为0.
    switch ( LOWORD( pCmdInfo->lpVerb ))
        {
        case 0:
            {
            TCHAR szMsg [MAX_PATH + 32];
            wsprintf ( szMsg, _T("The selected file was:/n/n%s"), m_szFile );
            MessageBox ( pCmdInfo->hwnd, szMsg, _T("SimpleShlExt"),
                         MB_ICONINFORMATION );
            return S_OK;
            }
        break;
        default:
            return E_INVALIDARG;
        break;
        }
}

注册Shell扩展
现在我们已经实现了所有需要的COM接口. 可是我们怎样才能让浏览器使用我们的扩展呢?
ATL 自动生成注册COM DLL服务器的代码, 但这只是让其它程序可以使用我们的DLL.
为了告诉浏览器使用我们的扩展, 我们需要在文本文件类型的注册表键下注册扩展:
HKEY_CLASSES_ROOT/txtfile
在这个键下, 有个名为 ShellEx 的键保存了一个与文本文件关联的Shell扩展的列表. 在 ShellEx 键下, ContextMenuHandlers 键保存了上下文菜单扩展的列表.
每个扩展都在ContextMenuHandlers下创建了一个子键并将其默认值设为扩展COM的GUID.
所以, 对于我们这个简单的扩展, 我们将创建下键:
HKEY_CLASSES_ROOT/txtfile/ShellEx/ContextMenuHandlers/SimpleShlExt
并将其默认值设为我们的 GUID: "{5E2121EE-0300-11D4-8D3B-444553540000}".

你不必写任何代码就可以完成注册操作. 如果你看一下Fileview页的文件列表, 你会看到SimpleShlExt.rgs.
该文本文件将被ATL分析, 并指导ATL在该COM服务器注册时添加附加的注册键, 而注销时又该删除哪些键. 以下是所指定添加的注册表项:

HKCR
{
    NoRemove txtfile
    {
        NoRemove ShellEx
        {
            NoRemove ContextMenuHandlers
            {
                ForceRemove SimpleShlExt = s '{5E2121EE-0300-11D4-8D3B-444553540000}'
            }
        }
    }
}

每一行代表一个注册表键, "HKCR"HKEY_CLASSES_ROOT 的缩写.
NoRemove 关键字表示当该COM服务器注销时该键 不用被删除.
最后一行有些复杂. ForceRemove 关键字表示如果该键已存在, 那么在新键添加之前该键先应被删除.
这行脚本的余下部分指定一个字符串,它将被存为 SimpleShlExt 键的默认值.

在这我插几句话. 我们是在 HKCR/txtfile下注册的. 但是 "txtfile" 名并不是一个永久的或预定好的名称.
如果你看一下 HKCR/.txt, 该键的默认值正是txtfile. 这样就会有一些副作用:
1.我们不能可靠地使用 RGS 教本,因为 "txtfile" 可能不是正确的键名.
2. 一些文本编辑软件可能安装到系统并直接关联到 .TXT 文件. 如果它改变了HKCR/.txt 键的默认值, 所有现存的Shell扩展都将停止工作.

在我看来,这确是个设计上的错误. 我认为微软也是这么想的, 因为最新的扩展类型, 如QueryInfo扩展注册在 .txt 键下.

好了,到此为止. 最后还有一个注册细节. 在NT/2000上, 我们还得将我们的扩展放到 "approved" 扩展列表中.
如果我们不这样做, 我们的扩展不会被没有管理员权限的用户调用. 该列表保存在:
HKEY_LOCAL_MACHINE/Software/Microsoft/Windows/CurrentVersion/Shell Extensions/Approved
在该键下, 我们要创建一个以我们的GUID 为名的字符串键,键的内容任意. 与之有关的代码在DllRegisterServer() DllUnregisterServer() 函数中.
只是些简单的注册表获取, 我也就不在这写出了. 你可以在例子工程代码中找到它.

调试Shell 扩展
最终你会写一个不会这么简单的扩展, 那时你就不得不进行调试.
打开你的工程设置, 在 Debug 页” Executable for debug session”编辑框中输入浏览器程序的全路径, 如:"C:/windows/explorer.exe".
如果你使用的是 NT 或 2000, 并且你已经设置了上述的 DesktopProcess 注册键, 那么当你按F5进行调试时就会打开一个新的浏览器窗口.
只要你在这个窗口内完成你所有的工作,当你关闭该窗口时扩展同时会被卸出内存,这样就不会防碍我们重建 DLL了.

在Windows 9x上, 在重新调试之前你不得不关闭Shell. 你可以: 点击 “开始”, 再点击”关闭”. 按住 Ctrl+Alt+Shift 并点击”取消”.
这样就会关闭Shell, 你会看到桌面消失了.
接着,你可以切换到 MSVC 再按 F5进行调试. 要中止调试, 按 Shift+F5 关闭. 完成调试后, 你可以从”开始 运行” Explorer.exe以正常重起.

扩展的样子
下面就是我们添加自定义菜单项后的样子:

看,我们的菜单项在那!

下图是浏览器状态栏的帮助提示字符串的显示:


弹出信息框如下图, 显示了所选的文件名:

相关文章推荐

Windows Shell扩展系列文章 1 - .NET 4 编写Windows Shell上下文菜单扩展

示例代码下载在MSDN论坛,大量的开发人员经常问道这样一个问题:如何编写.NET代码开发Windows Shell扩展?在.NET 4问世之前,使用.NET编写Windows Shell扩展是不被支持...

VC 向windows系统菜单中添加菜单项---Windows shell扩展编程

vs2008的方法和vc6.0做法几乎一样。下面是转载一位新浪博客-丢丢的。   打开VC6,新建一个工程,选ATL COM APPWIZARD,工程名写BlogTest。然后OK。如果要用到MF...

C++高级开发之一 Shell扩展编程

名词解释  所谓的Shell扩展就是能够添加某种功能到Windows Shell的COM对象。 Windows里有着各种各样的扩展,但关于Shell扩展的原理以及如何编写Shell扩展的文档却很少...
  • xdrt81y
  • xdrt81y
  • 2012年04月20日 13:42
  • 4925

gnome-shell扩展(带任务栏)

推荐一个Fedora 非常好用的gnome-shell扩展,带任务栏。 官方网站: http://intgat.tigress.co.uk/rmy/extensions/index.html 该...
  • Zachaxy
  • Zachaxy
  • 2014年10月11日 11:00
  • 1565

Fedora 15 Gnome 3 配置与gnome-shell扩展介绍

01、 安装gnome3 tweak tool工具 fedora 15 采用gnome3之后,gnome-tweak-tool为配置桌面必不可少的工具。 终端中输入命令:$ sudo yum in...

Shell扩展是个进程内服务器(运行在Explorer进程内)

一个Shell扩展就是一个为Explorer添加功能的COM对象。 Shell扩展是个进程内服务器(运行在Explorer进程内),它实现了一些接口来处理与 Explorer 的通信。 ATL在...

自己动手写一个简单的Windows shell扩展程序

作者:朱金灿来源:http://blog.csdn.net/clever101                关于什么叫Windowsshell扩展程序,这里不作介绍,不懂的同学请google之。  ...

VC Windows shell扩展编程(为系统右键菜单添加二级菜单傻瓜教程)

打开VC6,新建一个工程,选ATL COM APPWIZARD,工程名写BlogTest。然后OK。如果要用到MFC,那把Support MFC打上勾,然后按完成。     新工程生成完毕后,在Cl...
  • wzsy
  • wzsy
  • 2012年08月07日 16:26
  • 3403

(转)用Shell扩展实现源代码统计程序

用Shell扩展实现源代码统计程序 分享到:新浪微博腾讯微博更多-- 收藏 发布日期: 2003-09-07 20:10 浏览次数: 514次 标  ...
  • gxj1680
  • gxj1680
  • 2012年08月09日 14:26
  • 869

再谈利用SharpShell实现Windows Shell扩展

之前写过一篇文章介绍过如何用SharpShell提供的接口去实现Windows PreviewHandler的扩展功能,但用的方法是先实现SharpShell中的各类接口编译DLL,再手动给注册表安装...
  • hey_zng
  • hey_zng
  • 2016年06月02日 12:38
  • 1284
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:一步步教你如何编写Shell扩展
举报原因:
原因补充:

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