青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

隨筆-250  評(píng)論-20  文章-55  trackbacks-0

起步篇

??? 在本文的第一部分,我們簡(jiǎn)要介紹了ATL的一些背景知識(shí)以及ATL所面向的開(kāi)發(fā)技術(shù)和環(huán)境。在這一部分 將開(kāi)始走進(jìn)ATL,講述ATL編程的基本方法、原則和必須要注意的問(wèn)題。
??? 理解ATL最容易的方法是考察它對(duì)客戶端編程的支持。對(duì)于COM編程新手而言,一個(gè)棘手的主要問(wèn)題之一是正確管理接口指針的引用計(jì)數(shù)。COM的引用計(jì)數(shù)法則是沒(méi)有運(yùn)行時(shí)強(qiáng)制 性的,也就是說(shuō)每一個(gè)客戶端必須保證對(duì)對(duì)象的承諾。
??? 有經(jīng)驗(yàn)的COM編程者常常習(xí)慣于使用文檔中(如《Inside OLE》)提出的標(biāo)準(zhǔn)模式。調(diào)用某個(gè)函數(shù)或方法,返回接口指針,在某個(gè)時(shí)間范圍內(nèi)使用這個(gè)接口指針,然后釋放它。下面是使用這種模式的代碼例子:

void f(void) {
   IUnknown *pUnk = 0;
   // 調(diào)用 
   HRESULT hr = GetSomeObject(&pUnk);
   if (SUCCEEDED(hr)) {
   // 使用
     UseSomeObject(pUnk);
 // 釋放
     pUnk->Release();
   }
}
    
??? 這個(gè)模式在COM程序員心中是如此根深蒂固,以至于他們常常不寫(xiě)實(shí)際使用指針的語(yǔ)句,而是先在代碼塊末尾敲入Release語(yǔ)句。這很像C程序員使用switch語(yǔ)句時(shí)的條件反射一樣,先敲入break再說(shuō)。
??? 其實(shí)調(diào)用Release實(shí)在不是什么可怕的負(fù)擔(dān),但是,客戶端程序員面臨兩個(gè)相當(dāng)嚴(yán)重的問(wèn)題。第一個(gè)問(wèn)題與獲得多接口指針有關(guān)。如果某個(gè)函數(shù)需要在做任何實(shí)際工作之前獲得三個(gè)接口指針,也就是說(shuō)在第一個(gè)使用指針的語(yǔ)句之前必須要由三個(gè)調(diào)用語(yǔ)句。在書(shū)寫(xiě)代碼時(shí),這常常意味著程序員需要寫(xiě)許多嵌套條件語(yǔ)句,如:
void f(void) {
  IUnknown *rgpUnk[3];
  HRESULT hr = GetObject(rgpUnk);
  if (SUCCEEDED(hr)) {
    hr = GetObject(rgpUnk + 1);
    if (SUCCEEDED(hr)) {
      hr = GetObject(rgpUnk + 2);
      if (SUCCEEDED(hr)) {
        UseObjects(rgpUnk[0], rgpUnk[1],
                     rgpUnk[2]);
        rgpUnk[2]->Release();
      }
      rgpUnk[1]->Release();
    }
    rgpUnk[0]->Release();
  }
}
    
??? 像這樣的語(yǔ)句常常促使程序員將TAB鍵設(shè)置成一個(gè)或兩個(gè)空格,甚至情愿使用大一點(diǎn)的顯示器。但事情并不總是按你想象的那樣,由于種種原因項(xiàng)目團(tuán)隊(duì)中的COM組件編程人員往往得不到 所想的硬件支持,而且在公司確定關(guān)于TAB鍵的使用標(biāo)準(zhǔn)之前,程序員常常求助于使用有很大爭(zhēng)議但仍然有效的“GOTO”語(yǔ)句:
void f(void) {
   IUnknown *rgpUnk[3];
   ZeroMemory(rgpUnk, sizeof(rgpUnk));
   if (FAILED(GetObject(rgpUnk))) 
     goto cleanup;
   if (FAILED(GetObject(rgpUnk+1))) 
     goto cleanup;
   if (FAILED(GetObject(rgpUnk+2))) 
     goto cleanup;
 
   UseObjects(rgpUnk[0], rgpUnk[1], rgpUnk[2]);
 
 cleanup:
   if (rgpUnk[0]) rgpUnk[0]->Release();
   if (rgpUnk[1]) rgpUnk[1]->Release();
   if (rgpUnk[2]) rgpUnk[2]->Release();
}    
這樣的代碼雖然不那么專(zhuān)業(yè),但至少減少了屏幕的水平滾動(dòng)。
使用以上這些代碼段潛在著更加棘手的問(wèn)題,那就是在碰到C++異常時(shí)。如果函數(shù)UseObjects丟出異常,則釋放指針的代碼被完全屏蔽掉了。 解決這個(gè)問(wèn)題的一個(gè)方法是使用Win32的結(jié)構(gòu)化異常處理(SEH)進(jìn)行終結(jié)操作:
void f(void) {
   IUnknown *rgpUnk[3];
   ZeroMemory(rgpUnk, sizeof(rgpUnk));
   __try {
    if (FAILED(GetObject(rgpUnk))) leave;
    if (FAILED(GetObject(rgpUnk+1))) leave;
    if (FAILED(GetObject(rgpUnk+2))) leave;
 
    UseObjects(rgpUnk[0], rgpUnk[1], rgpUnk[2]);
   } __finally {
    if (rgpUnk[0]) rgpUnk[0]->Release();
    if (rgpUnk[1]) rgpUnk[1]->Release();
    if (rgpUnk[2]) rgpUnk[2]->Release();
}
??? 可惜Win32 SHE在C++中的表現(xiàn)并不如想象得那么好。較好的方法是使用內(nèi)建的C++異常處理模型,同時(shí)停止使用沒(méi)有加工過(guò)的指針。標(biāo)準(zhǔn)C++庫(kù)有一個(gè)類(lèi):auto_ptr,在其析構(gòu)函數(shù)中定 死了一個(gè)操作指針的delete調(diào)用(即使在出現(xiàn)異常時(shí)也能保證調(diào)用)。與之類(lèi)似,ATL有一個(gè)COM智能指針,CComPtr,它的析構(gòu)函數(shù)會(huì)正確調(diào)用Release。
??? CComPtr類(lèi)實(shí)現(xiàn)客戶端基本的COM引用計(jì)數(shù)模型。CComPtr有一個(gè)數(shù)據(jù)成員,它是一個(gè)未經(jīng)過(guò)任何加工的COM接口指針。其類(lèi)型被作為模板參數(shù)傳遞:
      CComPtr<IUnknown> unk;
      CComPtr<IClassFactory> cf;
??? 缺省的構(gòu)造函數(shù)將這個(gè)原始指針數(shù)據(jù)成員初始化為NULL。智能指針也有構(gòu)造函數(shù),它的參數(shù)要么是原始指針,要么是相同類(lèi)型的智能參數(shù)。不論哪種情況,智能指針都調(diào)用AddRef控制引用。CComPtr的賦值操作符 既可以處理原始指針,也可以處理智能指針,并且在調(diào)用新分配指針的AddRef之前自動(dòng)釋放保存的指針。最重要的是,CComPtr的析構(gòu)函數(shù)釋放保存的接口(如果非空)。請(qǐng)看下列代碼:
void f(IUnknown *pUnk1, IUnknown *pUnk2) {
    // 如果非空,構(gòu)造函數(shù)調(diào)用pUnk1的AddRef 
    CComPtr unk1(pUnk1);
    // 如果非空,構(gòu)造函數(shù)調(diào)用unk1.p的AddRef
    CComPtr unk2 = unk1;
    // 如果非空,operator= 調(diào)用unk1.p的Release并且
    //如果非空,調(diào)用unk2.p的AddRef
    unk1 = unk2;
    //如果非空,析構(gòu)函數(shù)釋放unk1 和 unk2
}
??? 除了正確實(shí)現(xiàn)COM的AddRef 和 Release規(guī)則之外,CComPtr還允許實(shí)現(xiàn)原始和智能指針的透明操作,參見(jiàn)附表二所示。也就是說(shuō)下面的代碼按照你所想象的方式運(yùn)行:
void f(IUnknown *pUnkCO) {
    
    CComPtr cf;
    
    HRESULT hr;
    
    // 使用操作符 & 獲得對(duì) &cf.p 的存取
    hr = pUnkCO->QueryInterface(IID_IClassFactory,(void**)&cf);
    if (FAILED(hr)) throw hr;
    
    CComPtr unk;
    
    // 操作符 -> 獲得對(duì)cf.p的存取
    // 操作符 & 獲得對(duì) &unk.p的存取
    hr = cf->CreateInstance(0, IID_IUnknown, (void**)&unk);
    
    if (FAILED(hr)) throw hr;
    
    // 操作符 IUnknown * 返回 unk.p
    UseObject(unk);
    
    // 析構(gòu)函數(shù)釋放unk.p 和 cf.p
}
??? 除了缺乏對(duì)Release的顯式調(diào)用外,這段代碼像是純粹的COM代碼。有了CComPtr類(lèi)的武裝,前面所遇到的麻煩問(wèn)題頓時(shí)變得簡(jiǎn)單了:
void f(void) {
CComPtr<IUnknown> rgpUnk[3];
if (FAILED(GetObject(&rgpUnk[0]))) return;
if (FAILED(GetObject(&rgpUnk[1]))) return;
if (FAILED(GetObject(&rgpUnk[2]))) return;
UseObjects(rgpUnk[0], rgpUnk[1], rgpUnk[2]);
}
由于CComPtr對(duì)操作符重載用法的擴(kuò)展,使得代碼的編譯和運(yùn)行無(wú)懈可擊。
??? 假定模板類(lèi)知道它所操縱的指針類(lèi)型,你可能會(huì)問(wèn):那為什么智能指針不能在它的功能操作符或構(gòu)造函數(shù)中自動(dòng)調(diào)用QueryInterface,從而更有效地包裝IUnknown呢?在Visual C++ 5.0出來(lái)以前,沒(méi)有辦法將某個(gè)接口的GUID與它的本身的C++類(lèi)型關(guān)聯(lián)起來(lái)——Visual C++ 5.0用私有的declspec將某個(gè)IID與一個(gè)接口定義綁定在一起。因?yàn)锳TL的設(shè)計(jì) 考慮到了它要與大量不同的C++編譯器一起工作,它需要用與編譯器無(wú)關(guān)的手段提供GUID。下面我們來(lái)探討另一個(gè)類(lèi)——CComQIPtr類(lèi)。
??? CComQIPtr與CComPtr關(guān)系很密切(實(shí)際上,它只增加了兩個(gè)成員函數(shù))。CComQIPtr必須要兩個(gè)模板參數(shù):一個(gè)是被操縱的指針類(lèi)型 ,另一個(gè)是對(duì)應(yīng)于這個(gè)指針類(lèi)型的GUID。例如,下列代碼聲明了操縱IDataObject 和IPersist接口的智能指針:
      CComQIPtr<IDataObject, &IID_IDataObject> do;
      CComQIPtr<IPersist, &IID_IPersist> p;
??? CComQIPtr的優(yōu)點(diǎn)是它有重載的構(gòu)造函數(shù)和賦值操作符。同類(lèi)版本(例如,接受相同類(lèi)型的接口)僅僅AddRef右邊的賦值/初始化操作。這實(shí)際上就是CComPtr的功能。異類(lèi)版本(接受類(lèi)型不一致的接口)正確調(diào)用QueryInterface來(lái)決定是否這個(gè)對(duì)象確實(shí)支持所請(qǐng)求的接口:
  
      void f(IPersist *pPersist) {
          CComQIPtr<IPersist, &IID_IPersist> p;
          // 同類(lèi)賦值 - AddRef''s
          p = pPersist;
    
          CComQIPtr<IDataObject, &IID_IDataObject> do;
          // 異類(lèi)賦值 - QueryInterface''s
          do = pPersist;
      }
 
??? 在第二種賦值語(yǔ)句中,因?yàn)閜Persist是非IDataObject *類(lèi)型,但它是派生于IUnknown的接口指針,CComQIPtr通過(guò)pPersist調(diào)用QueryInterface來(lái)試圖獲得這個(gè)對(duì)象的IDataObject接口指針。如果QueryInterface調(diào)用成功,則此智能指針將含有作為結(jié)果的原始IDataObject指針。如果QueryInterface調(diào)用失敗,則do.p將被置為null。如果QueryInterface返回的HRESULT值很重要,但又沒(méi)有辦法從賦值操作獲得其值時(shí),則必須顯式調(diào)用QueryInterface。
??? 既然有了CComQIPtr,那為什么還要CComPtr呢?由幾個(gè)理由:首先,ATL最初的發(fā)布版本只支持CComPtr,所以它就一直合法地保留下來(lái)了。其二(也是最重要的理由),由于重載的構(gòu)造函數(shù)和賦值操作,對(duì)IUnknown使用CComQIPtr是非法的。因?yàn)樗蠧OM接口的類(lèi)型定義都必須與IUnknown兼容。
      CComPtr<IUnknown> unk;
 
從功能上將它等同于
      CComQIPtr<IUnknown, &IID_IUnknown> unk;
 
前者正確。后者是錯(cuò)誤的用法。如果你這樣寫(xiě)了,C++編譯器將提醒你改正。
??? 將CComPtr作為首選的另外一個(gè)理由可能是一些開(kāi)發(fā)人員相信靜悄悄地調(diào)用QueryInterface,沒(méi)有警告,削弱了C++系統(tǒng)的類(lèi)型。畢竟,C++在沒(méi)有進(jìn)行強(qiáng)制類(lèi)型轉(zhuǎn)換的情況下不允許對(duì)類(lèi)型不一致的原始指針 進(jìn)行賦值操作,所以為什么要用智能指針的道理也在這,幸運(yùn)的是開(kāi)發(fā)人員可以選擇最能滿足需要的指針類(lèi)型。
??? 許多開(kāi)發(fā)人員將智能指針看成是對(duì)過(guò)于的復(fù)雜編程任務(wù)的簡(jiǎn)化。我最初也是這么認(rèn)為的。但只要留意它們使用COM智能指針的方法。就會(huì)逐漸認(rèn)識(shí)到它們引入的潛在危險(xiǎn)與它們解決的問(wèn)題一樣多。
關(guān)于這一點(diǎn),我用一個(gè)現(xiàn)成的使用原始指針的函數(shù)為例:
 
void f(void) {
   IFoo *pFoo = 0;
   HRESULT hr = GetSomeObject(&pFoo);
   if (SUCCEEDED(hr)) {
      UseSomeObject(pFoo);
      pFoo->Release();
   }
} 
將它自然而然轉(zhuǎn)換到使用CComPtr。
  void f(void) {
   CComPtr<IFoo> pFoo = 0;
   HRESULT hr = GetSomeObject(&pFoo);
   if (SUCCEEDED(hr)) {
      UseSomeObject(pFoo);
      pFoo->Release(); 
   }
}
 
??? 注意CComPtr 和 CComQIPtr輸出所有受控接口成員,包括AddRef和Release。可惜當(dāng)客戶端通過(guò)操作符->的結(jié)果調(diào)用Release時(shí),智能指針很健忘 ,會(huì)二次調(diào)用構(gòu)造函數(shù)中的Release。顯然這是錯(cuò)誤的,編譯器和鏈接器也欣然接受了這個(gè)代碼。如果你運(yùn)氣好的話,調(diào)試器會(huì)很快捕獲到這個(gè)錯(cuò)誤。
??? 使用ATL智能指針的另一個(gè)要引起注意的風(fēng)險(xiǎn)是類(lèi)型強(qiáng)制轉(zhuǎn)換操作符對(duì)原始指針提供的訪問(wèn)。如果隱式強(qiáng)制轉(zhuǎn)換操作符的使用存在爭(zhēng)議。當(dāng) ANSI/ISO C++ 委員會(huì)在決定采用某個(gè)C++串類(lèi)時(shí),他們明確禁止隱式類(lèi)型轉(zhuǎn)換。而是要求必須顯式使用c_str函數(shù)在需要常量char *(const char *)的地方傳遞標(biāo)準(zhǔn)C++串。ATL提供了一種隱含式的類(lèi)型轉(zhuǎn)換操作符順利地解決了這個(gè)問(wèn)題。通常,這個(gè)轉(zhuǎn)換操作符可以根據(jù)你的喜好來(lái)使用,允許你將智能指針傳遞到需要用原始指針的函數(shù)。
 void f(IUnknown *pUnk) { 
    CComPtr unk = pUnk;
    // 隱式調(diào)用操作符IUnknown *()
    CoLockObjectExternal(unk, TRUE, TRUE);
}
 
這段代碼能正確運(yùn)行,但是下面的代碼也不會(huì)產(chǎn)生警告信息,編譯正常通過(guò):
HRESULT CFoo::Clone(IUnknown **ppUnk) { 
   CComPtr unk;
   CoCreateInstance(CLSID_Foo, 0, CLSCTX_ALL,
   IID_IUnknown, (void **) &unk);
   // 隱式調(diào)用操作符IUnknown *()
   *ppUnk = unk;
   return S_OK;
}
??? 在這種情況下,智能指針(unk)對(duì)原始值針**ppUnk的賦值觸發(fā)了與前面代碼段相同的強(qiáng)制類(lèi)型轉(zhuǎn)換。在第一個(gè)例子中,不需要用AddRef。在第二個(gè)例子中,必須要用AddRef。
??? 有關(guān)使用智能指針的更詳細(xì)一般信息,請(qǐng)參見(jiàn)Scott Meyer的《More Effective C++》(Addison-Wesley, 1995年出版)。國(guó)內(nèi)目前還沒(méi)有這本書(shū)的中譯本或影印本。有關(guān)COM智能指針的更多特定信息,請(qǐng)參見(jiàn)Don Box的一篇關(guān)于智能指針的專(zhuān)題文章http://www.develop.com/dbox/cxx/SmartPointer.htm。 (待續(xù))
posted on 2007-03-12 21:29 jay 閱讀(419) 評(píng)論(0)  編輯 收藏 引用 所屬分類(lèi): ATL
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            美国十次成人| 夜夜嗨av色一区二区不卡| 黄网站色欧美视频| 国产一区二区电影在线观看 | 欧美sm视频| 欧美jizz19hd性欧美| 欧美成人一区二区三区在线观看| 久久亚洲春色中文字幕| 免费观看成人www动漫视频| 久久婷婷激情| 欧美日韩国产影片| 国产精品一区一区三区| 亚洲一级二级| 午夜日韩在线| 葵司免费一区二区三区四区五区| 欧美freesex8一10精品| 欧美日韩综合网| 国产午夜亚洲精品不卡| 亚洲福利电影| 亚洲在线不卡| 美日韩免费视频| 夜夜躁日日躁狠狠久久88av| 欧美一区日本一区韩国一区| 欧美韩日一区二区三区| 国产精品久久久久影院色老大 | 亚洲欧洲免费视频| 亚洲欧美日韩电影| 欧美成人精品一区二区三区| 99视频日韩| 老牛影视一区二区三区| 国产精品国产三级国产aⅴ9色| 激情文学综合丁香| 亚洲欧美三级在线| 亚洲欧洲视频在线| 亚洲综合国产激情另类一区| 久久只精品国产| 国产日韩一区二区三区| 中文日韩电影网站| 欧美激情亚洲综合一区| 午夜亚洲一区| 欧美午夜影院| 日韩视频在线观看免费| 看欧美日韩国产| 香蕉乱码成人久久天堂爱免费| 欧美日韩国产小视频在线观看| 激情综合色丁香一区二区| 午夜久久久久久| 最新国产乱人伦偷精品免费网站 | 久久av老司机精品网站导航| 欧美三级网址| 亚洲免费黄色| 欧美激情一区二区三级高清视频| 亚洲欧美日韩一区在线| 欧美日韩大片| 亚洲狠狠婷婷| 亚洲高清影视| 久久亚洲精品一区二区| 精品动漫一区| 蜜桃久久av一区| 久久中文久久字幕| 亚洲第一精品夜夜躁人人躁| 久久乐国产精品| 久久成人精品电影| 国产一区清纯| 最新日韩在线| 欧美二区在线观看| 欧美成人午夜| 99视频精品全部免费在线| 欧美福利在线观看| 欧美成人精品一区| 99re热精品| 一区二区三区 在线观看视| 欧美日韩综合另类| 午夜精品视频在线| 欧美一激情一区二区三区| 国产一区二区丝袜高跟鞋图片| 久久精品99国产精品酒店日本| 香蕉久久a毛片| 在线国产精品播放| 亚洲电影激情视频网站| 免费成人你懂的| 制服诱惑一区二区| 午夜精品区一区二区三| 韩国一区二区三区在线观看| 久久久久久一区| 免费永久网站黄欧美| 999亚洲国产精| 亚洲自拍偷拍麻豆| 精品av久久707| 亚洲国产欧美在线人成| 欧美日韩三级| 久久久不卡网国产精品一区| 鲁大师成人一区二区三区| 一区二区三区高清| 欧美一区二区三区成人| 亚洲精品一区二区三区四区高清| 一区二区精品国产| 狠狠干狠狠久久| 日韩小视频在线观看专区| 国产三级欧美三级| 亚洲国产人成综合网站| 国产精品一二三视频| 亚洲国产高潮在线观看| 国产精品日日摸夜夜添夜夜av| 欧美1区2区视频| 欧美性大战久久久久久久蜜臀| 久久裸体艺术| 国产精品久久久久久久久久直播| 欧美mv日韩mv亚洲| 国产欧美一区二区三区沐欲 | 女仆av观看一区| 欧美黄色精品| 久久久久久夜精品精品免费| 欧美日韩视频一区二区| 久久综合图片| 国产欧美欧洲在线观看| 亚洲精品裸体| 亚洲国产综合视频在线观看| 欧美一级理论性理论a| 亚洲一级在线| 欧美精品日韩三级| 欧美激情国产日韩精品一区18| 国产一区日韩二区欧美三区| 99re8这里有精品热视频免费| 在线观看一区视频| 久久久久久久999| 久久久久国产精品人| 亚洲一区二区黄| 日韩视频精品在线| 久久精品最新地址| 久久激情网站| 国产日韩在线一区| 亚洲欧美中文字幕| 性欧美暴力猛交69hd| 欧美日韩在线视频一区| 亚洲看片免费| 一本大道久久a久久综合婷婷| 欧美国产日韩在线| 亚洲国产导航| 一级日韩一区在线观看| 欧美激情小视频| 91久久精品国产| 在线亚洲高清视频| 国产精品久久激情| 新67194成人永久网站| 久久久久久久国产| 亚洲成色777777女色窝| 欧美成人dvd在线视频| 亚洲激情国产精品| 在线视频欧美日韩精品| 欧美视频在线视频| 亚洲私人影院| 久久精品一本| 亚洲欧洲日产国码二区| 欧美日韩一级片在线观看| 亚洲视频在线一区| 久久夜精品va视频免费观看| 伊人久久大香线| 欧美精品一区在线播放| 国产精品99久久久久久www| 久久精品亚洲一区二区| 在线不卡欧美| 欧美日韩在线观看一区二区| 亚洲深夜福利视频| 久久深夜福利免费观看| 亚洲三级网站| 国产精品性做久久久久久| 久久久久高清| 日韩视频欧美视频| 久久久久久网址| 亚洲精品美女| 国产精品一区视频| 欧美电影资源| 亚洲欧美日韩精品久久久| 欧美成人综合在线| 午夜亚洲视频| 亚洲人成艺术| 国产一级一区二区| 欧美另类一区| 久久国产婷婷国产香蕉| 亚洲日本乱码在线观看| 久久国内精品自在自线400部| 亚洲高清自拍| 国产欧美一区二区色老头| 欧美高清视频免费观看| 亚洲欧美国产制服动漫| 亚洲电影网站| 久久综合婷婷| 欧美中文在线免费| 亚洲视频1区| 91久久精品国产91久久| 国产欧美91| 欧美视频在线观看免费网址| 久久九九精品| 午夜精品久久久久久久久久久| 亚洲毛片视频| 亚洲国产婷婷香蕉久久久久久| 久久精品二区| 欧美在线一区二区| 午夜精品久久久|