微軟的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年不重復。

GUID的例子: 54BF6567--1007--11D1--B0AA--444553540000

                HKEY_CLASSES_ROOT\CLSID\{002B9E07-2E10-438F-AF1E-40E6A96F1EE4}

在微軟的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()
 

 

 

。二者都可以用來標識,只是采用了不同的表示形式。

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

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM