COM技术学习总结
COM技術
COM(Component Object Model,組件對象模型),是由微軟推出的一套接口規范,通過設定不同組件之間需要遵守的標準與協議,主要用來跨語言、跨進程之間的模塊通信。
中文名 COM技術 外文名 Component Object Model 正 ? ?文 首先,用戶一般希望能夠定 正文2 COM+是由微軟推出的
目錄
1 簡介
2 優點
簡介
所謂COM(Component Object Model,組件對象模型)是一種說明如何建立可動態互變組件的規范,此規范提供了為保證能夠互操作,客戶和組件應遵循的一些二進制和網絡標準。通過這種標準將可以在任意兩個組件之間進行通信而不用考慮其所處的操作環境是否相同、使用的開發語言是否一致以及是否運行于同一臺計算機。
優點
COM的優點?
首先,用戶一般希望能夠定制所用的應用程序,而組件技術從本質上講就是可被定制的,因而用戶可以用更能滿足他們需要的某個組件來替換原來的那個。其次,由于組件是相對應用程序獨立的部件,我們可以在不同的程序中使用同一個組件而不會產生任何問題,軟件的可重用性將大大的得到增強。第三,隨著網絡帶寬及其重要性的提高,分布式網絡應用程序毫無疑問的成為軟件市場上越來越重要的買點。組件架構可以使得開發這類應用程序的過程得以簡化。
什么是COM+?
COM+是由微軟推出的,基于COM進行再一次優化。
COM+并不是COM的簡單升級,COM+的底層結構仍然以COM為基礎,它幾乎包容了COM的所有內容,COM+綜合了COM、DCOM和MTS這些技術要素,它把COM組件軟件提升到應用層而不再是底層的軟件結構,它通過操作系統的各種支持,使組件對象模型建立在應用層上,把所有組件的底層細節留給操作系統,因此,COM+與操作系統的結合更加緊密。
COM+不再局限于COM的組件技術,它更加注重于分布式網絡應用的設計和實現。COM+繼承了COM幾乎全部的優勢,同時又避免了COM實現方面的一些不足,把COM、DCOM和MTS的編程模型結合起來,繼承了它們的絕大多數特性,在原有的特性上增加了新的功能。
COM+的新的優點?
以下列出COM+的幾個主要特性:
COM+不僅繼承了COM所有的優點,而且還增加了一些服務,比如隊列服務、負載平衡、內存數據庫、事件服務等。
隊列服務對于分布式應用非常有意義,特別是在現在網絡速度很慢的情況下,這種機制可以保證應用系統能夠可靠地運行。在應用系統包含大量節點但服務器又繁忙的情況下,客戶應用程序可以把它們的請求放到隊列中,當服務器負載比較輕的時候再處理這些請求;
又如COM+提供了負載平衡服務,它可以實現動態負載平衡,而且COM+應用程序的負載平衡特性并不需要編寫代碼來支持,客戶程序和組件程序都可以按通常的方式實現。獲得負載平衡特性并不是用程序設計的方式來實現的,而是通過配置實現分布式應用程序的負載平衡,如上所講的隊列服務,其實也反映了一種負載平衡。
(1) 真正的異步通訊。COM+底層提供了隊列組件服務,這使客戶和組件有可能在不同的時間點上協同工作,COM+應用無須增加代碼就可以獲得這樣的特性。
(2) 事件服務。新的事件機制使事件源和事件接收方實現事件功能更加靈活,利用系統服務簡化了事件模型,避免了COM可連接對象機制的瑣碎細節。
(3) 可伸縮性。COM+的可伸縮性來源于多個方面,動態負載平衡以及內存數據庫、對象池等系統服務都為COM+的可伸縮性提供了技術基礎,COM+的可伸縮性原理上與多層結構的可伸縮特性一致。
(4) 可管理和可配置性。管理和配置是應用系統開發完成后的行為,在軟件維護成本不斷增加的今天,COM+應用將有助于軟件廠商和用戶減少這方面的投入。
(5) 易于開發。COM+應用開發的復雜性和難易程度將決定COM+的成功與否,雖然COM+開發模型比以前的COM組件開發更為簡化,但真正提高開發效率仍需要借助于一些優秀的開發工具。
COM+標志著Microsoft的組件技術達到了一個新的高度,它不再局限于一臺機器上的桌面系統,它把目標指向了更為廣闊的企業內部網,甚至Internet國際互連網絡。COM+與多層結構模型以及Windows操作系統為企業應用或Web應用提供了一套完整的解決方案。
========
COM技術初探(一)
http://blog.csdn.net/yongjunhe/article/details/8588208目錄
一. COM是一個更好的 C++
? ?1. COM 是什么
? ?2. 從 C++ 到 DLL 再到 COM
? ? ? 2.1 C++
? ? ? 2.2 DLL
? ? ? 2.3 COM
二. COM基礎
? ?1. COM基本知識
? ? ? 1.1 返回值HRESULT
? ? ? 1.2 初識idl
? ? ? 1.3 IUnkown接口
? ?2. 一個比較簡單的COM
? ? ? 2.1 interface.h文件
? ? ? 2.2 math.h文件
? ? ? 2.3 math.cpp文件
? ? ? 2.4 simple.cpp文件
? ? ? 2.5 Math組件的二進制結構圖
? ? ? 2.6 小結
三. 純手工創建一個COM組件
? ?1. 從建工程到實現注冊
? ? ? 1.1 創建一個類型為win32 dll工程
? ? ? 1.2 定義接口文件
? ? ? 1.3 增加注冊功能
? ? ? ? ?1.3.1 增加一個MathCOM.def文件
? ? ? ? ?1.3.2 DllRegisterServer()和DllUnregisterServer()
? ? ? 1.4 MathCOM.cpp文件
? ? ? 1.5 小結
? ?2. 實現ISmipleMath,IAdvancedMath接口和DllGetClassObject()
? ? ? 2.1 實現ISmipleMath和IAdvancedMath接口
? ? ? 2.2 COM組件調入大致過程
? ? ? 2.3 DllGetClassObject()實現
? ? ? 2.4 客戶端
? ? ? 2.5 小結
? ?3. 類廠
附錄 ??
A 我對dll的一點認識
一. 沒有lib的dll
? ?1.1 建一個沒有lib的dll
? ?1.2 調試沒有lib的dll
二. 帶有lib的dll
? ?2.1 創建一個帶有lib的dll
? ?2.2 調試帶有引用但沒有頭文件的dll
三. 帶有頭文件的dll
? ?3.1 創建一個帶有引出信息頭文件的dll
? ?3.2 調試帶有頭文件的dll
四. 小結一、COM是一個更好的C++
1、COM 是什么
Don Box 說"COM IS LOVE"。COM 的全稱是 Component Object Model 組件對象模型。
2、從 C++ 到 DLL 再到 COM
2.1 C++
如某一軟件廠商發布一個類庫(CMath四則運算),此時類庫的可執行代碼將成為客戶應用中不可分割的一部分。假設此類庫的所產生的機器碼在目標可執行文件中占有4MB的空間。當三個應用程序都使用CMath庫時,那么每個可執行文件都包含4MB的類庫代碼(見圖1.1)。當三個應用程序共同運行時,他們將會占用12MB的虛擬內存。問題還遠不于此。一旦類庫廠商發現CMath類庫有一個缺陷后,發布一個新的類庫,此時需要要求所有運用此類庫的應用程序。此外別無他法了。
圖1.1 CMath 的三個客戶
2.2 DLL
解決上面問題的一個技術是將CMath類做成動態鏈接庫(DLL ,Dynamic Link Library)的形式封裝起來 。
在使用這項技術的時候,CMath的所有方法都將被加到 CMath dll 的引出表(export list)中,而且鏈接器將會產生一個引入庫(import library)。這個庫暴露了CMath的方法成員的符號 。當客戶鏈接引入庫時,有一些存根會被引入到可執行文件中,它在運行時通知裝載器動態裝載 CMath Dll。
當 CMath 位于dll中時,他的運行模型見圖1.2
圖1.2 CMath引入庫
2.3 COM
"簡單地把C++類定義從dll中引出來"這種方案并不能提供合理的二進制組件結構。因為C++類那既是接口也是實現。這里需要把接口從實現中分離出來才能提供二進制組件結構。此時需要有二個C++類,一個作為接口類另一個作為實現類。讓我們開始COM之旅吧。
二、COM基礎
1、 COM基本知識
1.1 返回值HRESULT
COM要求所有的方法都會返回一個HRESULT類型的錯誤號。HRESULT 其實就一個類型定義:
1.
typedef LONG HRESULT;
有關HRESULT的定義見 winerror.h 文件
01.
// ?Values are 32 bit values layed out as follows:
02.
//
03.
// ?3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
04.
// ?1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
05.
// ?+-+----+-------------------------+---------------------------------+
06.
// ?|S| Res| ? ? Facility ? ? ? ? ? ?| ? ? Code ? ? ? ? ? ? ? ? ? ? ? ?|
07.
// ?+-+----+-------------------------+---------------------------------+
08.
//
09.
// ?where
10.
//
11.
// ? ? ?S - is the severity code
12.
//
13.
// ? ? ? ? ?0 - Success
14.
// ? ? ? ? ?1 - Error
15.
//
16.
// ? ? ?Res- is a reserved bit
17.
//
18.
// ? ? ?Facility - is the facility code
19.
//
20.
// ? ? ?Code - is the facility''s status code
我們一般下面的宏來判斷方法是否成功:
1.
#define SUCCEEDED(hr)(long(hr)>=0)
2.
#define FAILED(hr)(long(hr)<0)
1.2 初識 IDL
每個標準的COM組件都需要一個接口定義文件,文件的擴展名為IDL。讓我們看IUnknow接口的定義文件是怎樣的。
01.
[
02.
local,
03.
object,
04.
uuid(00000000-0000-0000-C000-000000000046),
05.
pointer_default(unique)
06.
]
07.
?
08.
interface IUnknown
09.
{
10.
typedef [unique] IUnknown *LPUNKNOWN;
11.
?
12.
cpp_quote("")
13.
cpp_quote("// IID_IUnknown and all other system IIDs are provided in UUID.LIB")
14.
cpp_quote("// Link that library in with your proxies, clients and servers")
15.
cpp_quote("")
16.
?
17.
HRESULT QueryInterface(
18.
[in] REFIID riid,
19.
[out, iid_is(riid)] void **ppvObject);
20.
ULONG AddRef();
21.
ULONG Release();
22.
}
23.
?
24.
[local]屬性禁止產生網絡代碼。
25.
[object]屬性是表明定義的是一個COM接口,而不是DEC風格的接口。
26.
[uuid]屬性給接口一個GUID。
27.
[unique]屬性表明null(空)指針為一個合法的參數值。
28.
[pointer_defaul]屬性所有的內嵌指針指定一個默認指針屬性
29.
typedef [unique] IUnknown *LPUNKNOWN;這是一個類型定義
30.
cpp_quote這個比較有趣,這是一個在idl文件寫注解的方法。這些注解將保存到***.h和***_i.c文件中
31.
[in]表示這個參數是入參
32.
[out]表示這個參數是出參
33.
[iid_is(riid)]表示這個參數需要前一個的riid 參數。
注意:所有具有out屬性的參數都需要是指針類型。
1.3 IUnkown接口
在整個例子除了IUnkown這個東西,其他應該不會感到陌生吧!COM要求(最基本的要求)所有的接口都需要從IUnknown接口直接或間接繼承,所以IUnknown接口有"萬惡之源"之稱。
IUnkown接口定義了三個方法。
1.
HRESULT QueryInterface([in] REFIID riid,[out] void **ppv);
2.
ULONG AddRef();
3.
ULONG Release();
其中 AddReft() 和Release()負責對象引用計數用的,而 QueryInterface()方法是用于查詢所實現接口用的。每當COM組件被引用一次就應調用一次AddRef()方法。而當客戶端在釋放COM組件的某個接口時就需要調用Release()方法。
這里所講的請在下面的例子仔細體會。
2、一個比較簡單的COM
此例子共有四個文件組成:
文件名 說明
Interface.h 接口類定義文件
Math.h和Math.cpp 實現類文件
Simple.cpp 主函數文件 這里用來當作COM的客戶端
2.1 interface.h 文件
01.
#ifndef INTERFACE_H
02.
#define INTERFACE_H
03.
#include
04.
?
05.
//{7C8027EA-A4ED-467c-B17E-1B51CE74AF57}
06.
static const GUID IID_ISimpleMath =
07.
{ 0x7c8027ea, 0xa4ed, 0x467c, { 0xb1, 0x7e, 0x1b, 0x51, 0xce, 0x74, 0xaf, 0x57 } };
08.
?
09.
//{CA3B37EA-E44A-49b8-9729-6E9222CAE84F}
10.
static const GUID IID_IAdvancedMath =
11.
{ 0xca3b37ea, 0xe44a, 0x49b8, { 0x97, 0x29, 0x6e, 0x92, 0x22, 0xca, 0xe8, 0x4f } };
12.
?
13.
interface ISimpleMath : public IUnknown
14.
{
15.
public:
16.
virtual int Add(int nOp1, int nOp2) = 0; ? ? ??
17.
virtual int Subtract(int nOp1, int nOp2) = 0;
18.
virtual int Multiply(int nOp1, int nOp2) = 0;
19.
virtual int Divide(int nOp1, int nOp2) = 0;
20.
};
21.
?
22.
interface IAdvancedMath : public IUnknown
23.
{
24.
public:
25.
virtual int Factorial(int nOp1) = 0;
26.
virtual int Fabonacci(int nOp1) = 0;
27.
};
28.
#endif
此文件首先 #include 將 IUnknown 接口定義文件包括進來。
接下來定義了兩個接口,GUID(Globally Unique Identifier全局唯一標識符)它能保證時間及空間上的唯一。
ISmipleMath接口里定義了四個方法,而IAdvancedMath接口里定義了二個方法。這些方法都是虛函數,而整個 ISmipleMath 與 IAdvancedMath 抽象類就作為二進制的接口。
2.2 math.h文件
01.
#include "interface.h"
02.
?
03.
class CMath : public ISimpleMath,
04.
public IAdvancedMath
05.
{
06.
private:
07.
ULONG m_cRef;
08.
?
09.
private:
10.
int calcFactorial(int nOp);
11.
int calcFabonacci(int nOp);
12.
?
13.
public:
14.
//IUnknown Method
15.
STDMETHOD(QueryInterface)(REFIID riid, void **ppv);
16.
STDMETHOD_(ULONG, AddRef)();
17.
STDMETHOD_(ULONG, Release)();
18.
?
19.
// ?ISimpleMath Method
20.
int Add(int nOp1, int nOp2);
21.
int Subtract(int nOp1, int nOp2);
22.
int Multiply(int nOp1, int nOp2);
23.
int Divide(int nOp1, int nOp2);
24.
?
25.
// ?IAdvancedMath Method
26.
int Factorial(int nOp);
27.
int Fabonacci(int nOp);
28.
};
此類為實現類,他實現了ISmipleMath和IAdvancedMath兩個接口類(當然也可以只實現一個接口類)。
請注意:m_cRef 是用來對象計數用的。當 m_cRef 為0組件對象應該自動刪除。
2.3 math.cpp文件
01.
#include "interface.h"
02.
#include "math.h"
03.
?
04.
STDMETHODIMP CMath::QueryInterface(REFIID riid, void **ppv)
05.
{// 這里這是實現dynamic_cast的功能,但由于dynamic_cast與編譯器相關。
06.
if(riid == IID_ISimpleMath)
07.
*ppv = static_cast(this);
08.
else if(riid == IID_IAdvancedMath)
09.
*ppv = static_cast(this);
10.
else if(riid == IID_IUnknown)
11.
*ppv = static_cast(this);
12.
else {
13.
*ppv = 0;
14.
return E_NOINTERFACE;
15.
}
16.
?
17.
reinterpret_cast(*ppv)->AddRef(); ? ?//這里要這樣是因為引用計數是針對組件的
18.
return S_OK;
19.
}
20.
?
21.
STDMETHODIMP_(ULONG) CMath::AddRef()
22.
{
23.
return ++m_cRef;
24.
}
25.
?
26.
STDMETHODIMP_(ULONG) CMath::Release()
27.
{
28.
ULONG res = --m_cRef; ? // 使用臨時變量把修改后的引用計數值緩存起來
29.
if(res == 0) ? ? ? ?// 因為在對象已經銷毀后再引用這個對象的數據將是非法的
30.
delete this;
31.
return res;
32.
}
33.
?
34.
int CMath::Add(int nOp1, int nOp2)
35.
{
36.
return nOp1+nOp2;
37.
}
38.
?
39.
int CMath::Subtract(int nOp1, int nOp2)
40.
{
41.
return nOp1 - nOp2;
42.
}
43.
?
44.
int CMath::Multiply(int nOp1, int nOp2)
45.
{
46.
return nOp1 * nOp2;
47.
}
48.
?
49.
int CMath::Divide(int nOp1, int nOp2)
50.
{
51.
return nOp1 / nOp2;
52.
}
53.
?
54.
int CMath::calcFactorial(int nOp)
55.
{
56.
if(nOp <= 1)
57.
return 1;
58.
?
59.
return nOp * calcFactorial(nOp - 1);
60.
}
61.
?
62.
int CMath::Factorial(int nOp)
63.
{
64.
return calcFactorial(nOp);
65.
}
66.
?
67.
int CMath::calcFabonacci(int nOp)
68.
{
69.
if(nOp <= 1)
70.
return 1;
71.
?
72.
return calcFabonacci(nOp - 1) + calcFabonacci(nOp - 2);
73.
}
74.
?
75.
int CMath::Fabonacci(int nOp)
76.
{
77.
return calcFabonacci(nOp);
78.
}
79.
CMath::CMath()
80.
{
81.
m_cRef=0;
82.
}
此文件是CMath類定義文件。
2.4 simple.cpp文件
01.
#include "math.h"
02.
#include
03.
?
04.
using namespace std;
05.
?
06.
int main(int argc, char* argv[])
07.
{
08.
ISimpleMath *pSimpleMath = NULL;//聲明接口指針
09.
IAdvancedMath *pAdvMath = NULL;
10.
?
11.
//創建對象實例,我們暫時這樣創建對象實例,COM有創建對象實例的機制
12.
CMath *pMath = new CMath; ?
13.
?
14.
//查詢對象實現的接口ISimpleMath
15.
pMath->QueryInterface(IID_ISimpleMath, (void **)&pSimpleMath); ?
16.
if(pSimpleMath)
17.
cout << "10 + 4 = " << pSimpleMath->Add(10, 4) << endl;
18.
?
19.
//查詢對象實現的接口IAdvancedMath
20.
pSimpleMath->QueryInterface(IID_IAdvancedMath, (void**)&pAdvMath);
21.
if(pAdvMath)
22.
cout << "10 Fabonacci is " << pAdvMath->Fabonacci(10) << endl;
23.
?
24.
pAdvMath->Release();
25.
pSimpleMath->Release();
26.
return 0;
27.
}
此文件相當于客戶端的代碼,首先創建一個CMath對象,再根據此對象去查詢所需要的接口,如果正確得到所需接口指針,再調用接口的方法,最后再將接口的釋放掉。
2.5 Math組件的二進制結構
2.6 小結
此例子從嚴格意義上來并不是真正的COM組件(他不是dll),但他已符合COM的最小要求(實現IUnknown接口)。接下來我們來做一COM dll(但還不用ATL)。
========
資源鏈接
http://blog.csdn.net/libin_1/article/details/45271443
COM技術內幕筆記
http://blog.csdn.net/byxdaz/article/details/6595210
COM編程小結
http://blog.csdn.net/zang141588761/article/category/6202247
COM編程精彩實例
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀
總結
- 上一篇: Spring.NET 学习总结
- 下一篇: VC++ 常用编程技巧总结