com-采用sdk方式编写概述

com相关的概念

1. 何为com
微软提出了C O M(Component Object Model, 中文也可以译作"组件对象模型")
COM组件是完全与语言无关的,开发后,可以供其它开发语言使用。

2.如何调用com
进程内com其实是一个Dll, 这个dll与普通dll有些区别,就是可以通过regsvr32之类的程序进行注册,注册过程其实是将com的clsid写入注册表,
并给出dll的物理路径,以便调用程序加载dll,下面是调用方法(VC):

CoInitialize(NULL);  //初始化com库

ISimpleMsgBox* pIMsgBox;
 HRESULT hr;

    hr = CoCreateInstance ( __uuidof(CSimpleMsgBoxImpl), NULL, CLSCTX_INPROC_SERVER,
  __uuidof(ISimpleMsgBox), (void**) &pIMsgBox );
 
    if ( FAILED(hr) ) {  //com调用失败
  AfxMessageBox("error.");
        return;
 }
 
    pIMsgBox->DoSimpleMsgBox ( NULL, _bstr_t("Hello my sdk com!") ); //对com中方法的调用
 
    pIMsgBox->Release();

CoUninitialize();  //关闭com库

可以看到调用com很简单,先将com库初始化,再利用CoCreateInstance 调用即可
 __uuidof(CSimpleMsgBoxImpl) 是指com库的clsid
__uuidof(ISimpleMsgBox) 是指要请求的接口的iid
(void**) &pIMsgBox 得到com对象实例指针

其中clsid, iid都是UUID的重定义,可以根据com在注册表中的注册信息,直接调用uuid

3。com分类

COM组件有三种,进程内、本地、远程。对于后两者情况必须调度接口指针及函数参数。
进程内:它可以是进程内的,即和调用者在同一个进程内,
本地:也可以和调用者在同一个机器上但在不同的进程内,
远程:还可以根本就和调用者在两台机器上

com是一个dll, 自己不能独立运行。要运行起来必须通过父进程。

我们先讨论进程内com

另外说一下com服务器,com客户端
com服务器:即com程序本身
com客户端:调用com程序的程序

com原理

可以简单总结如下:
a.如果com支持自注册,要有输出自注册相关的函数(向注册表中写注册信息)
b.通过调用DllGetClassObject得到类工厂(这个函数作为dll的输出)
c.从类工厂中返回接口实例

当然具体过程没这么简单,下面具体介绍一下。


com其实就是一个Dll, 只是这个dll遵循了Com的规范,一个典型的自注册的COM DLL所必有的四个函数:

DllGetClassObject:用于获得类厂指针

DllRegisterServer:注册一些必要的信息到注册表中

DllUnregisterServer:卸载注册信息

DllCanUnloadNow:系统空闲时会调用这个函数,以确定是否可以卸载DLL

(1)。在用regsvr32注册com时,会调用DllRegisterServer将clsid等信息写入注册表.

(2)。CoCreateInstance()函数调用时会做以下操作:
1、客户端程序调用CoCreateInstance(),传递组件对象类的CLSID以及所要接口的IID。
2、COM库在HKEY_CLASSES_ROOT/CLSID.键值下查找服务器的CLSID键值,这个键值包含服务器的注册信息。
3、COM库读取服务器DLL的全路径并将DLL加载到客户端的进程空间。
4、COM库调用在服务器中DllGetClassObject()函数为所请求的组件对象类请求类工厂。
5、服务器创建一个类工厂并将它从DllGetClassObject()返回。
6、COM库在类工厂中调用CreateInstance()方法创建客户端程序请求的COM对象。
7、CreateInstance()返回一个接口指针到客户端程序。

可以看到com具体实现的调用是通过类工厂的。而每一个Com的实现都要继承自IUnknown接口,并实现接口中的方法
class CUnknownImpl : public IUnknown
{
public:
    // 构造函数和析构器
    CUnknownImpl();
    virtual ~CUnknownImpl();

    // IUnknown 方法
    ULONG AddRef();
    ULONG Release)();
    HRESULT QueryInterface( REFIID riid, void** ppv );

protected:
    UINT m_uRefCount;  // 对象的引用计数
};

QueryInterface()简称QI(),由客户端程序调用这个函数从COM对象请求不同的接口。

下面说一下CoCreateInstance调用后的各步骤实现的说明:
1、客户端程序调用CoCreateInstance(),传递组件对象类的CLSID以及所要接口的IID。
 其中CLSID是com注册时写入注册表HKEY_CLASSES_ROOT//CLSID//下的GUID
   这个值会在dll输出函数DllGetClassObject中验证
 因为一个类可以实现多个接口,IID是要请求接口的GUID,在实现类的QueryInterface函数中将验证。
 可以参考下面的代码直接利用GUID串来生成UUID

//uuid的创建
void CAboutDlg::OnUuid()
{
 unsigned char* _clsid;
 unsigned char* _iid;
 _clsid = (unsigned char*)new char(128);
 _iid = (unsigned char*)new char(128);

 memcpy(_clsid, "7193E4CD-89AF-412F-930E-C131F9981CAC", sizeof("7193E4CD-89AF-412F-930E-C131F9981CAC"));
 memcpy(_iid, "BF239963-D427-4910-92A6-92A52F070382", sizeof("BF239963-D427-4910-92A6-92A52F070382"));

 UUID CLSID_first_alt;
 UUID IID_Ifirst_alt;
 UuidFromStringA(_clsid, &CLSID_first_alt);
 UuidFromStringA(_iid, &IID_Ifirst_alt);

 HRESULT   hr;
 Ifirst_alt  *IFirstATL = NULL;

 hr = CoInitialize(0);
 if(SUCCEEDED(hr))
 {  
  hr = CoCreateInstance( CLSID_first_alt, NULL, CLSCTX_INPROC_SERVER,
      IID_Ifirst_alt, (void**) &IFirstATL);
 
  // 如果成功,则调用fun 方法,否则显示相应的出错信息
  if(SUCCEEDED(hr))
  {
   IFirstATL->fun();
   IFirstATL->Release();
  }
  else
  {
   AfxMessageBox("CoCreateInstance Failed.");
  }
 }

 RpcStringFreeA(&_clsid);
 RpcStringFreeA(&_iid);

 // 释放 COM
 CoUninitialize();

}

2、COM库在HKEY_CLASSES_ROOT/CLSID.键值下查找服务器的CLSID键值,这个键值包含服务器的注册信息。
3、COM库读取服务器DLL的全路径并将DLL加载到客户端的进程空间。

   2,3步做完后,com库已经将com组件加载入内存

4、COM库调用在服务器中DllGetClassObject()函数为所请求的组件对象类请求类工厂。
STDAPI DllGetClassObject ( REFCLSID rclsid, REFIID riid, void** ppv )
{
 HRESULT hrRet;
 CSimpleMsgBoxClassFactory* pFactory;

    if ( !IsEqualGUID ( rclsid, __uuidof(CSimpleMsgBoxImpl) ))
        return CLASS_E_CLASSNOTAVAILABLE;

    // Check that ppv really points to a void*.

    if ( IsBadWritePtr ( ppv, sizeof(void*) ))
        return E_POINTER;

    *ppv = NULL;

    // Construct a new class factory object.

    pFactory = new CSimpleMsgBoxClassFactory;

    if ( NULL == pFactory )
        return E_OUTOFMEMORY;

    // AddRef() the factory since we're using it.

    pFactory->AddRef();

    // QI() the factory for the interface the client wants.

    hrRet = pFactory->QueryInterface ( riid, ppv );
   
    // We're done with the factory, so Release() it.

    pFactory->Release();

    return hrRet;
}

DllGetClassObject是dll的输出函数之一,用来得到类工厂,从而创建对象。可以看到这个函数中对传入的clsid进行了检查。

5、服务器创建一个类工厂并将它从DllGetClassObject()返回。
6、COM库在类工厂中调用CreateInstance()方法创建客户端程序请求的COM对象。

STDMETHODIMP CSimpleMsgBoxClassFactory::CreateInstance ( IUnknown* pUnkOuter,
                                                         REFIID    riid,
                                                         void**    ppv )
{
 HRESULT hrRet;
 CSimpleMsgBoxImpl* pMsgbox;

    // We don't support aggregation, so pUnkOuter must be NULL.

    if ( NULL != pUnkOuter )
        return CLASS_E_NOAGGREGATION;

    // Check that ppv really points to a void*.

    if ( IsBadWritePtr ( ppv, sizeof(void*) ))
        return E_POINTER;

    *ppv = NULL;

    // Create a new COM object!

    pMsgbox = new CSimpleMsgBoxImpl;

    if ( NULL == pMsgbox )
        return E_OUTOFMEMORY;

    // QI the object for the interface the client is requesting.

    hrRet = pMsgbox->QueryInterface ( riid, ppv );

    // If the QI failed, delete the COM object since the client isn't able
    // to use it (the client doesn't have any interface pointers on the object).

    if ( FAILED(hrRet) )
        delete pMsgbox;

    return hrRet;
}

可以看到pMsgbox = new CSimpleMsgBoxImpl;这一句创建了对象,
然后再通过hrRet = pMsgbox->QueryInterface ( riid, ppv );对iid进行验证,并得到对象指针

例子程序在机器的D:/Test/vc/com

这是通过sdk研究com的原理。但应用中一般使用Alt来简化开发。

参考资料:

http://vckbase.com/document/viewdoc/?id=212

http://vckbase.com/document/viewdoc/?id=213

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值