Shell扩展编程完全指南(4)如何编写提供定制拖放功能的Shell扩展

Shell扩展编程完全指南 本文来自织梦

linghuye
第四节如何编写提供定制拖放功能的Shell扩展

织梦内容管理系统

下载示例工程 - 15 Kb (ChardLinkShlExt扩展) 织梦好,好织梦

在第一节 和第二节 中, 我示范了如何编写上下文菜单扩展. 在这一节中, 我将说明另一扩展类型, 拖放目标处理器,添加右键拖放文件时弹出的菜单项. 我也将演示更多使用MFC 的例子.

dedecms.com

第四节假设你理解Shell扩展的基础知识(参见第一节), 并熟悉MFC. 该扩展是一个在Windows 2000上创建硬链接(hard link)的工具, 但即使你没有运行Windows 2000也仍可以继续. 这些代码使用了一些版本4.71的shlwapi.dll的函数, 因此你需要IE 4 或更高(但你不必需要安装活动桌面)的版本.

织梦好,好织梦

拖放目标处理器

正如每个高级用户所知,你可以在浏览器中用右键拖放文件. 当你松开鼠标键时, 浏览器会弹出一个上下文菜单列出所有可能的操作. 通常有移动,复制,和创建快捷方式: 织梦内容管理系统

 [Drag and drop menu - 7K] 内容来自dedecms

浏览器让我们使用拖放目标扩展处理器添加菜单项到该菜单中. 这个类型的扩展当任何拖放操作发生时被激发, 并且当必要时可以添加菜单项. 一个拖放目标扩展处理器的例子是WinZip. 以下是WinZip 所添加的拖放菜单项: copyright dedecms

 [WinZip menu - 13K] 本文来自织梦

WinZip的扩展对任何一个拖放操作都激活, 但它只在压缩文件被拖放时才添加菜单项.

织梦好,好织梦

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

这个Shell扩展将使用新的Windows 2000 API CreateHardLink()来创建到NTFS卷上的文件的硬链接. 我们将添加一个定制菜单项来建立链接, 所以用户可以用常规的创建快捷方式的方法来创建硬链接. 内容来自dedecms

使用AppWizard 开始

运行AppWizard 并生成一个名为HardLinkATL工程. 由于这次我们要使用MFC,所以要选中Support MFC设置, 点击”完成”. 然后,在ClassView树中右击HardLink classes项,在弹出的菜单中选择New ATL Object,添加一个COM 对象类到DLL中. dedecms.com

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

dedecms.com

初始化接口

如我们前面的上下文菜单扩展一样, 浏览器通过IShellExtInit接口让我们进行初始化.我们需要添加IShellExtInit接口到CHardLinkShlExt实现的接口列表中. 打开HardLinkShlExt.h ,并添加如下标红的代码: copyright dedecms

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

内容来自dedecms

 本文来自织梦 
class ATL_NO_VTABLE CTxtInfoShlExt :  dedecms.com 
public CComObjectRootEx<CComSingleThreadModel>, 

织梦内容管理系统

public CComCoClass<CTxtInfoShlExt, &CLSID_TxtInfoShlExt>, 内容来自dedecms 
public IDispatchImpl<ITxtInfoShlExt, &IID_ITxtInfoShlExt, &LIBID_TXTINFOLib>, 织梦内容管理系统 
public IShellExtInit copyright dedecms 
{ 

dedecms.com

BEGIN_COM_MAP(CTxtInfoShlExt) 
内容来自dedecms
COM_INTERFACE_ENTRY(ITxtInfoShlExt) 织梦内容管理系统 
COM_INTERFACE_ENTRY(IDispatch) 织梦内容管理系统 
COM_INTERFACE_ENTRY(IShellExtInit) 
copyright dedecms
END_COM_MAP() 

织梦内容管理系统

 
内容来自dedecms
public: 内容来自dedecms 
// IShellExtInit 

copyright dedecms

STDMETHOD(Initialize)(LPCITEMIDLIST, LPDATAOBJECT, HKEY); 
dedecms.com

我们需要一些变量来保存位图及被拖放的的文件名:

织梦内容管理系统

protected: 
内容来自dedecms
// IHardLinkShlExt 
dedecms.com
CBitmapm_bitmap; 
织梦内容管理系统
TCHARm_szFolderDroppedIn [MAX_PATH]; 

内容来自dedecms

CStringList m_lsDroppedFiles; 

内容来自dedecms

我们也需要在stdafx.h中添加一些#define 以方便获取CreateHardLink()及shlwapi.dll 输出的函数原型: 织梦好,好织梦

#define WINVER 0x0500 本文来自织梦 
#define _WIN32_WINNT 0x0500 
copyright dedecms
#define _WIN32_IE 0x0400 copyright dedecms 

定义WINVER为0x0500 使得可以使用Win 98 和2000的一些特性, 而定义_WIN32_WINNT为0x0500 使得可以使用Win 2000独有的特性. 定义_WIN32_IE为0x0400 使得可以利用IE 4的特性功能. 织梦好,好织梦

现在, 讨论Initialize()方法. 这一次, 我将示范怎样使用MFC 获取被拖放的文件列表. MFC 有个类, COleDataObject, 其包装了IDataObject接口. 先前, 我们不得不直接调用IDataObject方法. 担幸运的是, MFC 使得这些工作简单了很多. 以下是Initialize()的原型:

织梦好,好织梦

HRESULT IShellExtInit::Initialize ( 

dedecms.com

LPCITEMIDLIST pidlFolder, 本文来自织梦 
LPDATAOBJECTpDataObj, 
dedecms.com
HKEYhProgID ); copyright dedecms 

对于拖放目标扩展处理扩展, pidlFolder是被拖放的项目所在的文件夹. (稍后我将深入讨论PIDL.) pDataObj是个IDataObject接口指针,使用其我们可以列举被拖放的所有项目. hProgID是个打开的我们的Shell扩展的注册键.

织梦内容管理系统

第一步我们为菜单加载位图. 接着, 我们将传进来的IDataObject接口指针赋值给ColeDataObject变量.

本文来自织梦

HRESULT CHardLinkShlExt::Initialize ( 本文来自织梦 
LPCITEMIDLIST pidlFolder, copyright dedecms 
LPDATAOBJECTpDataObj, 织梦好,好织梦 
HKEYhProgID ) 
本文来自织梦
{ dedecms.com 
AFX_MANAGE_STATE(AfxGetStaticModuleState());// init MFC 
dedecms.com
 
dedecms.com
COleDataObject dataobj; 本文来自织梦 
HGLOBALhglobal; dedecms.com 
HDROPhdrop; 
织梦内容管理系统
TCHARszRoot [MAX_PATH]; dedecms.com 
TCHARszFileSystemName [256]; dedecms.com 
TCHARszFile [MAX_PATH]; 织梦内容管理系统 
UINTuFile, uNumFiles; 织梦内容管理系统 
 
织梦内容管理系统
m_bitmap.LoadBitmap ( IDB_LINKBITMAP ); 
织梦好,好织梦
 

dedecms.com

dataobj.Attach ( pDataObj, FALSE ); dedecms.com 

传递FALSE 作为第二个参数给Attach()意思是当dataobj变量析构时不要释放IDataObject接口. 下一步要取得被拖放项目所在的文件夹. 我们有该文件夹的PIDL , 但我们如何能取得路径名? 这要另外花点时间讲... 织梦内容管理系统

"PIDL" 是pointer to an ID list 的缩写. 一个PIDL 唯一地标识在Shell等级结构空间中的任一对象. 每个在Shell空间中的对象, 不论它是不是真实文件系统的一部分, 都有个PIDL. PIDL的准确结构依赖于对象所处的位置, 但除非你正写一个你自己的名字空间扩展, 你不用去考虑PIDL的内部结构.

织梦好,好织梦

我们可以使用Shell API 来从PIDL 中解析出路径名. SHGetPathFromIDList()函数实现此功能. 如果目标目录不是真实文件系统中的目录(例如, 控制面板目录), SHGetPathFromIDList() 将会返回失败. 织梦内容管理系统

if ( !SHGetPathFromIDList ( pidlFolder, m_szFolderDroppedIn )) 

本文来自织梦

{ 

织梦内容管理系统

return E_FAIL; 
内容来自dedecms
} dedecms.com 

接着,我们检查目标文件夹是否在NTFS 卷上. 我们获取路径名的根目录部分(如, E:\), 并获取该卷的信息. 若其不在NTFS上, 我们不能生成硬链接.简单返回.

copyright dedecms

lstrcpy ( szRoot, m_szFolderDroppedIn ); 

织梦内容管理系统

PathStripToRoot ( szRoot ); 织梦好,好织梦 
 copyright dedecms 
if ( !GetVolumeInformation ( szRoot, NULL, 0, NULL, NULL, NULL,  本文来自织梦 
szFileSystemName, 256 )) 

内容来自dedecms

{ 
dedecms.com
// 不能决定文件系统类型. 

本文来自织梦

return E_FAIL; 
本文来自织梦
} 织梦好,好织梦 
 

织梦好,好织梦

if ( 0 != lstrcmpi ( szFileSystemName, _T("ntfs") )) 织梦内容管理系统 
{ 
dedecms.com
// 文件系统不是NTFS, 所以不支持硬链接. dedecms.com 
return E_FAIL; 

本文来自织梦

} dedecms.com 

接着, 我们从数据对象中获取HDROP 句柄, 我们将使用它来列举被拖放的文件名. 这类似于第三节中使用的方法, 不同的是我们使用MFC类来获取数据. COleDataObject为我们处理了FORMATETCSTGMEDIUM结构的设置工作. 本文来自织梦

hglobal = dataobj.GetGlobalData ( CF_HDROP ); 
本文来自织梦
 织梦好,好织梦 
if ( NULL == hglobal ) 

织梦内容管理系统

return E_INVALIDARG; 
内容来自dedecms
 

copyright dedecms

hdrop = (HDROP) GlobalLock ( hglobal ); 

本文来自织梦

 copyright dedecms 
if ( NULL == hdrop ) 
内容来自dedecms
return E_INVALIDARG; copyright dedecms 

我们使用HDROP 句柄来列举被拖放的文件. 对每一个对象, 我们检查是否为一个文件夹. 由于文件夹不能被硬链接,所以要是发现有文件夹就退出不作处理. 内容来自dedecms

uNumFiles = DragQueryFile ( hdrop, 0xFFFFFFFF, NULL, 0 ); copyright dedecms 
 copyright dedecms 
for ( uFile = 0; uFile < uNumFiles; uFile++ ) dedecms.com 
{ 织梦内容管理系统 
if ( DragQueryFile ( hdrop, uFile, szFile, MAX_PATH )) 内容来自dedecms 
{ 织梦好,好织梦 
if ( PathIsDirectory ( szFile )) 

内容来自dedecms

{ 
织梦内容管理系统
// 发现一个文件夹,退出. 
本文来自织梦
m_lsDroppedFiles.RemoveAll(); 内容来自dedecms 
break; dedecms.com 
} 内容来自dedecms 

我们还得检查目标文件是否也在同一个卷上. 我所做的是比较每个文件的根目录与目标文件夹, 如果不同就退出. 这不是个完善的解决方案,因为在Win 2000 上,你可以把一个卷映射到另一个卷中. 例如, 你有个C: 卷,然后映射另一个卷为C:\dev. 这里的代码不会拒绝建立一个从C:\dev 到C上某个地方的链接.

本文来自织梦

下面是根目录的检查代码:

织梦内容管理系统

if ( !PathIsSameRoot ( szFile, m_szFolderDroppedIn )) 织梦内容管理系统 
{ 本文来自织梦 
// 被放置的文件来自于另外一个卷 – 退出. 
本文来自织梦
m_lsDroppedFiles.RemoveAll(); 
织梦内容管理系统
break; 

内容来自dedecms

} 
本文来自织梦

如果传进的文件都通过了检查,我们就将其加入m_lsDroppedFiles, 它是一个CStringList变量. 织梦内容管理系统

// 添加文件名道列表. copyright dedecms 
m_lsDroppedFiles.AddTail ( szFile ); dedecms.com 
} 本文来自织梦 
}// end for 织梦好,好织梦 

循环后, 我们释放资源并返回. 如果文件名列表不为空, 我们返回S_OK以表明我们需要更变上下文菜单. 否则,我们返回E_FAIL这样在文件拖放时我们的代码就不会被调用. 织梦内容管理系统

GlobalUnlock ( hglobal ); 织梦内容管理系统 
 
织梦好,好织梦
return ( m_lsDroppedFiles.GetCount() > 0 ) ? S_OK : E_FAIL; 

本文来自织梦

} 

dedecms.com

修改上下文菜单

正如其它上下文菜单扩展, 拖放目标处理器实现IContextMenu接口与菜单进行交互. 要添加IContextMenu到我们的扩展, 打开HardLinkShlExt.h 添加如下标红的代码:

织梦好,好织梦

class ATL_NO_VTABLE CHardLinkShlExt :  内容来自dedecms 
public CComObjectRootEx<CComSingleThreadModel>, 

dedecms.com

public CComCoClass<CHardLinkShlExt, &CLSID_HardLinkShlExt>, 织梦内容管理系统 
public IDispatchImpl<IHardLinkShlExt, &IID_IHardLinkShlExt, &LIBID_HARDLINKLib>, 

copyright dedecms

public IShellExtInit, 织梦内容管理系统 
public IContextMenu 织梦好,好织梦 
{ 织梦好,好织梦 
BEGIN_COM_MAP(CHardLinkShlExt) 本文来自织梦 
COM_INTERFACE_ENTRY(IHardLinkShlExt) 

内容来自dedecms

COM_INTERFACE_ENTRY(IDispatch) copyright dedecms 
COM_INTERFACE_ENTRY(IShellExtInit) 

本文来自织梦

COM_INTERFACE_ENTRY(IContextMenu) 织梦好,好织梦 
END_COM_MAP() 内容来自dedecms 
 本文来自织梦 
public: dedecms.com 
// IContextMenu copyright dedecms 
STDMETHOD(GetCommandString)(UINT, UINT, UINT*, LPSTR, UINT); 织梦好,好织梦 
STDMETHOD(InvokeCommand)(LPCMINVOKECOMMANDINFO); 

本文来自织梦

STDMETHOD(QueryContextMenu)(HMENU, UINT, UINT, UINT, UINT); 

织梦好,好织梦

浏览器将调用QueryContextMenu() 函数来让我们修改上下文菜单. 这里没什么新鲜玩意; 我们添加一个菜单项并设好它的图标. 织梦内容管理系统

HRESULT CHardLinkShlExt::QueryContextMenu ( 
dedecms.com
HMENU hmenu, copyright dedecms 
UINTuMenuIndex, dedecms.com 
UINTuidFirstCmd, 内容来自dedecms 
UINTuidLastCmd, 
copyright dedecms
UINTuFlags ) 

本文来自织梦

{ copyright dedecms 
AFX_MANAGE_STATE(AfxGetStaticModuleState());// init MFC 织梦内容管理系统 
 

本文来自织梦

// 如果标志包含CMF_DEFAULTONLY 我们不作任何事情. 本文来自织梦 
if ( uFlags & CMF_DEFAULTONLY ) 

本文来自织梦

{ 
copyright dedecms
return MAKE_HRESULT ( SEVERITY_SUCCESS, FACILITY_NULL, 0 ); 织梦内容管理系统 
} copyright dedecms 
 

dedecms.com

// 添加硬链接菜单项. 织梦好,好织梦 
InsertMenu ( hmenu, uMenuIndex, MF_STRING | MF_BYPOSITION, uidFirstCmd, 

copyright dedecms

_T("Create hard link(s) here") ); 本文来自织梦 
 
copyright dedecms
if ( NULL != m_bitmap.GetSafeHandle() ) 

copyright dedecms

{ 本文来自织梦 
SetMenuItemBitmaps ( hmenu, uMenuIndex, MF_BYPOSITION, 织梦内容管理系统 
(HBITMAP) m_bitmap.GetSafeHandle(), NULL ); dedecms.com 
} 织梦好,好织梦 
 

内容来自dedecms

// 返回 1 通知Shell我们添加了一个菜单项. dedecms.com 
return MAKE_HRESULT ( SEVERITY_SUCCESS, FACILITY_NULL, 1 ); 织梦好,好织梦 
} 织梦好,好织梦 

下面是新加菜单项的样子: 本文来自织梦

 [Hard link menu item - 12K]

本文来自织梦

创建链接

如果用户点击我们的菜单项, 浏览器就调用我们的InvokeCo... 什么? 我漏掉一个函数功能? 哦, 抱歉. copyright dedecms

提供提示帮助

HRESULT CHardLinkShlExt::GetCommandString (  dedecms.com 
UINTidCmd, 内容来自dedecms 
UINTuFlags, 内容来自dedecms 
UINT* pwReserved, 内容来自dedecms 
LPSTR pszName, 
dedecms.com
UINTcchMax ) 

dedecms.com

{ 

copyright dedecms

return E_NOTIMPL; 
织梦好,好织梦
} 
织梦内容管理系统

我是认真的. :)对于拖放目标扩展处理器, 浏览器不会调用GetCommandString(). 现在, 回到原处... 内容来自dedecms

创建链接

如我上面所说, 当用户点击我们的菜单项浏览器调用InvokeCommand(). 我们将为所有被拖放的文件创建链接. 链接文件的名称将是"Hard link to <文件名>", 或, 如果该文件名已被使用, "Hard link (2) to <文件名>". 这个序数将可以上升到99的上限. dedecms.com

首先, 检查lpVerb参数,它一定是0 ,因为我们只有一个添加的菜单项. 内容来自dedecms

HRESULT CHardLinkShlExt::InvokeCommand ( LPCMINVOKECOMMANDINFO pInfo ) 

织梦内容管理系统

{ 织梦内容管理系统 
AFX_MANAGE_STATE(AfxGetStaticModuleState());// init MFC 
织梦好,好织梦
 内容来自dedecms 
TCHARszNewFilename [MAX_PATH+32]; 
织梦好,好织梦
CStringsSrcFile; 
copyright dedecms
TCHARszSrcFileTitle [MAX_PATH]; 

本文来自织梦

CStringsMessage; 织梦好,好织梦 
UINTuLinkNum; 

内容来自dedecms

POSITION pos; copyright dedecms 
 织梦内容管理系统 
// 检查示是不是我们的菜单项被单击了 – lpVerb必须为0 

内容来自dedecms

if ( 0 != pInfo->lpVerb ) copyright dedecms 
{ copyright dedecms 
return E_INVALIDARG; 
织梦内容管理系统
} 本文来自织梦 

接着, 我们获取指向字符串列表头的POSITION值. 一个POSITION是一个你不直接使用的抽象类型,而只是将其传递给CstringList类的其它成员函数. 它与STL iterator类不同, iterator有直接获取数据的操作符. 要取得列表头的POSITION, 我们调用GetHeadPosition():

织梦好,好织梦

pos = m_lsDroppedFiles.GetHeadPosition(); dedecms.com 
ASSERT ( NULL != pos ); 内容来自dedecms 

如果列表为空pos将是NULL, 但列表决不能为空, 所以,我加了ASSERT 来检查. 接下来循环列表中的每个文件名并为之创建链接. dedecms.com

while ( NULL != pos ) dedecms.com 
{ copyright dedecms 
// 取得下一个文件名. 织梦好,好织梦 
sSrcFile = m_lsDroppedFiles.GetNext ( pos ); 织梦内容管理系统 
 织梦内容管理系统 
// 移除该路径 – 这缩减 "C:\xyz\foo\stuff.exe" 为 "stuff.exe" copyright dedecms 
lstrcpy ( szSrcFileTitle, sSrcFile ); 内容来自dedecms 
PathStripPath ( szSrcFileTitle ); dedecms.com 
 
dedecms.com
// 生成硬链接的文件名– 我们先试一下"Hard link to stuff.exe" 
dedecms.com
wsprintf ( szNewFilename, _T("%sHard link to %s"), m_szFolderDroppedIn, 织梦好,好织梦 
szSrcFileTitle ); 

织梦内容管理系统

GetNext()返回下一个由pos指出的CString, 并递增pos以指向下一个字符串. 如果pos已在末尾, pos将变成NULL (这样循环就会结束). 织梦内容管理系统

所以在这时, szNewFilename存放着硬链接的全路径名. 我们检查一下该文件名是否已存在, 如果是, 我们试着将序数从2加到99, 以找到没被使用的文件名. 我们也要确认文件名长度不得超过(包含NULL结束符) MAX_PATH个字符. 本文来自织梦

for ( uLinkNum = 2; dedecms.com 
PathFileExists ( szNewFilename )&&uLinkNum < 100;  本文来自织梦 
uLinkNum++ ) 
本文来自织梦
{ 
copyright dedecms
// 试用另外的文件名. 

copyright dedecms

wsprintf ( szNewFilename, _T("%sHard link (%u) to %s"), copyright dedecms 
m_szFolderDroppedIn, uLinkNum, szSrcFileTitle ); 本文来自织梦 
 内容来自dedecms 
// 如果得出的文件名超过 MAX_PATH 个字符, 显示一个错误框. 

织梦好,好织梦

if ( lstrlen ( szNewFilename ) >= MAX_PATH ) 内容来自dedecms 
{ 本文来自织梦 
sMessage.Format ( _T("Failed to make a link to %s. The resulting filename would be too long.\n\nDo you want to continue making links?"), dedecms.com 
(LPCTSTR) sSrcFile ); dedecms.com 
 

copyright dedecms

if ( IDNO == MessageBox ( pInfo->hwnd, sMessage, _T("Create Hard Links"), copyright dedecms 
MB_ICONQUESTION | MB_YESNO )) 
本文来自织梦
break; 

织梦好,好织梦

else 织梦好,好织梦 
continue; 内容来自dedecms 
} dedecms.com 
} dedecms.com 

弹出的消息框可以让你取消整个操作. 接着, 我们检查我们是否到了99 个链接的上限. 同样,我们让用户来取消操作. 织梦内容管理系统

if ( 100 == uLinkNum ) copyright dedecms 
{ 织梦内容管理系统 
sMessage.Format ( _T("Failed to make a link to %s. Reached limit of 99 links in a single directory.\n\nDo you want to continue making links?"), 内容来自dedecms 
(LPCTSTR) sSrcFile ); 本文来自织梦 
 织梦好,好织梦 
if ( IDNO == MessageBox ( pInfo->hwnd, sMessage, _T("Create Hard Links"), 织梦内容管理系统 
MB_ICONQUESTION | MB_YESNO )) 

织梦好,好织梦

break; 织梦内容管理系统 
else copyright dedecms 
continue; 

dedecms.com

} 
dedecms.com

剩下的只是创建硬链接. 我略掉了错误检查的代码. 内容来自dedecms

CreateHardLink ( szNewFilename, sSrcFile, NULL ); 内容来自dedecms 
}// end while loop dedecms.com 
 内容来自dedecms 
return S_OK; 织梦内容管理系统 
} 
copyright dedecms

硬链接看上去没什么不同, 它就像普通的文件一样. 但是如果你修改一个拷贝, 更改会反映在其它拷贝中(好像嵌入对象特性).

本文来自织梦

 [Hard link to the file - 5K] 织梦好,好织梦

最后, 总结一下CStringList类的使用: 本文来自织梦

  1. 声明一个POSITION变量, 如pos.
  2. 调用CStringList::GetHeadPosition()取得列表头位置.
  3. 使用条件(pos != NULL)开始循环.
  4. 调用CStringList::GetNext(pos)并将返回值赋给一Cstring 变量.
  5. 在循环内利用文件名做你想要的操作. (很难想象理解了COM的人不会用Clist,作者真是有点…)

注册Shell扩展

注册拖放扩展处理器比其它扩展都要简单. 所有的处理器注册在HKCR\Directory键下. 然而, 文档未谈及的是在HKCR\Directory下注册不能满足所有情况. 你需要在HKCR\Folder下注册以处理桌面上的拖放操作, 以及在HKCR\Drive下注册处理跟目录下的拖放.

本文来自织梦

以下是处理上述三种情况的的RGS 脚本: 本文来自织梦

HKCR 内容来自dedecms 
{ 
copyright dedecms
NoRemove Directory 
织梦好,好织梦
{ 

织梦内容管理系统

NoRemove shellex dedecms.com 
{ copyright dedecms 
NoRemove DragDropHandlers 织梦内容管理系统 
{ 本文来自织梦 
ForceRemove HardLinkShlExt = s '{3C06DFAE-062F-11D4-8D3B-919D46C1CE19}' 

本文来自织梦

} 
内容来自dedecms
} 
dedecms.com
} 
内容来自dedecms
NoRemove Folder 织梦内容管理系统 
{ 
织梦好,好织梦
NoRemove shellex dedecms.com 
{ 

织梦好,好织梦

NoRemove DragDropHandlers 
dedecms.com
{ 织梦内容管理系统 
ForceRemove HardLinkShlExt = s '{3C06DFAE-062F-11D4-8D3B-919D46C1CE19}' 

copyright dedecms

} 本文来自织梦 
} 
dedecms.com
} 内容来自dedecms 
NoRemove Drive 织梦好,好织梦 
{ 

本文来自织梦

NoRemove shellex copyright dedecms 
{ 

织梦好,好织梦

NoRemove DragDropHandlers 本文来自织梦 
{ 

dedecms.com

ForceRemove HardLinkShlExt = s '{3C06DFAE-062F-11D4-8D3B-919D46C1CE19}' 
copyright dedecms
} copyright dedecms 
} 
本文来自织梦
} dedecms.com 
} 织梦好,好织梦 

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

copyright dedecms

如果你没有Windows 2000

你仍然可以在早先的Windows版本上创建例子工程. 然后打开stdafx.h 文件, 删除下行代码:

内容来自dedecms

//#define NOT_ON_WIN2K 织梦好,好织梦 

这会使扩展跳过文件系统检查(因此它会在任何系统上运行,而不只是NTFS), 并显示消息框而非作实际的链接.

dedecms.com

待续...

在接下来的第五节 中, 我们将学习一种新的扩展类型: 属性页处理器, 其添加额外的页面到属性对话框 织梦内容管理系统