前言:隨著技術(shù)的不斷完善,微軟Windows操作系統(tǒng)已成為當(dāng)前個人計算機(jī)應(yīng)用領(lǐng)域的主流操作系統(tǒng)。Windows操作系統(tǒng)提供了頗受用戶喜愛的圖形用戶界面(GUI),微軟為Windows的這個戶界面保留了可擴(kuò)充性,它使得基于32位的Windows應(yīng)用程序可以通過多種方式來增強(qiáng)系統(tǒng)所提供的操作環(huán)境(也稱為外殼,英文名稱:Shell)。 通過對外殼的擴(kuò)展,開發(fā)人員可以為用戶提供其他的文件對象操作方式,或者簡化文件系統(tǒng)和網(wǎng)絡(luò)的瀏覽,或者使用戶能更方便地調(diào)用文件系統(tǒng)中對各種對象進(jìn)行處理的工具。為了說明基于COM接口技術(shù)的外殼擴(kuò)展技術(shù),本文首先編寫一個簡單的音頻播放器,然后編寫幾個外殼擴(kuò)展處理程序方便用戶對指定文件的操作。
一、外殼編程的方法
編寫外殼擴(kuò)展(以下簡稱Shell)的應(yīng)用程序有兩種方法,第一種外殼擴(kuò)展是指無需編程即可實(shí)現(xiàn)的Shell擴(kuò)展,只需要修改相應(yīng)的注冊表?xiàng)l目就可以讓Shell按照我們的意愿行事。第二種Shell擴(kuò)展需要編程來實(shí)現(xiàn),它的功能比第一種Shell擴(kuò)展要強(qiáng)大得多,在本文中提到的Shell擴(kuò)展所指的就是這一種。
Windows中Shell擴(kuò)展處理程序都是基于COM接口,它通過特定的COM接口與Windows Shell進(jìn)行正常的交互。為了讓W(xué)indows Shell能找到擴(kuò)展處理程序并與之交互,Windows Shell擴(kuò)展處理程序需要遵循一定的規(guī)則,這個規(guī)則也是編寫Windows Shell擴(kuò)展處理程序時所要遵循的設(shè)計思路。這個規(guī)則包括兩個方面:(1)Windows Shell擴(kuò)展處理程序應(yīng)該在注冊表中預(yù)先設(shè)定的位置登記自己,以便Windows Shell能找到它;
(2)Windows Shell擴(kuò)展處理程序應(yīng)該實(shí)現(xiàn)Windows Shell知道的幾個特定的COM接口,以便與Windows Shell進(jìn)行正常的交互。
編程實(shí)現(xiàn)Shell擴(kuò)展一般按如下步驟:(1)創(chuàng)建一個服務(wù)器項(xiàng)目(通常為DLL);(2)為項(xiàng)目添加一個實(shí)現(xiàn)特定Shell接口的COM類;(3)為該COM類實(shí)現(xiàn)一個類工廠;(4)為服務(wù)器實(shí)現(xiàn)一些框架性的代碼;(5)編譯鏈接以生成COM服務(wù)器;(6)編輯需要的注冊文件;(7)測試和調(diào)試Shell擴(kuò)展程序。
二、外殼擴(kuò)展編程實(shí)例
由于編寫外殼擴(kuò)展處理程序的基本步驟是相同的,所以本文僅給出了編寫上下文相關(guān)菜單處理程序的詳細(xì)編寫過程。關(guān)于文件類的上下文相關(guān)菜單,當(dāng)用戶用鼠標(biāo)右鍵單擊Shell名字空間中某個元素時(如某文件、目錄、服務(wù)器、工作組等,本文中特指AC-3音頻文件),Shell將生成此類元素的缺省上下文相關(guān)菜單(注意不同的文件對象生成缺省菜單是不一樣的),Shell會搜索注冊表,裝載此類文件對象所登記的上下文相關(guān)菜單擴(kuò)展,以便讓這些擴(kuò)展程序向已生成的上下文相關(guān)菜單中加入另外一些定制的菜單項(xiàng)。如果要為某個文件類對象增加一個上下文相關(guān)菜單擴(kuò)展程序,需要在Windows注冊表的下列位置進(jìn)行注冊:
HKEY_CLASSES_ROOT
<.擴(kuò)展名>=<文件類描述>
......
<文件類描述>
Shellex
ContextMenuHandlers
ContextMenuHandler_Name={該上下文相關(guān)菜單處理程序GUID }
需要說明的是,還需要為上下文相關(guān)菜單處理程序進(jìn)行登記,以便Shell能夠知道在哪里找到處理程序:
HKEY_CLASS_ROOT
CLSID
{該上下文相關(guān)菜單處理程序GUID}=<處理程序描述>
InProcServer32=<服務(wù)器所在完整路徑>
"Threading Model"="Apartment"
解決這個問題后,剩下的問題就是Windows Shell與上下文相關(guān)菜單處理程序交互問題,這就需要上下文相關(guān)菜單處理程序在代碼中實(shí)現(xiàn)兩個Shell知道的接口,即IContextMenu接口和IShellExtInit接口。IShellExtInit接口只有一個成員函數(shù),即Initialize,當(dāng)Shell決定對選定調(diào)用上下文相關(guān)菜單處理程序時,它會首先調(diào)用IshellExtInit接口的Initialize方法,以要求處理程序?qū)ψ约哼M(jìn)行初始化。IContexMenu接口中定義了三個方法,它們分別是QueryContexMenu、InvokeCommand和GetCommandString()。當(dāng)用戶用鼠標(biāo)右鍵單擊文件對象時,Shell將要顯示出其上下文相關(guān)菜單。
這時系統(tǒng)將此文件對象的上下文相關(guān)菜單的地址傳給此上下文相關(guān)菜單處理程序。但在處理程序中只應(yīng)用此地址來向上下文相關(guān)菜單中加入菜單項(xiàng),而不應(yīng)修改或刪除已有的菜單項(xiàng),因?yàn)榭赡苓€會有其他處理程序在此前或此后向菜單中加入菜單項(xiàng)。最后,當(dāng)所有的上下文相關(guān)菜單處理程序都被調(diào)用后,Shell再向此菜單中加入它的菜單選項(xiàng)。菜單中的菜單項(xiàng)可以是與特定類相關(guān)的(即適用于某種類型的所有文件),也可以是與某特定事例相關(guān)的(即只適用于單個文件對象)。
當(dāng)Windows Shell將要為某文件對象顯示其上下文相關(guān)菜單(或顯示菜單條上的File菜單)時,系統(tǒng)將調(diào)用上下文相關(guān)菜單處理程序中IContextMenu接口的QueryContexMenu成員函數(shù)。此時上下文相關(guān)菜單處理程序可通過調(diào)用InsertMenu函數(shù)按位置(MF_POSITION)直接將想要加入的菜單選項(xiàng)加入列上下文相關(guān)菜單中。加入的菜單項(xiàng)可以是通常的字符串(MF_STRING),也可以是位圖(MF_BITMAP)。當(dāng)用戶選中某個由上下文相關(guān)菜單處理程序維護(hù)的菜單選項(xiàng)時,Shell將調(diào)用此處理程序IContextMenu接口的InvokeCommand成員函數(shù)以使處理程序有機(jī)會處理用戶選擇的命令。若對某類文件登記有多個上下文相關(guān)菜單處理程序,則各命令的順序是由文件類下的ContextMenuHandlers關(guān)鍵字決定。
上下文相關(guān)菜單處理程序的具體的編寫過程如下:
(1)創(chuàng)建一個空的DLL項(xiàng)目
上下文相關(guān)菜單處理程序仍將以進(jìn)程內(nèi)COM服務(wù)器的形態(tài)存在,因此需要用為上下文相關(guān)菜單處理程序創(chuàng)建應(yīng)該空的DLL項(xiàng)目,本文將項(xiàng)目命名為ContextMenuExt;
(2)為項(xiàng)目添加一個類CContextMenuExt
首先還是來考慮DLL需要實(shí)現(xiàn)的功能,上下文相關(guān)菜單處理程序需要一個類來實(shí)現(xiàn)COM接口IContextMenu和IShellExtInit。為此,筆者給項(xiàng)目添加一個類CContextMenuExt。添加了CContextMenuExt類之后,從項(xiàng)目的文件視圖可以看出項(xiàng)目中多了兩個文件,它們分別是類CContextMenuExt的頭文件(CContextMenuExt.h)和源代碼文件(CcontextMenuExt.cpp)。接下來修改CContextMenuExt的類聲明,使之繼承IContextMenu和IShellExtInit接口,并添加3個成員變量m_cRef、m_pDataObj、m_szFileName和一個受保護(hù)的成員函數(shù)。在CContextMenuExt類的公有聲明部分,定義了類的構(gòu)造函數(shù)、析構(gòu)函數(shù)以及為了實(shí)現(xiàn)IUnknown、IContextMenu和IShellExtInit接口而設(shè)的成員函數(shù)。之所以要實(shí)現(xiàn)IUnknown接口,是因?yàn)镮Unknown接口是一切接口的祖先。除繼承了IUnknown接口的3個基本方法之外,IContextMenu接口還定義了真正實(shí)現(xiàn)菜單擴(kuò)展的QueryContextMenu、InvokeCommand和GetCommandString方法,關(guān)于這些方法的作用,前面已經(jīng)做了簡要的說明,這里不在贅述。
在CContextMenuEx類的受保護(hù)部分,聲明了一個成員函數(shù)ClickSample,這個函數(shù)正是為了處理添加的菜單命令而設(shè)的。定義了一個數(shù)組m_szFileName用以保存選中文件的完整路徑;
(3)為類CContextMenuExt添加實(shí)現(xiàn)代碼
為了清楚起見,筆者將逐步給出其各個成員函數(shù)的說明。首先來看看CContextMenuExt類的構(gòu)造函數(shù)。在構(gòu)造函數(shù)中對兩個成員變量進(jìn)行了初始化,其中m_cRef表示對該類的引用計數(shù),m_pDataObj則表示系統(tǒng)傳送過來的IDataObject接口的指針,將在后面使用。g_nDllRefCount是DLL中需要定義的一個全局變量,用來記錄整個DLL被引用的次數(shù),并將以此決定是否運(yùn)行Shell對DLL服務(wù)器進(jìn)行卸載。當(dāng)用戶選中了某個AC3文件類的上下文菜單DLL服務(wù)器維護(hù)的菜單選項(xiàng)時,Shell將調(diào)用CContextMenuExt類中IContextMenu接口的InvokeCommand成員函數(shù)來處理用戶的選擇。此處CContextMenuExt的做法是通過參數(shù)lpcmi的lpVerb找到用戶選擇的菜單的標(biāo)志符,并調(diào)用相應(yīng)的成員函數(shù)進(jìn)行處理。在本文中這個成員函數(shù)啟動AC3播放器播放存放在m_szFileName數(shù)組中的文件;
(4)為項(xiàng)目添加另一個類CcontextMenuExtFactory。Dll中有了CContextMenuExt類之后,還需要一種手段來創(chuàng)建DLL中的這個功能類。于是便需要添加另一個類CContextMenuExtFactory來作為類CContextMenuExt類工廠。所謂"類工廠",是專門用來生產(chǎn)別的功能類的COM對象。它需要實(shí)現(xiàn)IClassfactory接口,當(dāng)然也需要實(shí)現(xiàn)IUnknown接口。其中的CreateInstance和LockServer兩個成員函數(shù)是IClassFactory接口要求其實(shí)現(xiàn)類必須實(shí)現(xiàn)的方法;
(5)為服務(wù)器實(shí)現(xiàn)一些框架性代碼
需要為整個DLL定義一個引用計數(shù)變量g_nDllRefCount和一個用來記錄DLL的模塊句柄的變量g_hThisDll。作為Win32的動態(tài)鏈接庫,需要為DLL實(shí)現(xiàn)幾個標(biāo)準(zhǔn)函數(shù),如DLLMain、DLLCanUnloadNow、DLLGet ClassObject等,首先為這些函數(shù)添加原型。函數(shù)DllMain是所有Win32動態(tài)鏈接庫的入口函數(shù)。在DLL的全局變量g_hThisDll中保存進(jìn)程的實(shí)例標(biāo)識hInstance以備后用。當(dāng)系統(tǒng)的進(jìn)程或線程初始化或者被終止時,Dll中的DllMain函數(shù)即被系統(tǒng)以相應(yīng)的參數(shù)調(diào)用。此外,當(dāng)別的進(jìn)程或者線程調(diào)用LoadLibrary函數(shù)裝載Dll或者調(diào)用FreddLibray函數(shù)卸載Dll時,DllMain函數(shù)也會被系統(tǒng)調(diào)用。函數(shù)DllCanUnloadNow通過全局變量g_nDllRefCount來決定實(shí)現(xiàn)該函數(shù)的DLL是否正在使用之中,如果不是,調(diào)用者就可以將該DLL安全地從內(nèi)層中卸載。DllGetClassObject函數(shù)被用來獲取DLL中定義的類的對象實(shí)例。DllGetClassObject函數(shù)通常在CoGetClassObject函數(shù)中被調(diào)用。
(6)編輯上下文菜單處理程序的注冊文件制作一個 .reg文件,按照一定的格式書寫完后,在操作系統(tǒng)中雙擊這個文件,文件表的注冊條目被合并到注冊表中,這里需要強(qiáng)調(diào)的是該處理程序的類和接口的全局唯一標(biāo)志符是使用Visual C++6.0提供的UUIDGEN實(shí)用工具生成的。還需要說明的,由于筆者使用的操作系統(tǒng)是Windows2000,所以編譯連接好的ContextMenuExt.dll文件應(yīng)放到C:WINNTSystem下.
新聞熱點(diǎn)
疑難解答
圖片精選