windows編程入門篇本文目的是為剛剛接觸COM程序員提供指南并幫助他們理_第1頁
windows編程入門篇本文目的是為剛剛接觸COM程序員提供指南并幫助他們理_第2頁
windows編程入門篇本文目的是為剛剛接觸COM程序員提供指南并幫助他們理_第3頁
windows編程入門篇本文目的是為剛剛接觸COM程序員提供指南并幫助他們理_第4頁
windows編程入門篇本文目的是為剛剛接觸COM程序員提供指南并幫助他們理_第5頁
已閱讀5頁,還剩17頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

本文的目的是為剛剛接觸COMCOM的基本概念。內(nèi)容包括COM規(guī)范簡介,重要的COM術(shù)語以及如何重用現(xiàn)有的COM組件。本文不包括如COM對(duì)象和接口。母在Windows的世界中隨處可見。隨時(shí)涌現(xiàn)出來的大把大把的新技術(shù)都以COM為基COMCOM的所有一切。本文由淺入深描述COM的內(nèi)在運(yùn)行機(jī)制,教你如何使用第提供的COM對(duì)象(以Windows外殼組件S為例)。讀完本文后,你就能掌握如何使用Windows操作系統(tǒng)中內(nèi)建的組件和第提供的COM對(duì)象。COM——到底是什么?——COM基本元素的定義——COMCOMCOM基本接口IUnknownCOM,舉例說明本文所討論的所有概念。處理HRESULT——HRESULT類型描述,如何監(jiān)測錯(cuò)誤及成功代WindowsDLLsWindows程序運(yùn)行的關(guān)鍵——重用kernel32.dlluser32.dllDLLsCCC調(diào)用規(guī)DLLsDLLs的MFC引入了另外一種MFC擴(kuò)展DLLs——COM通過定義二進(jìn)制標(biāo)準(zhǔn)解決了這些問題,即COM明確二進(jìn)制模塊(DLLs和但是我好像還從來沒有在Windows以外的地方聽COM。C++ISLink,接口被設(shè)計(jì)成一個(gè)抽象基類,其中只有純粹的虛擬函數(shù)。COMC++類。COM服務(wù)器是包含了一個(gè)或多個(gè)coclass的二進(jìn)制(DLL或EXE)。(Registration)是創(chuàng)建表的一個(gè)過程,告訴Windows操作系統(tǒng)COM服務(wù)器放在什么位置。取消(Unregistration)則相反——從表刪除這些。(COMcoclassGUID。因?yàn)槊恳粋€(gè)GUID都是全球唯一的,所以避免了名字只要你用COMAPI創(chuàng)建它們)。有時(shí)你還會(huì)碰到另一個(gè)術(shù)語UUID(意思也是全球唯一標(biāo)示符——universallyuniqueidentifier)。UUIDsGUIDs在實(shí)際使用時(shí)的用途是一樣的。(IDCLSIDcoclassGUIDIDIIDGUID。COMGUID有兩個(gè)理由:消除了集中發(fā)布GUIDs的必要。身。但是為了避免才分開描述的。態(tài)分配因?yàn)镃OM必須獨(dú)立于語言COM庫為自己提供對(duì)象管理下面是對(duì)COMC++對(duì)象管理所做的一個(gè)比較:創(chuàng)建一個(gè)新對(duì)C++中,用new刪除對(duì)COM中,所有的對(duì)象保持它們自己的計(jì)數(shù)。調(diào)用者必須通知對(duì)象什么時(shí)候用完這個(gè)對(duì)象。當(dāng)計(jì)數(shù)為零時(shí),COM對(duì)象將自己從內(nèi)存中釋放。由此可見,對(duì)象處理的兩個(gè)階段:創(chuàng)建和銷毀,。當(dāng)創(chuàng)建COM對(duì)象時(shí)要通知C++對(duì)象指針一樣。為了創(chuàng)建COM對(duì)象并從這個(gè)對(duì)象獲得接口,必須調(diào)用COM庫的API函數(shù),HRESULTCoCreateInstance(REFCLSIDrclsid,LPUNKNOWNpUnkOuter, ppv表示所使用COM服務(wù)器的種類。本文使用的是最簡單的COM服務(wù)器,一個(gè)進(jìn)程內(nèi)(in-process)DLL,所以傳遞的參數(shù)值為CLSCTX_INPROC_SERVER。注意這里不要隨意使用CLSCTX_ALL(在ATL中,它是個(gè)缺省值),因?yàn)樵跊]有安裝DCOM的Windows95系統(tǒng)上當(dāng)你調(diào)用CoCreateInstance()時(shí),它負(fù)責(zé)在表中查找COM服務(wù)器的位置,將服務(wù)器加coclass實(shí)例。以下是一個(gè)調(diào)用的例子,創(chuàng)建一個(gè)CLSID_SLink對(duì)象的實(shí)例并請(qǐng)求指向這個(gè)對(duì) ISLink*hr=CoCreateInstance(CLSID_S //coclass的 //服務(wù)器類型 //接口的IID(void**)&pISL); //指向接口的指針if(SUCCEEDED(hr){pISL}{COM,hr}首先一個(gè)接受CoCreateInstance()返回值的HRESULT和ISLink指針。調(diào)用CoCreateInstance()來創(chuàng)建新的COM對(duì)象。如果hr接受到一個(gè)表示成功的代碼,則SUCCEEDED宏返回TRUEFALSE。FAILED是一個(gè)與SUCCEEDED對(duì)應(yīng)的宏用來如果你的應(yīng)用程序使用許多不同的COM對(duì)象,因此在用完某個(gè)接口后調(diào)用Release()就顯得非常重要。如果你不釋放接口,這個(gè)COM對(duì)象(包含代碼的DLLs)將保留在內(nèi)存中,調(diào)用Co UnusedLibraries()API。這個(gè)API將卸載任何沒有明顯的COM服務(wù)器,因COMif(SUCCEEDED(hr){pISLCOM}COMIUnknown。IUnknownAddRef()–通知COM對(duì)象增加它的計(jì)數(shù)。如果你進(jìn)行了一次接口指針的拷貝,就必須Release()–通知COM對(duì)象減少它的計(jì)數(shù)。參見前面的Release()示例代碼段。()–前面已經(jīng)看到了Release的使用,但如何使用QueryInterface呢當(dāng)你用CoCreateInstance()創(chuàng)建對(duì)象的時(shí)候,你得到一個(gè)返回的接口指針。如果這個(gè)COM對(duì)象實(shí)現(xiàn)一個(gè)以上的接口(IUnknown),QueryInterface()方法來獲得任何你HRESULTIUnknown::QueryInterface(REFIIDiid,void**ppvIID。讓我們繼續(xù)外殼的例子。它實(shí)現(xiàn)了ISLink和IPersistFile接口。如果你已經(jīng)有一HRESULTIPersistFile*hr=pISL->QueryInterface(IID_IPersistFile,(void**)&pIPF話你就可以象使用其它接口指針那樣使用新的接口指針,pIPF。但必須記住調(diào)用pIPF->Release()COM對(duì)象已經(jīng)用完這個(gè)接口。COMUnicodeANSI,COMUnicode串(這里指的是寫入TCHAR類型串。ANSIANSIANSIWindowsAPIs,所TCHAR類型。COMUnicode串時(shí),可以用下列幾種方法之一將它轉(zhuǎn)換char類型串:1WideCharToMultiByteAPI。2CRTwcstombs()。3CString構(gòu)造器或賦值操作(MFC4ATL你可以用WideCharToMultiByte()UnicodeANSI串。此函數(shù)的原型intWideCharToMultiByte( LPCWSTRlpWideCharStr, LPCSTRlpDefaultChar,LPBOOLlpUsedDefaultChar);dwFlagsWindows如何處理“復(fù)合”Unicode字符,它是一種后面帶讀音符號(hào)的字符。èCodePage參數(shù)指定的代碼頁中,不會(huì)出什么事。否則,Windows必須對(duì)之進(jìn)行轉(zhuǎn)換。傳 WC_SEPCHARS使得Windowse`。WC_DISCARDNS使得Windows丟棄讀音符號(hào)。WC_DEFAULTCHARWindowslpDefaultChar參數(shù)中說明的缺省字符替代復(fù)缺省行為是WC_SEPCHARS。lpWideCharStr在Unicode字符中的長度。通常傳遞-10x00可選——當(dāng)dwFlags包含 POSITECHECK|WC_DEFAULTCHAR并且某個(gè)Unicode字符不能被映射到同等的ANSI串時(shí)所傳遞的一個(gè)單字符ANSI串,包含入的“缺省”字符??梢詡鬟fNULL,讓API使用系統(tǒng)缺省字符(一種寫法是一個(gè)問號(hào))。NULL來忽略這個(gè)參數(shù)。都有點(diǎn)暈菜了……!,萬事開頭難啊……COM的串API的例UnicodewszSomeString...charszANSIString[MAX_PATH];WideCharToMultiByte //ANSIPOSITECHECK, Unicode 10x00 charsizeof(szANSIString), NULL); 調(diào)用這個(gè)函數(shù)后,szANSIStringUnicodeANSICRT函數(shù)wcstombs()WideCharToMultiByte()的調(diào)用,所size_twcstombschar*mbstr,constwchar_t*wcstr,size_tcountwcstombs()在它對(duì)WideCharToMultiByte()的調(diào)用中使用 POSITECHECK|WC_SEPCHARS標(biāo)志。用wcstombs()轉(zhuǎn)換前面例子中的Unicode串,結(jié)果一樣:wcstombs(szANSIString,wszSomeString,sizeof(szANSIString)MFCCString包含有構(gòu)造函數(shù)和接受Unicode//Unicode串wszSomeString...CStringstr1wszSomeString);//用構(gòu)造器轉(zhuǎn)換CStringstr2wszSomeString;//ATL方法是“widetoANSI”——ANSI)OLE2A()更精確,“OLE”表示的意COMOLE串。下面是使用這些宏的例子:#include//Unicode{charszANSIStringUSES_CONVERSION;//這個(gè)宏要使用的局部變lstrcpy(szANSIString,OLE2A(wszSomeString)}OLE2A()宏“返回”轉(zhuǎn)換的串的指針,但轉(zhuǎn)換的串被在某個(gè)臨時(shí)棧變量中,所以要lstrcpy()W2T()(UnicodeTCHAR)控制臺(tái)應(yīng)用程序,輸出/顯示Unicodestd::wcout,如:wcout<<但是要記住,std::wcoutUnicode,所以你要是“正?!眘td::cout輸出/顯示。對(duì)于UnicodeL標(biāo)示,如:wcout<<L"TheOraclesays..."<<endl<<——wcsXXXUnicodewcslen()——Windows9xWindowsAPIUnicode9xCOM對(duì)象統(tǒng)中安裝了活動(dòng)桌面(ActiveDesktop)。COMCOMIActiveDesktopCOMGetWallpaper()方法。GetWallpaper()成功,則輸出/顯示墻紙文件名。 wszWallpaper[MAX_PATH];CStringstrPath;HRESULTIActiveDesktop*1.COM(WindowsDLLs)CoInitializeNULLMFCAfxOleInit()。CoInitialize(NULL);2.COMCOMhr=CoCreateInstance((void**)&pIAD);if(SUCCEEDED(hr){3.COMGetWallpaper()hr=pIAD->GetWallpaper(wszWallpaper,MAX_PATH,0

if(SUCCEEDED(hr){4.GetWallpaperwcoutUnicodewszWallpaper.wcout//Unicode,功能與cout.相同wcout<<L"Wallpaperpath "<<wszWallpaper<<endl}{cout<<_T("GetWallpaper()failed.")<<endl<<}5.}{cout<<_T("CoCreateInstance()failed.")<<endl<<}6.COMMFC在這個(gè)例子中,輸出/UnicodewszWallpaper用的是std::wcout的代碼用外殼的SLink組件對(duì)象類創(chuàng)建我們?cè)诘谝粋€(gè)例子中獲得的墻紙文件的快捷方COM創(chuàng)建一個(gè)用于建立快捷方式的COM對(duì)象并取得ISLink接口。調(diào)用ISLink接口的SetPath()方法QueryInterface(IPersistFileIPersistFileSave()方法。COM sWallpaper=wszWallpaper;//將墻紙路徑轉(zhuǎn)換為ANSI IPersistFile*1.COMWindowsDLLsInitInstanceCoInitializeNULLMFCAfxOleInit()CoInitialize(NULL//2.使用外殼提供的SLink組件對(duì)象類創(chuàng)建COM對(duì)象。//第四個(gè)參數(shù)通知COM需要什么接口(這里是ISLink)hr=CoCreateInstance(CLSID_S(void**)&pISL);if(SUCCEEDED(hr){3.hr=pISL->SetPath(sWallpaperif(SUCCEEDED(hr){4.(IPersistFilehr=pISL->QueryInterface(IID_IPersistFile,(void**)&pIPFif(SUCCEEDED(hr){5.Save()Unicodehr=pIPF->Save(L"C:\\wallpaper.lnk",FALSE6a.IPersistFile}}//6.釋放ISLink接口}7.COMMFC這一部分準(zhǔn)備用SUCCEEDEDFAILED宏進(jìn)行一些簡單的出錯(cuò)處理。主要是深入研究COMHRESULT,以便達(dá)到完全理解和熟練應(yīng)用。域:程度位(表示成功或失敗),功能碼和狀態(tài)碼。功能碼表示HRESULT來自什么組件或個(gè)16位的值,僅此而已,沒有其它內(nèi)在含義;它在數(shù)字和意義之間是隨意關(guān)聯(lián)的;類似如果你在winerror.h[功能]_[程度]_[描述]命名規(guī)范列出的HRESULT值,由組件返回的通用的HRESULT(類似E_OUTOFMEMORY)在名=E意思是錯(cuò)誤(error);描述=READREGDB是對(duì)錯(cuò)誤的描述(意思是不能讀表數(shù)S_OK:沒有功能碼——通用(generic)HRESULT;程度=S;表示成功(success);OK是winerror.hHRESULTVC提供的錯(cuò)誤查找工具(ErrorLookup)可以輕松查到為HRESULT內(nèi)建功能碼。例如,假設(shè)你CoCreateInstance()CoInitialize()。CoCreateInstance()返回的值是0x800401F0。你只要將這個(gè)值輸入到錯(cuò)誤查找工具按“LookUp”按鈕,便可以看到錯(cuò)誤信息描述“CoInitialize”如下圖所示:HRESULT描述的方法是在調(diào)試器中。假設(shè)有一個(gè)HRESULThres。在Watch窗口的左邊框中輸入“hres,hr”,表示想要看的值,“hr”VCHRESULTCOMCOM的內(nèi)C++編寫自己的接口。本文為剛剛接觸COM的程序員提供編程指南,解釋COM服務(wù)器以及如何繼上一篇COM編程之后,本文將討論有關(guān)COM服務(wù)器的內(nèi)容,解釋編寫自己的COMCOMCOMCOM服務(wù)器進(jìn)行調(diào)用時(shí),COM服務(wù)器運(yùn)行的內(nèi)部機(jī)制。如果你讀過上一篇文章。應(yīng)該很熟悉COM客戶端是怎么會(huì)事了。本文將討論COM的另一端——COM服務(wù)器。內(nèi)容包括如何用 COM服務(wù)器的內(nèi)部過程,毫無遮掩地研究那些庫代碼是充分理COM服務(wù)器內(nèi)部機(jī)制的最好方法。C++并掌握了上一篇文章所討論的概念和術(shù)語。在這一部分將包括如走馬觀花看COM服務(wù)器——描述COM服務(wù)器的基本要求服務(wù)器生命其管理——描述COM服務(wù)器如何控制加載時(shí)間IUnknown開始——展示如何用C++類編寫一個(gè)接口實(shí)現(xiàn)并描述IUnknown之方法的目的。深入CoCreateInstance()——探究CoCreateInstance()的調(diào)用機(jī)COM服務(wù)器的——描述完成服務(wù)器所需要的表創(chuàng)建COM對(duì)象——類工廠——描述創(chuàng)建客戶端要使用的COM對(duì)象的過程一個(gè)定制接口的例子——例子代碼示范了上述概一個(gè)使用服務(wù)器的客戶端——舉例說明一個(gè)簡單的客戶端應(yīng)用程序,用它來測試走馬觀花看COM服務(wù)器本文討論最簡單的一種COM服務(wù)器,進(jìn)程內(nèi)服務(wù)器(in-process)?!斑M(jìn)程內(nèi)”DLLs,并且與1、必須正確在表的HKEY_CLASSES_ROOT\CLSID鍵值冊(cè)2、必須輸出DllGetClassObject()這是進(jìn)程內(nèi)服務(wù)器運(yùn)行的最小需求。在表的HKEY_CLASSES_ROOT\CLSID鍵值下必須創(chuàng)建一個(gè)鍵值,用服務(wù)器的GUID作為鍵名字,這個(gè)鍵值必須包含兩個(gè)鍵值,一是服務(wù)器的位置,而是服務(wù)器的線程模型。COM庫對(duì)DllGetClassObject()CoCreateInstance()API中完成的。DllUnregisterServer():DllRegisterServer()創(chuàng)建的表。另外,只輸出正確的函數(shù)是不夠的——COMCOM庫和客戶端服務(wù)器生命其管DLL服務(wù)器的一個(gè)與眾不同的方面是控制它們被加載的時(shí)間?!皹?biāo)準(zhǔn)的”DLLs的并且是在應(yīng)用程序使用它們時(shí)被隨機(jī)加載/或卸載。從技術(shù)上講,DLL服務(wù)器也是的,因?yàn)椴还茉鯓铀鼈儺叡M還是DLLCOM庫提供了一種機(jī)制,它允許某個(gè)服務(wù)COM卸載它。這是通過輸出函數(shù)DllCanUnloadNow()實(shí)現(xiàn)的。這個(gè)函數(shù)的HRESULT當(dāng)客戶應(yīng)用程序調(diào)用MPIo UnsLbrris(時(shí),通常出其空閑處期間,M庫遍歷這個(gè)戶端應(yīng)用已載所有的LL服務(wù)器并通過用它的llanUlado)函數(shù)查每一個(gè)服器。一方面,如果某服務(wù)器確定不再需要駐留內(nèi)存它可以返回K讓M將它卸載。服務(wù)器通過簡單的計(jì)數(shù)來確定它是否能被卸載。下面是DllCanUnloadNow()的externUINTg_uDllRefCount; //服務(wù)器的計(jì)數(shù)HRESULT return(g_uDllRefCount>0)?S_FALSE:S_OK;}如何處理計(jì)數(shù)將在下一節(jié)涉及到具體代碼時(shí)討論。實(shí)現(xiàn)接口,從IUnknown開有必要回想一下IUnknown派生的每一個(gè)接口。因?yàn)镮UnknownCOM對(duì)象的基本特性——計(jì)數(shù)和接口查詢。當(dāng)你編寫組件對(duì)象類時(shí)(coclass),還要寫一個(gè)滿足自己需要的IUnknown實(shí)現(xiàn)。以實(shí)現(xiàn)IUnknown接口的組件對(duì)象類為例classCUnknownImpl:publicIUnknown{public: //構(gòu)造函數(shù)和析構(gòu)器 virtual~CUnknownImpl(); //IUnknown方法ULONGAddRef(); ULONGRelease)(); QueryInterface(REFIIDriid,void**ppv);protected: //對(duì)象的計(jì)數(shù)};構(gòu)造器和析構(gòu)構(gòu)造器和析構(gòu)器管理服務(wù)器的計(jì)數(shù) m_uRefCount=0; -當(dāng)創(chuàng)建新的COM對(duì)象時(shí),構(gòu)造器被調(diào)用,它增加服務(wù)器的計(jì)數(shù)以保持這個(gè)服務(wù)它減少服務(wù)器的計(jì)數(shù)。AddRef()和Release()這兩個(gè)方法控制COM對(duì)象的生命期。AddRef()很簡單:ULONGCUnknownImpl::AddRef(){ return++m_uRefCount;}AddRef()只增加對(duì)象的計(jì)數(shù)并返回更新的計(jì)數(shù)。Release()更簡單:ULONGCUnknownImpl::Release(){ULONGuRet=--m_uRefCount; if(0==m_uRefCount) //是否釋放了最后的? deletethis; 除了減少對(duì)象的計(jì)數(shù)外,如果沒有另外的明確,Release()將摧毀對(duì)象。Release()也返回更新的計(jì)數(shù)。注意Release()的實(shí)現(xiàn)假設(shè)COM對(duì)象在堆中創(chuàng)AddRef()Release()是如此內(nèi)存會(huì)很快溢出導(dǎo)致應(yīng)用程序下次存取服務(wù)器代碼時(shí)。如果你編寫多線程應(yīng)用,可能會(huì)想到使用++&InterlockedIncrement()和InterlockedDecrement()的線程安全問題。++&——用于單線程服務(wù)器很,因?yàn)榧词箍蛻舳藨?yīng)用是多線程的并從不同的線程中進(jìn)行方法調(diào)用,COM庫都會(huì)按順序進(jìn)線程都將阻塞,直到第一個(gè)方法返回。COM庫本身確保服務(wù)器一次不會(huì)被一個(gè)以上的QueryInterface()QI()COM對(duì)象請(qǐng)求不同的接IID,一個(gè)是指針的緩沖大小,如果查詢成功,QI()將接口指針地址在這個(gè)緩沖指針中。HRESULTCUnknownImpl::QueryInterface(REFIIDriid,ppv){HRESULThrRet=S_OK;QI()初始化–置*ppv為*ppv=NULL; //如果客戶端請(qǐng)求提供的接口,給*ppv.賦值 (IsEqualIID(riid,IID_IUnknown)) *ppv=(IUnknown*)this; //不提供客戶端請(qǐng) hrRet=E_NOINTERFACE; //如果返回一個(gè)接口指針。調(diào)用AddRef()增加計(jì)數(shù). if(S_OK==hrRet){((IUnknown*)*ppv)->AddRef();}returnhrRet;}在QI()中做了三件不同的事情:1NULL[*ppvNULL;]。2riid,確定組件對(duì)象類(coclass)實(shí)現(xiàn)了客戶端所請(qǐng)求接口.[if(IsEqualIID(riid,IID_IUnknown))]3、如果確實(shí)實(shí)現(xiàn)請(qǐng)求的接口,則增加COM對(duì)象的計(jì)數(shù)。[((IUnknown*)*ppv)->AddRef();]AddRef()調(diào)用 *ppv=(IUnknown*)this;要?jiǎng)?chuàng)建新的COM對(duì)象,就必須調(diào)用這個(gè)函數(shù)通知COM對(duì)象這個(gè)新成立。AddRef()IUnknown*看起來好像多余,但是在QI()*ppv有可能不是IUnknown*上面我們已經(jīng)討論了一些DLL服務(wù)器的內(nèi)部細(xì)節(jié),接下來讓我們回頭看一看當(dāng)客戶端CoCreateInstance()時(shí)是如何處理服務(wù)器的。深入CoCreateInstance()API,其作用是當(dāng)客戶端請(qǐng)求COM對(duì)象。它并沒有什么魔法,只是在一個(gè)定義良好的過程中COMCOM對(duì)象并返回所要的指針。就這些。1、客戶端程序調(diào)用CoCreateInstance(),傳遞組件對(duì)象類的CLSID以及所要 。、 庫在HKEY_CLASSES_ROOT\CLSID.鍵值下查找服務(wù)器的CLSID鍵值,這個(gè)鍵值包含服務(wù)器的信息。3、COM庫服務(wù)器DLL的全路徑并將DLL加載到客戶端的進(jìn)程空間。4、COM庫調(diào)用在服務(wù)器中一個(gè)類工廠并將它從DllGetClassObject()返回。6、COM庫在類工廠中調(diào)用CreateInstance()方法創(chuàng)建客戶端程序請(qǐng)求的COM對(duì)象。7、CreateInstance()COM服務(wù)COM服務(wù)器必須在Windows表中正確以后才能正常工作。如果你看冊(cè)表中的HKEY_CLASSES_ROOT\CLSID鍵,就會(huì)發(fā)現(xiàn)大把大把子鍵,它們就是在這個(gè)計(jì)算機(jī)上的COM服務(wù)器。當(dāng)某個(gè)COM服務(wù)器后(通常是用DllRegisterServer()進(jìn)行),就會(huì)以標(biāo)準(zhǔn)的表格式在CLSID鍵下創(chuàng)建一個(gè)鍵,它名字為服務(wù)器的GUID。下面是一個(gè)這樣的例子:這個(gè)鍵的默認(rèn)值是人可值別的組件對(duì)象類名,使用VC所帶的 在GUID鍵的子鍵中還可以其它信息。需要?jiǎng)?chuàng)建什么子鍵依賴于COM服務(wù)器的COM服務(wù)器的使用方法。對(duì)于本文例子中這個(gè)簡單的進(jìn)程內(nèi)服務(wù)器,我們InProcServer32鍵包含兩個(gè)串:這兩個(gè)串的缺省值是服務(wù)器DLL的全路徑和線程模念,這里我們指的是單線程服務(wù)器,用的模式為Apartment(即單線程公寓)。創(chuàng)建COM對(duì)象——類工COMCOM對(duì)CoCreateInstance()COM對(duì)象?,F(xiàn)在我們來看看它在服這個(gè)旁類就叫這個(gè)組件對(duì)象類的類工廠(classfactory),COM對(duì)象。之所以要一個(gè)類工廠,是因?yàn)檎Z言無關(guān)的緣故。COM本身并不創(chuàng)建對(duì)象,因?yàn)镃OM對(duì)象時(shí),COMCOM服務(wù)器請(qǐng)求類工廠。然COM對(duì)象并將它返回客戶端。它們的通訊機(jī)制由函數(shù)“類工廠”和“類對(duì)象”實(shí)際上是一回事。沒有那個(gè)單詞能精確描述類工廠的作用和COM對(duì)象,而不是COM類所為。將“類工廠”理解成“對(duì)象工廠”可能會(huì)更有助于理解(實(shí)際上MFC就是這樣理解的——它的類工廠實(shí)現(xiàn)就叫COleObjectFactory)。但“類工廠”是正式術(shù)語,所以本文也這樣用。COM庫調(diào)用DllGetClassObject()CLSID。服務(wù)器負(fù)責(zé)CLSID創(chuàng)建者各類工廠并將它返回。類工廠本身就是一個(gè)組件對(duì)象類,并且實(shí)現(xiàn)IClassFactory接口。如果DllGetClassObject()調(diào)用成功,它返回一個(gè)IClassFactoryCOMCOM庫用IClassFactory接口方法創(chuàng)建客戶COM對(duì)象實(shí)例。一下是IClassFactorystructIClassFactory:public CreateInstance(IUnknown*pUnkOuter,REFIIDriid,void**ppvObject);HRESULTLockServer(BOOLfLock);};庫增加或減少服務(wù)器的計(jì)數(shù)一個(gè)定制接口的例這個(gè)工程是一個(gè)能運(yùn)行的DLL服務(wù)器例子,對(duì)象由類工廠創(chuàng)建,此DLLCSimpleMsgBoxImpl組件對(duì)象類中實(shí)現(xiàn)了一個(gè)接口:ISimpleMsgBox。我們的新接口是ISimpleMsgBox。所有的接口多必須從IUnknown派生。這個(gè)接口只有一個(gè)方法:DoSimpleMsgBox()HRESULT。所有的方法都應(yīng)該返回HRESULT類型,并且所有返回到調(diào)用者的其它數(shù)據(jù)都應(yīng)該通過指針參數(shù)structISimpleMsgBox:publicIUnknown{ //IUnknown方法 ULONGRelease(); HRESULTQueryInterface(REFIIDriid,void**ppv); //ISimpleMsgBox方法 DoSimpleMsgBox(HWNDhwndParent,BSTRbsMessageTextdeclspec(uuid("{7D51904D-1645-4a8c-BDE0- declspec的一行將一個(gè)GUID賦值給ISimpleMsgBox,并且以后可以 uuidof操作符來獲取GUIDC++的擴(kuò)展。DoSimpleMsgBox()BSTR類型。意思是二進(jìn)制串——即定長序列COM表示。BSTRs主要用于VisualBasic和WindowsScriptingHost之類classCSimpleMsgBoxImpl:publicISimpleMsgBox virtual~CSimpleMsgBoxImpl(); IUnknown方法 ULONGAddRef(); ULONGRelease(); QueryInterface(REFIIDriid,void**ppv); //ISimpleMsgBox方法HRESULTDoSimpleMsgBox(HWNDhwndParent,BSTRbsMessageText ULONGdeclspec(uuid("{7D51904E-1645-4a8c-BDE0-CSimpleMsgBoxImpl;SimpleMsgBoxCOM應(yīng)該用下面這樣的代碼:ISimpleMsgBox*pIMsgBox;HRESULThr;//組件對(duì)象CLSIDhr=CoCreateInstance( //非聚 //進(jìn)程內(nèi)服務(wù) uuidof(ISimpleMsgBox),//所請(qǐng)求接口的IID(void**)&pIMsgBox 類工廠實(shí)我們的類工廠SimpleMsgBoxCSimpleMsgBoxClassFactoryclassCSimpleMsgBoxClassFactory:publicIClassFactory{public: virtual//IUnknown方法 ULONGAddRef(); ULONGRelease(); QueryInterface(REFIIDriid,void**ppv); //IClassFactory方法HRESULTCreateInstance(IUnknown*pUnkOuter,REFIIDriid,void**ppv); HRESULTLockServer(BOOLfLock);protected: m_uRefCount;};IUnknownIClassFactory,LockServer(),看起來相當(dāng)更簡單:HRESULTCSimpleMsgBoxClassFactory::LockServer(BOOLfLock){ fLock?g_uDllLockCount++:g_uDllLockCount--; HRESULTCSimpleMsgBoxClassFactory::CreateInstance(IUnknown* ppv第一個(gè)參數(shù)pUnkOuter只用于聚合的新對(duì)象,指向“外部的”COM對(duì)象,也就是說,riid和ppv與在QueryInterface()中的用法一樣——它們是客戶端所請(qǐng)求的接口IID和接口指針的指針緩沖。下面是CreateInstance()的實(shí)現(xiàn)。它從參數(shù)的有效性檢查和參數(shù)的初始化開始。HRESULTCSimpleMsgBoxClassFactory::CreateInstance(IUnknown* ppvpUnkOuterNULL.if(NULL!=pUnkOuter)returnCLASS_E_NOAGGREGATION;//檢查ppvvoid*類型if(IsBadWritePtr(ppv,sizeof(void*)returnE_POINTER;*ppvNULL;檢查完參數(shù)的有效性后,就可以創(chuàng)建一個(gè)新的對(duì)象了。CSimpleMsgBoxImpl*pMsgbox;//COMpMsgbox=newCSimpleMsgBoxImpl;if(==pMsgbox) returnE_OUTOFMEMORY;最后,用QI()來查詢客戶端所請(qǐng)求的新對(duì)象的接口。如果QI()失敗,則這個(gè)對(duì)象不可用,必須刪除它。HRESULThrRet; //用QI查詢客戶端所請(qǐng)求的對(duì)象接口 hrRet=pMsgbox->QueryInterface(riid,ppv); //如果QI失敗,則刪除這個(gè)COM對(duì)象,因?yàn)榭蛻舳瞬荒苁褂盟蛻舳藳]有 if(FAILED(hrRet)) deletepMsgbox; returnhrRet;}深入現(xiàn)在讓我們深入DllGetClassObject()HRESULTDllGetClassObject(REFCLSIDrclsid,REFIIDriid,void**ppv);rclsidCLSID。這個(gè)函數(shù)必須返回指定組件對(duì)象類的riidppv類似QI()riidDllGetClassObject()也創(chuàng)建一個(gè)新的COM對(duì)象(類工廠),所以代碼與HRESULTDllGetClassObject(REFCLSIDrclsid,REFIIDriid,void**ppv){ //檢查客戶端所要的CSimpleMsgBoxImpl類工廠 (!InlineIsEqualGUID(rclsid,uuidof(CSimpleMsgBoxImpl)returnCLASS_E_CLASSNOTAVAILABLE; //檢查指針ppv是不是void*類型if(IsBadWritePtr(ppv,sizeof(void*) return*ppv=第一個(gè)if語句檢查rclsid參數(shù)。我們的服務(wù)器只有一個(gè)組件對(duì)象類,所以rclsid必須是CSimpleMsgBoxImpl類的CLSID。 uuidof操作符獲取先前在 declspec(uuid())中指定的CsimpleMsgBoxImpl類的GUID下一步是創(chuàng)建一個(gè)類工廠對(duì)象。CSimpleMsgBoxClassFactory*//構(gòu)造一個(gè)新的類工廠對(duì)象 pFactory=new if(NULL==pFactory) CreateInstance()CreateInstance()中是調(diào)用了QI()COM對(duì)象。COM對(duì)象的客戶端,調(diào)用AddRef()進(jìn)行一次引用計(jì)數(shù)(COUNT=1)。然后調(diào)用QI()QI()調(diào)用成功,它將再一次用AddRef()進(jìn)行計(jì)數(shù)(COUNT=2)。如果QI()調(diào)用失敗。計(jì)數(shù)將保持為原來的值(COUNT=1)。在QI()Release()來釋放它。如果QI()調(diào)用失敗,這個(gè)對(duì)象將自我刪除(因?yàn)橛?jì)數(shù)將為零),所以最終結(jié)果是一樣//調(diào)用AddRef()增加一個(gè)類工廠計(jì)數(shù),因?yàn)槲覀冋谑褂胮Factory->AddRef();HRESULThrRet; //調(diào)用QI()查詢客戶端所要的類工 hrRet=pFactory->QueryInterface(riid,ppv); 使用完類工廠后調(diào)用Release()釋放它 再談前面討論過QI()QI(),因?yàn)樗且粋€(gè)很現(xiàn)實(shí)COM對(duì)象實(shí)現(xiàn)的不光是IUnknown。首先進(jìn)行的是對(duì)ppv緩沖的有效HRESULTCSimpleMsgBoxClassFactory::QueryInterface(REFIIDriid,void**ppv){HRESULThrRet=S_OK;ppvvoid*類型if(IsBadWritePtr(ppv,sizeof(void*)))return//標(biāo)準(zhǔn)的QI初始化,將賦值為NULL. *ppv=NULL;接下來檢查riid,看看它是不是類工廠實(shí)現(xiàn)的接口之一:IUnknown或IclassFactory。 //如果客戶端請(qǐng)求一個(gè)有效接口則扶植給*ppv. if(InlineIsEqualGUID(riid,IID_IUnknown)) *ppv=(IUnknown*)this; *ppv=(IClassFactory*) hrRet=E_NOINTERFACE; }最后,如果riid是有效接口,則調(diào)用接口的AddRef(),然后返回。 有效接口指針,則調(diào)用 if(S_OK==hrRet ((IUnknown*)*ppv)- returnISimpleMsgBox實(shí)最后的也是必不可少的一關(guān)是ISimpleMsgBox實(shí)現(xiàn),我們的代碼只實(shí)現(xiàn)ISimpleMsgBox的方法DoSimpleMsgBox()。首先用微軟的擴(kuò)展類_bstr_t將bsMessageText轉(zhuǎn)換成TCHAR串。HRESULTCSimpleMsgBoxImpl::DoSimpleMsgBox(HWNDhwndParent,BSTRbsMessageText){_bstr_tbsMsg=bsMessageText;LPCTSTRszMsg=(TCHAR*)bsMsg; //如果需要的話,用_bstr_t將串轉(zhuǎn)換為ANSI做完轉(zhuǎn)換 MessageBox(hwndParent,szMsg,_T("SimpleMessageBox"),MB_OK return使用服務(wù)器的客戶我們已經(jīng)完成了一個(gè)超級(jí)棒的COM服務(wù)器,如何使用它呢?我們的接口一個(gè)定制接口,也就是說它只能被C或C++客戶端使用。(如果在組件對(duì)象類中同時(shí)實(shí)現(xiàn)IDispatch接口,那我們幾乎就可以在任何客戶端環(huán)境中——VisualBasic,WindowsScriptingHost,Web頁面,PerlScript等使用COM對(duì)象。有關(guān)這方面的內(nèi)容我們留待另外的文章討論)。本文提供了一個(gè)使用ISimpleMsgBox的例子程序。這個(gè)程序基于用Win32應(yīng)用程序向?qū)Ы⒌?oWorld例子。文件菜單包含兩個(gè)測試服務(wù)器令:TestMsgBoxCOMServer菜單命令創(chuàng)建CSimpleMsgBoxImpl對(duì)象并調(diào)用DoSimpleMsgBox()。因?yàn)檫@是個(gè)簡單的方法,要寫的代碼不長。我們先用CoCreateInstance()創(chuàng)建一個(gè)COM對(duì)象。voidDoMsgBoxTest(HWNDhMainWnd){ISimpleMsgBox*pIMsgBox;HRESULThr;hr= uuidof(CSimpleMsgBoxImpl), 組件對(duì)象類的 非聚合 只使用進(jìn)程內(nèi)服務(wù)uuidof(ISimpleMsgBox), (void**)&pIMsgBox); //容納接口指針的緩沖 if(FAI

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論