欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
VC知識庫文章 - COM 組件設計與應用(四)——簡(jiǎn)單調用組件

  在 VCKBASE 的頂力支持下,在各位網(wǎng)友回帖的鼓勵下,我才能順利完成系列論文的前三回。書(shū)到本回,我們終于開(kāi)始寫(xiě)代碼啦。寫(xiě)點(diǎn)啥那?恩,有了!咱們先從如何調用現成的簡(jiǎn)單的組件開(kāi)始吧,同時(shí)也順便介紹一些相關(guān)的知識。


二、組件的啟動(dòng)和釋放


  在第三回中,大家用“小本本”記錄了一個(gè)原則:COM 組件是運行在分布式環(huán)境中的 。于是,如何啟動(dòng)組件立刻就遇到了嚴重的問(wèn)題,大家看這段代碼:

      p = new 對象;      p->對象函數();      delete p;

  這樣的代碼再熟悉不過(guò)了,在本地進(jìn)程中運行是不會(huì )有問(wèn)題的。但是你想想,如果這個(gè)對象是在“地球另一邊”的計算機上,結果會(huì )如何?嘿嘿,C++ 在設計 new 的時(shí)候,可沒(méi)有考慮遠程的實(shí)現呀(計算機語(yǔ)言當然不會(huì ),也沒(méi)必要去設計)。因此啟動(dòng)組件、調用接口的功能,當然就由 COM 系統來(lái)實(shí)現了。


圖一 組件調用機制

  由上圖可以看出,當調用組件的時(shí)候,其實(shí)是依靠代理(運行在本地)和存根(運行在遠端)之間的通訊完成的。具體來(lái)說(shuō),當客戶(hù)程序通過(guò) CoCreateInstance() 函數啟動(dòng)組件,則代理接管該調用,它和存根通訊,存根則它所在的本地(相對于客戶(hù)程序來(lái)說(shuō)就是遠程了)執行 new 操作加載對象。對于初學(xué)者,你可以不用理它,代理和存根對我們來(lái)說(shuō)是透明的。只要大約知道是怎么一回事就一切OK了。
  問(wèn)題又來(lái)了,這個(gè)遠程的對象什么時(shí)候消滅呢?在第二回介紹接口概念的時(shí)候,當時(shí)我們特意忽略了兩個(gè)函數,就是IUnknown::AddRef()和IUnknown::Release(),從函數名就能猜到了,一個(gè)是對內部引用記數器(Ref)加1,一個(gè)是釋放(減1),當記數器減為0的時(shí)候,就是釋放的機會(huì )啦??雌饋?lái)很復雜,沒(méi)辦法,因為這是在介紹原理。其實(shí)在我們寫(xiě)程序的時(shí)候到比較簡(jiǎn)單,請大家遵守幾個(gè)原則:
  1、啟動(dòng)組件得到一個(gè)接口指針(Interface)后,不要調用AddRef()。因為系統知道你得到了一個(gè)指針,所以它已經(jīng)幫你調用了AddRef()函數;
  2、通過(guò)QueryInterface()得到另一個(gè)接口指針后,不要調用AddRef()。因為......和上面的道理一樣;
  3、當你把接口指針賦值給(保存到)另一個(gè)變量中的時(shí)候,請調用AddRef();
  4、當不需要再使用接口指針的時(shí)候,務(wù)必執行Release()釋放;
  5、當使用智能指針的時(shí)候,可以省略指針的維護工作;(注1)


三、內存分配和釋放


  自從學(xué)習了C語(yǔ)言,老師就教導我們說(shuō):對于動(dòng)態(tài)內存的申請和釋放,一定要遵守“誰(shuí)申請,誰(shuí)釋放”的原則。在此原則的指導下,不僅是我、不僅是你,就連特級大師都設計了這樣怪怪的函數:
 

函數說(shuō)明評論
GetWindowText(HWND,LPTSTR,int)取得窗口標題。需要在參數中給出保存標題所使用的內存指針,和這塊內存的尺寸。暈!我又不知道窗口標題的長(cháng)度,居然還要我提供尺寸?!沒(méi)辦法,只能估摸著(zhù)給一個(gè)大一些的尺寸吧。
sprintf(char *,const char *,...)格式化一個(gè)字符串。這個(gè)函數不用給出緩沖區的長(cháng)度啦。恩,雖然不用給出長(cháng)度了,但你敢給個(gè)小尺寸嗎?哼!
int CListBox::GetTextLen(int)
CListBox::GetText(int,LPTSTR)
取得列表窗中子項目的標題。需要調用兩個(gè)函數,先取得長(cháng)度,然后分配內存,再實(shí)際取得標題內容。真煩!

  說(shuō)實(shí)在的,不但函數調用者感覺(jué)別扭,就連函數設計者心情也不會(huì )爽的,而這一切都是為了滿(mǎn)足所謂“誰(shuí)申請,誰(shuí)釋放”的原則。 解決這個(gè)問(wèn)題最好的方式就是:函數內部根據實(shí)際需要動(dòng)態(tài)申請內存,而調用者負責釋放。這雖然違背了上述原則,但 COM 從方便性和效率出發(fā),確實(shí)是這么設計的。
 

 C語(yǔ)言C++語(yǔ)言Windows 平臺COMIMalloc 接口BSTR
申請malloc()newGlobalAlloc()CoTaskMemAlloc()Alloc()SysAllocString()
重新申請realloc() GlobalReAlloc()CoTaskRealloc()Realloc()SysReAllocString()
釋放free()deleteGlobalFree()CoTaskMemFree()Free()SysFreeString()

  以上這些函數必須要按類(lèi)型配合使用(比如:new 申請的內存,則必須用 delete 釋放)。在 COM 內部,當然你可以隨便使用任何類(lèi)型的內存分配釋放函數,但組件如果需要與客戶(hù)進(jìn)行內存的交互,則必須使用上表中的后三類(lèi)函數族。
  1、BSTR 內存在上回書(shū)中,已經(jīng)有比較豐富的介紹了,不再重復;
  2、CoTaskXXX()函數族,其本質(zhì)上就是調用C語(yǔ)言的函數(malloc...);
  3、IMalloc 接口又是對 CoTaskXXX() 函數族的一個(gè)包裝。包裝后,同時(shí)增強了一些功能,比如:IMalloc::GetSize()可以取得尺寸,使用 IMallocSpy 可以監視內存的使用;


四、參數傳遞方向

  在C語(yǔ)言的函數聲明中,尤其當參數為指針的時(shí)候,你是看不出它傳遞方向的。比如:
void fun(char * p1, int * p2); 請問(wèn),p1、p2 哪個(gè)是入參?哪個(gè)是出參?甚或都是入參或都是出參?由于牽扯到內存分配和釋放等問(wèn)題,COM 需要明確標注參數方向。以后我們寫(xiě)程序,就類(lèi)似下面的樣子:

      HRESULT Add([in] long n1, [in] long n2, [out] long *pnSum);  // IDL文件(注2)      STDMETHOD(Add)(/*[in]*/ long n1, /*[in]*/ long n2, /*[out]*/ long *pnSum);  // .h文件
如果參數是動(dòng)態(tài)分配的內存指針,那么遵守如下的規定:
 
方向申請人釋放人提示
[in]調用者調用者組件接收指針后,不能重新分配內存
[out]組件調用者組件返回指針后,調用者“愛(ài)咋咋地”(注3)
[in,out]調用者調用者組件可以重新分配內存


五、示例程序

  示例一、由 CLSID 得到 ProgID。(程序以 word 為例子。如果運行不正確,嘿嘿,你沒(méi)有安裝 word 吧?)

	::CoInitialize( NULL );	HRESULT hr;	// {000209FF-0000-0000-C000-000000000046} = word.application.9	CLSID clsid = {0x209ff,0,0,{0xc0,0,0,0,0,0,0,0x46}};	LPOLESTR lpwProgID = NULL;		hr = ::ProgIDFromCLSID( clsid, &lpwProgID );	if ( SUCCEEDED(hr) )	{		::MessageBoxW( NULL, lpwProgID, L"ProgID", MB_OK );		IMalloc * pMalloc = NULL;		hr = ::CoGetMalloc( 1, &pMalloc );  // 取得 IMalloc		if ( SUCCEEDED(hr) )		{			pMalloc->Free( lpwProgID );  // 釋放ProgID內存			pMalloc->Release();          // 釋放IMalloc		}	}	::CoUninitialize();

示例二、如何使用“瀏覽文件夾”選擇對話(huà)窗。
CString BrowseFolder(HWND hWnd, LPCTSTR lpTitle){    // 調用 SHBrowseForFolder 取得目錄(文件夾)名稱(chēng)    // 參數 hWnd: 父窗口句柄    // 參數 lpTitle: 窗口標題        char szPath[MAX_PATH]={0};    BROWSEINFO m_bi;    m_bi.ulFlags = BIF_RETURNONLYFSDIRS  | BIF_STATUSTEXT;    m_bi.hwndOwner = hWnd;    m_bi.pidlRoot = NULL;    m_bi.lpszTitle = lpTitle;    m_bi.lpfn = NULL;    m_bi.lParam = NULL;    m_bi.pszDisplayName = szPath;    LPITEMIDLIST pidl = ::SHBrowseForFolder( &m_bi );    if ( pidl )    {        if( !::SHGetPathFromIDList ( pidl, szPath ) )  szPath[0]=0;        IMalloc * pMalloc = NULL;        if ( SUCCEEDED ( ::SHGetMalloc( &pMalloc ) ) )  // 取得IMalloc分配器接口        {            pMalloc->Free( pidl );    // 釋放內存            pMalloc->Release();       // 釋放接口        }    }    return szPath;}
示例三、在窗口中顯示一幅 JPG 圖象。
void CxxxView::OnDraw(CDC* pDC){	::CoInitialize(NULL);  // COM 初始化	HRESULT hr;	CFile file;		file.Open( "c:\\aa.jpg", CFile::modeRead | CFile::shareDenyNone );  // 讀入文件內容	DWORD dwSize = file.GetLength();	HGLOBAL hMem = ::GlobalAlloc( GMEM_MOVEABLE, dwSize );	LPVOID lpBuf = ::GlobalLock( hMem );	file.ReadHuge( lpBuf, dwSize );	file.Close();	::GlobalUnlock( hMem );	IStream * pStream = NULL;	IPicture * pPicture = NULL;		// 由 HGLOBAL 得到 IStream,參數 TRUE 表示釋放 IStream 的同時(shí),釋放內存	hr = ::CreateStreamOnHGlobal( hMem, TRUE, &pStream );	ASSERT ( SUCCEEDED(hr) );		hr = ::OleLoadPicture( pStream, dwSize, TRUE, IID_IPicture, ( LPVOID * )&pPicture );	ASSERT(hr==S_OK);		long nWidth,nHeight;  // 寬高,MM_HIMETRIC 模式,單位是0.01毫米	pPicture->get_Width( &nWidth );    // 寬	pPicture->get_Height( &nHeight );  // 高		////////原大顯示//////	CSize sz( nWidth, nHeight );	pDC->HIMETRICtoDP( &sz );  // 轉換 MM_HIMETRIC 模式單位為 MM_TEXT 像素單位	pPicture->Render(pDC->m_hDC,0,0,sz.cx,sz.cy,		0,nHeight,nWidth,-nHeight,NULL);			////////按窗口尺寸顯示//////////	CRect rect;	GetClientRect(&rect);//	pPicture->Render(pDC->m_hDC,0,0,rect.Width(),rect.Height(),//		0,nHeight,nWidth,-nHeight,NULL);	if ( pPicture ) pPicture->Release();// 釋放 IPicture 指針	if ( pStream ) pStream->Release();  // 釋放 IStream 指針,同時(shí)釋放了 hMem		::CoUninitialize();}
示例四、在桌面建立快捷方式
    在閱讀代碼之前,先看一下關(guān)于“快捷方式”組件的結構示意圖。

 

圖二、快捷方式組件的接口結構示意圖

  從結構圖中可以看出,“快捷方式”組件(CLSID_ShellLink),有3個(gè)(其實(shí)不止)接口,每個(gè)接口完成一組相關(guān)功能的函數。IShellLink 接口(IID_IShellLink)提供快捷方式的參數讀寫(xiě)功能(見(jiàn)圖三),IPersistFile 接口(IID_IPersistFile)提供快捷方式持續性文件的讀寫(xiě)功能。對象的持續性(注5),是一個(gè)非常常用,并且功能強大的接口家族。但今天,我們只要了解其中兩函數,就可以了:IPersistFile::Save()和IPersistFile:Load()。(注6)

 

圖三、快捷方式中的各種屬性
#include < atlconv.h >void CreateShortcut(LPCTSTR lpszExe, LPCTSTR lpszLnk){	// 建立塊捷方式	// 參數 lpszExe: EXE 文件全路徑名	// 參數 lpszLnk: 快捷方式文件全路徑名		::CoInitialize( NULL );	IShellLink * psl = NULL;	IPersistFile * ppf = NULL;	HRESULT hr = ::CoCreateInstance(  // 啟動(dòng)組件		CLSID_ShellLink,      // 快捷方式 CLSID		NULL,                 // 聚合用(注4)		CLSCTX_INPROC_SERVER, // 進(jìn)程內(Shell32.dll)服務(wù)		IID_IShellLink,       // IShellLink 的 IID		(LPVOID *)&psl );     // 得到接口指針	if ( SUCCEEDED(hr) )	{		psl->SetPath( lpszExe );  // 全路徑程序名//		psl->SetArguments();      // 命令行參數//		psl->SetDescription();    // 備注//		psl->SetHotkey();         // 快捷鍵//		psl->SetIconLocation();   // 圖標//		psl->SetShowCmd();        // 窗口尺寸				// 根據 EXE 的文件名,得到目錄名		TCHAR szWorkPath[ MAX_PATH ];		::lstrcpy( szWorkPath, lpszExe );		LPTSTR lp = szWorkPath;		while( *lp )    lp++;		while( ‘‘\\‘‘ != *lp )    lp--;		*lp=0;		// 設置 EXE 程序的默認工作目錄		psl->SetWorkingDirectory( szWorkPath );		hr = psl->QueryInterface(  // 查找持續性文件接口指針			IID_IPersistFile,      // 持續性接口 IID			(LPVOID *)&ppf );      // 得到接口指針		if ( SUCCEEDED(hr) )		{			USES_CONVERSION;       // 轉換為 UNICODE 字符串			ppf->Save( T2COLE( lpszLnk ), TRUE );  // 保存		}	}	if ( ppf )	ppf->Release();	if ( psl )	psl->Release();	::CoUninitialize();}void OnXXX(){	CreateShortcut(		_T("c:\\winnt\\notepad.exe"),  // 記事本程序。注意,你的系統是否也是這個(gè)目錄?		_T("c:\\Documents and Settings\\Administrator\\桌面\\我的記事本.lnk")	);	// 桌面上建立快捷方式(lnk)文件的全路徑名。注意,你的系統是否也是這個(gè)目錄?	// 如果用程序實(shí)現尋找桌面的路徑,則可以查注冊表	// HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders}

七、小結


  本回介紹的內容比較實(shí)用。大家不要只抄襲代碼,而一定要理解它。結合 MSDN 的說(shuō)明去思索代碼、理解其含義。好了,想方設法把代碼忘掉!三天后(如過(guò)你還沒(méi)有忘記,那就再過(guò)三天),你在不參考示例代碼,但可以隨便翻閱 MSDN 的情況下,自己能獨立地再次完成這四個(gè)例程,那么恭喜你,你已經(jīng)入門(mén)了:0) 從下回開(kāi)始,我們要用 ATL 做 COM 的開(kāi)發(fā)工作啦,您老人家準備好了嗎?


作業(yè),留作業(yè)啦......
  1、你已經(jīng)學(xué)會(huì )如何建立快捷方式了,那么你知道怎么讀取它的屬性嗎?(如果寫(xiě)不出這個(gè)程序,那么你就不用繼續學(xué)習了。因為......動(dòng)點(diǎn)腦筋呀!我還沒(méi)有見(jiàn)過(guò)象你這么笨的學(xué)生呢?。?br>  2、示例程序三中使用了 IPicture 接口顯示一個(gè) JPG 圖象。那么你現在去完成一個(gè)功能,把 JPG 文件轉換為 BMP 文件。

注1:智能指針的概念和用法,后續介紹。
注2:IDL 文件,下回就要介紹啦。
注3:東北話(huà),想干什么都可以,反正我不管啦。
注4:聚合,也許在第30回中介紹吧:-)
注5:持續性,IPersistXXXXXX是一個(gè)非常強大的接口家族,后續介紹。
注6:想知道 IShellLink、IPersistFile接口的所有函數嗎?別愣著(zhù),快去看MSDN呀......

本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
淺談COM對象的內存布局
com的知識點(diǎn)
COM組件設計與應用(十三)
必需要掌握哪些COM理論知識--blueleopard的博客
COM聚合
COM筆記_QueryInterface函數
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久