利用Windows外殼擴展保護文件夾
在Win32操作系統(包括Win9X、Windows NT、Windows 2000)不但有方便的圖形用戶(GUI)界面,微軟還為windows用戶界面保留了強大的可擴充性。其中對於Windows界面的操作環境(這裡稱為外殼Shell),微軟提供了一種稱為外殼擴展(Shell Extensions)的功能來實現文件系統操作的可編程性。如果你的機器中安裝了Word 7.0以上的版本,當你鼠標右鍵單擊一個DOC文件,在彈出菜單中選“屬性”項,在屬性頁中不僅顯示顯示文件的大小、建立日期等信息,同時還增加了Doc文檔的摘要、統計等信息;又例如安裝了winZip 6.0以上版本後,當選中一個或多個文件或文件夾後在單擊鼠標右鍵,在彈出的右鍵菜單中就增加了“Add To Zip”等一個zip文件壓縮選項。上面的這些功能都是通過Windows外殼擴展來實現的。
Windows外殼擴展是這樣實現的。首先要編寫外殼擴展程序,一個外殼擴展程序是基於COM(Component Object Model)組件模型的。外殼是通過接口(Interface)來訪問對象的。外殼擴展被設計成32位的進程中服務器程序,並且都是以動態鏈接庫的形式為操作系統提供服務的。
寫好外殼擴展程序後,必須將它們注冊才能生效。所有的外殼擴展都必須在Windows注冊表的HKEY_CLASSES_ROOTCLSID鍵之下進行注冊。在該鍵下面可以找到許多名字像{ACDE002F-0000-0000-C000-000000000046}的鍵,這類鍵就是全局唯一類標識符。每一個外殼擴展都必須有一個全局唯一類標識符,Windows正是通過此唯一類標識符來找到外殼擴展處理程序的。在類標識符之下的InProcServer32子鍵下記錄著外殼擴展動態鏈接庫在系統中的位置。
Windows系統支持以下7類的外殼擴展功能:
(1)Context menu handlers向特定類型的文件對象增添上下文相關菜單;
(2)Drag-and-drop handlers用來支持當用戶對某種類型的文件對象進行拖放操作時的OLE數據傳輸;
(3)Icon handlers用來向某個文件對象提供一個特有的圖標,也可以給某一類文件對象指定圖標;
(4)Property sheet handlers給文件對象增添屬性頁,屬性頁可以為同一類文件對象所共有,也可以給一個文件對象指定特有的屬性頁;
(5)Copy-hook handlers在文件夾對象或者打印機對象被拷貝、移動、刪除和重命名時,就會被系統調用,通過為Windows增加Copy-hook handlers,可以允許或者禁止其中的某些操作;
(6)Drop target handlers在一個對象被拖放到另一個對象上時,就會被系統被調用;
(7)Data object handlers在文件被拖放、拷貝或者粘貼時,就會被系統被調用。
本文介紹的文件夾保護功能就是通過上面的第5類,既Copy-hook handlers來實現的。一個支持Copy-hook handlers的程序除了上面提到的要在注冊表的HKEY_CLASSES_ROOTCLSID下注冊之外,還需要在HKEY_CLASSES_ROOTDirectoryshellexCopyHookHandlers下注冊服務器程序的類。
由於Windows外殼服務器程序是基於COM組件模型的,所以編寫外殼程序就是構造一個COM對象的過程,由於Delphi4.0以上的版本支持Windows外殼擴展和COM組件模型,所以可以利用Delphi來編寫外殼擴展程序。
利用Delphi編寫Copy-hook handle需要實現ICopyHook接口。ICopyHook是一個十分簡單的接口,要實現的只有CopyCallBack方法。ICopyHook的CopyCallBack方法的定義如下:
UINT CopyCallback(
HWND hwnd, file://Handle of the parent window for displaying UI objects
UINT wFunc, file://Operation to perform.
UINT wFlags, file://Flags that control the operation
LPCSTR pszSrcFile, file://Pointer to the source file
DWORD dwSrcAttribs, file://Source file attributes
LPCSTR pszDestFile, file://Pointer to the destination file
DWORD dwDestAttribs file://Destination file attributes
);
其中的參數hwnd是一個窗口句柄,Copy-hook handle以此為父窗口。參數wFunc指定要被執行的操作,其取值為下表中所列之一:
常量 取值 含義
FO_COPY $2 復制由pszSrcFile指定的文件到由pszDestFile指定的位置。
FO_DELETE $3 刪除由pszSrcFile指定的文件。
FO_MOVE $1 移動由pszSrcFile指定的文件到由pszDestFile指定的位置。
FO_RENAME $4 重命名由pszSrcFile指定的文件到由pszDestFile指定的文件名。
PO_DELETE $13 刪除pszSrcFile指定的打印機。
PO_PORTCHANGE $20 改變打印機端口。PszSrcFile和pszDestFile為兩個以Null結尾的字符串,分別指定當前和新的打印機端口名。
PO_RENAME $14 重命名由pszSrcFile指定的打印機端口。
PO_REN_PORT $34 PO_RENAME和PO_PORTCHANGE的組合。
參數wFlags指定操作的標志;參數pszSrcFile和pszDestFile指定源文件夾和目標文件夾。參數dwSrcAttribs和dwDesAttribs指定源文件夾和目標文件夾的屬性。函數返回值可以為IDYES、IDNO和IDCANCEL。分別指示Windows外殼允許操作、阻止操作,但是其他操作繼續、阻止當前操作,取消為執行的操作。
下面是具體的程序實現:
首先在Delphi的菜單中選 File|New選項,選擇其中的DLL圖標,按Ok鍵建立一個DLL工程文件,在其中添加以下代碼:
library CopyHook;
uses
ComServ,
CopyMain in CopyMain.pas;
exports
DllGetClassObject,
DllCanUnloadNow,
DllRegisterServer,
DllUnregisterServer;
{$R *.TLB}
{$R *.RES}
begin
end.
將文件保存為 CopyHook.dpr。再在Delphi菜單中選File|New選項,選擇其中的Unit圖標,按Ok鍵建立一個Pas文件,在其中加入以下代碼:
unit CopyMain;
interface
uses Windows, ComObj, ShlObj;
type
TCopyHook = class(TComObject, ICopyHook)
protected
function CopyCallback(Wnd: HWND; wFunc, wFlags: UINT; pszSrcFile: PAnsiChar;
dwSrcAttribs: DWORD; pszDestFile: PAnsiChar; dwDestAttribs: DWORD): UINT; stdcall;
end;
TCopyHookFactory = class(TComObjectFactory)
protected
function GetProgID: string; override;
procedure ApproveShellExtension(Register: Boolean; const ClsID: string);
virtual;
public
procedure UpdateRegistry(Register: Boolean); override;
end;
implementation
uses ComServ, SysUtils, Registry;
{ TCopyHook }
file://當Windows外殼程序執行文件夾或者打印機端口操作時,CopyCallBack
file://方法就會被調用。
function TCopyHook.CopyCallback(Wnd: HWND; wFunc, wFlags: UINT;
pszSrcFile: PAnsiChar; dwSrcAttribs: DWORD; pszDestFile: PAnsiChar;
dwDestAttribs: DWORD): UINT;
const
FO_COPY = 2;
FO_DELETE = 3;
FO_MOVE = 1;
FO_RENAME = 4;
var
sOp:string;
begin
Case wFunc of
FO_COPY: sOp:=format(你確定要將 %s 拷貝到 %s 嗎?,[pszSrcFile,pszDestFile]);
FO_DELETE: sOp:=format(你確定要將 %s 刪除嗎?,[pszSrcFile]);
FO_MOVE: sOp:=format(你確定要將 %s 轉移到 %s 嗎?,[pszSrcFile,pszDestFile]);
FO_RENAME: sOp:=format(你確定要將 %s 重命名為 %s 嗎?,[pszSrcFile,pszDestFile]);
else
sOp:=format(無法識別的操作代碼 %d,[wFlags]);
end;
// 提示,讓用戶決定是否執行操作
Result := MessageBox(Wnd, PChar(sOp),
文件掛鉤演示, MB_YESNOCANCEL);
end;
{ TCopyHookFactory }
function TCopyHookFactory.GetProgID: string;
begin
Result := ;
end;
procedure TCopyHookFactory.UpdateRegistry(Register: Boolean);
var
ClsID: string;
begin
ClsID := GUIDToString(ClassID);
inherited UpdateRegistry(Register);
ApproveShellExtension(Register, ClsID);
if Register then
file://將clsid 加入到注冊表的CopyHookHandlers中
CreateRegKey(directoryshellexCopyHookHandlers + ClassName, ,
ClsID)
else
DeleteRegKey(directoryshellexCopyHookHandlers + ClassName);
end;
procedure TCopyHookFactory.ApproveShellExtension(Register: Boolean;
const ClsID: string);
const
SAp