使用COM技術(shù)完成外殼擴展的屬性頁
發(fā)表時間:2023-08-17 來源:明輝站整理相關(guān)軟件相關(guān)文章人氣:
[摘要]當用戶在資源管理器中調(diào)用右鍵菜單時,會顯示一個"屬性"菜單項,點擊屬性菜單項會顯示一個屬性頁,用戶可以獲得甚至修改文件信息。我們可以定制屬性頁通過實現(xiàn)屬性頁擴展。如下圖所示,本文...
當用戶在資源管理器中調(diào)用右鍵菜單時,會顯示一個"屬性"菜單項,點擊屬性菜單項會顯示一個屬性頁,用戶可以獲得甚至修改文件信息。我們可以定制屬性頁通過實現(xiàn)屬性頁擴展。如下圖所示,本文實現(xiàn)了一個顯示wave(波形)文件的信息如聲道數(shù)等信息的屬性頁擴展。
屬性頁擴展通常是同某類文件相關(guān)聯(lián)的來實現(xiàn)同之相關(guān)的操作和信息顯示,另外可以同驅(qū)動器相關(guān)聯(lián),我們還可以用屬性頁擴展來替換控制面板程序的屬性頁。象其他外殼擴展程序一樣,屬性頁擴展也是以動態(tài)連接庫形式實現(xiàn)的進程內(nèi)COM對象。它除了IUnknown接口外還要實現(xiàn)IShellExtInit和IShellPropSheetExt接口。
建立同文件關(guān)聯(lián)的屬性頁擴展
首先,我們用命令File New...,創(chuàng)建一個ActiveX Library,然后新建一個COM Object,實現(xiàn)的接口為IShellExtInit和IShellPropSheetExt。
同文件建立關(guān)聯(lián)需要注冊屬性頁,要在注冊表中同相應(yīng)文件對應(yīng)的表項下添加Shellex/PropertySheetHandlers子鍵,每增加一個頁面就需要注冊一個表項,最大可以添加的頁面數(shù)是24,我們可以用一個擴展實現(xiàn)多個頁面。這里我們通過從TComObjectFactory繼承類實現(xiàn)的UpdateRegistry實現(xiàn)了注冊。
type
TCXPropSheetFactory=class(TComObjectFactory)
public
procedure UpdateRegistry(Register: Boolean); override;
end;
procedure TCXPropSheetFactory.UpdateRegistry(Register: Boolean);var
ClassID: string;
Str,KeyName : string;begin
inherited UpdateRegistry(Register);
if Register then
begin
ClassID:=GUIDToString(Class_CXPropSheet);
with TRegistry.Create do
try
RootKey:=HKEY_CLASSES_ROOT;
OpenKey(’\.wav’,TRUE);
KeyName := ReadString(’’);
if Keyname = ’’ then
begin
WriteString(’’,’WaveFile’);
OpenKey(’\.wav’,TRUE);
KeyName := ReadString(’’);
end;
OpenKey(’\’+KeyName+’\shellex\Propert eetHandlers\Wav’,TRUE);
WriteString(’’,Classid);
finally
Free;
end;
if(Win32Platform=VER_PLATFORM_WIN32_NT)then
begin
with TRegistry.Create 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實現(xiàn)的,當外殼調(diào)用IShellExtInit.Initialize時,它傳遞一個數(shù)據(jù)對象包含來文件對應(yīng)的目錄的PIDL標識符。Initialize方法需要從數(shù)據(jù)對象中提取文件名,并把文件名和PIDL標識符保存起來為了以后使用。
function TCXPropSheet.SEIInitialize(pidlFolder: PItemIDList;
lpdobj: IDataObject; hKeyProgID: HKEY): HResult;
var
StgMedium: TStgMedium;
FormatEtc: TFormatEtc;
szFile: array[0..MAX_PATH+1]of Char;
filecount: integer;begin
Result:=E_FAIL;
if(lpdobj=nil)then
begin
Result:=E_INVALIDARG;
messagebox(0, ’1’, ’錯誤’, mb_ok);
Exit;
end;
with FormatEtc do
begin
cfFormat:=CF_HDROP;
ptd:=nil;
dwAspect:=DVASPECT_CONTENT;
lindex:=-1;
tymed:=TYMED_HGLOBAL;
end;
Result:=lpdobj.GetData(FormatEtc, StgMedium);
if Failed(Result)then
Exit;
//如果只有一個文件被選中,獲得文件名并保存。
filecount:=DragQueryFile(stgmedium.hGlobal, $FFFFFFFF, nil, 0);
if filecount=1 then
begin
Result:=NOERROR;
DragQueryFile(stgmedium.hGlobal, 0, szFile, SizeOf(szFile));
FFilename:=strpas(szFile);
end;
ReleaseStgMedium(StgMedium);end;添加頁面的操作是通過IShellPropSheetExt接口來實現(xiàn)的。如果屬性頁是和文件相關(guān)聯(lián),外殼會調(diào)用IShellPropSheetExt.AddPages給屬性頁添加一個頁面。如果屬性頁同控制面板程序相關(guān)聯(lián),外殼調(diào)用IShellPropSheetExt.ReplacePage來替換頁面。
IShellPropSheetExt.AddPages方法有兩個參數(shù),lpfnAddPage是一個指向AddPropSheetPageProc回調(diào)函數(shù)的指針,回調(diào)函數(shù)用來提供要添加的頁面信息給外殼。lParam是一個用戶自定義的值,這里我們用它來返回給回調(diào)函數(shù)對象。
一般的IShellPropSheetExt.AddPages方法實現(xiàn)步驟是:
給PROPSHEETPAGE結(jié)構(gòu)設(shè)定正確的值,特別是:
把擴展的對象引用記數(shù)變量付值給pcRefParent成員,這可以防止頁面還在顯示時,擴展對象被卸載。
實現(xiàn)PropSheetPageProc回調(diào)函數(shù)來處理頁面創(chuàng)建和銷毀的情況。
調(diào)用CreatePropertySheetPage函數(shù)來創(chuàng)建頁面。
調(diào)用lpfnAddPage指向的函數(shù)來來添加創(chuàng)建好的頁面。
function TCXPropSheet.AddPages(lpfnAddPage: TFNADDPROPSHEETPAGE;
lParam: LPARAM): HResult;var
PSP: TPropSheetPage;
HPSP: HPropSheetPage;begin
result:=E_FAIL;
try
psp.dwSize:=SizeOf(psp);
psp.dwFlags:=PSP_USEREFPARENT or PSP_USETITLE or PSP_USECALLBACK;
psp.hInstance:=hInstance;
//這里我們使用了事先儲存在wave.res中的對話框模板,模板是用delphi5自帶的
//resource workshop編輯的,使用delphi5\bin\brcc32.exe編譯的。
psp.pszTemplate:=MakeIntResource(100);
//標題名
psp.pszTitle:=’波文件信息’;
//設(shè)定回調(diào)函數(shù)
psp.pfnDlgProc:=@DialogProc;
psp.pfnCallBack:=@PropCallback;
//設(shè)定對象引用記數(shù)變量
psp.pcRefParent:=@comserver.objectcount;
//用lParam向回調(diào)函數(shù)傳遞對象
psp.lParam:=integer(self);
HPSP:=CreatePropertySheetPage(psp);
if HPSP$#@60;$#@62;nil then begin
if not lpfnAddPage(HPSP, lParam)then begin
DestroyPropertySheetPage(HPSP);
end else begin
_addref;//增加引用記數(shù),否則一脫離這個方法的作用域,delphi自動釋放對象。
result:=S_OK;
end
end
except
on e: exception do begin
e.message:=’添加頁面’+e.message;
messagebox(0, pchar(e.message), ’錯誤’, mb_ok);
end;
end;end;
function TCXPropSheet.ReplacePage(uPageID: UINT;
lpfnReplaceWith: TFNADDPROPSHEETPAGE; lParam: LPARAM): HResult;begin
Result:=E_NOTIMPL;//同文件關(guān)聯(lián)時,外殼不調(diào)用ReplacePage,所以不用實現(xiàn)end;回調(diào)函數(shù)處理屬性頁的消息,主要要響應(yīng)WM_INITDIALOG消息來初始化頁面顯示信息,響應(yīng)WM_COMMAND消息來處理用戶交互,響應(yīng)WM_NOTIFY消息來處理頁面切換或關(guān)閉后處理操作結(jié)果。
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));
//設(shè)置界面顯示波文件信息
SetDlgItemText(hwndDlg, 100, PChar(ExtractFileName(PageObj.FFileName)));
OpenMedia(PageObj.FFileName);
SetDlgItemText(hwndDlg, 101, PChar(IntToStr(GetWavStatus(MCI_WAVE_STATUS_AVGBYTESPERSEC))));
SetDlgItemText(hwndDlg, 102, PChar(IntToStr(GetWavStatus(MCI_WAVE_STATUS_BITSPERSAMPLE))));
SetDlgItemText(hwndDlg, 103, PChar(IntToStr(GetWavStatus(MCI_WAVE_STATUS_CHANNELS))));
CloseMedia;
SetWindowLong(hwndDlg, DWL_MSGRESULT, 0);
Result:=TRUE;
end
else if(Msg=WM_COMMAND)then begin
if Lo(wParam)=110 then//用戶點擊了關(guān)于按鈕(id=110)
MessageBox(0,’作者:hubdog’+#13#10+’email:hubdog@263.net’,’關(guān)于...’,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, 0);
Result:=TRUE;
end;
end;
end;
except
on e: exception do begin
e.message:=’回調(diào)處理’+e.message;
messagebox(0, pchar(e.message), ’錯誤’, mb_ok);
end;
end;
end;
建立同驅(qū)動器相關(guān)聯(lián)的屬性頁擴展用
同上面講的有兩點不同:
IShellExtInit.Initialize方法傳遞過來的數(shù)據(jù)對象包含的驅(qū)動器路徑可能是CFSTR_MOUNTEDVOLUME格式而不是CF_HDROP格式的。標準驅(qū)動器是CF_HDROP格式的,而在NTFS文件系統(tǒng)中映射的遠程設(shè)備則是CFSTR_MOUNTEDVOLUME格式的。
注冊表項是HKEY_CLASSES_ROOT\Drive\Shellex\PropertySheetHandlers子鍵。
建立控制面板屬性頁擴展
同上面講的有兩點不同:
控制面板程序調(diào)用IShellPropSheetExt.ReplacePage方法來替換頁面,它不調(diào)用IShellPropSheetExt。AddPages方法。
注冊方式:子鍵可以在不同位置創(chuàng)建,這依賴于擴展是針對用戶還是針對機器的。對用戶方式子鍵是HKEY_CURRENT_USER\REGSTR_PATH_CONTROLPANEL,否則子鍵是HKEY_LOCAL_MACHINE\REGSTR_PATH_CONTROLSFOLDER。
本程序在Delphi5,Win NT 4.0,K6-233系統(tǒng)下調(diào)試成功。例子程序可以到http://chaozhi.com/lgc去下載