摘自:http://blog.csdn.net/zhongguoren666/article/details/6711396算法
當初微軟設計com規範的時候,有兩種選擇來保證用戶的設計的com組件能夠全球惟一:併發
第一種是採用和Internet地址同樣的管理方式,成立一個管理機構,用戶若是想開發一個COM組件的時候須要向該機構提出申請,並交必定的費用。函數
第二種是發明一種算法,每次都能產生一個全球惟一的COM組件標識符。工具
第一種方法,用戶使用起來太不方便,微軟採用第二種方法,併發明瞭一種算法,這種算法用GUID(Globally Unique Identifiers)來標識COM組件,GUID是一個128位長的數字,通常用16進製表示。算法的核心思想是結合機器的網卡、當地時間、一個隨即數來生成GUID。從理論上講,若是一臺機器每秒產生10000000個GUID,則能夠保證(機率意義上)3240年不重複。ui
GUID的例子: 54BF6567--1007--11D1--B0AA--444553540000spa
HKEY_CLASSES_ROOT\CLSID\{002B9E07-2E10-438F-AF1E-40E6A96F1EE4}.net
在微軟的COM中GUID和UUID、CLSID、IID是一回事,只不過各自表明的意義不一樣:設計
UUID : 表明COM指針
CLSID : 表明COM組件中的類 code
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() |
。兩者均可以用來標識,只是採用了不一樣的表示形式。
2.實現技巧
經過上面的分析,二者之間的轉換,能夠經過查詢註冊表達獲得,還能夠經過函數CLSIDFromProgID和ProgIDFromCLSID完成轉換,函數原型以下:
HRESULT CLSIDFromProgID( |
3.實例代碼
本實例演示了CLSID和ProgID之間的相互轉換。首先建立一個簡單的組件,而後利用一個調用者程序進行兩者之間的轉換。
(1)創建一個ATL工程Object,選擇DLL方式,如圖12-2所示。
Allow merging of proxy/stub code、Support MFC和Support MTS爲默認便可。
(2)添加ATL類對象Cfun,設置其類對象的屬性如圖12-3所示。
(點擊查看大圖)圖12-2 組件建立 |
圖12-3 組件建立 |
圖12-4 組件屬性配置 |
這樣,一個簡單的COM組件就作好了,這個組件,沒有任何功能實現。從這個COM組件中找出它的 CLSID,查看idl文件。其中86A70E6F-3F1C-46B5-86F9-C21DAD69C756爲CLSID。
下面寫一個函數,完成CLSID和ProgID的轉換。
CLSID clsid = {0x86A70E6F,0x3F1C,0x46B5,{0x86,0xF9,0xC2,0x1D, |
圖12-5 CLSID 轉換爲ProgID |