摘要:本文介紹了一種有別於通常的Windows外殼編程方法。采用COM技術,通過Windows提供的外殼接口實現對其的編程。
一、 引言
在Windows環境下,不論是使用Visual C++還是Delphi或是其他一些軟件開發工具,盡管存在著差別,但有一點是相同的:都是運行於Windows操作系統之下的。在程序開發過程中也經常要在自己的應用程序中加入一些Windows系統本身就有的功能,比如文件的拷貝、刪除、查找以及運行程序等等。而這些功能在Windows操作系統下都是具備的,顯然如果能直接從系統中調用這些功能將不僅僅減少程序的大小和開發人員的工作量,而且由於是直接通過操作系統來完成這些功能,將會大大減小這部分程序出現異常錯誤的概率。Windows系統雖說也存在不少錯誤,但常用功能的錯誤還是比較少的,而且通過補丁程序可以更低限度減少系統錯誤,因此程序員可以將調試檢錯的注意力放在應用程序的其他地方,對於調用系統功能這部分代碼則可以不必投入太大的精力去調試,因為這部分調試的工作在操作系統發布的時候就已經由微軟做好了。
二、 Windows外殼編程
前面所說的直接使用Windows操作系統部分功能的編程方法就是針對Windows操作系統外殼的編程,可以通過對操作系統提供的幾個編程接口對操作系統的部分功能進行調用,甚至可以按照自己的意圖在應用程序中對部分功能進行修改、擴展。但這方面的資料介紹不是特別多,講的也大都語焉不詳,而且用通常的編程方法去進行外殼編程是非常麻煩的,動辄就要對相關的結構對象進行設置,而這樣的結構裡的數據成員少則十來個多則幾十個,因此配置起來非常煩瑣,下面就以一個比較簡單的外殼操作--拷貝文件進行舉例說明:
……
SHFILEOPSTRUCT FileOp; //外殼的文件操作結構
FileOp.hwnd=m_hWnd; //設置句柄
//設置操作方式,拷貝用FO_COPY,刪除用 FO_DELETE
FileOp.wFunc=FO_COPY;
FileOp.pFrom=m_source; //源文件路徑
FileOp.pTo=m_detect; //目標文件路徑
FileOp.fFlags=FOF_ALLOWUNDO; //允許恢復
FileOp.hNameMappings=NULL;
FileOp.lpszProgressTitle=strTitle; //設置標題
SHFileOperation(&FileOp); //執行外殼拷貝
if(FileOp.fAnyOperationsAborted) //監測有無中止
TRACE("An Operation was aborted!!! ");
……
上述代碼實現起來雖然效果還是不錯的,但然實現起來卻是比較麻煩的,這僅僅是一個比較簡單的外殼操作,對於一些比較復雜的外殼操作比如系統托盤、任務條等等的編程,更是尤為嚴重,而且象此類編程,MFC裡並沒有提供封裝好的程序類庫,提供的只有系統的WinAPI 應用程序接口,因此在程序開發過程中往往會有一種在進行SDK編程的感覺。
三、 COM技術在Windows外殼編程中的應用
COM (Component Object Model,組件對象模型)是Microsoft創建的一種二進制和網絡標准,也是Microsoft大力推廣並已取得廣泛認可的一種組件標准。在COM標准中,COM對象被很好的封裝起來,客戶無法訪問對象的實現細節,提供給用戶的唯一的訪問途徑是通過COM接口來訪問。對於COM接口有兩方面的含義:首先它是一組可供調用的函數,由此客戶可以讓該對象做某些事情;其次,也是更為重要的,接口是組件及其客戶程序之間的協議。也就是說接口不但定義了可用什麼函數,也定義了當調用這些函數時對象要做什麼。Windows操作系統本身作為一個大的COM組件對象,也提供了一些必要的COM接口給客戶程序,因此我們可以通過這些COM接口來直接對Windows外殼進行編程。
在程序進行正式編寫設計之前有一點是肯定的:程序裡需要用到COM接口,要對COM對象進行操作。因此首先要加入初始化COM和終止COM的代碼。一般是在應用程序類的InitInstance()函數的開始處和返回前添加初始化COM和終止COM代碼的:
……
CoInitialize(NULL); //初始化COM
……
CoUninitialize(); //終止COM代碼
……
以上兩個函數在MFC程序和非MFC程序中都可以很好的使用。另外,如果程序框架是以MFC為基礎的,那麼只需簡單的調用AfxOleInit()函數就可以達到同樣的目的。而且不必顯式調用終止COM的代碼。在COM標准中,訪問COM對象的唯一途徑是COM接口,因此在編寫操縱Windows 系統外殼程序首先要得到其提供的COM接口。所用的COM接口是IShellDispatch,它是從IDispatch接口派生來的,在VC安裝目錄的VC98IncludeExdisp.h頭文件中有定義,下面節選了一些將要用到的接口定義:
……
EXTERN_C const IID IID_IShellDispatch;
#if defined(__cplusplus) && !defined(CINTERFACE)
interface DECLSPEC_UUID("D8F015C0-C278-11CE-A49E-444553540000")
IShellDispatch : public IDispatch
{
public:
……
virtual HRESULT STDMETHODCALLTYPE MinimizeAll( void) = 0;
virtual HRESULT STDMETHODCALLTYPE UndoMinimizeALL( void) = 0;
virtual HRESULT STDMETHODCALLTYPE FileRun( void) = 0;
virtual HRESULT STDMETHODCALLTYPE CascadeWindows( void) = 0;
virtual HRESULT STDMETHODCALLTYPE TileVertically( void) = 0;
virtual HRESULT STDMETHODCALLTYPE TileHorizontally( void) = 0;
virtual HRESULT STDMETHODCALLTYPE ShutdownWindows( void) = 0;
virtual HRESULT STDMETHODCALLTYPE Suspend( void) = 0;
virtual HRESULT STDMETHODCALLTYPE SetTime( void) = 0;
virtual HRESULT STDMETHODCALLTYPE TrayPropertIEs( void) = 0;
virtual HRESULT STDMETHODCALLTYPE Help( void) = 0;
virtual HRESULT STDMETHODCALLTYPE FindFiles( void) = 0;
virtual HRESULT STDMETHODCALLTYPE FindComputer( void) = 0;
};
……
該接口在CoCreateInstance()函數創建COM對象時將會得到指向其的指針,通過這個函數客戶程序可以避免顯式同類廠打交道,其實該函數內部也調用了CoGetClassObject()函數來獲取COM對象的類廠,只不過它把通過類廠創建對象的過程封裝起來了,只需用戶指定對象類的CLSID和待輸出的接口指針及接口ID,顯然這樣直接創建COM對象是非常便捷的,在獲取到COM對象指針之後就可以通過這個指針去訪問調用COM對象裡的方法來實現Windows 外殼的種種功能調用了,下面是實現該功能的部分關鍵代碼:
……
HRESULT sc;//返回結果
IShellDispatch *pShellDisp = NULL; //初始化接口指針
//直接創建COM對象
sc = CoCreateInstance( CLSID_Shell,//指定待創建的COM對象標識符
NULL, //指定被聚合時的外部對象的接口指針
CLSCTX_SERVER, //指定組件類別,可以指定進程內組件進程外組件或者進程內控制對象。
IID_IDispatch, //指定接口ID,需要注意的是這裡指的是待
//創建的COM對象的接口ID,而非類廠對象的接口標識符
(LPVOID *) &pShellDisp );//存放函數返回的對象的接口指針
/* 在上述代碼中,CoCreateInstance首先調用CoGetClassObject函數創建類廠對象,然後用得到的類廠對象的接口指針創建真正的COM對象,最後把類廠對象釋放並返回,這樣就很好的把類廠屏蔽起來,使用戶用起來更為簡單。*/
if( FAILED(sc) )//必須用FAILED 或SUCCECCED來判斷COM對象是否創建成功
return;
pShellDisp->FindFiles(); //調用COM對象裡的方法
pShellDisp->Release(); //釋放申請到的接口指針
……
在這裡通過pShellDisp接口指針調用了COM對象的FindFiles()方法去進行查找文件的系統外殼操作。同樣,可以根據實際需要靈活調用響應的方法來執行相應的外殼操作,主要有以下幾個方法:
MinimizeAll 所有窗口最小化
UndoMinimizeALL 恢復窗口最小化
FileRun 開始菜單的"運行…"
CascadeWindows 層疊窗口
TileVertically 垂直平鋪
TileHorizontally 水平平鋪
ShutdownWindows 關閉Windows
Suspend 掛起計算機
SetTime 設定時間
TrayPropertIEs 任務欄屬性
Help Windows幫助
FindFiles 查找文件
FindComputer 查找計算機
……
這些接口均在VC安裝目錄的VC98IncludeExdisp.h頭文件中有定義,可以通過對該文件的查看來編寫響應的外殼操作代碼。
小結:本文介紹了一種利用COM技術實現Windows系統外殼程序的簡便實用的方法,對Windows系統外殼的程序設計和COM程序設計的方法和思想做了闡述。在掌握了本文編程的中心思想前提下,不僅可以對Windows系統外殼進行程序設計,而且對於其他一些提供COM接口的應用程序進行編程,比如可以在自己的應用程序中用類似的方法加入對Office辦公套件的支持等等。因此重點不應放在具體的程序代碼中,而是在於程序的設計思想與方法。