com组件

引用本为说明出处:chinabinlang的专栏;(以下示例均在VC6上编译执行通过)

 

 

一:COM的创建:

 网上有很多关于com的文章,比较好的有杨老师文章(在vc知识库上有连载),还有一些经典的com书籍,这里不做过多介绍,从com编写直接开始,简单迅速的了解com;

一般com开发可以用ATL向导(这里vc6),当然如果你很牛,可以不用向导;

开发步骤:ATL COM AppWizard -> Dynamic Link Library(DLL)   ->  Insert   ->  New ATL Object...   ->  Category Objects(选择 Simple Object)  -> next ->  Names属性框中写你的com对象名字, Attributes属性框,可以修改 Interface 下的为 Custom(也可以默认Dual)-> 确定;

这样就添加了com对象了,如何先进接口的方法呢? 鼠标右键到接口上(像一个小勺子)选择 Add Method...,具体实现在类里面;

 

用ATL创建com组件:
1:通常通过添加 New ATL Object | Simple Object 添加com组件的一个对象;默认情况下,一个组件对象对应一个接口(一个dll可以添加多个Simple Object ,每个Simple Object 都是一个组件对象,通过CoCreateInstance创建同时创建一个接口),可以通过这种方式添加多个组件对象;
    可以在一个对象下添加多个接口类(com组件的接口就是外部看见的类)(就是QueryInterface到同一个组件下的多个不同接口类,如第2点所示);

2:com中对象的可以包含多个接口,在ATL中,默认情况下,一个对象对应一个接口;
   可以通过鼠标右键到某个对象上 选择:Implement Interface,给这个对象添加新的接口,但是同名函数会被隐藏;
   注意:虽然都是同一个接口,但是,在不同对象下的具体函数内容要具体实现,如:
         Objectone对象添加 Objecttwo对象的接口Iobjtow,同时Iobjtow有objtowFun1函数,
         这时,Objectone 和 Objecttwo 都要具体实现objtowFun1函数,函数内容可以不同;

3:调用Iobjtow接口,如果是想调用Objecttwo对象的实现,这样调用:
                   ::CoCreateInstance(CLSID_objtwo,NULL, CLSCTX_INPROC_SERVER, IID_Iobjtwo, (LPVOID *) &pobjtwo);
                   pobjtow-objtowFun1();
                 
                  如果是想调用Objectonw对象的实现,这样调用:
                  hr = ::CoCreateInstance(CLSID_objone,NULL, CLSCTX_INPROC_SERVER,  IID_Iobjone,  (LPVOID *) &pobjone); 
                  hr = pobjone->QueryInterface( IID_Iobjtow,  (LPVOID *)&pobjtow );              
                  pobjtow-objtowFun1();


4:如果我们只想在一个对象添加多个接口,不想看见其他对象,我们可以屏蔽(注释)某个对象的源文件,.h和.cpp文件 和 OBJECT_ENTRY(CLSID_objtow, Cobjtow);

 

示例:

一:一个dll中一个组件对象关联多个接口:

     组件中,一个clsid关联多个iiid;

 

   调用方法:

 

 

======================================================================================================================================================================================================================================================================

 

二:COM的调用(注:COM必须先注册):

 网上有关com调用的方法比较多,这里我详细说明一下:

 

方法1:

如果我创建了一个com组件,那么可以通过已有的文件调用导入调用com(这样就和directshow调用差不多了):


#include " ..\ATLComDemo\ATLComDemo.h"
#include "..\ATLComDemo_i.c"


#include <atlbase.h>
HRESULT QueryInterfacecom()
{
 HRESULT hr = CoInitialize(NULL);
 
 
 Iobjone * pobjone = NULL;
 Iobjtow * pobjtow = NULL ;
    Iobjthree * pobjthree = NULL;
 
 //´´½¨COM ¶ÔÏóCobjoneʵÀý£¬Í¬Ê±»ñÈ¡CobjoneÏ嵀 Iobjone ½Ó¿Ú£»
 hr = ::CoCreateInstance(CLSID_objone,NULL, CLSCTX_INPROC_SERVER,  IID_Iobjone,  (LPVOID *) &pobjone);     if( FAILED( hr ) ){cout<<"hr";};
 pobjone->fun1();                                                                                              if( FAILED( hr ) ){cout<<"hr";};
 
 //»ñÈ¡CobjoneÏ嵀 Iobjtow ½Ó¿Ú£»
 hr = pobjone->QueryInterface( IID_Iobjtow,  (LPVOID *)&pobjtow );                                             if( FAILED( hr ) ){cout<<"hr";};
 pobjtow->fun3();
 
 //»ñÈ¡CobjoneÏ嵀 Iobjtow ½Ó¿Ú£»
 hr = pobjone->QueryInterface( IID_Iobjthree,  (LPVOID *)&pobjthree );                                         if( FAILED( hr ) ){cout<<"hr";};
 pobjthree->funthree1();
 pobjthree->funthree2();
 
 
 pobjone->Release();
 pobjtow->Release();
 pobjthree->Release();
 
 CoUninitialize();
 
 
 return hr;
}#include <atlbase.h>
HRESULT QueryInterfacecom()
{
 HRESULT hr = CoInitialize(NULL);
 
 
 Iobjone * pobjone = NULL;
 Iobjtow * pobjtow = NULL ;
 Iobjthree * pobjthree = NULL;
 
 //´´½¨COM ¶ÔÏóCobjoneʵÀý£¬Í¬Ê±»ñÈ¡CobjoneÏ嵀 Iobjone ½Ó¿Ú£»
 hr = ::CoCreateInstance(CLSID_objone,NULL, CLSCTX_INPROC_SERVER,  IID_Iobjone,  (LPVOID *) &pobjone);     if( FAILED( hr ) ){cout<<"hr";};
 pobjone->fun1();                                                                                              if( FAILED( hr ) ){cout<<"hr";};
 
 //»ñÈ¡CobjoneÏ嵀 Iobjtow ½Ó¿Ú£»
 hr = pobjone->QueryInterface( IID_Iobjtow,  (LPVOID *)&pobjtow );                                             if( FAILED( hr ) ){cout<<"hr";};
 pobjtow->fun3();
 
 //»ñÈ¡CobjoneÏ嵀 Iobjtow ½Ó¿Ú£»
 hr = pobjone->QueryInterface( IID_Iobjthree,  (LPVOID *)&pobjthree );                                         if( FAILED( hr ) ){cout<<"hr";};
 pobjthree->funthree1();
 pobjthree->funthree2();
 
 
 pobjone->Release();
 pobjtow->Release();
 pobjthree->Release();
 
 CoUninitialize();
 
 
 return hr;
}

 

方法2:

通过向导添加com组件到工程,这样组件中的接口类和相应的成员函数就显示到工程中了,想到ocx一样:

 

调用方法:

1:导入生成的头文件

2: 声明对象,然后用CreateDispatch创建实例,参数为:com名.接口类名(类名不需要前面默认的I);

3:最后释放示例;

 

#include "atlone.h"

void CTalonecomexeDlg::OnButton1()
{
 // TODO: Add your control notification handler code here
 CoInitialize(NULL);
 
 
 Icomone Icomoneobj;

 if ( Icomoneobj.CreateDispatch("atlone.comone") !=0 )
 {
  Icomoneobj.fun1();

  Icomoneobj.fun2();
 }
 

  Icomoneobj.ReleaseDispatch();  
 
 CoUninitialize();
}

 

 

 

方法3:导入com组件后会生成相应的tlh,可以用下列方法使用dll;

#import "../atlone/Debug/atlone.dll" no_namespace

void CTDlg::OnButton1()
{
 // TODO: Add your control notification handler code here
 
 CoInitialize(NULL);
 
 CLSID clsid;
 CLSIDFromProgID(L"atlone.comone",&clsid);
 
 Icomone * pcomone = NULL;

 HRESULT hr = CoCreateInstance( /*CLSID_comone*/clsid,NULL,CLSCTX_INPROC_SERVER,/*IID_Icomone*/__uuidof(Icomone), (void**)&pcomone );
 

 pcomone->fun1();
 pcomone->fun2();
 pcomone->Release();
 
 
 CoUninitialize();
 
 
}

 

 

 

//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

转载一篇VC调用com组件的文章:《vc中调用Com组件的方法详解》(注:没有全部验证)

 

Requirement:
1.创建myCom.dll,该COM只有一个组件,两个接口IGetRes--方法Hello(),IGetResEx--方法HelloEx();
2.在工程中导入组件或类型库
#import "组件所在目录myCom.dll" no_namespace 或
#import "类型库所在目录myCom.tlb"
using namespace MYCOM;

------------------------------------ Method 1 ------------------------------------
CoInitialize(NULL);
CLSID clsid;
CLSIDFromProgID(OLESTR("myCom.GetRes"), //
&clsid);//
CComPtr<IGetRes> pGetRes; // 智能指针
pGetRes.CoCreateInstance(clsid);
pGetRes->Hello();
pGetRes.Release(); // 小心哦,请看最后的“注意”
CoUninitialize();
------------------------------------ Method 2 ------------------------------------
CoInitialize(NULL);
CLSID clsid;
HRESULT hr = CLSIDFromProgID(OLESTR("myCom.GetRes"),
&clsid);
IGetRes *ptr = NULL;
hr=CoCreateInstance(clsid,
NULL,
CLSCTX_INPROC_SERVER,
__uuidof(IGetRes),
(LPVOID*)&ptr);
ptr->Hello();
CoUninitialize();
------------------------------------ Method 3 ------------------------------------
CoInitialize(NULL);
HRESULT hr;
CLSID clsid;
hr = CLSIDFromProgID(OLESTR("myCom.GetRes"),
&clsid);
IGetRes* ptr = NULL;
IGetResEx* ptrEx = NULL;

// 使用CoCreateClassObject创建一个组件(特别是mutilThreads)的多个对象的时候,效率更高.
IClassFactory* p_classfactory = NULL;
hr=CoGetClassObject(clsid,
CLSCTX_INPROC_SERVER,
NULL,
IID_IClassFactory,
(LPVOID*)&p_classfactory);
p_classfactory->CreateInstance(NULL,
__uuidof(IGetRes),
(LPVOID*)&ptr);
p_classfactory->CreateInstance(NULL,
__uuidof(IGetResEx),
(LPVOID*)&ptrEx);
ptr->Hello();
ptrEx->HelloEx();
CoUninitialize();
------------------------------------ Method 4 ------------------------------------
直接从dll中得到DllGetClassObject,接着生成类对象及类实例(这方法可以使组件不用在注册表里注册,这是最原始的方法,但这样做没什么意义,至少失去了COM对用户的透明性),不推荐使用。
// 定义一个函数指针类型
typedef HRESULT (__stdcall * pfnHello)(REFCLSID,REFIID,void**);
pfnHello fnHello = NULL; // 创建一个函数指针变量
// 将COM组件DLL像一般DLL一样加载,得到DLL句柄
HINSTANCE hdllInst = LoadLibrary("*:\*.dll");
// 判断DLL加载是否成功
if (NULL != hdllInst)
{
// 取得相应的函数地址
fnHello = (pfnHello)GetProcAddress(hdllInst,
      "DllGetClassObject");
if (NULL != fnHello)
{
// 得到类工厂接口指针
    IClassFactory* pcf = NULL;
HRESULT hr = (fnHello)(CLSID_GetRes,
      IID_IClassFactory,
      (void**)&pcf);
      if (SUCCEEDED(hr) &&
      (pcf != NULL))
      {
// 得到具体方法接口指针
      IGetRes* pGetRes = NULL;
      hr = pcf->CreateInstance(NULL,
      IID_IFoo,
      (void**)&pGetRes);
    if (SUCCEEDED(hr) &&
      (pGetRes != NULL))
      {
      pGetRes->Hello();
    pGetRes->Release();
}
pcf->Release();
    }
}
FreeLibrary(hdllInst);
}
------------------------------------ Method 5 ------------------------------------
通过ClassWizard利用类型库生成包装类,不过前提是com组件的接口必须是派生自IDispatch,具体方法:
调出添加类向导(.NET中),选择类型库中MFC类,打开,选择"文件",选择"myCom.dll"或"myCom.tlb",接下来会出来该myCom中的所有接口,选择你想生成的接口包装类后,向导会自动生成相应的.h文件.这样你就可以在你的MFC中像使用普通类那样使用组件了.(CreateDispatch("myCom.GetRes") 中的参数就是ProgID通过Clsid在注册表中可以查询的到)
CoInitialize(NULL);
CGetRes getRest;
if (getRest.CreateDispatch("myCom.GetRes") != 0)
{
getRest.Hello();
getRest.ReleaseDispatch();
}
CoUninitialize();
------------------------------------ Notice------------------------------------
COM中的智能指针实际上是重载了->的类,目的是为了简化引用记数,不需要程序员显示的调用AddRef()和Release(),但是为什么我们在Method 1中pGetRes.Release(),问题在于,我们的智能指针pGetRes生命周期的结束是在CoUninitialize()之后,CoInitialize所开的套间在CoUninitialize()后已经被关闭,而pGetRes此时发生析构,导致了程序的崩溃,解决这个问题的另一个方法是:
CoInitialize(NULL);
CLSID clsid;
CLSIDFromProgID(OLESTR("myCom.GetRes"),
&clsid);
CComPtr<IGetRes> pGetRes; // 智能指针
pGetRes.CoCreateInstance(clsid);
pGetRes->Hello();
// pGetRes.Release(); // 将其注释掉
CoUninitialize();
--------------------------------------------------------------------
以上就是COM的5中方法,当然具体怎么使用还是要根据具体情况而定。

 

 

======================================================================================================================================================================================================================================================================

转一篇关于com基本概念的文章:

组件不一定就是一个dll,一个dll中可以包含很多个组件,而且一般见到的dll里也不止包含一个组件,一个dll里的每个组件都有自己的CLSID和ProgID。对于ATL工程来说,一个ATL Object就是一个组件,很明显,我们可以在一个工程里加入很多个ATL Object。

对于ATL来说,一个组件对应着一个C++类,这在IDL中叫做coclass,一般情况下,一个coclas对应一个接口。实际上,一个coclass可以有很多个接口,这就是一个组件实现多个接口,并且这些接口共享一个组件中的函数。

比如:coclass A有一个接口IA,接口导出了一个函数Foo(),那么coclass A里有一个Foo()函数来实现IA接口中的Foo()函数。后来想要对组件A升级,加入一个导出函数Foo2(),但是又不想影响以前的用户,当然重做 一个新组件是可以的,但是没有必要,并且有了重复代码。这时我们可以在组件A中新建一个接口IA2,IA2导出两个函数:Foo()和Foo2(),IA2仍然由coclass A来实现,也就是说现在coclass A里有了两个函数Foo()和Foo2(),接口IA和IA2共享coclass A里的Foo()函数。这样一来,以前的用户不用变化,因为接口IA没有改动,新的用户可以使用接口IA2,使用IA2也仍然可以完成IA的功能,因为时 而也有Foo()函数。

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

chinabinlang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值