COM學習筆記(五)

 一般接口函數需要具備的函數是什麼呢?

初始化,釋放,顯示,消息處理……至於它內部想幹什麼?我也管不着啊?我只要在需要的時候調用這七個函數,就可以了。

再次看接口的概念:

函數是通過vtable虛函數表來提供地址,從另外一個角度來看,不管是什麼語言開發,編譯器產生的代碼都能生成這個表。這就實現了組件的“二進制特性”,輕鬆地實現了組件跨語言的要求。

每個接口都要有一個名字:叫IID.

接口一發表,就不能改了,如果改了,就容易出現向前兼容性,這個特性就叫做“接口不變性”。

任何接口都是從IUnknown 接口繼承的,所以都包含IUnknown接口。

C/C++語言中需要事先對函數聲明,那麼就 會要求組件也必須提供C語言的頭文件。不行!爲了能使COM具有跨語言的能力,決定不再爲任何語言提供對應的函數接口聲明,而是獨立地提供一個叫類型庫(TLB)的聲明。每個語言的IDE環境自己去根據TLB生成自己語言需要的包裝。這個性質叫“接口聲明的獨立性”(注8)

客戶程序和組件程序協商調用過程:

根據CLSID啓動組件:CoCreateInstance();

你有IUnknown接口麼?有,給你。

那你有其他接口麼?IUnknown::QueryInterface(IID_IPersistStorage...);

判斷有沒有,有怎麼處理,沒有怎麼處理?

有的話,差不多,下邊就是初始化,然後是讀取數據

執行一些消息處理函數,

如果用戶要退出程序,那麼你的數據保存了麼?執行判斷函數,

IPersistStreamInit:: IsDirty();

算算你需要多大的存儲空間,

IPersistStreamInit::GetSizeMax();

然後執行save()函數保存你的數據。

然後是執行析構函數,釋放接口和IUnknown接口

然後再退出

PostQuistMessage();

容器(或者說客戶端)就是這樣和組件進行對話,協商調用的。如果組件甲實現了 IA 接口,那麼容器就會使用它,如果組件乙沒有提供 IA 接口,但是它提供了 IB 接口,那麼容器就會調用 IB 接口的函數......如此,容器程序根本就不需要知道組件到底是幹什麼的,組件到底是用什麼語言開發的,組件的磁盤位置到底在哪裏,它都可以正常運行。太奇妙了!太精彩了!怎一個“爽”字了得!

IID_IUnknown,如果用註冊表樣式表示,那麼它的值是{00000000-0000-0000-C000-000000000046}。

註冊表子鍵 ProgID 和 VersionIndependentProgID 分別表示真正的 ProgID 和版本無關的 ProgID。比如在我計算機上安裝的 Excel,它的 ProgID = "Excel.Application.9",而 VersionIndependentProgID = "Excel.Application"。

IDispatch 接口的 IID 是多少?hresult = punk->QueryInterface(IID_IDispatch, &pdisp);

IPicture 接口的功能函數是什麼?直接查找MSDN.

 

下邊是COM的數據內型:

COM組件是運行在分佈環境中的。

比如,你寫了一個組件程序(DLL或EXE),那麼使用者可能是在本機的某個進程內加載組件(INPROC_SERVER);也可能是從另一個進程中調用組件的進程(LOCAL_SERVER);也可能是在這臺計算機上調用地球那邊計算機上的組件(REMOTE_SERVER)。

double sin(double);

BOOL DeleteFile(LPCTSTR);布爾值,如果失敗,需要GetLastError()才能取得失敗的原因。

void* malloc(size_t): 內存指針

COM 的設計規範終於對他們進行了統一。組件API及接口指針中,除了IUnknown::AddRef()和IUnknown::Release()兩個函數外,其它所有的函數,都以 HRESULT 作爲返回值。

COM 組件是運行在分佈式環境中的。也就是說,這個函數可能運行在“地球另一邊”的計算機上,既然運行在那麼遙遠的地方,就有可能出現服務器關機、網絡掉線、運行超時、對方不在服務區......等異常。於是,這個加法函數,除了需要返回運算結果以外,還應該返回一個值------函數是否被正常執行了。

所以:

HRESULT Add(long n1, long n2, long *pSum)

{

        *pSum = n1 + n2;

        return S_OK;
}

如果函數正常執行,則返回 S_OK,同時真正的函數運行結果則通過參數指針返回。如果遇到了異常情況,則COM系統經過判斷,會返回相應的錯誤值。

      HRESULT hr = 調用組件函數;
      if( SUCCEEDED( hr ) ){...} // 如果成功
      ......
      if( FAILED( hr ) ){...} // 如果失敗

HRESULT 其實是一個雙字節的值,其最高位(bit)如果是0表示成功,1表示錯誤。具體參見 MSDN 之"Structure of COM Error Codes"說明。我們在程序中如果需要判斷返回值,則可以使用比較運算符號;switch開關語句;也可以使用VC提供的宏:

說英語的人終於變“聰明”一些了。爲了把全世界人民所有的所有的文字符號都統一進行編碼,於是制定了UNICODE標準字符集。UNICODE 使用2個字節表示一個字符(unsigned shor int、WCHAR、_wchar_t、OLECHAR)。這下終於好啦,全世界任何一個地區的軟件,可以不用修改地就能在另一個地區運行了。雖然我用 IE 瀏覽日本網站,顯示出我不認識的日文文字,但至少不會是亂碼了。UNICODE 的範圍是 0x0000 - 0xFFFF 共6萬多個字符,其中光漢字就佔用了4萬多個。嘿嘿,中國人賺大發了:0)

      const char * p = "Hello"; // 使用 ASCII 字符集
      const char * p = "你好"; // 使用 MBCS 字符集,由於 MBCS 完全兼容 ASCII,多數情況下,我們並不嚴格區分他們
      LPCSTR p = "Hello,你好"; // 意義同上
     
      const WCHAR * p = L"Hello,你好"; // 使用 UNICODE 字符集
      LPCOLESTR p = L"Hello,你好"; // 意義同上
     
      // 如果預定義了_UNICODE,則表示使用UNICODE字符集;如果定義了_MBCS,則表示使用 MBCS
      const TCHAR * p = _T("Hello,你好");
      LPCTSTR p = _T("Hello,你好"); // 意義同上

     使用 T 類型,是非常好的習慣,強烈推薦。

BSTR:其實是一個指針類型,最常用的自動化數據類型。

BSTR是一個指向UNICODE字符串的指針,且BSTR向前的4個字節中,使用DWORD保存這個字節的長度,沒有結束符。

BSTR的處理函數:

各種字符串內型的轉換

1、函數 WideCharToMultiByte(),轉換 UNICODE 到 MBCS。

VARIANT
  C++、BASIC、Java、Pascal、Script......計算機語言多種多樣,而它們各自又都有自己的數據類型,COM 產生目的,其中之一就是要跨語言(注3)。而 VARIANT 數據類型就具有跨語言的特性,同時它可以表示(存儲)任意類型的數據。從C語言的角度來講,VARIANT 其實是一個結構,結構中用一個域(vt)表示------該變量到底表示的是什麼類型數據,同時真正的數據則存貯在 union 空間中。結構的定義太長了(雖然長,但其實很簡單)大家去看 MSDN 的描述吧,這裏給出如何使用的簡單示例:

學生:我想用 VARIANT 表示一個4字節長的整數,如何做?
老師:VARIANT v; v.vt=VT_I4; v.lVal=100;

學生:我想用 VARIANT 表示布爾值“真”,如何做?
老師:VARIANT v; v.vt=VT_BOOL; v.boolVal=VARIANT_TRUE;

VARIANT v; v.vt=VT_BSTR; v.bstrVal=SysAllocString(L"Hello,你好");

CComVariant、COleVariant、_variant_t。比如上面三個問題就可以這樣書寫:CComVariant v1(100),v2(true),v3("Hello,你好");

那數組呢?

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章