COM实现
潘爱民
http://www.icst.pku.edu.cn/compcourse
内容
!复习:COM接口与COM对象
!注册表
!类厂
!COM库
!总结和例子
组件接口
!第一个里程碑
–用vtable作为接口
–解决了名字冲突和二进制结构兼容问题
!第二个里程碑
–接口转换:Dynamic_cast
!第三个里程碑
–管理对象生命周期:引用计数
COM接口
!接口标识:IID
! IUnknown接口:
class IUnknown
{
public:
virtual HRESULT__stdcall QueryInterface(
const IID& iid, void **ppv) = 0 ;
virtual ULONG __stdcall AddRef() = 0;
virtual ULONG __stdcall Release() = 0;
};
COM接口结构
接口指针 指针 指针函数1
指针函数2
指针函数3
。。。。。。
对象实现
vtablepVtable
COM接口引用计数
!引用计数的含义
– Outstanding reference
!对象实现引用计数
– AddRef、Release
!客户显式地操纵引用计数
–客户有责任维护好引用计数
!引用计数规则
COM接口QueryInterface
HRESULT __stdcall QueryInterface(
const IID& iid, void **ppv)
!一个COM对象可以实现多个接口
– QueryInterface是技术保
!QueryInterface实现
–多 : 用static_cast 转换
–对象 :IUnknown 一
!客户 QueryInterface 用对象的接口
–注 :QueryInterface内含AddRef
IDL
!类C的 是 程
– 的
! :MIDL.exe 生C++ 件 义
– 生C++ 件 义 ?C++ 义
!MIDL.exe 生TLB类¢库
–£多 程 ?用COM对TLB的¥?
!§有的标currency1接口'可以“SDK ??IDL?fi
–¥?import/importlib fl
IDL
xxx.IDL 件
MIDL.exe
xxx.h C++ 件
xxx_i.c GUID
xxx_p.c P/S
dlldata.c
xxx.tlb
用?客户/ –
proxy/stub
用??? 程
·Java、VB
COM对象
!对象标识:CLSID
! ?:??和?fl ”?…‰和操作
!对象与接口的 ?
–· C++对象与`′函数? 的 ?
!COM对象的
–对象 一??ˉcurrency1则
实现?式
!进程内组件
– in process component
!进程˙组件
– out of process component
进程内组件
!组件:¨`DLL——引 函数
!客户:用?的API函数 LoadLibrary、
GetProcAddress、FreeLibrary
!??:
– 1. 可以引 ??ˇ—
– 2. DumpBin 组件的引 函数和ˇ—
进程˙组件
!实现?式:EXE
!IPC:DDE、 制、 内 、
RPC/LPC
!例: 用 用? –
用A
进程A
DLL
进程B
–f
用A
用?
API函数
? DLL
LPC 用 –f
– f
LPCa 结
DLL a
?结
进程˙组件(?)
客户程?
客户进程
o理DLL
o理对象
组件程?
组件进程
DLL
客户
用接口`
′函数
?o理对象
LPC 用组件
DLL
LPC a 结
o理对象a
?结
组件对象
用组件
对象接口`
′函数
– `
a
?
:对象与客户? 的?接
!客户 vtable与对象进
!客户· ? 第一个接口指针
– CreateString引 函数
!· ??(create) ??(activate)
!?? 作一 是 组件 的一个函数
`:??函数CreateObject
!客户· 问这个函数
??函数
!?案1
–直接引 ??函数
–优点:对?DLL非常?便
!?案2
–把??函数 ??一个对象 vtable 用
–优点:灵? 客户以一致的?式 用??函数
??函数(?)
客户 组件
??函数指针
客户
组件
??函数指针
??函数§“的对象
!该对象被称为类对象 称为类厂
!现“问题是:· ??类厂对象
–对?DLL 引 函数
–对?EXE EXE的引 函数
!客户-〉引 函数-〉类厂对象-〉用户对象
!引 函数的名字固 :DllGetClassObject
!增加了一层 接? 带 灵??
??对象结构示 图
客户
组件
??实例对象
DllGetClassObject
{
}
??类厂对象
类厂(Class Factory)
!类厂:用???COM对象的COM对象
!目标: `COM对象的?? 程 更好
地把客户与对象隔离开 。
!特殊?:
–实现一个”多个??接口 缺省的接口
为IClassFactory
–类厂本 没有CLSID
类厂(?)
!类厂与COM对象有一一对 关?
字典组件
IClassFactory
类厂
字典对象
IDictionary
ISpellCheck
多对象、多类厂组件
类厂1
对象1
类厂 2
对象 2
??类厂对象
!DllGetClassObject??类厂对象
–??类厂对象需要哪些
!DllGetClassObject原¢:
HRESULT DllGetClassObject(
const CLSID& clsid,
const IID& iid,
(void **)ppv
);
??函数需要哪些
!clsid
–与类厂绑“一起
!iid
–客户提供
!结 接口指针
–类¢取决?iid
IClassFactory接口
class IClassFactory : public IUnknown
{
virtual HRESULT __stdcall CreateInstance(
IUnknown *pUnknownOuter,
const IID& iid,
void **ppv) = 0;
virtual HRESULT __stdcall LockServer(
BOOL bLock) = 0;
};
小结:客户??对象 程
!客户提供
–组件位置、clsid、iid、结 接口指针地址ppv
! 程:
– 据组件位置 LoadLibrary
– GetProcAddress ?取DllGetClassObject
–用clsid和IID_IClassFactory? 类厂对象接口
指针pFactory
–用iid、ppv 用pFactory->CreateInstance
?? 程的位置透??
!位置透??可以极大地?便客户程?
!· ¨?位置透??
–“ 前环境下 每个clsid 与某个组件
?
–· 从clsid映射?组件位置
!解决?案:
–维护clsid与组件位置的映射关?
–“客户与组件? 插入 介
COM?案
!“Windows平台 用? 注册表保
映射关? §以 从clsid可以??对
组件的位置
!“客户与组件? 插入COM库 COM
库 `??的细节 作
Windows? 注册表
!树…结构
– 是“My Computer”
–预 义的5个子节点
– HKEY_CLASSES_ROOT
!为HKEY_LOCAL_MACHINE的一个子节点
– HKEY_CURRENT_USER
!为HKEY_USERS的一个子节点
– HKEY_LOCAL_MACHINE
– HKEY_USERS
– HKEY_CURRENT_CONFIG
注册表管理COM对象
!HKEY_CLASSES_ROOT\CLSID
TreeView组件的注册
Microsoft TreeView Control
C:\WINDOWS\SYSTEM\COMCTL32.OCX
COMCTL.TreeCtrl.1
{6B7E6392-850A-101B-AFC0-4210102A8DA7}
C:\WINDOWS\SYSTEM\COMCTL32.OCX
:COM对象的标识
!CLSID 两种?式
– 128位整数 随 数 需要运算功能 是
需要比较和 ?功能
–字符串?式
例·: {72d3edc2-a4c4-11d0-8533-00c04fd8d503}
!ProgID:友好名 字符串?式
–有可能重名 用一种约 避免重名
–例·:Word.Document
–包含版本:Word.Document.8
TreeControl的ProgID
{0713E8A2-850A-101B-AFC0-4210102A8DA7}
{0713E8A2-850A-101B-AFC0-4210102A8DA7}
COMCTL.TreeCtrl.1
注册表??事项
!? ??的注册 、公 仓库
! 具RegEdit.exe、Regedt32、OLEView
!程? 问 :Win32 API
!Component Categories 组件类
组件类
COM组件的注册
!进程内组件
– 两个引 函数DllRegisterServer和DllUnregisterServer
注册 具:RegSvr32.exe
例·:RegSvr32 c:\DictComp\DictComp.dll
RegSvr32 /u c:\DictComp\DictComp.dll
!进程˙组件
– 命 数/RegServer和/UnregServer
COM库
!?? 程
– COM库 ?COM组件和客户
! 用 程
–对?进程内组件 COM库 与 理
COM对象?? 程
客户
组件
COM??函数
COM库
DllGetClassObject
类厂对象接口指针
COM??函数
!COM库 三个用???组件的函数:
CoGetClassObject
CoCreateInstance
CoCreateInstanceEx
CoGetClassObject
!??一个类厂
HRESULT CoGetClassObject(
const CLSID& clsid,
DWORD dwClsContext,
COSERVERINFO *pServerInfo,
const IID& iid,
(void **)ppv
);
CoCreateInstance
HRESULT CoCreateInstance(
const CLSID& clsid,
IUnknown *pUnknownOuter,
DWORD dwClsContext,
const IID& iid,
(void **)ppv
);
CoCreateInstance实现
HRESULT CoCreateInstance(const CLSID& clsid, IUnknown *pUnknownOuter,
DWORD dwClsContext, const IID& iid, void *ppv)
{
IClassFactory *pCF;
HRESULT hr;
hr = CoGetClassObject(clsid, dwClsContext, NULL,
IID_IClassFactory, (void *)pCF);
if (FAILED(hr))
return hr;
hr = pCF->CreateInstance(pUnkOuter, iid, (void *)ppv);
pCF->Release();
return hr;
}
CoCreateInstanceEx
HRESULT CoCreateInstanceEx(
const CLSID& clsid,
IUnknown *pUnknownOuter,
DWORD dwClsContext,
COSERVERINFO *pServerInfo,
DWORD dwCount,
MULTI_QI *rgMultiQI
);
三个??函数 用原则
!· 客户?? 程对象”? 一 ?
取对象的多个接口指针 则 用
CoCreateInstanceEx函数
!· 客户 ?取类厂对象”?要 用
类厂的某些`′函数 则 用
CoGetClassObject函数
!“?? 下 用CoCreateInstance函
数??对象 这是 常用的?fl。
?? 程示 图
客户程?
用CoCreateInstance
用CoGetClassObject
? 类厂接口指针
??对象
用对象
COM 库
CoGetClassObject:
据注册表??DLL
?入DLL程?
用DllGetClassObject函数
a 类厂
组件程?
DllGetClassObject
??类厂
a 类厂接口指针
IClassFactory
类厂
COM对象
?
类
厂
的
实
现
class CDictionaryFactory : public IClassFactory
{
protected:
ULONG m_Ref;
public:
CDictionaryFactory (void);
~ CDictionaryFactory (void);
//IUnknown members
HRESULT QueryInterface(const IID& iid, void **ppv);
ULONG AddRef();
ULONG Release();
//IClassFactory members
HRESULT CreateInstance(IUnknown *, const IID& iid, void **ppv)
HRESULT LockServer(BOOL);
};
CreateInstance函数的实现
HRESULT CDictionaryFactory::CreateInstance(IUnknown
*pUnknownOuter, const IID& iid, void **ppv)
{
CDictionary * pObj;
HRESULT hr;
*ppv=NULL;
hr=E_OUTOFMEMORY;
if (pUnknownOuter != NULL)
return CLASS_E_NOAGGREGATION;
pObj
pObj
=new CDictionary();
if (pObj== NULL) return hr;
// ?
CreateInstance函数的实现(?)
//?
//Obtain the first interface pointer (which does an AddRef)
hr=pObj->QueryInterface(iid, ppv);
if (hr != S_OK)
{
g_DictionaryNumber --;
delete pObj;
}
return hr;
}
DllGetClassObject的实现
extern "C" HRESULT __stdcall DllGetClassObject(const CLSID&
clsid, const IID& iid, void **ppv)
{
if (clsid == CLSID_Dictionary ) {
CDictionaryFactory *pFactory = new CDictionaryFactory;
if (pFactory == NULL) {
return E_OUTOFMEMORY ;
}
HRESULT result = pFactory->QueryInterface(iid, ppv);
return result;
} else {
return CLASS_E_CLASSNOTAVAILABLE;
}
}
类厂对组件生 期的 制
!组件引用计数 计类厂
!IClassFactory::LockServer函数
COM库
!COM库的
!COM库的内 管理
!组件程?的? 和
!常用函数和HRESULT
COM库的组`
!用??? 程的SCM(Service Control
Manager)
– rpcss.exe
– ole32.dll
!??
–提供COM环境
–管理server、组件
– ……
COM库的组`(?)
COM 用
(COM client)
Service Control Manager
RPCSS.EXE
COM 用
(COM server)
OLE32.DLL OLE32.DLL
COM库的
! 本的 函数:
– HRESULT CoInitialize(void *pReserved);
! ?前 一可以 用的函数:
– DWORD CoBuildVersion();
! 一个 函数:
– CoInitializeEx
!COM库的? 函数:
– void CoUninitialize(void);
有关CLSID和ProgID的COM函数
!IsEqualGUID、IsEqualIID、IsEqualCLSID
!CLSIDFromProgID、ProgIDFromCLSID
!StringFromCLSID、CLSIDFromString
!StringFromIID、 IIDFromString
!StringFromGUID2
–内 用?
!注 :COM库函数的字符串 用OLECHAR
类¢
COM库的内 管理
!COM库提供了内 管理 以 内 管理
的标currency1
HRESULT CoGetMalloc(DWORD dwMemContext, IMalloc **ppMalloc);
class IMalloc : public IUnknown
{
void * Alloc(ULONG cb) = 0;
void * Realloc( void * pv, ULONG cb) = 0;
void Free(void* pv) = 0;
ULONG GetSize( void * pv) = 0;
int DidAlloc(void * pv) = 0;
void HeapMinimize()= 0;
};
COM库内 管理用fl 一
DWORD length = MAX_LENGTH;
IMalloc * pIMalloc;
HRESULT hr;
hr=CoGetMalloc(MEMCTX_TASK, &pIMalloc);
if (hr != S_OK)
// return failure
psz=pIMalloc->Alloc(length);
pIMalloc->Release();
if (NULL==psz)
// return failure
......
pszText = psz;
COM库内 管理用fl 二
!三个 ?函数:
void * CoTaskMemAlloc(ULONG cb);
void CoTaskMemFree(void *pv);
void CoTaskMemRealloc(void *pv, ULONG cb);
COM库内 管理用fl 三
DWORD length = MAX_LENGTH;
IMalloc * pIMalloc;
HRESULT hr;
psz=CoTaskMemAlloc (length);
if (NULL==psz)
// return failure
......
pszText = psz;
COM库内 管理用fl ?
WCHAR *pwProgID;
char pszProgID[128];
hResult = ::ProgIDFromCLSID(CLSID_Dictionary,
&pwProgID);
if (hResult != S_OK) {
……
}
wcstombs(pszProgID, pwProgID, 128) ;
CoTaskMemFree(pwProgID);
组件程?的? 和
!进程内组件的?
– DllGetClassObject
!进程˙组件的?
– “/Embedding”命 数
!进程内组件的
– CoFreeUnusedLibraries
!进程˙组件的
– main”?WinMain函数¢
进程内组件的
!组件 能£?
!客户 用COM库函数CoFreeUnusedLibraries
!COM库 用DLL组件的引 函数
– HRESULT DllCanUnloadNow();
–¥DllCanUnloadNowa S_OK 则
–¥DllCanUnloadNowa S_FALSE 则
!DllCanUnloadNow实现:对象计数+?计数
COM库 一些常用函数
类 函数 功能
CoBuildVersion ?取COM库的版本§
CoUnitialize COM库的
CoUninitialize COM库功能 –?
函数
CoFreeUnusedLibraries currency1'进程 §有 用的组件程?
IsEqualGUID ?ˉ两个GUID 是“
IsEqualIID ?ˉ两个IID是“
IsEqualCLSID ?ˉ两个CLSID是“
CLSIDFromProgID 把字符串?式的对象标识转 为CLSID结构?式
StringFromCLSID 把CLSID结构?式转 为字符串?式
IIDFromString 把字符串?式的IID转 为IID结构?式
StringFromIID 把IID结构?式转 为字符串?式
GUID有关的函数
StringFromGUID2 把GUID 结构?式转 为字符串?式
CoGetClassObject ?取对象的类厂
CoCreateInstance ??COM对象
CoCreateInstanceEx ??COM对象 可指 多个接口” 程对象
CoRegisterClassObject ??一个对象 以便?? 用可以?接?该对象
CoRevokeClassObject 取 对象的??操作
对象??函数
CoDisconnectObject ˉ开?? 用与对象的?接
CoTaskMemAlloc 内 函数
CoTaskMemRealloc 内 重fi 函数
CoTaskMemFree 内 currency1'函数
内 管理函数
CoGetMalloc ?取COM库的内 管理 接口
COM库 一些常用函数
! 函数
!GUID有关的函
数
!对象??函数
!内 管理函数
!表fl?fl的操作结 32位整数
!类 : 映了函数 用结 的 本
!操作 :标识了结 操作 –
HRESULT数据结构
0
操作结 操作
16 1531 30 29 28
?重程?
£ 义标·
!操作
#define FACILITY_WINDOWS 8
#define FACILITY_STORAGE 3
#define FACILITY_RPC 1
#define FACILITY_SSPI 9
#define FACILITY_WIN32 7
#define FACILITY_CONTROL 10
#define FACILITY_NULL 0
#define FACILITY_INTERNET 12
#define FACILITY_ITF 4
#define FACILITY_DISPATCH 2
#define FACILITY_CERT 11
!Win32 SDK的 件WinError.h
HRESULT
!类
00 - 表示函数 用`功
01 - 包含了一些
10 - ?
11 - ??
常用的HRESULT? 义
a ” ??
S_OK 函数? `功 ?”为0。*
S_FALSE 函数? `功 a …‰FALSE ?”为1。*
E_FAIL 函数? ? ?原 ` 。
E_OUTOFMEMORY 函数? ? ?原 为内 `功。
E_NOTIMPL 函数? ? `′函数没有被实现。
E_NOTINTERFACE
函数? ? 组件没有实现指 的接口 用?
QueryInterface`′函数的a ”。
HRESULT(?)
!FormatMessage函数
!SUCCEEDED和FAILED?
!常用 义
COM实现 程
!进程内组件与客户的′作 程
!进程˙组件与客户的′作 程
实现一个进程内COM组件的??
! 义 要的CLSID和IID
!实现COM对象
– QueryInterfaceˉ??接口
–管理引用计数 注 对??引用计数的维护
!实现类厂对象
–对象的引用计数 ?“??对象引用计数内
–维护?计数
!实现DllGetClassObject、DllCanUnloadNow
!(可 )实现两个注册函数
客户程? COM 库组件程?(DLL)
CLSID clsid
IClassFactory *pClf;
IUnknown *pUnknown;
CoInitialize(NULL);
CLSIDFromProgID(
"Dictionary.Object", &clsid);
COM “注册表 ?字典 CLSID
CoGetClassObject(clsid,
CLSCTX_INPROC_SERVER,
NULL, IID_IClassFactory,
(void **)&pClf);
COM 库“内 ? clsid 组件
if (DictComp.dll˙没被?入内 )
{
从注册表 ?取组件程?
?¨ 名 ...\DictComp.dll
CoLoadLibrary(...)
}
用DLL的 DllGetClassObject
??类厂对象
CDictionaryFactory
a IClassFactory接口
COM 库a IClassFactory 接口?
客户
进程内组件与客户的′作 程
pClf->CreateInstance(NULL,
IID_IUnknown,
(void **)&pUnknown);
类厂对象的 CreateInstance 函数被
用( 组件的 vtable 被客
户直接 用)
用 new 操作符构?字典组件对象
new CDictionary;
a IUnknown 接口指针
客户 用字典组件 ?接口
进 种操作......
pClf->Release();
pUnknown->Release();
组件对象的 Release 函数被 用
( 是 组件的 vtable)
if (m_Ref == 0)
{
delete this;
return 0;
}
CoFreeUnusedLibraries()
COM 库 用字典组件的引 函数
DllCanUnloadNow
DllCanUnloadNow 函数 :
if ( “字典对象
&& ?计数 为 0)
return TRUE;
else
return FALSE;
if (字典组件 DllCanUnloadNow 函
数a TRUE)
CoFreeLibrary(...);
CoUninitialize()
COM 库currency1'?–
客户程?¢
客户程? COM库 组件程?(EXE)
CLSID clsid
IClassFactory *pClf;
IUnknown *pUnknown;
CoInitialize(NULL);
CLSIDFromProgID(
"...", &clsid);
COM “注册表 组件对象CLSID
CoGetClassObject(clsid,
CLSCTX_LOCAL_SERVER,
NULL,
IID_IClassFactory,
(void **)&pClf);
COM库“内 ?clsid组件
if (组件EXE˙没有?ˇ
|| COM需要 一个实例)
{
从注册表 ?取EXE组件名
??组件进程
}
进程˙组件与客户的′作 程
用 CoInitialize
??组件¥?的 种类厂对象
用 COM 函数注册§有类厂对
象:CoRegisterClassObject
组件注册 ` COM 库把
IClassFactory接口a ?客户
pClf->CreateInstance(NULL,
IID_IUnknown,
(void **)&pUnknown);
类厂对象的 CreateInstance 函数被
用( 进程 o理对象被
接 用)
构?组件对象
a IUnknown 接口指针
客户 用字典组件 ?接口
( 接)进 种操作......
pClf->Release();
pUnknown->Release();
组件对象的Release函数被 用
if (m_Ref == 0)
{
delete this;
return 0;
}
· — ¢ 件则 常¢
CoUninitialize ()
COM 库对§有该客户没有`功currency1
'的对象 用Release函数
CoUninitialize ()
组件程?¢
COM 库currency1'?–
客户程?¢
£ 作
! 程?
– COM原理与 用
!第二 的 字典组件和字典客户
!两个 程:DictComp和DictCtrl
–”? Inside COM
!第 的例子程?
–目标:
了解COM组件的?? 程
进一?理解COM组件的位置透??