潘爱民 2003-10-10 http://www.icst.pku.edu.cn/CompCourse2003/ COM对象的实现(续) 潘爱民 2003-10-10 http://www.icst.pku.edu.cn/CompCourse2003/
复习 COM对象 IDL描述 对象的创建:类厂 位置透明 注册表 自注册的COM组件
回顾:客户创建对象过程 客户提供信息 过程: 位置透明性的实现 组件位置、clsid、iid、结果接口指针地址ppv 根据组件位置,LoadLibrary GetProcAddress,获取DllGetClassObject 用clsid和IID_IClassFactory获得类厂对象接口指针pFactory 用iid、ppv调用pFactory->CreateInstance 位置透明性的实现 利用注册表 在COM组件和客户之间引入中介
COM库 创建过程 COM库处于COM组件和客户中间 调用过程 对于进程内组件, COM库不再参与处理
COM对象创建过程 客户 组件 COM创建函数 COM库 DllGetClassObject 类厂对象接口指针
COM创建函数 COM库中三个用于创建组件的函数: CoCreateInstance CoCreateInstanceEx 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(); }
CoCreateInstanceEx HRESULT CoCreateInstanceEx( const CLSID& clsid, IUnknown *pUnknownOuter, DWORD dwClsContext, COSERVERINFO *pServerInfo, DWORD dwCount, MULTI_QI *rgMultiQI );
三个创建函数选用原则 如果客户创建远程对象或者希望一次获取对象的多个接口指针,则选用CoCreateInstanceEx函数; 如果客户希望获取类厂对象或者要调用类厂的某些成员函数,则选用CoGetClassObject函数; 在其他情况下,使用CoCreateInstance函数创建对象,这是最常用的方法。
创建过程示意图
类厂的实现 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=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 管理server、组件等 ……
Service Control Manager COM库的组成(续) COM应用 (COM client) COM应用 (COM server) OLE32.DLL OLE32.DLL Service Control Manager RPCSS.EXE
COM库的初始化 基本的初始化函数: 初始化之前唯一可以调用的函数: 另一个初始化函数: 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库提供了内存管理器以及内存管理器的标准 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库内存管理用法(一) 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) ...... pszText = psz;
COM库内存管理用法(二) 三个封装函数: void * CoTaskMemAlloc(ULONG cb); void CoTaskMemFree(void *pv); void CoTaskMemRealloc(void *pv, ULONG cb);
COM库内存管理用法(三) DWORD length = MAX_LENGTH; IMalloc * pIMalloc; HRESULT hr; psz=CoTaskMemAlloc (length); if (NULL==psz) // return failure ...... pszText = psz;
COM库内存管理用法(四) 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(); 若DllCanUnloadNow返回S_OK,则同意卸载 若DllCanUnloadNow返回S_FALSE,则不同意卸载 DllCanUnloadNow实现:对象计数+锁计数
COM库中一些常用函数 初始化函数 GUID有关的函数 对象创建函数 内存管理函数
HRESULT数据结构 表达方法的操作结果,32位整数 类别码:反映了函数调用结果的基本情况 操作码:标识了结果操作来源
HRESULT 操作码 类别码 Win32 SDK的头文件WinError.h 00 - 表示函数调用成功 01 - 包含了一些信息 #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 类别码 00 - 表示函数调用成功 01 - 包含了一些信息 10 - 警告 11 - 错误 Win32 SDK的头文件WinError.h
HRESULT(续) FormatMessage函数 SUCCEEDED和FAILED宏 常用定义
总结:实现一个进程内COM组件的步骤 定义必要的CLSID和IID 实现COM对象 实现类厂对象 通过QueryInterface暴露其接口 管理引用计数,注意对全局引用计数的维护 实现类厂对象 对象的引用计数不记在全局对象引用计数内 维护锁计数 实现DllGetClassObject、DllCanUnloadNow (可选)实现两个注册函数
进程内组件与客户的协作过程