微軟的COM中GUID和UUID、CLSID、IID

摘自: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(
LPCOLESTR lpszProgID,     // 指向ProgID的指針
LPCLSID pclsid             // 指向CLSID的指針
);
WINOLEAPI ProgIDFromCLSID(
REFCLSID clsid,       // CLSID 的值,已知
LPOLESTR * lplpszProgID   // 指向接收ProgID的緩衝區
     
);

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-3能夠知道ProgID = OBJECT.Fun,默認爲工程名+ShortName,單擊Attributes選項卡,如圖12-4所示。
 
圖12-4  組件屬性配置

這樣,一個簡單的COM組件就作好了,這個組件,沒有任何功能實現。從這個COM組件中找出它的 CLSID,查看idl文件。其中86A70E6F-3F1C-46B5-86F9-C21DAD69C756爲CLSID。

下面寫一個函數,完成CLSID和ProgID的轉換。

 CLSID clsid = {0x86A70E6F,0x3F1C,0x46B5,{0x86,0xF9,0xC2,0x1D,
0xAD,0x69,0xC7,0x56}};
CString strClsID;
strClsID.Format("%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x",clsid.Data1,
clsid.Data2,clsid.Data3,clsid.Data4[0],clsid.Data4[1],clsid.Data4[2],
clsid.Data4[3],clsid.Data4[4],clsid.Data4[5],clsid.Data4[6],
clsid.Data4[7]);
SetDlgItemText(IDC_CLSID_ED,strClsID);
HRESULT hr;
LPOLESTR lpwProgID = NULL;
hr = ::ProgIDFromCLSID( clsid, &lpwProgID );
if ( SUCCEEDED(hr) )
{
//::MessageBoxW( NULL, lpwProgID, L"ProgID", MB_OK );
USES_CONVERSION;
LPCTSTR lpstr =  OLE2CT( lpwProgID );
SetDlgItemText(IDC_PROGID_ED,lpstr); 
IMalloc * pMalloc = NULL;
hr = ::CoGetMalloc( 1, &pMalloc );   // 取得 IMalloc
if ( SUCCEEDED(hr) )
{
pMalloc->Free( lpwProgID );      // 釋放ProgID內存
pMalloc->Release();               // 釋放IMalloc
}
}
其中OLE2CT完成了LPCOLESTR到LPCTSTR的轉換,運行結果如圖12-5所示。
 
圖12-5  CLSID 轉換爲ProgID
相關文章
相關標籤/搜索