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

隨筆-250  評論-20  文章-55  trackbacks-0

起步篇

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

void f(void) {
   IUnknown *pUnk = 0;
   // 調用 
   HRESULT hr = GetSomeObject(&pUnk);
   if (SUCCEEDED(hr)) {
   // 使用
     UseSomeObject(pUnk);
 // 釋放
     pUnk->Release();
   }
}
    
??? 這個模式在COM程序員心中是如此根深蒂固,以至于他們常常不寫實際使用指針的語句,而是先在代碼塊末尾敲入Release語句。這很像C程序員使用switch語句時的條件反射一樣,先敲入break再說。
??? 其實調用Release實在不是什么可怕的負擔,但是,客戶端程序員面臨兩個相當嚴重的問題。第一個問題與獲得多接口指針有關。如果某個函數需要在做任何實際工作之前獲得三個接口指針,也就是說在第一個使用指針的語句之前必須要由三個調用語句。在書寫代碼時,這常常意味著程序員需要寫許多嵌套條件語句,如:
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();
  }
}
    
??? 像這樣的語句常常促使程序員將TAB鍵設置成一個或兩個空格,甚至情愿使用大一點的顯示器。但事情并不總是按你想象的那樣,由于種種原因項目團隊中的COM組件編程人員往往得不到 所想的硬件支持,而且在公司確定關于TAB鍵的使用標準之前,程序員常常求助于使用有很大爭議但仍然有效的“GOTO”語句:
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();
}    
這樣的代碼雖然不那么專業,但至少減少了屏幕的水平滾動。
使用以上這些代碼段潛在著更加棘手的問題,那就是在碰到C++異常時。如果函數UseObjects丟出異常,則釋放指針的代碼被完全屏蔽掉了。 解決這個問題的一個方法是使用Win32的結構化異常處理(SEH)進行終結操作:
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++中的表現并不如想象得那么好。較好的方法是使用內建的C++異常處理模型,同時停止使用沒有加工過的指針。標準C++庫有一個類:auto_ptr,在其析構函數中定 死了一個操作指針的delete調用(即使在出現異常時也能保證調用)。與之類似,ATL有一個COM智能指針,CComPtr,它的析構函數會正確調用Release。
??? CComPtr類實現客戶端基本的COM引用計數模型。CComPtr有一個數據成員,它是一個未經過任何加工的COM接口指針。其類型被作為模板參數傳遞:
      CComPtr<IUnknown> unk;
      CComPtr<IClassFactory> cf;
??? 缺省的構造函數將這個原始指針數據成員初始化為NULL。智能指針也有構造函數,它的參數要么是原始指針,要么是相同類型的智能參數。不論哪種情況,智能指針都調用AddRef控制引用。CComPtr的賦值操作符 既可以處理原始指針,也可以處理智能指針,并且在調用新分配指針的AddRef之前自動釋放保存的指針。最重要的是,CComPtr的析構函數釋放保存的接口(如果非空)。請看下列代碼:
void f(IUnknown *pUnk1, IUnknown *pUnk2) {
    // 如果非空,構造函數調用pUnk1的AddRef 
    CComPtr unk1(pUnk1);
    // 如果非空,構造函數調用unk1.p的AddRef
    CComPtr unk2 = unk1;
    // 如果非空,operator= 調用unk1.p的Release并且
    //如果非空,調用unk2.p的AddRef
    unk1 = unk2;
    //如果非空,析構函數釋放unk1 和 unk2
}
??? 除了正確實現COM的AddRef 和 Release規則之外,CComPtr還允許實現原始和智能指針的透明操作,參見附表二所示。也就是說下面的代碼按照你所想象的方式運行:
void f(IUnknown *pUnkCO) {
    
    CComPtr cf;
    
    HRESULT hr;
    
    // 使用操作符 & 獲得對 &cf.p 的存取
    hr = pUnkCO->QueryInterface(IID_IClassFactory,(void**)&cf);
    if (FAILED(hr)) throw hr;
    
    CComPtr unk;
    
    // 操作符 -> 獲得對cf.p的存取
    // 操作符 & 獲得對 &unk.p的存取
    hr = cf->CreateInstance(0, IID_IUnknown, (void**)&unk);
    
    if (FAILED(hr)) throw hr;
    
    // 操作符 IUnknown * 返回 unk.p
    UseObject(unk);
    
    // 析構函數釋放unk.p 和 cf.p
}
??? 除了缺乏對Release的顯式調用外,這段代碼像是純粹的COM代碼。有了CComPtr類的武裝,前面所遇到的麻煩問題頓時變得簡單了:
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對操作符重載用法的擴展,使得代碼的編譯和運行無懈可擊。
??? 假定模板類知道它所操縱的指針類型,你可能會問:那為什么智能指針不能在它的功能操作符或構造函數中自動調用QueryInterface,從而更有效地包裝IUnknown呢?在Visual C++ 5.0出來以前,沒有辦法將某個接口的GUID與它的本身的C++類型關聯起來——Visual C++ 5.0用私有的declspec將某個IID與一個接口定義綁定在一起。因為ATL的設計 考慮到了它要與大量不同的C++編譯器一起工作,它需要用與編譯器無關的手段提供GUID。下面我們來探討另一個類——CComQIPtr類。
??? CComQIPtr與CComPtr關系很密切(實際上,它只增加了兩個成員函數)。CComQIPtr必須要兩個模板參數:一個是被操縱的指針類型 ,另一個是對應于這個指針類型的GUID。例如,下列代碼聲明了操縱IDataObject 和IPersist接口的智能指針:
      CComQIPtr<IDataObject, &IID_IDataObject> do;
      CComQIPtr<IPersist, &IID_IPersist> p;
??? CComQIPtr的優點是它有重載的構造函數和賦值操作符。同類版本(例如,接受相同類型的接口)僅僅AddRef右邊的賦值/初始化操作。這實際上就是CComPtr的功能。異類版本(接受類型不一致的接口)正確調用QueryInterface來決定是否這個對象確實支持所請求的接口:
  
      void f(IPersist *pPersist) {
          CComQIPtr<IPersist, &IID_IPersist> p;
          // 同類賦值 - AddRef''s
          p = pPersist;
    
          CComQIPtr<IDataObject, &IID_IDataObject> do;
          // 異類賦值 - QueryInterface''s
          do = pPersist;
      }
 
??? 在第二種賦值語句中,因為pPersist是非IDataObject *類型,但它是派生于IUnknown的接口指針,CComQIPtr通過pPersist調用QueryInterface來試圖獲得這個對象的IDataObject接口指針。如果QueryInterface調用成功,則此智能指針將含有作為結果的原始IDataObject指針。如果QueryInterface調用失敗,則do.p將被置為null。如果QueryInterface返回的HRESULT值很重要,但又沒有辦法從賦值操作獲得其值時,則必須顯式調用QueryInterface。
??? 既然有了CComQIPtr,那為什么還要CComPtr呢?由幾個理由:首先,ATL最初的發布版本只支持CComPtr,所以它就一直合法地保留下來了。其二(也是最重要的理由),由于重載的構造函數和賦值操作,對IUnknown使用CComQIPtr是非法的。因為所有COM接口的類型定義都必須與IUnknown兼容。
      CComPtr<IUnknown> unk;
 
從功能上將它等同于
      CComQIPtr<IUnknown, &IID_IUnknown> unk;
 
前者正確。后者是錯誤的用法。如果你這樣寫了,C++編譯器將提醒你改正。
??? 將CComPtr作為首選的另外一個理由可能是一些開發人員相信靜悄悄地調用QueryInterface,沒有警告,削弱了C++系統的類型。畢竟,C++在沒有進行強制類型轉換的情況下不允許對類型不一致的原始指針 進行賦值操作,所以為什么要用智能指針的道理也在這,幸運的是開發人員可以選擇最能滿足需要的指針類型。
??? 許多開發人員將智能指針看成是對過于的復雜編程任務的簡化。我最初也是這么認為的。但只要留意它們使用COM智能指針的方法。就會逐漸認識到它們引入的潛在危險與它們解決的問題一樣多。
關于這一點,我用一個現成的使用原始指針的函數為例:
 
void f(void) {
   IFoo *pFoo = 0;
   HRESULT hr = GetSomeObject(&pFoo);
   if (SUCCEEDED(hr)) {
      UseSomeObject(pFoo);
      pFoo->Release();
   }
} 
將它自然而然轉換到使用CComPtr。
  void f(void) {
   CComPtr<IFoo> pFoo = 0;
   HRESULT hr = GetSomeObject(&pFoo);
   if (SUCCEEDED(hr)) {
      UseSomeObject(pFoo);
      pFoo->Release(); 
   }
}
 
??? 注意CComPtr 和 CComQIPtr輸出所有受控接口成員,包括AddRef和Release。可惜當客戶端通過操作符->的結果調用Release時,智能指針很健忘 ,會二次調用構造函數中的Release。顯然這是錯誤的,編譯器和鏈接器也欣然接受了這個代碼。如果你運氣好的話,調試器會很快捕獲到這個錯誤。
??? 使用ATL智能指針的另一個要引起注意的風險是類型強制轉換操作符對原始指針提供的訪問。如果隱式強制轉換操作符的使用存在爭議。當 ANSI/ISO C++ 委員會在決定采用某個C++串類時,他們明確禁止隱式類型轉換。而是要求必須顯式使用c_str函數在需要常量char *(const char *)的地方傳遞標準C++串。ATL提供了一種隱含式的類型轉換操作符順利地解決了這個問題。通常,這個轉換操作符可以根據你的喜好來使用,允許你將智能指針傳遞到需要用原始指針的函數。
 void f(IUnknown *pUnk) { 
    CComPtr unk = pUnk;
    // 隱式調用操作符IUnknown *()
    CoLockObjectExternal(unk, TRUE, TRUE);
}
 
這段代碼能正確運行,但是下面的代碼也不會產生警告信息,編譯正常通過:
HRESULT CFoo::Clone(IUnknown **ppUnk) { 
   CComPtr unk;
   CoCreateInstance(CLSID_Foo, 0, CLSCTX_ALL,
   IID_IUnknown, (void **) &unk);
   // 隱式調用操作符IUnknown *()
   *ppUnk = unk;
   return S_OK;
}
??? 在這種情況下,智能指針(unk)對原始值針**ppUnk的賦值觸發了與前面代碼段相同的強制類型轉換。在第一個例子中,不需要用AddRef。在第二個例子中,必須要用AddRef。
??? 有關使用智能指針的更詳細一般信息,請參見Scott Meyer的《More Effective C++》(Addison-Wesley, 1995年出版)。國內目前還沒有這本書的中譯本或影印本。有關COM智能指針的更多特定信息,請參見Don Box的一篇關于智能指針的專題文章http://www.develop.com/dbox/cxx/SmartPointer.htm。 (待續)
posted on 2007-03-12 21:29 jay 閱讀(419) 評論(0)  編輯 收藏 引用 所屬分類: 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>
            一区二区三区精品视频| 亚洲精品一区二区三区福利| 一本色道久久综合亚洲精品高清| 亚洲第一主播视频| 久久精品亚洲一区二区三区浴池| 久久久精品动漫| 亚洲国产精品精华液2区45| 亚洲日韩中文字幕在线播放| 日韩小视频在线观看专区| 性欧美1819sex性高清| 美日韩精品视频免费看| 国产精品激情电影| 亚洲精品永久免费精品| 久久xxxx| 99精品免费视频| 免费人成网站在线观看欧美高清| 国产精品激情av在线播放| 亚洲娇小video精品| 午夜宅男欧美| 亚洲美女免费精品视频在线观看| 亚洲性图久久| 欧美日韩hd| 亚洲国产日韩一区二区| 久久精品99国产精品| 亚洲国产欧美久久| 久久久久久久一区二区三区| 午夜欧美大尺度福利影院在线看| 久久综合伊人77777麻豆| 99视频精品全部免费在线| 久久综合99re88久久爱| 你懂的国产精品| 欧美四级在线| 一本久久a久久精品亚洲| 蜜臀91精品一区二区三区| 国产精品久久久久久妇女6080 | 国产精品成人久久久久| 亚洲国产精品999| 久久久蜜桃精品| 亚洲欧美日韩精品综合在线观看| 欧美日韩另类字幕中文| 亚洲精品一区二区在线| 欧美国产日韩一二三区| 久久青草福利网站| 最新亚洲一区| 亚洲欧洲一区二区三区| 欧美高清在线精品一区| 亚洲乱码国产乱码精品精天堂| 欧美成人性生活| 亚洲欧洲综合另类| 日韩午夜激情av| 欧美成熟视频| 黄色成人片子| 亚洲国产高清高潮精品美女| 久久亚洲免费| 国产在线观看一区| 美女精品一区| 欧美日韩国产黄| 欧美一区二区三区视频免费| 亚洲一区二区免费看| 国产精品一区在线观看| 久久精品欧美日韩精品| 久久久成人精品| 日韩午夜三级在线| 亚洲欧美国产制服动漫| 韩国成人精品a∨在线观看| 欧美不卡一卡二卡免费版| 欧美精品xxxxbbbb| 欧美影院精品一区| 免费久久精品视频| 亚洲自拍另类| 久久综合伊人77777| 亚洲人成在线观看网站高清| 亚洲二区在线视频| 国产精品乱码| 欧美成年人视频网站| 欧美精品久久天天躁| 亚洲欧美日韩在线播放| 久久久久久一区| 亚洲自拍偷拍福利| 免费欧美视频| 久久久www免费人成黑人精品 | 久久综合伊人77777蜜臀| 99re这里只有精品6| 亚洲欧美第一页| 亚洲久色影视| 久久人体大胆视频| 久久av老司机精品网站导航| 久久精品国产亚洲5555| 一区二区三区视频在线观看| 亚洲视频国产视频| 国内成人精品2018免费看| 久久―日本道色综合久久| 欧美激情按摩| 美乳少妇欧美精品| 国产人成精品一区二区三| 亚洲激情一区| 国产精品久久国产精麻豆99网站| 欧美国产日产韩国视频| 欧美啪啪一区| 亚洲大片av| 伊人精品久久久久7777| 欧美韩日视频| 韩国精品一区二区三区| 欧美国内亚洲| 国产日韩1区| 亚洲综合国产精品| 亚洲免费视频观看| 欧美日韩亚洲一区三区| 亚洲福利视频二区| 亚洲国产欧美一区二区三区同亚洲| 亚洲自拍偷拍福利| 亚洲日韩成人| 欧美国产日韩在线| 亚洲激情在线视频| 99re6这里只有精品| 牛夜精品久久久久久久99黑人| 久久精品日韩欧美| 国产一二三精品| 性欧美暴力猛交69hd| 欧美一级视频免费在线观看| 欧美一二三区精品| 亚洲美女少妇无套啪啪呻吟| 一本久久综合| 亚洲永久免费| 国产女主播一区| 欧美在线视频不卡| 噜噜爱69成人精品| 国产一区二区黄| 亚洲免费综合| 久久精品在线视频| 国产精品你懂得| 亚洲精品一二| 91久久国产综合久久蜜月精品| 亚洲综合视频一区| 久久久国产精品一区二区中文| 国产亚洲欧美日韩日本| 亚洲欧美精品在线观看| 欧美一区=区| 曰本成人黄色| 欧美精品v日韩精品v国产精品| 亚洲日本va在线观看| 在线亚洲一区二区| 国产精品乱码| 久久青草久久| 99riav1国产精品视频| 欧美一区二区三区四区在线观看地址 | 久久精品青青大伊人av| 老牛国产精品一区的观看方式| 激情亚洲网站| 欧美理论电影在线播放| 日韩午夜精品视频| 中文欧美在线视频| 国产精品成人一区二区三区吃奶| 亚洲精选视频在线| 久久av最新网址| 亚洲伦理在线| 国产一区二区黄色| 欧美日韩免费观看一区二区三区 | 欧美精品乱码久久久久久按摩| 亚洲日本成人网| 久久精品视频在线免费观看| 国产专区一区| 欧美日韩在线电影| 久久亚洲风情| 亚洲欧美成人网| 亚洲国产一区二区视频| 亚洲综合日本| 亚洲国产福利在线| 欧美色123| 香蕉免费一区二区三区在线观看 | 欧美大片一区二区三区| 亚洲一区二区三区在线观看视频 | 久久久久国内| 亚洲综合视频在线| 亚洲麻豆视频| 欧美激情国产高清| 久久亚洲一区二区| 性感少妇一区| 红桃视频亚洲| 欧美色视频一区| 欧美成人伊人久久综合网| 中国成人黄色视屏| 欧美一区二区三区免费在线看| 在线观看一区欧美| 国产欧美一区视频| 国产一区二区三区在线免费观看| 制服丝袜亚洲播放| 亚洲精品你懂的| 亚洲三级免费电影| 亚洲精选中文字幕| 亚洲精品自在久久| 国产精品99久久久久久久女警 | 久久久成人网| 欧美国产日韩一区二区| 久久综合网hezyo| 欧美aa在线视频| 欧美激情亚洲综合一区| 欧美高清视频在线| 亚洲精品日本| 亚洲一区在线免费观看|