當初微軟設計com規範的時候,有兩種選擇來保證用戶的設計的com組件能夠全球惟一: 算法
第一種是採用和Internet地址同樣的管理方式,成立一個管理機構,用戶若是想開發一個COM組件的時候須要向該機構提出申請,並交必定的費用。 併發
第二種是發明一種算法,每次都能產生一個全球惟一的COM組件標識符。 函數
第一種方法,用戶使用起來太不方便,微軟採用第 二種方法,併發明瞭一種算法,這種算法用GUID(Globally Unique Identifiers)來標識COM組件,GUID是一個128位長的數字,通常用16進製表示。算法的核心思想是結合機器的網卡、當地時間、一個隨即 數來生成GUID。從理論上講,若是一臺機器每秒產生10000000個GUID,則能夠保證(機率意義上)3240年不重複。 工具
GUID的例子: 54BF6567--1007--11D1--B0AA--444553540000 ui
HKEY_CLASSES_ROOT/CLSID/{002B9E07-2E10-438F-AF1E-40E6A96F1EE4} spa
在微軟的COM中GUID和UUID、CLSID、IID是一回事,只不過各自表明的意義不一樣: 設計
UUID : 表明COM 對象
CLSID : 表明COM組件中的類 接口
IID :表明COM組件中的接口 內存
在程序中,實際對象數據對應的處理程序路徑string每每不盡相同,好比有的放C盤有的D盤,微軟想出了一個解決方案,那就是不使用直接的路徑表示方法,而使用一個叫 CLSID的方式間接描述這些對象數據的處理程序路徑。
CLSID 其實就是一個號碼,CLSID 的結構定義以下:
typedef struct _GUID {
DWORD Data1; // 隨機數
WORD Data2; // 和時間相關
WORD Data3; // 和時間相關
BYTE Data4[8]; // 和網卡MAC相關
} GUID;
typedef GUID CLSID; // 組件ID
typedef GUID IID; // 接口ID
#define REFCLSID const CLSID &
// 常見的聲明和賦值方法
CLSID CLSID_Excel = {0x00024500,0x0000,0x0000,{0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}};
struct __declspec(uuid("00024500-0000-0000-C000-000000000046")) CLSID_Excel;
class DECLSPEC_UUID("00024500-0000-0000-C000-000000000046") CLSID_Excel;
// 註冊表中的表示方法
{00024500-0000-0000-C000-000000000046}
若是使用開發環境編寫組件程序,則IDE會自動幫你產生 CLSID;
能夠用函數 CoCreateGuid() 產生 CLSID;
使用"vc目錄/Common/Tools/GuidGen.exe"工具產生GUID
每個COM組件都須要指定一個 CLSID,而且不能重名。它之因此使用16個字節,就是要從機率上保證重複是「不可能」的。可是,微軟爲了使用方便,也支持另外一個字符串名稱方式,叫 ProgID。。因爲 CLSID 和 ProgID 實際上是一個概念的兩個不一樣的表示形式,因此咱們在程序中能夠隨便使用任何一種。
下面是 CLSID 和 ProgID 之間的轉換方法和相關的函數:
函數 | 功能說明 |
CLSIDFromProgID()、CLSIDFromProgIDEx() | 由 ProgID 獲得 CLSID。沒什麼好說的,你本身均可以寫,查註冊表貝 |
ProgIDFromCLSID() | 由 CLSID 獲得 ProgID,調用者使用完成後要釋放 ProgID 的內存(注5) |
CoCreateGuid() | 隨機生成一個 GUID |
IsEqualGUID()、IsEqualCLSID()、IsEqualIID() | 比較2個ID是否相等 |
StringFromCLSID()、StringFromGUID2()、StringFromIID() | 由 CLSID,IID 獲得註冊表中CLSID樣式的字符串,注意釋放內存 |
客戶端軟件和組件之間的調用以下:
容器 協商部分 | 組件 應答部分 | |
1 | 根據CLSID啓動組件 。 CoCreateInstance() |
生成對象,執行構造函數,執行初始化動做。 |
2 | 你有IUnknown接口嗎? | 有,給你! |
3 | 恩,太好了,那麼你有IPersistStorage接口嗎?(注9) IUnknown::QueryInterface(IID_IPersistStorage...) |
沒有! |
4 | 真差勁,連這個都沒有。那你有IPersistStreamInit接口嗎?(注10) IUnknown::QueryInterface(IID_IPersistStreamInit...) |
哈,這個有,給! |
5 | 好,好,這還差很少。你如今給我初始化吧。 IPersistStreamInit::InitNew() |
OK,初始化完成了。 |
6 | 完成了?好!如今你讀數據去吧。 IPersistStreamInit::Load() |
讀完啦。我根據數據,已經在窗口中顯示出來了。 |
7 | 好,如今我們各自處理用戶的鼠標、鍵盤消息吧...... | ...... |
8 | 哎呀!用戶要保存退出程序了。你的數據被用戶修改了嗎? IPersistStreamInit::IsDirty() |
改了,用戶已經修改啦。 |
9 | 那好,那麼用戶修改後,你的數據須要多大的存儲空間呀? IPersistStreamInit::GetSizeMax() |
恩,我算算呀......好了,總共須要500KB。 |
10 | 暈,你這麼個小玩意竟然佔用這麼大空間?!......好了,你能夠存了。 IPersistStreamInit::Save() |
謝謝,我已經存好了。 |
11 | 恩。拜拜了您那。(注11) IPersistStreamInit::Release();IUnknown::Release() |
執行析構函數,刪除對象。 |
12 | 我本身也該退出了...... PostQuitMessage() |