当用户在资源管理器中调用右键菜单时会显示一个属性菜单项点击属性菜单项会显示一个属性页用户可以获得甚至修改文件信息我们可以定制属性页通过实现属性页扩展如下图所示本文实现了一个显示wave(波形)文件的信息如声道数等信息的属性页扩展 属性页扩展通常是同某类文件相关联的来实现同之相关的操作和信息显示另外可以同驱动器相关联我们还可以用属性页扩展来替换控制面板程序的属性页象其他外壳扩展程序一样属性页扩展也是以动态连接库形式实现的进程内COM对象它除了IUnknown接口外还要实现IShellExtInit和IShellPropSheetExt接口 一建立同文件关联的属性页扩展 首先我们用命令File|New创建一个ActiveX Library然后新建一个COM Object实现的接口为IShellExtInit和IShellPropSheetExt 同文件建立关联需要注册属性页要在注册表中同相应文件对应的表项下添加Shellex/PropertySheetHandlers子键每增加一个页面就需要注册一个表项最大可以添加的页面数是我们可以用一个扩展实现多个页面这里我们通过从TComObjectFactory继承类实现的UpdateRegistry实现了注册 type TCXPropSheetFactory=class(TComObjectFactory) public procedure UpdateRegistry(Register: Boolean); override; end; procedure TCXPropSheetFactoryUpdateRegistry(Register: Boolean);var ClassID: string; StrKeyName : string;begin inherited UpdateRegistry(Register); if Register then begin ClassID:=GUIDToString(Class_CXPropSheet); with TRegistryCreate do try RootKey:=HKEY_CLASSES_ROOT; OpenKey(\wavTRUE); KeyName := ReadString(); if Keyname = then begin WriteString(WaveFile); OpenKey(\wavTRUE); KeyName := ReadString(); end; OpenKey(\+KeyName+\shellex\Propert eetHandlers\WavTRUE); WriteString(Classid); finally Free; end; if(WinPlatform=VER_PLATFORM_WIN_NT)then begin with TRegistryCreate do try RootKey:=HKEY_LOCAL_MACHINE; OpenKey(SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions True); OpenKey(Approved True); WriteString(ClassID Wave File Property Sheet); finally Free; end; end; end else 删除注册表项 end;初始化扩展是通过IShellExtInit实现的当外壳调用IShellExtInitInitialize时它传递一个数据对象包含来文件对应的目录的PIDL标识符Initialize方法需要从数据对象中提取文件名并把文件名和PIDL标识符保存起来为了以后使用 function TCXPropSheetSEIInitialize(pidlFolder: PItemIDList; lpdobj: IDataObject; hKeyProgID: HKEY): HResult; var StgMedium: TStgMedium; FormatEtc: TFormatEtc; szFile: array[MAX_PATH+]of Char; filecount: integer;begin Result:=E_FAIL; if(lpdobj=nil)then begin Result:=E_INVALIDARG; messagebox( 错误 mb_ok); Exit; end; with FormatEtc do begin cfFormat:=CF_HDROP; ptd:=nil; dwAspect:=DVASPECT_CONTENT; lindex:=; tymed:=TYMED_HGLOBAL; end; Result:=lpdobjGetData(FormatEtc StgMedium); if Failed(Result)then Exit; //如果只有一个文件被选中获得文件名并保存 filecount:=DragQueryFile(stgmediumhGlobal $FFFFFFFF nil ); if filecount= then begin Result:=NOERROR; DragQueryFile(stgmediumhGlobal szFile SizeOf(szFile)); FFilename:=strpas(szFile); end; ReleaseStgMedium(StgMedium);end;添加页面的操作是通过IShellPropSheetExt接口来实现的如果属性页是和文件相关联外壳会调用IShellPropSheetExtAddPages给属性页添加一个页面如果属性页同控制面板程序相关联外壳调用IShellPropSheetExtReplacePage来替换页面 IShellPropSheetExtAddPages方法有两个参数lpfnAddPage是一个指向AddPropSheetPageProc回调函数的指针回调函数用来提供要添加的页面信息给外壳lParam是一个用户自定义的值这里我们用它来返回给回调函数对象 一般的IShellPropSheetExtAddPages方法实现步骤是 · 给PROPSHEETPAGE结构设定正确的值特别是把扩展的对象引用记数变量付值给pcRefParent成员这可以防止页面还在显示时扩展对象被卸载 · 实现PropSheetPageProc回调函数来处理页面创建和销毁的情况 · 调用CreatePropertySheetPage函数来创建页面 · 调用lpfnAddPage指向的函数来来添加创建好的页面 function TCXPropSheetAddPages(lpfnAddPage: TFNADDPROPSHEETPAGE; lParam: LPARAM): HResult;var PSP: TPropSheetPage; HPSP: HPropSheetPage;begin result:=E_FAIL; try pspdwSize:=SizeOf(psp); pspdwFlags:=PSP_USEREFPARENT or PSP_USETITLE or PSP_USECALLBACK; psphInstance:=hInstance; //这里我们使用了事先储存在waveres中的对话框模板模板是用delphi自带的 //resource workshop编辑的使用delphi\bin\brccexe编译的 psppszTemplate:=MakeIntResource(); //标题名 psppszTitle:=波文件信息; //设定回调函数 psppfnDlgProc:=@DialogProc; psppfnCallBack:=@PropCallback; //设定对象引用记数变量 psppcRefParent:=@comserverobjectcount; //用lParam向回调函数传递对象 psplParam:=integer(self); HPSP:=CreatePropertySheetPage(psp); if HPSP$#@;$#@;nil then begin if not lpfnAddPage(HPSP lParam)then begin DestroyPropertySheetPage(HPSP); end else begin _addref;//增加引用记数否则一脱离这个方法的作用域delphi自动释放对象 result:=S_OK; end end except on e: exception do begin emessage:=添加页面+emessage; messagebox( pchar(emessage) 错误 mb_ok); end; end;end; function TCXPropSheetReplacePage(uPageID: UINT; lpfnReplaceWith: TFNADDPROPSHEETPAGE; lParam: LPARAM): HResult;begin Result:=E_NOTIMPL;//同文件关联时外壳不调用ReplacePage所以不用实现end;回调函数处理属性页的消息主要要响应WM_INITDIALOG消息来初始化页面显示信息响应WM_COMMAND消息来处理用户交互响应WM_NOTIFY消息来处理页面切换或关闭后处理操作结果 function DialogProc(hwndDlg: HWnd; Msg: UINT; wParam: wParam; lParam: LPARAM): Bool; stdcall; var PageObj: TCXPropSheet; filename: string; displayName : string; SheetHWnd: HWnd; begin result:=false; try if Msg=WM_INITDIALOG then begin//初始化界面 //获得lparam传递过来的对象 pageObj:=TCXPropSheet(PPropSheetPage(lParam)^lParam); //保存对象信息 SetWindowLong(hwndDlg DWL_USER integer(pageObj)); //设置界面显示波文件信息 SetDlgItemText(hwndDlg PChar(ExtractFileName(PageObjFFileName))); OpenMedia(PageObjFFileName); SetDlgItemText(hwndDlg PChar(IntToStr(GetWavStatus(MCI_WAVE_STATUS_AVGBYTESPERSEC)))); SetDlgItemText(hwndDlg PChar(IntToStr(GetWavStatus(MCI_WAVE_STATUS_BITSPERSAMPLE)))); SetDlgItemText(hwndDlg PChar(IntToStr(GetWavStatus(MCI_WAVE_STATUS_CHANNELS)))); CloseMedia; SetWindowLong(hwndDlg DWL_MSGRESULT ); Result:=TRUE; end else if(Msg=WM_COMMAND)then begin if Lo(wParam)= then//用户点击了关于按钮(id=) MessageBox(作者:hubdog+##+email:hubdog@net关于MB_OK); end else if(msg=WM_NOTIFY)then begin sheetHwnd:=getparent(hwndDlg);//获得属性页的窗口句柄 case PNMHdr(lparam)^code of //页面失去焦点 PSN_KILLACTIVE: begin SetWindowLong(hwndDlg DWL_MSGRESULT ); Result:=TRUE; end; end; end; except on e: exception do begin emessage:=回调处理+emessage; messagebox( pchar(emessage) 错误 mb_ok); end; end; end; 二建立同驱动器相关联的属性页扩展 同上面讲的有两点不同 IShellExtInitInitialize方法传递过来的数据对象包含的驱动器路径可能是CFSTR_MOUNTEDVOLUME格式而不是CF_HDROP格式的标准驱动器是CF_HDROP格式的而在NTFS文件系统中映射的远程设备则是CFSTR_MOUNTEDVOLUME格式的 注册表项是HKEY_CLASSES_ROOT\Drive\Shellex\PropertySheetHandlers子键 三建立控制面板属性页扩展 同上面讲的有两点不同 控制面板程序调用IShellPropSheetExtReplacePage方法来替换页面它不调用IShellPropSheetExtAddPages方法 注册方式子键可以在不同位置创建这依赖于扩展是针对用户还是针对机器的对用户方式子键是HKEY_CURRENT_USER\REGSTR_PATH_CONTROLPANEL否则子键是HKEY_LOCAL_MACHINE\REGSTR_PATH_CONTROLSFOLDER |