七.建立DIRECTDRAW對象 象我前面說過的,有三種方法。我們可以用兩個DIRECTDRAW函數中的任何一個,或者直接調用COM對象。讓我們每一個都試試,使我們自己熟悉它們。我將告訴你的最后一個方法可能是目前為止最簡單的,可能你會喜歡用它。至于另外兩個,打眼兒一看,你會覺得有些希奇。首先,看看DirectDrawCreate(): HRESULT WINAPI DirectDrawCreate( GUID FAR *lpGUID, LPDIRECTDRAW FAR *lplpDD, IUnknown FAR *pUnkOuter ); 看起來是不是有點兒復雜?HRESULT返回的類型是DirectDraw函數的標準。假如成功,返回值是DD_OK。假如失敗,函數將返回一個錯誤常量,有幾個錯誤常量供選擇,但我不想細講,更不想列出這些常量,反正你可以通過幫助文件隨時查閱它們。但有一件事兒我得告訴你,有兩個非常有用的宏可以幫助你知道函數調用成功與否:SUCCEEDED()和FAILED()。從字面上你就知道它們的分工了,是不是?只要把函數放到宏里面,你就知道結果了。無論如何,我們還得看看函數的參數: · GUID FAR *lpGUID:是一個全局唯一標識符(GUID)的地址,代表將要創建的驅動程序。假如該參數是NULL,那么該調用指向當前的顯示驅動程序。新版本的DirectDraw答應向該參數傳遞下列兩種標志之一,以控制當前顯示的行為: DDCREATE_EMULATIONONLY:DirectDraw只使用仿真(HEL),不使用硬件支持特性?! ?DDCREATE_HARDWAREONLY:DirectDraw對象不使用仿真特性。只能使用硬件抽象層(HAL),假如硬件不能支持應用程序,將不再尋求硬件仿真層(HEL)的支持而返回錯誤信號。 · LPDIRECTDRAW FAR *lplpDD:表示假如調用成功則返回有效的DirectDraw對象指針的地址,它是DirectDraw對象指針的指針(“DD”表示DirectDraw,“lp”表示32位長指針,“lplp”表示長指針的長指針)。應用程序一般需要使用此指針的地址(即DirectDraw對象指針)初始化DirectDraw對象。 · IUnknown FAR *pUnkOuter:這是為高級COM應用保留的參數,設置為NULL好了?! 〔灰晃伊_里羅嗦的解釋嚇倒,實際應用起來很簡單,解釋這么多,不過是為了讓你明白根本道理。現在有一個問題,這個函數給你一個指向IDirectDraw接口的指針,但我們想要一個指向IDirectDraw7接口的指針,我們應該怎么做呢?一旦DirectDraw應用程序通過DirectDrawCreate()函數獲得了指向DirectDraw對象的指針,COM就有一種機制可以用來查看該對象是否支持其它接口。IUnknown的QueryInterface()方法使得你能夠確定一個對象是否支持一個特定的接口: HRESULT QueryInterface( REFIID iid, // Identifier of the requested interface void **ppvObject // Address of output variable that receives the ); 第一個參數是一個要查詢的對象的引用標識符。對于IDirectDraw7來說就是IID_IDirectDraw7。使用它,你必須把dxguid.lib鏈接入你的項目中;第二個參數是一個變量的地址,我們應該在程序的頭部先聲明一個LPDIRECTDRAW7類型的指針,再把指針的地址傳遞給這個參數。假如你使用的是Visual C++6.0,你在這兒或許還需要一個類型強制符。假如機器支持你指定的接口,函數將返回一個指向該接口的指針。通過該指針,代碼就獲得對新接口的方法的訪問。假如函數調用成功,返回值是S_OK。 現在我們有了兩個接口指針:一個是IDirectDraw接口,另一個是IDirectDraw7。后一個是我們想要的;前一個就沒有用了。我們注重,在代碼中每當找到一個新的有效對象時,前一個對象就通過Release()函數被釋放掉。這個函數很簡單:ULONG Release(void); 返回的值是一個參考數字,只有在程序測試和調試時才用得著這個數字。為了安全起見,你還應該把釋放了的指針賦值為NULL。我們也通常在聲明這樣的指針時設置它為NULL。你跟上我的節奏了嗎?可能要記憶的東西太多了,但是你不得不記憶。讓我們把談到的這些做個實例吧,實例的目的是得到IDirectDraw7接口的指針:LPDIRECTDRAW lpdd = NULL; // pointer to IDirectDraw (temporary) LPDIRECTDRAW7 lpdd7 = NULL; // pointer to IDirectDraw7 (what we want)// get the IDirectDraw interface pointer if (FAILED(DirectDrawCreate(NULL, &lpdd, NULL))) { // error-handling code here }// query for IDirectDraw7 pointer if (FAILED(lpdd->QueryInterface(IID_IDirectDraw7, (void**)&lpdd7))) { // error-handling code here } else { // success! release IDirectDraw since we don t need it anymore
lpdd->Release(); lpdd = NULL; } 現在,假如你是一個C程序員,你可能被調用QueryInterface()和Release()這兩個函數的方法弄得有點模糊。你以前可能看過“->”這個符號,在C語言的結構部分,當結構聲明了一個指針變量,調用結構成員時,就用“結構指針名->結構成員”,同樣的道理,只是這里把結構成員換成了函數。既然說到這個話題,我就介紹一下另一個C++符號,范圍定義符號“::”,它是表示從屬關系的符號,舉個例子你就明白了:比如QueryInterface()函數是屬于IUnknown類的,就可以表示為IUnknown::QueryInterface()。我們將來會經常用到這個符號的,所以記住它?! ≌f實在的,以上的主要目的是為了演示怎樣使用QueryInterface()方法,它是所有DirectX接口的一部分,所以讓我們往下進行。我們將直接使用COM方法獲得接口指針。這種方法的好處是你可以立即獲得IDirectDraw7接口指針,不用象剛才那么麻煩。首先,你必須得初始化COM,象這樣:HRESULT CoInitialize(LPVOID pvReserved); 不能在輕易了,你必須把參數設置為NULL。當你結束COM調用,你需要拋棄它,也很簡單:void CoUninitialize(void); 我通常在DirecX程序的一開始就調用CoInitialize()函數,在程序的最末端,當我釋放了所有的DirectX對象后,使用CoUninitialize()。一旦你初始化了COM,你就可以用CoCreateInterface()函數得到你想要的指針,它看起來有點丑陋:STDAPI CoCreateInstance( REFCLSID rclsid, // Class identifier (CLSID) of the object LPUNKNOWN pUnkOuter, // Pointer to whether object is or isn t part // of an aggregate DWord dwClsContext, // Context for running executable code REFIID riid, // Reference to the identifier of the interface LPVOID *ppv // Address of output variable that receives ); // the interface pointer requested in riid 假如成功,返回值是S_OK。參數需要好好解釋一下,看下面: · REFCLSID rclsid:這是一個類標識符(不要同GUID搞混了哦),有為它預備好的常量標識符供你選擇。對于IDirectDraw7來說,使用CLSID_DirectDraw。注重沒有版本號,因為它是類標識符,不是接口標識符。 · LPUNKNOWN pUnkOuter:這個同我們在DirectDrawCreate()中看到的一樣,設置為NULL?! ?DWORD dwClsContext:這個必需的值叫作執行上下文,它定義了控制新生成對象的代碼將要執行的方式。這個值可以從CLSCTX列表中選取,對于我們現在的情況,我們用CLSCTX_ALL,它包含了所有可能的值?! ?REIID riid:我們在QueryInterface()中看過它。這個IID是IID_DirectDraw7?! ?LPVOID *ppv:依然同DirectDrawCreate()中的一樣,是指向接口指針的地址?! ≌{用這個函數將取代我們上一個方法中的DirectDrawCreate()、QueryInterface()和Release()三個函數,所以簡捷一些。當然,使用哪種隨便你了。直接調用COM比我們先前用的方法少了一個多于地接口指針。一旦你用CoCreateInstance()建立了一個對象,你還得調用Initialize()函數初始化這個對象。在C++里可能寫成這樣IDirectDraw7::Initialize()。以下是它的原形:HRESULT Initialize(GUID FAR *lpGUID); 將使用同DirectDrawCreate()中一樣的GUID,就是NULL。在我們繼續前,讓我給你看一個使用COM創建DirectDraw對象的例子:LPDIRECTDRAW7 lpdd7; // interface pointer// initialize COM CoInitialize(NULL);// create the object CoCreateInstance(CLSID_DirectDraw, NULL, CLSCTX_ALL, IID_IDirectDraw7, (void**)&lpdd7);// initialize the object lpdd7->Initialize(NULL); 直接看例子可能使你更輕易理解一些。好了,建立DirectDraw對象的最難的兩種方法你已經學會了,那就讓我們看看最簡單的方法吧! 它只有一步,沒有多于的接口指針,不用設置COM,什么都沒有。就是下面這個函數:DirectDrawCreateEx( GUID FAR *lpGuid, LPVOID *lplpDD, REFIID iid, IUnknown FAR *pUnkOuter ); 所有的參數我們看起來都比較熟悉,因為我們剛才看過它們了。第一個,第二個和第四個參數同DirectDrawCreate()中的一樣,只是這里需要用(void**)來修飾一下我們接口指針的地址——別問我為什么,這不是我的主意。第三個參數,riid,是我們在函數CoCreateInstance()中傳遞的接口ID,所以我們就用IID_IDirectDraw7。就這樣,無論用哪種方法,我們得到了我們的DirectDraw對象,我們可以繼續使用這個對象了。要做的頭兩件事是設置協作等級和顯示協議。 八.作等級和顯示模式 我不需要說太多。Windows編程設置協作級別你只需要調用IDirectDraw7::SetCoOperativeLevel()函數;設置顯示模式你就調用IDirectDraw7::SetDisplayMode()函數。就這么簡單!先來看看協作級別。這就是函數原形: