Shell可以實現擴充,Shell擴充程式可以實現以下功能。
◇為特定檔案類型、所有檔案類型、近端分享、資源回收筒、磁碟機、網際網路共用檔案夾、印表機、光碟片磁碟機、目錄、檔案夾背景(視窗的空白處)等Shell對象的右鍵菜單(稱作上下檔案菜單,Context Menu)增加功能表項目。
◇當選中上下檔案菜單的功能表項目時進行相關處理。
◇定製將檔案、檔案夾拖拽至其他目錄中時的操作。
◇定製右鍵拖曳Shell對象表徵圖的菜單及操作。
◇根據檔案內容定製表徵圖。
◇定製屬性對話方塊及分頁項。
◇定製提示對話方塊(當滑鼠指標停留在表徵圖時彈出的提示資訊)。
◇當使用“詳細資料”顯示表徵圖時定製列。
◇加速或禁止目錄、特定檔案的移動、複製、刪除、重新命名等操作。
◇在開始菜單或Explorer中增加定製的搜尋引擎。
從這些功能還可以擴充出很多特殊的應用,比如:
◇監視檔案或目錄的移動。
◇監視資源回收筒動作,如還原項目、清空等。
一、基本概念
Shell擴充處理常式的代碼實現位於DLL檔案中,是一個符合COM介面的對象。在完成DLL的設計後需要在註冊表進行註冊,Shell擴充才能正常工作。
1、Handler(處理常式)
處理常式(Handler)是一系列函數及介面,當Shell事件發生時,會調用處理常式進行處理。
比如當在表徵圖上點擊右鍵時,會調用上下檔案菜單處理常式(Context Menu Handler),選中操作功能表中定製的功能表項目後,也會調用相關的函數介面用於處理菜單選擇事件。
Shell擴充程式DLL應包括如下匯出函數。
◇DllMain:標準的DLL入口函數。
◇DllGetClassObject:這個匯出函數的作用是給出類廠(class factory)介面。
◇DllCanUnloadNow:表示該DLL是否仍然在使用,系統是否可以卸載此DLL。和其他所有COM程式一樣,Shell擴充程式必須實現一個IUnlnown介面和一個類廠。大多數情況下還需要實現一個IPersistFile或者IShellExtInit介面。
2、ProgID及特殊ProgID
每一種檔案類型都對應一個ProgID,ProgID代表了一種檔案類型。一些特殊的ProgID如表所示。
ProgID的shellex子鍵指明了ProgjD對應的處理常式。這些子鍵包括ColumnHandlers(使用“詳細資料”方式查看檔案清單,顯示列時調用)、ContextMenuHandlers(右鍵菜單時調用)、CopyHookHandlers(移動、複製、刪除、重新命名等操作時調用)、DragDropHandlers(右鍵拖曳菜單時調用)、PropertySheetHandlers(查看Shell對象屬性時調用)、DataHandler、DropHandler、IconHandler(顯示表徵圖時調用,用於個人化表徵圖)等。
3、CLSID,處理常式的GUID
GUID(Global unique identifier,通用唯一識別碼)用於在系統中唯一標識一個對象。CLSID是GUID在註冊表中的表示,用於在註冊表中用於唯一標識一個COM對象。
CLSID可以有很多子鍵,在Shell擴充中常用到的是InprocHandler32和InprocServer32這兩個子鍵,分別用於向Shell註冊處理常式DLL和in-process服務DLL。
關於具體的例子,可以參見[3]。
4、COM
Shell擴充程式是COM(Component Object Model,元件物件模型)程式。
COM是一個平台無關、分布式、物件導向的程式介面標準,一般用於擴充應用程式的功能。
大多數微軟公司開發的應用程式都支援COM介面擴充,COM程式一般是DLL檔案(ActiveX程式是.ocx副檔名的檔案)。
COM程式被提供給主調程式調用。COM程式為主調程式提供固定的介面(DllGetClassObject匯出函數)主調程式先通過固定的函數介面來獲得其他的介面。Shell擴充中通過在註冊表中配置的情況來在不同情況調用不同的介面函數。不同的COM程式具有不同的介面,但是所有的介面都是從類廠(Class Factory)和IUnknown介面獲得的。所以COM程式必須實作類別廠和IUnknown介面。
實際上,類廠和IUnknown介面所返回的其他介面,都是以函數指標的形式返回的。因此任何具有結構體指標,並可以通過函數指標來調用函數的程式語言都可以用來編寫COM程式。
只有理解了Shell擴充作為COM程式的一種所必須遵守的介面規範才可能理解Shell擴充程式的工作原理。
由於COM程式是由其他程式調用的,因此COM程式存在一個主調程式。如果COM是對Shell的擴充,那麼主調程式就是Explorer.exe,如果COM程式是對IE瀏覽器的擴充,那麼主調程式就是iexplore.exe。
5、相關的幾個API
1)DllGetClassObject函數的主要功能是為主調程式(COM程式都是實現好後供其他程式調用的,調用COM程式的程式就是COM程式的主調程式)返回調用介面。
2)IclassFactory COM程式發布實現IClassFactory介面。IClassFactory介面的作用是根據主調程式的要求返回本COM程式所實現了的介面,供主調函數調用。IClassFactory介面必須要實現兩個成員函數,CreateInstance和LockServer,主調程式會調用這兩個成員函數。CreateInstance成員函數由主調程式調用,作用執行個體化主調程式所需要的介面,並返回介面執行個體。
3)Iunknown。
任何一個COM程式都必須要實現兩個介面,一是IClassFactory另外一個就是Iunknown。
IUnknown需要有3個成員函數:QueryInterface、AddRef和Release。
6、QueryInterface的功能與其他介面類似,AddRef和Release分別用於增加和減少COM對象的引用次數。當COM被引用及被釋放時,主調程式將會調用這兩個介面。
7、Shell擴充程式必須實現IShellExtInit介面。IShellExtInit介面應該具有成員函數Initialize,Initialize函數用於初始化Shell擴充程式所實現的屬性頁面擴充處理常式、上下菜單處理常式、拖拽處理常式等。此成員函數在Shell需要彈出操作功能表或彈出屬性頁面前會被調用,以執行個體化相關介面。
參數pIDFolder是指向ITEMIDLIST類型的變數,用於標識哪個對象的菜單將會被彈出。如果是屬性頁面擴充,那麼此參數為NULL;如果是上下檔案菜單擴充,那麼這個參數代表了所需快顯功能表的對象所在的目錄。
參數pDataObj指向IDataObj ect介面,用於擷取操作的對象。
參數hkeyProgID是表示目錄類型的註冊表值。
1)在一般隋況下,Initialize成員函數需要根據參數所指定的擴充類型來執行個體化不同的Handler介面,比如對Context Menu Handlers就應用執行個體化IContextMenu介面。
IContextMenu需要實現GetCommandString、InvokeCommand和QueryContextMenu這3個成員函數。
當Shell需要顯示上下檔案菜單時,會調用QueryContextMenu函數。QueryContextMenu函數應當設定菜單中的項。GetCommandString用於向主調程式返回與功能表項目關聯的字串。當使用者點擊操作功能表中的項後,InvokeCommand會被調用。InvokeCommand函數的實現中,應該處理功能表項目被點擊後所應該執行的動作。
2)實現屬性頁面的擴充需再實現IShellPropSheetExt介面,Ishell Prop SheetExt介面包括兩個成員函數:AddPages和ReplacePage。
主調程式會根據註冊表中的配置調用AddPages和ReplacePage,分別實現了屬性頁面的增加和替換。屬性頁面實質是一個對話方塊。
3)要在Shell擴充中實現表徵圖的擴充,需要實現IExtractIcon介面。
IExtractIcon需要實現Extract和GetIconLocation兩個成員函數。在GetIconLocation函數中調用了GetPrivateProfileInt來獲得檔案中配置的內容。GetIconLocation函數通過piIndex參數和szIconFile參數返回,表示應該使用szIconFile所指明的檔案中的piIndex號表徵圖。
4)如果在註冊表中註冊了CopyHookHandlers,那麼在使用者操作與檔案類型相關的檔案或檔案夾時,ICopyHook介面CopyCallback成員函數將被調用。
參考
[1] 精通Windows API 函數、介面、編程執行個體
[2] http://msdn.microsoft.com/en-us/library/bb773177%28VS.85%29.aspx
[3] http://www.cnblogs.com/mydomain/archive/2010/12/03/1895050.html