COM技术7
来源:互联网 发布:如何报考网络大专 编辑:程序博客网 时间:2024/05/21 09:39
回忆一下COM技术5中我们创建组件的方法,客户以DLL的名称做为参数.装载此DLL并调用其中所输出的函数CreateInstance.这个函数可以建立一个组件的实例并给客户返回一个IUnknown接口的指针.但是组件对我们来说仍是不透明的,我们需要知道实现组件DLL的位置.但是COM组件实际上可以透明地在网络上(或本地)被重新分配位置,而不会影响本地客户程序.所以,由客户端来调用DLL并不是什么好主意.必须有一种更好的办法让组件的实现更透明,更灵活.于是就出现了类厂的概念.这和设计模式中封装变化点的思想一致,哪里有变化,我就封装哪里.类厂封装了组件的产生过程.下面来看看这一系列活动内部运转的详细过程:我在 http://www.cnblogs.com/shipfi/archive/2007/02/13/649196.html 里找到了详细介绍:
1.实现二个接口IX,IY
2.实现一个组件CA,实现了IX,IY接口.
3.对于这个组件进行注册,把组件的信息加入到注册表中.实现DllRegisterServer和DllUnregisterServer函数.函数具体功能就是把本组件的CLSID,ProgID,DLL的位置放入注册表中.这样程序就可以通过查询注册表来获得组件的位置.
*/
/**//*
4.创建本组件类厂的实例
*/
class CFactory:public IClassFactory
...{
virtual HRESULT __stdcall QueryInterface(const IID& iid,void** ppv);
virtual ULONG __stdcall AddRef();
virtual ULONG __stdcall Release();
virtual HRESULT __stdcall CreateInstance(IUnknown* pUnknownOuter, const IID& iid, void** ppv);
}
/**//*
在类厂实例中,主要的功能就是CreateInstance了,这个函数就是创建组件的相应实例.看它的实现:
*/
HRESULT __stdcall CFactory::CreateInstance(IUnknown* pUnknownOuter,const IID& iid,void** ppv)
...{
//...
CA* pA = new CA;
if(pA == NULL)
...{
return E_OUTOFMEMORY;
}
HRESULT hr = pA->QueryInterface(iid,ppv);
pA->Release();
return hr;
}
/**//*
5.在这个组件的DLL中导出DllGetClassObject函数.这个函数的功能就是创建类厂的实例对象并查询接口.看其实现:
*/
STDAPI DllGetClassObject(const CLSID& clsid,const IID& iid,void** ppv)
...{
//....
CFactory* pFactory = new CFactory();
if(pFactory == NULL)
...{
return E_OUTOFMEMORY;
}
HRESULT hr = pFactory->QueryInterface(iid,ppv);
pFactory->Release();
return hr;
}
/**//*
组件的实现差不多就这么多,在客户端怎么调用组件呢?这就需要用到COM函数库了,由COM函数库去查找注册表,调用组件的类厂,创建组件实例,返回接口.如下所示:
*/
...{
//...
IUnknown* pUnk = NULL;
IX* iX = NULL;
CoInitialize(NULL);
CoCreateInstance(CLSID_Component1,CLSCTX_INPROC_SERVER,IID_IUnknown,(void**)&pUnk);
pUnk->QueryInterface(IID_IX,(void**)&iX);
pUnk->Release();
iX->Fx();
iX->Release();
CoUninitialize();
}
/**//*
至于客户是通过CoCreateInstance怎么获得组件的类厂,创建组件实例的.下面,清晰的说明了这一切:
*/
//CoCreateInstance内部实现的伪代码:
CoCreateInstance(....)
...{
//.......
IClassFactory *pClassFactory=NULL;
CoGetClassObject(CLSID_Object, CLSCTX_INPROC_SERVER, NULL, IID_IClassFactory, (void **)&pClassFactory);
pClassFactory->CreateInstance(NULL, IID_IUnknown, (void**)&pUnk);
pClassFactory->Release();
//........
}
//这段话的意思就是先得到类厂对象,再通过类厂创建组件从而得到IUnknown指针.
//继续深入一步,看看CoGetClassObject的内部伪码:
CoGetClassObject(.....)
...{
//通过查注册表CLSID_Object,得知组件DLL的位置,文件名
//装入DLL库
//使用函数GetProcAddress(...)得到DLL库中函数DllGetClassObject的函数指针.
//调用DllGetClassObject
}
//DllGetClassObject是干什么的,它是用来获得类厂对象的.只有先得到类厂才能去创建组件.
//下面是DllGetClassObject的伪码:
DllGetClassObject(...)
...{
//......
CFactory* pFactory= new CFactory; //类厂对象
pFactory->QueryInterface(IID_IClassFactory, (void**)&pClassFactory);
//查询IClassFactory指针
pFactory->Release();
//......
}
//CoGetClassObject的流程已经到此为止,现在返回CoCreateInstance,看看CreateInstance的伪码:
CFactory::CreateInstance(.....)
...{
//......
CA* pA = new CA; //组件对象
CA->QueryInterface(IID_IUnknown, (void**)&pUnk);
CA->Release();
}
总结一下上面描述的过程:客户端调用CoCreateInstance,导致调用CoGetClassObject,CoGetClassObject通过查找注册表,得知DLL位置,文件名,然后调用DLL中DllGetClassObject,DllGetClassObject的功能是返回CFactory的实例.返回后,回到CoCreateInstance,通过CFactory的指针,调用pClassFactory->CreaetInstance()创建组件实例.这样就返回了组件实例的指针.
CoCreateInstace --> CoGetClassObject --> DllGetClassObject --> Get CFactory*
<-------------------------------------------------------
--> CFactory->CreateInstance(); --> Get IX*
IX->Fx();
下面插图也很好说明了这个过程:
下面是得到桌面背景的一个例子,可以看到,客户其实只需要调用CoCreateInstance就可以了:
...{
WCHAR wszWallpaper [MAX_PATH];
CString strPath;
HRESULT hr;
IActiveDesktop* pIAD;
// 1. 初始化COM库(让Windows加载DLLs)。通常是在程序的InitInstance()中调用
// CoInitialize ( NULL )或其它启动代码。MFC程序使用AfxOleInit()。
CoInitialize ( NULL );
// 2. 使用外壳提供的活动桌面组件对象类创建COM对象。
// 第四个参数通知COM需要什么接口(这里是IActiveDesktop).
hr = CoCreateInstance ( CLSID_ActiveDesktop,
NULL,
CLSCTX_INPROC_SERVER,
IID_IActiveDesktop,
(void**) &pIAD );
if ( SUCCEEDED(hr) )
...{
// 3. 如果COM对象被创建成功,则调用这个对象的GetWallpaper() 方法。
hr = pIAD->GetWallpaper ( wszWallpaper, MAX_PATH, 0 );
if ( SUCCEEDED(hr) )
...{
// 4. 如果 GetWallpaper() 成功,则输出它返回的文件名字。
// 注意这里使用wcout 来显示Unicode 串wszWallpaper. wcout 是
// Unicode 专用,功能与cout.相同。
wcout << L"Wallpaper path is: " << wszWallpaper << endl << endl;
}
else
...{
cout << _T("GetWallpaper() failed.") << endl << endl;
}
// 5. 释放接口。
pIAD->Release();
}
else
...{
cout << _T("CoCreateInstance() failed.") << endl << endl;
}
// 6. 收回COM库。MFC 程序不用这一步,它自动完成。
CoUninitialize();
}
也许图形最能说明问题,再发一图:
首先是客户,它将调用CoGetClassObject来启动组件的创建过程.其次是COM库,它实现了CoGetClassObject函数.第三个角色是DLL,其中实现了被CoGetClassObject调用的DLLGetClassObject函数.DLLGetClassObject的任务就是创建客户所请求的类厂.当然它创建此类厂的方式完全是由开发人员决定的.
在创建好类厂之后,客户将使用IClassFactory接口来创建相应的组件,IClassFactory::CreateInstance如何创建组件也是由开发人员决定的.IClassFactory将把这个过程封装起来,以便类厂能够使用关于组件的内部知识来创建它.
下面的图片,展示了主要元素之间的协作关系:
在例子中是通过CoCreateInstace创建组件的,当然客户还可以调用CoGetClassObject,利用CoGetClassObject会有更大的灵活性.和CoCreateInstace不同的是,CoGetClassObject返回的是创建类厂的接口.
但一般使用CoCreateInstace就足够了,有两个情况,我们要使用CoGetClassObject,一是如果想用不同于IClassFactory的某个创建接口(比如IClassFactory2)来创建组件,则必须使用CoGetClassObject.第二种情况是,如需创建同一个组件的多个实例,那么使用CoGetClassObject将可以获得更高的效率.因为只需要创建相应的类厂一次,而CoCreateInstace则需为每一个实例分别创建并释放相应的类厂.见下面代码(是得到桌面背景的另一个实现方法):
...{
WCHAR wszWallpaper [MAX_PATH];
CString strPath;
HRESULT hr;
IActiveDesktop* pIAD;
// 1. 初始化COM库(让Windows加载DLLs)。通常是在程序的InitInstance()中调用
// CoInitialize ( NULL )或其它启动代码。MFC程序使用AfxOleInit()。
CoInitialize ( NULL );
// 2. 使用外壳提供的活动桌面组件对象类创建COM对象。
// 第四个参数通知COM需要什么接口(这里是IActiveDesktop).
#if 1
IClassFactory* pIFactory = NULL;
hr = CoGetClassObject(CLSID_ActiveDesktop,
CLSCTX_INPROC_SERVER,
NULL,
IID_IClassFactory, //lei:这里还可以用其他创建型接口
(void**)&pIFactory);
if (SUCCEEDED(hr))
...{
hr = pIFactory->CreateInstance(NULL,IID_IActiveDesktop,(void **)&pIAD);
//lei:这里可以多次调用,获得多个实例.
pIFactory->Release();
}
#else
hr = CoCreateInstance ( CLSID_ActiveDesktop,
NULL,
CLSCTX_INPROC_SERVER,
IID_IActiveDesktop,
(void**) &pIAD );
#endif
if ( SUCCEEDED(hr) )
...{
// 3. 如果COM对象被创建成功,则调用这个对象的GetWallpaper() 方法。
hr = pIAD->GetWallpaper ( wszWallpaper, MAX_PATH, 0 );
if ( SUCCEEDED(hr) )
...{
// 4. 如果 GetWallpaper() 成功,则输出它返回的文件名字。
// 注意这里使用wcout 来显示Unicode 串wszWallpaper. wcout 是
// Unicode 专用,功能与cout.相同。
wcout << L"Wallpaper path is: " << wszWallpaper << endl << endl;
}
else
...{
cout << _T("GetWallpaper() failed.") << endl << endl;
}
// 5. 释放接口。
pIAD->Release();
}
else
...{
cout << _T("CoCreateInstance() failed.") << endl << endl;
}
// 6. 收回COM库。MFC 程序不用这一步,它自动完成。
CoUninitialize();
}
- COM技术7
- COM技术
- COM技术
- COM技术
- COM技术
- COM技术
- COM技术
- COM技术
- COM技术
- COM与COM+技术
- com技术内幕--读书笔记(7)
- COM学习笔记七《COM技术内幕》§7 —— 类厂、COM库
- 对象组件技术COM+
- COM技术内幕摘要
- COM技术经典书籍
- COM技术初探
- 《COM技术内幕》
- COM组件技术
- 使用JavaMail发送邮件
- 关于未来的若干计划和时间分配
- JAVA2核心技术卷I:基础知识(原书第7版) -- 第3章. Java基本程序结构
- 更新入侵分析收集证据一书
- 爱过了,就不悔 缘尽了,已无谓
- COM技术7
- c#中邮件收发处理(POP3,IMAP,SMTP)
- 中英对照UNIX操作系统
- 简明批处理教程
- 玩转 Log4Net 五步走
- 根据窗口大小自动调整元素大小
- 关于C# winform treeview 的两个问题(点击空白处的时间响应和复选框选择问题)
- oracle自增长字段
- 监听到了CTabItem的关闭事件后,如何不关闭它