VC++中访问COM组件的五种方法

王影  -(广州大学计算机教育软件研究所,广东广州510405)

一、使用ATL开发一个COM组件一般有以下几个步骤:
(1)创建一个新的ATL工程;
(2)向工程添加新的ATL对象;
(3)根据COM应用的要求向新的ATL对象添加接口;
(4)完成代码编写,编译链接工程,注册COM组件。

二、使用ATL开发一个COM组件
在说明访问COM组件的方法前,我们应该先建立一个简单的COM组件。该COM只有一个组件对象,一个接口ISamplelnterface。
(1)打开Microsoft Visual C++6.0,用ATL COMApp-Wizard创建一个ATL工程,取名为AlSample,所有选项均用默认值(这样创建的组件程序将是一个动态链接库DLL文件,也称作进程内组件。组件程序也可以是一个可执行程序,EXE文件,被称作进程外组件)。

(2)在工程中插入新的ATL对象(选Simple Object),为对象取名为Samplelnterface。
(3)在接口ISamplelnterface中加入方法Hello,参数为空。
修改Hello方法的实现代码如下(该方法仅弹出一个提示对话框):

STDMETHODIMP CSamplelnterface:Hello){
MessageBox(NULL,_T("Hello,this is a ATL COM sample.").
_T("ATL COM Sample"),MB_OK);returm S_OK;

(4)将此COM组件编译链接后,组件将注册到系统中。
三、制作访问COM组件的客户程序
(1)用MFC AppWizard(exe)创建一个基于对话框的MFC应用程序,工程名为AtlClient,所有选项均为默认。在对话框增加“测试”按钮,为“测试”按钮添加响应函数OnTry。
(2)在工程中导入组件,让AtlClientDlg.cpp文件包含下面文件:
   #nclude<Atbase.h>/导入ATL支持
   #include“../AtISample/AtlSample.h"
 //为上面建立AtiSampl工程的路径
 #include"../AtSample/AtiSample_j.c"头文件AtlSample.h和C文件AtlSample_i.c包含了COM组件的CLSID的定义、组件中的接口定义和接口IDD的定义等,使得客户程序能使用COM组件。
AtlSample.h与AtlSample_i.c可以通过导入AtlSample.tlb来替换。TLB文件是一种OLE定义文件,它同样包括组件的CLSID、接口、接口IDD及常数等的定义。
#mport../AtSample/AtiSample.tb"no_namespace
四、访问COM组件的五种方法
    下面用五种方法编写OnTry 函数访问COM组件(代码中都略去对句柄的判断)。
方法一,代码如下:

void CAtlClientDlg:OnTry){
Colnitialize(NULL);
ISamplelnterface*pSample=NULL;
HRESULT hr=CoCreatelnstance(CLSID_Samplelnterface,NULL,CLSCTX_SERVER,ID_ISamplelnterface,(void**&pSample);
pSample->Hello();//此处应加判断if(hr==S_OK),确认API函数调用成功,下同
pSample->Release();
CoUninitialize();

     这是最常用的方法。首先调用COM库函数Colnitialize对COM库进行初始化(为了能使用COM库中的函数,必须对COM进行初始化),在程序退出前,一定要调用函数CoUninitialize 终止库服务,以便释放COM所消耗的资源。下面几种方法都是如此。通过库函数CoCreatelnstance 来创建 COM对象,获得所需的接口指针pSample,然后调用接口成员函数Hello,最后通过IUnknow::Release(任何接口都是从IUnknow继承而来的)释放COM对象实例。

     COM对象创建函数CoCreatelnstance原型如下:

WINOLEAPI CoCreatelnstance(REFCLSID rclsid,LPUN-KNOWN pUnkOuter,DWORD dwClsContext,REFlID riid,LPVOID FAR*ppV)

    参数rclsid为将访问COM组件的CLSID;参数pUnkOuter用于对象被聚合的情形,一般都为NULL;参数dwCIsContext指定组件类别,分为进程内组件、进程外组件或进程内控制对象;参数rid为用于指定接口的ID;参数ppv为存放对象的接口指针。因此客户程序只要指定组件的CLSID及接口ID,就可以得到对象的接口指针,进而调用所需的函数。

方法二,代码如下:

void CAtlClientDlg:OnTry){
Colnitialize(NULL);
ISamplelnterface*pSample=NULL;
IClassFactory*pCF=NULL;
HRESULT hr=CoGetClassObject(CLSID_Samplelnterface,CLSCTX_ALL,NULL,ID_IClassFactory,(LPVOID*)&PCF);
pCF->Createlnstance(NULL,__uuidof(ISamplelnterface),LPVOID*)&pSample); pSample->Hello(); pSample->Release(); pCF->Release(); CoUninitialize();

     这种方法首先利用COM库函数CoCreateClassObject创建类厂对象,然后用得到的类厂对象的接口指针创建真正的COM对象,最后把类厂对象释放。

       与方法一比较,不难看出方法一中CoCreatelnstance函数把通过类厂创建对象的过程封装起来,客户程序可以不与类厂打交道,使用起来更为简单。但是若希望获得类厂对象或者要调用类厂的某些成员函数,则可选用CoCreateClassObject函数,而且CoCreateClassObject函数创建一个组件(特别是mul-tiThreads)的多个对象时,效率更高。
 方法三,代码如下:

void CAtlClientDlg::OnTry(){
Colnitialize(NULL);
CComPtr<lSamplelnterface>pSample;
HRESULT hr=pSample. CoCreatelnstance(L"AtSample.Samplelnterface.1");
pSample->Hello();
pSample. Release();
CoUninitialize();

     这段代码使用ATL智能指针CComPtr<ISamplelInterface>pSample,这使得接口指针的获得变得很简单。由于有了智能指针,就可以调用CComPtr模板类成员函数CoCreatelnstance0来完成接口指针的创建工作。参数“L"AtSample.Samplelnterface.1””中L作为宏将ANSI转换为Unicode。该函数原型如下:
HRESULT CoCreatelnstance(LPCOLESTR szProgD,LPUNKNOWN pUnkOuter=NULL,DWORD dwCIsContext=CLSCTXALL)
     第一个参数LPCOLESTRszProgID(第一个参数也可以是REFCLSID rclsid形式,使用组件的CLSID),为组件的ProgID,可以在注册表中找到。剩下两个参数与上述意义相同,一般情况下我们都使用其默认值。
方法四,代码如下:

void CAtlClientDlg::OnTry(){
Colnitialize(NULL);
CSamplelnterface sample;
BOOL Res=sample.CreateDispatch"AtiSample.Samplelnterface.1");
sample.Hello();
sample.ReleaseDispatch();
CoUninitialize();

      这种方法通过ClassWizard利用类型库生成包装类,前提是COM组件的接口必须是派生自IDispatch。具体步骤:调出添加类向导(VSNET中),选择类型库中MFC类,打开,选择“文件”,选择AtlSample.dll或AtlSample.tlb,出现该COM组件中的所有接口,选择想生成的接口包装类后,向导会自动生成相应的.h文件。这样就可以在MFC中像使用普通类那样使用组件了。

方法五,代码如下:

void CAtlClientDlg::OnTry(){
typedef HRESULT(stdcall *GETCLASSOBJECT)(REFCLSID,REFlID,void**);
HINSTANCE hDllnst=LoadLibrary("..\\AtiSample\\Debug\\AtlSample.dI");
GETCLASSOBJECT fnGetClassObject = NULL;
fnGetClassObject=(GETCLASSOBJECT)GetProcAddress(hDllnst,"DIlGetClassObject");
IClassFactory*pCF=NULL;
ISamplelnterface*pSample= NULL;
HRESULT hr=(fnGetClassObject)(CLSID_Samplelnterface,lID_IClassFactory,(void**)&pCF);
hr=pCF->Createlnstance(NULL,__uuidof(ISamplelnterface),(void**)&pSample);
pSample->Hello();
pSample->Release();
pCF->Release();
FreeLibrary(hDllnst);

     这种方法是把COM当作普通DLL处理:直接从dll中得到DlIGetClassObject,接着的操作类似方法二,通过函数Dll-GetClassObject创建类厂对象,然后用得到的类厂对象的接口指针创建组件的ISamplelnterface接口指针,接口成员函数调用完成后把类厂对象释放,最后释放DLL资源。这种方法可以使组件不用在注册表里注册,也是最原始的方法。然而这样做没什么意义,至少失去了COM对用户的透明性,一般来说不宜选择这种方法。

五、结束语
    通过以上几种访问COM组件的方法介绍,有助于开发人员更好掌握COM技术,针对复杂的应用程序,实现组件化的软件设计。参考文献:
11]潘爱表.COM原道与意用.储华大学出版社,2001.
[2]徐使标,右而考.Visual C++程寻银什.科学出版社,2003.
[3]Dale Rogerson.Inside COM.Microsoft Press,1997.e

 原文:VC++中访问COM组件的五种方法 - 道客巴巴 (doc88.com)

posted on 2022-10-06 16:50  悠心不已  阅读(76)  评论(0)    收藏  举报

导航