Shell扩展编程完全指南(5)如何编写添加属性页到文件属性对话框中

Shell扩展编程完全指南

内容来自dedecms

linghuye

第五节-如何编写添加属性页到文件属性对话框中的Shell扩展 dedecms.com

在本节,我们将讨论属性页. 当你查看文件系统对象的属性时, 浏览器会显示标签视属性页. Shell让我们可以使用一种Shell扩展来扩展属性页的功能. 织梦内容管理系统

本文假设你理解Shell扩展的基础知识, 并熟悉STL的集合类. 如果你需要复习一下STL, 你可以阅读第二节, 因为本文将使用同样的技巧. 该代码使用shlwapi.dll 输出的一些函数, 所以你需要安装IE 4 或更高的版本(但你不必安装活动桌面).

本文来自织梦

属性页处理器

大家都熟悉浏览器的属性对话框. 更确切的讲, 它们是包含多个页面的属性页. 每一个页面都有标签Tab 列出文件路径, 修改时间, 及其它一些东西. 浏览器让我们可以添加自己的属性页面. 属性页处理器也可以添加或替换某些控制面板对象的属性页,但本文不会讨论这方面的内容.

dedecms.com

本文提供了一个可以让你修改文件的创建时间,访问时间,和修改时间的属性页. 我将用纯粹的SDK API来完成编码,不用MFC或ATL/WTL. 我没试过在扩展中使用MFC 或WTL的属性页; 这样做需要一定的技巧,因为Shell需要接收一个属性页的句柄(HPROPSHEETPAGE), 而MFC 将这些细节隐藏在CPropertyPage的实现中. (我不熟悉WTL实现的方式.) 织梦内容管理系统

如果你查看一个.URL 文件的属性框, 你会看到一个属性页扩展实例."CodeProject" 标签Tab 是本文要实现的. "Web Document" 标签Tab 显示由IE安装的一个扩展.

内容来自dedecms

 [Built-in prop sheet handler - 17K ]

织梦内容管理系统

使用AppWizard 开始

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

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

织梦内容管理系统

初始化接口

由于属性页处理器一次处理选中的多个文件, 它使用IShellExtInit作为初始化接口. 我们需要添加IShellExtInitCFileTimeShlExt实现的接口列表中. 具体细节见第四节. 该类还需要一个字符串列表来保存所选文件名. 织梦好,好织梦

typedef std::list<std::basic_string<TCHAR> > string_list; 
dedecms.com
 
织梦内容管理系统
protected: 织梦内容管理系统 
// IFileTimeShlExt 
织梦内容管理系统
string_list m_lsFiles; 内容来自dedecms 

Initialize()方法的行为同第二节 – 读取选中的文件名并将其加入列表. 下面是该函数的开头:

copyright dedecms

HRESULT CFileTimeShlExt::Initialize ( 
织梦好,好织梦
LPCITEMIDLIST pidlFolder, dedecms.com 
LPDATAOBJECTpDataObj, 

本文来自织梦

HKEYhProgID ) 

本文来自织梦

{ dedecms.com 
TCHARszFile [MAX_PATH]; 

内容来自dedecms

UINTuNumFiles; 
本文来自织梦
HDROPhdrop; 
copyright dedecms
FORMATETC etc = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; copyright dedecms 
STGMEDIUM stg; 

dedecms.com

INITCOMMONCONTROLSEX iccex = { sizeof(INITCOMMONCONTROLSEX), ICC_DATE_CLASSES }; 本文来自织梦 
 

织梦好,好织梦

// 初始化通用控件使用. dedecms.com 
InitCommonControlsEx ( &iccex ); copyright dedecms 

由于我们要使用Date/Time picker (DTP)控件,所以我们需要初始化通用控件. 接着我们使用IDataObject接口指针获取HDROP 句柄来列举所选文件.

本文来自织梦

// 重数据对象中读取项目.它们以 HDROP格式存放, 因此只需取得 HDROP 句柄并对它使用拖放 APIs. 
内容来自dedecms
if ( FAILED( pDataObj->GetData ( &etc, &stg ))) 

dedecms.com

return E_INVALIDARG; 织梦好,好织梦 
 copyright dedecms 
// 取得 HDROP 句柄. dedecms.com 
hdrop = (HDROP) GlobalLock ( stg.hGlobal ); 

内容来自dedecms

 

织梦好,好织梦

if ( NULL == hdrop ) 内容来自dedecms 
{ 本文来自织梦 
ReleaseStgMedium ( &stg ); dedecms.com 
return E_INVALIDARG; 织梦内容管理系统 
} 本文来自织梦 
 

dedecms.com

// 判断操作涉及几个文件. 

dedecms.com

uNumFiles = DragQueryFile ( hdrop, 0xFFFFFFFF, NULL, 0 ); 本文来自织梦 

循环列举所选择的文件. 该扩展将只对文件进行操作,对文件夹不起作用, 所以忽略调文件夹. 织梦内容管理系统

for ( UINT uFile = 0; uFile < uNumFiles; uFile++ ) dedecms.com 
{ 

内容来自dedecms

// 取得下一文件名. 织梦好,好织梦 
if ( 0 == DragQueryFile ( hdrop, uFile, szFile, MAX_PATH )) 织梦内容管理系统 
continue; 
dedecms.com
 
内容来自dedecms
// 跳过文件夹.其实我们可以处理文件夹,因此它们也有创建时间,但我选择对之不作处理. 织梦内容管理系统 
if ( PathIsDirectory ( szFile )) 

copyright dedecms

continue; 

织梦好,好织梦

 
copyright dedecms
// 添加文件名到我们的文件名列表. 织梦内容管理系统 
m_lsFiles.push_back ( szFile ); copyright dedecms 
}// end for dedecms.com 
 

内容来自dedecms

// 释放资源. 

本文来自织梦

GlobalUnlock ( stg.hGlobal ); 
织梦内容管理系统
ReleaseStgMedium ( &stg ); 本文来自织梦 

这里有些新东西! 一个属性表的页面个数是有限制的, 在头文件prsht.h中定义为常数MAXPROPPAGES. 在该扩展中,每一个文件都有其页面, 所以如果我们的列表有多于MAXPROPPAGES个文件, 多出部分将被截去. (即使MAXPROPPAGES为100, 属性表也不能显示这么多Tab. 它最多能有34个左右.) dedecms.com

// 检查有几个文件被选中. 如果大于属性表能有的最大属性页个数, 删减列表. 

dedecms.com

if ( m_lsFiles.size() > MAXPROPPAGES ) 

copyright dedecms

{ dedecms.com 
m_lsFiles.resize ( MAXPROPPAGES ); 

dedecms.com

} 
本文来自织梦
 

织梦内容管理系统

// 如果我们发现可以操作的任一文件返回 S_OK. 否则返回 E_FAIL. copyright dedecms 
return ( m_lsFiles.size() > 0 ) ? S_OK : E_FAIL; 

本文来自织梦

} 
copyright dedecms

添加属性页

如果Initialize()返回S_OK, 浏览器查询另一个新接口:IShellPropSheetExt. IShellPropSheetExt很简单,只有一个方法需要实现. 要添加IShellPropSheetExt接口到我们的类, 打开文件FileTimeShlExt.h 并添加如下标红的代码:

dedecms.com

class ATL_NO_VTABLE CFileTimeShlExt : 织梦内容管理系统 
public CComObjectRootEx<CComSingleThreadModel>, 

内容来自dedecms

public CComCoClass<CFileTimeShlExt, &CLSID_FileTimeShlExt>, 内容来自dedecms 
public IDispatchImpl<IFileTimeShlExt, &IID_IFileTimeShlExt, &LIBID_FILETIMELib>, 

织梦内容管理系统

public IShellExtInit, copyright dedecms 
public IShellPropSheetExt 

copyright dedecms

{ 
本文来自织梦
BEGIN_COM_MAP(CFileTimeShlExt) 织梦内容管理系统 
COM_INTERFACE_ENTRY(IFileTimeShlExt) copyright dedecms 
COM_INTERFACE_ENTRY(IDispatch) 
织梦内容管理系统
COM_INTERFACE_ENTRY(IShellExtInit) 

copyright dedecms

COM_INTERFACE_ENTRY(IShellPropSheetExt) dedecms.com 
END_COM_MAP() 

织梦内容管理系统

 

内容来自dedecms

public: 本文来自织梦 
// IShellPropSheetExt 

内容来自dedecms

STDMETHOD(AddPages)(LPFNADDPROPSHEETPAGE, LPARAM); 织梦好,好织梦 
STDMETHOD(ReplacePage)(UINT, LPFNADDPROPSHEETPAGE, LPARAM) { return E_NOTIMPL; } 

内容来自dedecms

AddPages()是我们要实现的方法. ReplacePage()只用于控制面板的属性页扩展的页面替换, 所以在这我们不需要实现它. 浏览器调用AddPages()函数来让我们添加页面到浏览器设置的属性表中. 织梦内容管理系统

AddPages()的参数是一个函数指针和一个LPARAM变量, 这两个都只被Shell使用. lpfnAddPageProc指向一个Shell进程内的函数以添加页面. lParam参数是一个重要的由Shell使用的值. 我们不深究它, 我们仅将其直接传回lpfnAddPageProc函数即可. copyright dedecms

HRESULT CFileTimeShlExt::AddPages ( copyright dedecms 
LPFNADDPROPSHEETPAGE lpfnAddPageProc, 
织梦好,好织梦
LPARAM lParam ) 
copyright dedecms
{ 
dedecms.com
PROPSHEETPAGEpsp; 

织梦内容管理系统

HPROPSHEETPAGE hPage; 

内容来自dedecms

TCHARszPageTitle [MAX_PATH]; 本文来自织梦 
string_list::const_iterator it, itEnd; 

织梦好,好织梦

 

dedecms.com

for ( it = m_lsFiles.begin(), itEnd = m_lsFiles.end(); 织梦内容管理系统 
it != itEnd; 织梦好,好织梦 
it++ ) 
本文来自织梦
{ 内容来自dedecms 
// 'it' 指向下一个文件名.分配一个给页面使用的字符串拷贝. 织梦内容管理系统 
LPCTSTR szFile = _tcsdup ( it->c_str() ); 
内容来自dedecms

我们首先复制一份文件名变量. 内容来自dedecms

下一步要创建我们的Tab页要使用的字符串. 如不带扩展名的文件名. 另外如果其大于24个字符将被删减. 这是绝对的; 我选择24 是因为我喜欢这个数字. 这要有一些限制以防止名字超出Tab的范围. 内容来自dedecms

// 从文件名中截去路径和扩展名 – 用其作为页面标题.该名称截取为 24 个字符以适合Tab的大小. 织梦好,好织梦 
lstrcpy ( szPageTitle, it->c_str() ); 织梦好,好织梦 
PathStripPath ( szPageTitle ); 本文来自织梦 
PathRemoveExtension ( szPageTitle ); 

copyright dedecms

szPageTitle[24] = '\0'; copyright dedecms 

由于我们使用直接的SDK 调用完成属性页, 我们不得不亲自处理PROPSHEETPAGE结构. 以下是对该结构的设置: 本文来自织梦

psp.dwSize= sizeof(PROPSHEETPAGE); 

copyright dedecms

psp.dwFlags= PSP_USEREFPARENT | PSP_USETITLE | PSP_DEFAULT | 
织梦好,好织梦
PSP_USEICONID | PSP_USECALLBACK; 

dedecms.com

psp.hInstance= _Module.GetModuleInstance(); 本文来自织梦 
psp.pszTemplate = MAKEINTRESOURCE(IDD_FILETIME_PROPPAGE); 
copyright dedecms
psp.pszIcon= MAKEINTRESOURCE(IDI_ICON); 本文来自织梦 
psp.pszTitle= szPageTitle; 织梦内容管理系统 
psp.pfnDlgProc= PropPageDlgProc; 本文来自织梦 
psp.lParam= (LPARAM) szFile; 本文来自织梦 
psp.pfnCallback = PropPageCallbackProc; 
copyright dedecms
psp.pcRefParent = (UINT*) &_Module.m_nLockCnt; copyright dedecms 

在这我们要注意一些重要的细节以让扩展正确地工作: 织梦好,好织梦

  1. pszIcon成员变量应该设为一个16x16 图标的资源ID, 该图标将被显示在Tab标签上. 要不要图标是可选的, 但我添加了一个使得我们的页面更突出.
  2. pfnDlgProc成员变量应该设为我们页面所在的对话框的窗口函数地址.
  3. lParam成员变量设为szFile, 是该页面所关联的文件名.
  4. pfnCallback成员变量设为一个回调函数的地址,该函数将在页面被创建或销毁时调用. 稍后将详细解释该函数.
  5. pcRefParent成员变量设为一个从CcomModule 继承下来的成员变量的地址. 其为DLL的锁定值. 当属性也显示时Shell 会增加该引用计数, 以使得当页面打开时我们的DLL始终在内存中. 该计数在页面销毁时将递减.

设置了该结构后, 我们调用API 来创建属性页. 内容来自dedecms

hPage = CreatePropertySheetPage ( &psp ); 
本文来自织梦

如果成功, 我们调用Shell的回调函数添加新创建的属性页到属性对话框中. 该回调函数返回BOOL值以表明成功与否. 如果失败,我们将销毁创建的页面资源. dedecms.com

if ( NULL != hPage ) 内容来自dedecms 
{ 
copyright dedecms
// 调用Shell的回调函数添加新创建的属性页到属性对话框中 

dedecms.com

if ( !lpfnAddPageProc ( hPage, lParam )) 
本文来自织梦
{ 织梦内容管理系统 
DestroyPropertySheetPage ( hPage ); 

内容来自dedecms

} 

织梦内容管理系统

} 
本文来自织梦
}// end for 
内容来自dedecms
 dedecms.com 
return S_OK; 
dedecms.com
} 
内容来自dedecms

一个对象存活期的棘手问题

该到了我实现解释关于字符串复制问题的承诺的时候了. 在这复制是必须的,因为AddPages()返回之后, Shell释放它的IShellPropSheetExt接口, 这会导致销毁CFileTimeShlExt对象. 这样属性页的窗口回调函数就不能获取CfileTimeShlExt的成员变量m_lsFiles了. copyright dedecms

我的解决方法是复制每一个文件名, 并将之传给页面. 页面将拥有该内存, 它有责任释放它. 如果有多个选择文件, 每一个页面都有一份对应文件名的拷贝. 该内存在PropPageCallbackProc函数中释放. 在AddPages()下行代码 copyright dedecms

psp.lParam = (LPARAM) szFile; dedecms.com 

是很重要的. 它保存指针于PROPSHEETPAGE结构中, 使得可以为页面窗口函数所用. copyright dedecms

属性页回调函数

现在, 谈论属性页本身. 以下是新页面的样子. 在你读完本文前请记住下图.

dedecms.com

 [Our new property page - 23K] 织梦内容管理系统

注意这里没有文件最后获取时间的显示. FAT 只支持最后文件获取日期. 其它文件系统支持该时间, 但我没有实现判断文件系统的逻辑. 如果获取最后访问时间失败,时间总保存为午夜12 点. 本文来自织梦

该页面有两个回调函数和两个消息处理器. 函数原型见FileTimeShlExt.cpp 开头部分:

dedecms.com

BOOL CALLBACK PropPageDlgProc ( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ); 
织梦好,好织梦
UINT CALLBACK PropPageCallbackProc ( HWND hwnd, UINT uMsg, LPPROPSHEETPAGE ppsp ); 织梦好,好织梦 
BOOL OnInitDialog ( HWND hwnd, LPARAM lParam ); 织梦好,好织梦 
BOOL OnApply ( HWND hwnd, PSHNOTIFY* phdr ); dedecms.com 

该对话框相当简单. 它处理三个消息: WM_INITDIALOG, PSN_APPLY DTN_DATETIMECHANGE. 以下是WM_INITDIALOG部分:

内容来自dedecms

BOOL CALLBACK PropPageDlgProc ( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) 
织梦好,好织梦
{ 
织梦内容管理系统
BOOL bRet = FALSE; dedecms.com 
 

织梦内容管理系统

switch ( uMsg ) dedecms.com 
{ 

copyright dedecms

case WM_INITDIALOG: copyright dedecms 
bRet = OnInitDialog ( hwnd, lParam ); 

织梦内容管理系统

break; 织梦内容管理系统 

OnInitDialog()将在稍后解释. 接着是当用户按下确定或应用按钮时发送的通知PSN_APPLY,. copyright dedecms

case WM_NOTIFY: 织梦好,好织梦 
{ dedecms.com 
NMHDR* phdr = (NMHDR*) lParam; 

dedecms.com

 织梦内容管理系统 
switch ( phdr->code ) 内容来自dedecms 
{ 

内容来自dedecms

case PSN_APPLY: 内容来自dedecms 
bRet = OnApply ( hwnd, (PSHNOTIFY*) phdr ); dedecms.com 
break; 本文来自织梦 

最后是DTN_DATETIMECHANGE. 这个简单– 我们发送一个消息给属性表使能“应用” 按钮. 织梦内容管理系统

case DTN_DATETIMECHANGE: 织梦好,好织梦 
// 如果用户改变任一DTP 控件的值就使能 应用 按钮. 织梦内容管理系统 
SendMessage ( GetParent(hwnd), PSM_CHANGED, (WPARAM) hwnd, 0 ); 

本文来自织梦

break; 

内容来自dedecms

} 本文来自织梦 
} 织梦内容管理系统 
break; 
织梦内容管理系统
} 

织梦好,好织梦

 
本文来自织梦
return bRet; 内容来自dedecms 
} 织梦好,好织梦 

到目前为止, 一切OK. 另一个回调函数在页面创建或销毁时被调用. 我们只关心后者, 因为在销毁时我们可以释放AddPages()所分配的字符串. ppsp参数指向用于创建页面的PROPSHEETPAGE结构, 并且lParam成员变量仍指向所拷贝的字符串.

织梦好,好织梦

UINT CALLBACK PropPageCallbackProc ( HWND hwnd, UINT uMsg, LPPROPSHEETPAGE ppsp ) 本文来自织梦 
{ 

dedecms.com

if ( PSPCB_RELEASE == uMsg ) dedecms.com 
{ 
copyright dedecms
free ( (void*) ppsp->lParam ); 

内容来自dedecms

} 
copyright dedecms
 

dedecms.com

return 1; 织梦好,好织梦 
} 

内容来自dedecms

该函数总是返回1,因为该函数在页面创建时被调用, 它可以返回0阻止该页面的创建. 返回1 则让该页面正常创建. 当页面销毁调用该函数时返回值将被忽略. 内容来自dedecms

属性页消息处理器

OnInitDialog() 有很多重要的代码. lParam参数再一次指向PROPSHEETPAGE结构用以创建页面. 该结构的成员lParam指向先前提供的文件名. 因为我们需要在OnApply()函数里获取文件名, 我们使用SetWindowLong()来保存该字符串指针. 织梦好,好织梦

BOOL OnInitDialog ( HWND hwnd, LPARAM lParam ) 
copyright dedecms
{ dedecms.com 
PROPSHEETPAGE*ppsp = (PROPSHEETPAGE*) lParam; dedecms.com 
LPCTSTRszFile = (LPCTSTR) ppsp->lParam; 内容来自dedecms 
HANDLEhFind; 
本文来自织梦
WIN32_FIND_DATA rFind; 织梦好,好织梦 
 dedecms.com 
// 在窗口用户数据区内保存文件名,已被后用. dedecms.com 
SetWindowLong ( hwnd, GWL_USERDATA, (LONG) szFile ); 
内容来自dedecms

接着, 我们使用FindFirstFile()获取文件创建,修改及访问时间. 若成功, 使用正确的数据初始化DTP 控件.

copyright dedecms

hFind = FindFirstFile ( szFile, &rFind ); 

本文来自织梦

 织梦内容管理系统 
if ( INVALID_HANDLE_VALUE != hFind ) 织梦内容管理系统 
{ 织梦内容管理系统 
// 初始化 DTP 控件. 织梦好,好织梦 
SetCombinedDateTime ( hwnd, IDC_MODIFIED_DATE, IDC_MODIFIED_TIME, 

copyright dedecms

&rFind.ftLastWriteTime ); 

织梦内容管理系统

 dedecms.com 
SetCombinedDateTime ( hwnd, IDC_ACCESSED_DATE, 0, 织梦内容管理系统 
&rFind.ftLastAccessTime ); 本文来自织梦 
 
dedecms.com
SetCombinedDateTime ( hwnd, IDC_CREATED_DATE, IDC_CREATED_TIME, 
dedecms.com
&rFind.ftCreationTime ); 织梦好,好织梦 
 copyright dedecms 
FindClose ( hFind ); dedecms.com 
} 织梦好,好织梦 

SetCombinedDateTime()是设置DTP控件内容的功能函数. 你可以在FileTimeShlExt.cpp文件的尾部找到这些代码.

织梦好,好织梦

另外, 文件的全路径名显示在页面的顶部Static控件里 织梦内容管理系统

PathSetDlgItemPath ( hwnd, IDC_FILENAME, szFile ); 

本文来自织梦

 

本文来自织梦

return FALSE;// Take the default focus handling. 
内容来自dedecms
} 织梦内容管理系统 

OnApply()函数处理相反的操作– 它读取DTP 控件的内容并修改文件的创建,修改及访问时间. 首先使用GetWindowLong()获取文件名,再打开文件进行写入.

织梦内容管理系统

BOOL OnApply ( HWND hwnd, PSHNOTIFY* phdr ) 

dedecms.com

{ 本文来自织梦 
LPCTSTRszFile = (LPCTSTR) GetWindowLong ( hwnd, GWL_USERDATA ); 

织梦内容管理系统

HANDLEhFile; 

本文来自织梦

FILETIME ftModified, ftAccessed, ftCreated; 
copyright dedecms
 内容来自dedecms 
// 打开文件. 
本文来自织梦
 dedecms.com 
hFile = CreateFile ( szFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, copyright dedecms 
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); 
内容来自dedecms

如果打开文件成功, 我们就读取DTP 控件的内容将时间回写到文件里. GetCombinedDateTime()SetCombinedDateTime()函数的反操作.

内容来自dedecms

if ( INVALID_HANDLE_VALUE != hFile ) 内容来自dedecms 
{ 织梦内容管理系统 
// 从DTP 控件中获取日期时间数据. dedecms.com 
GetCombinedDateTime ( hwnd, IDC_MODIFIED_DATE, IDC_MODIFIED_TIME, 内容来自dedecms 
&ftModified ); 织梦好,好织梦 
 dedecms.com 
GetCombinedDateTime ( hwnd, IDC_ACCESSED_DATE, 0, 
织梦好,好织梦
&ftAccessed ); 织梦好,好织梦 
 
织梦好,好织梦
GetCombinedDateTime ( hwnd, IDC_CREATED_DATE, IDC_CREATED_TIME, 
本文来自织梦
&ftCreated ); 
copyright dedecms
 
copyright dedecms
//修改文件的创建,修改及访问时间 织梦内容管理系统 
SetFileTime ( hFile, &ftCreated, &ftAccessed, &ftModified ); 
本文来自织梦
CloseHandle ( hFile ); 
织梦好,好织梦
} 内容来自dedecms 
else 
dedecms.com
{ 本文来自织梦 
// <<忽略错误处理>> 织梦内容管理系统 
} dedecms.com 
 dedecms.com 
// 返回 PSNRET_NOERROR 让页表正常关闭. copyright dedecms 
SetWindowLong ( hwnd, DWL_MSGRESULT, PSNRET_NOERROR ); 
copyright dedecms
return TRUE; 

dedecms.com

} 织梦内容管理系统 

注册Shell扩展

注册一个拖放目标扩展处理器类似于注册上下文菜单扩展. 该处理器可以为特定的文件类型所激发, 例如文本文件. 该扩展适用于任一文件, 所以我们可以在HKEY_CLASSES_ROOT\*键下注册.以下是扩展注册的RGS 脚本:

dedecms.com

HKCR 
copyright dedecms
{ 

dedecms.com

NoRemove * 织梦内容管理系统 
{ 

copyright dedecms

NoRemove shellex 织梦好,好织梦 
{ 本文来自织梦 
NoRemove PropertySheetHandlers 织梦好,好织梦 
{ 

copyright dedecms

{3FCEF010-09A4-11D4-8D3B-D12F9D3D8B02} 本文来自织梦 
} 织梦内容管理系统 
} 织梦好,好织梦 
} 本文来自织梦 
} dedecms.com 

你可能注意到扩展的GUID 作为注册键名存储在这, 而不是一个字符串值. 我所看过的文档和书籍在命名习惯上有冲突,但在我测试时两者都能工作. 我决定按Dino Esposito的书中的方法(Visual C++ Windows Shell Programming), 将GUID 作为注册键的名称.

织梦内容管理系统

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

织梦好,好织梦

待续...

在接下来的第六节 中, 我们将学习一种新的扩展类型: 放置处理器(drop handler), 它在Shell对象被放置到一个文件上时被激发. 织梦内容管理系统