C++ COM

前言:为了介绍C#写界面,C++写算法的快捷交互开发方式,首先介绍c++,C#内部的DLL,COM调用。

一,COM
COM (Component Object Model),微软为提高代码的可从用性而开发的组件对象模型的软件架构,在windows系统的开发中大量的使用了这种技术,使用这种技术我们尽可能的把我们的软件划分位许多组件,通过组件的组合调用最总实现软件的目的,COM的使用不仅大大的提高了代码的可从用性,而且减小了代码间的耦合。更多的关于OLE,COM,COM+,DCOM,ActiveX的概念

二,COM的创建
一般COM的创建有2中方法,使用ATL Wizard 和不使用ATL。

1)使用ATL Wizard创建COM非常的简单,可以参考下面2个链接,分别使用ATL6.0和ATL7.0,最新的类似:
     ATL7.0 COM: http://www.codeproject.com/atl/SimpleDlls.asp
     ATL6.0 com: http://www.codeproject.com/atl/com_atl.asp

2)不使用ATL,当然更没有Wizard,只有手动的一步一步的实现,不过这也是学习COM最好的方法和必经之路。可以参考:
     http://www.codeproject.com/com/LocalCOMServerClient.asp
    具体的步骤如下:
    1)建立一个Win32的DLL。例如CarLocalServer。
    2)首先加入IDL接口描述文件CarLocalServerTypeInfo.idl,编译后会生成4个文件CarLocalServerTypeInfo_h.h,CarLocalServerTypeInfo_i.cpp,CarLocalServerTypeInfo_p.cpp,      dlldata.cpp 。
    

import "oaidl.idl";
import "ocidl.idl";

// define IStats interface
[ object, uuid(FE78387F-D150-4089-832C-BBF02402C872),
 oleautomation, helpstring("Get the status information about this car")]
interface IStats : IUnknown
{
   HRESULT DisplayStats();
   HRESULT GetPetName([out,retval] BSTR* petName);
};

// define the IEngine interface
[ object, uuid(E27972D8-717F-4516-A82D-B688DC70170C),
 oleautomation, helpstring("Rev your car and slow it down")]
interface IEngine : IUnknown
{
   HRESULT SpeedUp();
   HRESULT GetMaxSpeed([out,retval]  int* maxSpeed);
   HRESULT GetCurSpeed([out,retval]  int* curSpeed);
};

// define the ICreateMyCar interface
[ object, uuid(5DD52389-B1A4-4fe7-B131-0F8EF73DD175),
 oleautomation, helpstring("This lets you create a car object")]
interface ICreateMyCar : IUnknown
{
   HRESULT SetPetName([in]BSTR petName);
   HRESULT SetMaxSpeed([in]  int maxSp);
};

// library statement
[uuid(957BF83F-EE5A-42eb-8CE5-6267011F0EF9), version(1.0),
 helpstring("Car server with typeLib")]
library CarLocalServerLib
{
   importlib("stdole32.tlb");
   [uuid(1D66CBA8-CCE2-4439-8596-82B47AA44E43)]
   coclass MyCar
   {
      [default] interface ICreateMyCar;
      interface IStats;
      interface IEngine;
   };
};


    3) 加入生命周期管理类managesycle.h,
 

#pragma once

class CManageSycle
{
public:
    CManageSycle(void);
    ~CManageSycle(void);

    static void InObject()  {++m_nObject;}
    static void DeObject()  {--m_nObject;}
    static bool IsZeroObject() { return m_nObject==0 ;}
    static void InLock()  {++m_nLock;}
    static void DeLock()  {--m_nLock;}
    static bool IsZeroLock() { return m_nLock==0 ; }
private:
    static ULONG m_nObject;
    static ULONG m_nLock;
};

managesycle.cpp实现文件:

#include "StdAfx.h"
#include "managesycle.h"
ULONG CManageSycle::m_nObject=0;
ULONG CManageSycle::m_nLock=0;
CManageSycle::CManageSycle(void)
{
}

CManageSycle::~CManageSycle(void)
{
}


    4)加入真正的com的接口的实现类,MyCar.h (除了实现COM的接口,还必须实现IUnKnown接口)


#pragma once

#include "unknwn.h"
#include "CarLocalServerTypeInfo_h.h"

const  int MAX_SPEED = 500;
const  int MAX_NAME_LENGTH = 20;

class MyCar : 
     public IEngine, 
     public ICreateMyCar, 
     public IStats  
{
public:
    MyCar();
    virtual ~MyCar();

    // IUnknown
    STDMETHODIMP QueryInterface(REFIID riid, void** pIFace);
    STDMETHODIMP_(DWORD)AddRef();
    STDMETHODIMP_(DWORD)Release();

    // IEngine
    STDMETHODIMP SpeedUp();
    STDMETHODIMP GetMaxSpeed( int* maxSpeed);
    STDMETHODIMP GetCurSpeed( int* curSpeed);
    
    // IStats
    STDMETHODIMP DisplayStats();
    STDMETHODIMP GetPetName(BSTR* petName);

    // ICreateMyCar
    STDMETHODIMP SetPetName(BSTR petName);
    STDMETHODIMP SetMaxSpeed( int maxSp);

private:
    DWORD    m_refCount;
    BSTR    m_petName;
     int        m_maxSpeed;
     int        m_currSpeed;
};



MyCar.cpp

#include "stdafx.h"
#include <stdio.h> 

#include "CarLocalServerTypeInfo_i.c"
#include "MyCar.h"
#include "ManageSycle.h"

//
// Construction/Destruction
//
MyCar::MyCar() : m_refCount(0), m_currSpeed(0), m_maxSpeed(0)
{
    m_refCount=0;
    CManageSycle::InObject();
    m_petName = SysAllocString(L"Default Pet Name");
    
}

MyCar::~MyCar()
{
    CManageSycle::DeObject();
     if(m_petName) SysFreeString(m_petName);
    MessageBox( NULL,
    L"MyCar is being distructed. Make sure you see this message, if not, you might have memory leak!",
    L"Destructor",MB_OK | MB_SETFOREGROUND);
}

// IUnknown
STDMETHODIMP MyCar::QueryInterface(REFIID riid, void** pIFace)
{
    // Which aspect of me  do they want?
     if(riid == IID_IUnknown)
    {
        *pIFace = (IUnknown*)(IEngine*)this;
        // MessageBox( NULL, "Handed out IUnknown","QI",MB_OK | MB_SETFOREGROUND);
    }
    
     else  if(riid == IID_IEngine)
    {
        *pIFace = (IEngine*)this;
        // MessageBox( NULL, "Handed out IEngine","QI",MB_OK | MB_SETFOREGROUND);
    }
    
     else  if(riid == IID_IStats)
    {
        *pIFace = (IStats*)this;
        // MessageBox( NULL, "Handed out IStats","QI",MB_OK | MB_SETFOREGROUND);
    }
    
     else  if(riid == IID_ICreateMyCar)
    {
        *pIFace = (ICreateMyCar*)this;
        // MessageBox( NULL, "Handed out ICreateMyCar","QI",MB_OK | MB_SETFOREGROUND);
    }
     else
    {
        *pIFace =  NULL;
        return E_NOINTERFACE;
    }

    ((IUnknown*)(*pIFace))->AddRef();
    return S_OK;
}

STDMETHODIMP_(DWORD) MyCar::AddRef()
{
    ++m_refCount;
    return m_refCount;
}

STDMETHODIMP_(DWORD) MyCar::Release()
{
     if(--m_refCount == 0)
    {
        delete this;
        return 0;
    }
     else
        return m_refCount;
}

// IEngine
STDMETHODIMP MyCar::SpeedUp()
{
    m_currSpeed += 10;
    return S_OK;
}

STDMETHODIMP MyCar::GetMaxSpeed( int* maxSpeed)
{
    *maxSpeed = m_maxSpeed;
    return S_OK;
}

STDMETHODIMP MyCar::GetCurSpeed( int* curSpeed)
{
    *curSpeed = m_currSpeed;
    return S_OK;
}


// IStats
STDMETHODIMP MyCar::DisplayStats()
{
    // Need  to transfer a BSTR  to a char  array.
    char buff[MAX_NAME_LENGTH];
    WideCharToMultiByte(CP_ACP,  NULL, m_petName, -1, buff, 
                        MAX_NAME_LENGTH,  NULLNULL);

    //MessageBox( NULL, buff, L"Pet Name",MB_OK | MB_SETFOREGROUND);
    memset(buff, 0, sizeof(buff));
    sprintf(buff, "%d", m_maxSpeed);
    //MessageBox( NULL, buff,L"Max Speed", MB_OK| MB_SETFOREGROUND);
    return S_OK;
}

STDMETHODIMP MyCar::GetPetName(BSTR* petName)
{
    *petName = SysAllocString(m_petName);
    return S_OK;
}


// ICreateMyCar
STDMETHODIMP MyCar::SetPetName(BSTR petName)
{
    SysReAllocString(&m_petName, petName);
    return S_OK;
}

STDMETHODIMP MyCar::SetMaxSpeed( int maxSp)
{
     if(maxSp < MAX_SPEED)
        m_maxSpeed = maxSp;
    return S_OK;
}


    5)加入工厂类MyCarClassFactory.h,(实现IUnKnown接口和IClassFactory接口)

// the class  object (class factory)  for CoMyCar class

#pragma once

class MyCarClassFactory :  public IClassFactory
{
public:
    MyCarClassFactory();
    virtual ~MyCarClassFactory();

    // IUnknown
    STDMETHODIMP QueryInterface(REFIID riid,void** pIFace);
    STDMETHODIMP_(ULONG)AddRef();
    STDMETHODIMP_(ULONG)Release();

    // IClassFactory
    STDMETHODIMP LockServer(BOOL fLock);
    STDMETHODIMP CreateInstance(LPUNKNOWN pUnkOuter,REFIID riid,void** ppv);

private:

    ULONG m_refCount;

};

MyCarClassFactory.cpp

#include "stdafx.h"
#include "MyCar.h"
#include "MyCarClassFactory.h"
#include "locks.h"
#include "ManageSycle.h"

MyCarClassFactory::MyCarClassFactory()
{
    m_refCount = 0;
}

MyCarClassFactory::~MyCarClassFactory()
{
    MessageBox( NULL,
    L"MyCarClassFactory is being distructed. Make sure you see this message, if not, you might have memory leak!",
    L"Destructor",MB_OK | MB_SETFOREGROUND);
}

STDMETHODIMP_(ULONG) MyCarClassFactory::AddRef()
{
    //return ++m_refCount;
    return 10;
}

STDMETHODIMP_(ULONG) MyCarClassFactory::Release()
{
    /*
     if ( --m_refCount == 0 )
    {
        delete this;
        return 0;
    }
    return m_refCount;
    */
    return 20;
}

STDMETHODIMP MyCarClassFactory::QueryInterface(REFIID riid,void** pIFace)
{
     if ( riid == IID_IUnknown )
        *pIFace = (IUnknown*)this;
     else  if ( riid == IID_IClassFactory )
        *pIFace = (IClassFactory*)this;
     else
    {
        *pIFace =  NULL;
        return E_NOINTERFACE;
    }
    ((IUnknown*)(*pIFace))->AddRef();
    return S_OK;
}

STDMETHODIMP MyCarClassFactory::LockServer(BOOL fLock)
{
    (VARIANT_TRUE == fLock) ? CManageSycle::InLock() : CManageSycle::DeLock();
    return S_OK;
}

STDMETHODIMP MyCarClassFactory::CreateInstance(LPUNKNOWN pUnkOuter,REFIID riid,void** ppv)
{
     if ( pUnkOuter !=  NULL ) return CLASS_E_NOAGGREGATION;

    MyCar* pMyCarObj =  NULL;
    HRESULT hr;

    pMyCarObj =  new MyCar();
    hr = pMyCarObj->QueryInterface(riid,ppv);

     if ( FAILED(hr) ) delete pMyCarObj;
    return hr;
}


    6)实现COM入口和自注册函数CarLocalServer.cpp,

// CarLocalServer.cpp : Defines the entry point  for the DLL application.
//

#include "stdafx.h"
#include <iostream>    
#include < string.h>

#include "CarLocalServerTypeInfo_h.h"
//#include "CarLocalServerTypeInfo_i.c"
#include "MyCarClassFactory.h"    
#include "ManageSycle.h"

using namespace std;

#ifdef _MANAGED
#pragma managed(push, off)
#endif

HMODULE g_hmodule;

BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
                     )
{
    g_hmodule = static_cast<HMODULE>(hModule);
    return  TRUE;
}


STDAPI DllCanUnloadNow()
{
    bool bDllCanUnloadNow = CManageSycle::IsZeroObject() && CManageSycle::IsZeroLock();
    return bDllCanUnloadNow ? S_OK : S_FALSE;
}

STDAPI DllGetClassObject(REFCLSID rclsid,REFIID riid,LPVOID* ppv)
{
    *ppv =  NULL;
     if(__uuidof(MyCar) != rclsid)
    {
        return E_NOINTERFACE;
    }

    MyCarClassFactory* pBirdFactory =  new MyCarClassFactory();
     if( NULL == pBirdFactory)
    {
        return E_OUTOFMEMORY;
    }

    HRESULT hr = pBirdFactory->QueryInterface(riid,ppv);
     if(FAILED(hr))
    {
        delete pBirdFactory;
    }
    return hr;
}

STDAPI DllRegisterServer()
{
    HKEY hRoot, hNew;
    ::RegOpenKey(HKEY_CLASSES_ROOT,L"CLSID",&hRoot);
    ::RegCreateKey(hRoot,L"{1F0A9759-FCBE-4870-8336-971BD19A7452}\\InprocServer32",&hNew);
    wchar_t strFile[MAX_PATH];
    ::GetModuleFileName(g_hmodule,strFile,MAX_PATH);
    ::RegSetValue(hNew, NULL,REG_SZ,strFile,MAX_PATH);
    ::RegCloseKey(hRoot);
    return S_OK;
}

STDAPI DllUnregisterServer()
{
    HKEY hRoot;
    ::RegOpenKey(HKEY_CLASSES_ROOT,L"CLSID",&hRoot);
    ::RegDeleteKey(hRoot,L"{1F0A9759-FCBE-4870-8336-971BD19A7452}");
    ::RegDeleteKey(hRoot,L"{1F0A9759-FCBE-4870-8336-971BD19A7452}\\InprocServer32");
    ::RegCloseKey(hRoot);
    return S_OK;
}
#ifdef _MANAGED
#pragma managed(pop)
#endif


    7)增加def到处文件CarLocalServer.def,

LIBRARY    "CarLocalServer"

EXPORTS
    DllCanUnloadNow                 PRIVATE
    DllGetClassObject                PRIVATE
    DllRegisterServer                PRIVATE
    DllUnregisterServer             PRIVATE


  

三,COM的调用过程
通过一个创建COM组件的最小框架结构,然后看一看其内部处理流程是怎样的

    IUnknown *pUnk=NULL;
    IObject *pObject=NULL;
    CoInitialize(NULL);
    CoCreateInstance(CLSID_Object, CLSCTX_INPROC_SERVER, NULL, IID_IUnknown, (void**)&pUnk);
    pUnk->QueryInterface(IID_IOjbect, (void**)&pObject);
    pUnk->Release();
    pObject->Func();
    pObject->Release();
    CoUninitialize();

这就是一个典型的创建COM组件的框架,不过我的兴趣在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(.....)
    {
    ...........
    CObject *pObject = new CObject; //组件对象
    pObject->QueryInterface(IID_IUnknown, (void**)&pUnk);
    pObject->Release();
    ...........
    }


四,COM的调用方法
对上面手动创建的COM的调用实例:

int main()
{
    // initialize the COM runtime
    cout << "Initialize the COM runtime ";
    CoInitialize( NULL);
    cout << "success." << endl;

    // declare variables
    HRESULT hr;
    IClassFactory* pICF =  NULL;
    ICreateMyCar*  pICreateMyCar =  NULL;
    IEngine*       pIEngine =  NULL;
    IStats*        pIStats =  NULL;

    cout << endl << "Get the class factory interface for the Car class ";
    hr = CoGetClassObject(CLSID_MyCar,CLSCTX_LOCAL_SERVER, NULL,IID_IClassFactory,(void**)&pICF);
     if ( FAILED(hr) )
    {
        cout<<"fail";
         exit(1);
    }
     else cout << "success." << endl;
    
    cout << "Create the Car object and get back the ICreateMyCar interface ";
    hr = pICF->CreateInstance( NULL,IID_ICreateMyCar,(void**)&pICreateMyCar);
     if ( FAILED(hr) )
    {
        //ShowErrorMessage("CoGetClassObject()",hr);
         exit(1);
    }
     else cout << "success." << endl;
    
    //  set parameters  on the car
    cout << endl << "Set different parameters on the car ";
    pICreateMyCar->SetMaxSpeed(30);
    BSTR carName = SysAllocString(OLESTR("COMCar?!"));
    pICreateMyCar->SetPetName(carName);
    SysFreeString(carName);
    cout << "success." << endl;

    cout << endl << "Query the IStats interface ";
    pICreateMyCar->QueryInterface(IID_IStats,(void**)&pIStats);
    cout << "success." << endl;

    cout << endl << "Use the IStats interface to display the status of the car:" << endl;
    pIStats->DisplayStats();

    cout << endl << "Query the IEngine interface ";
    pICreateMyCar->QueryInterface(IID_IEngine,(void**)&pIEngine);
    cout << "success." << endl;

    cout << endl << "Start to use the engine " << endl;
     int curSp = 0;
     int maxSp = 0;
    pIEngine->GetMaxSpeed(&maxSp);
     do
    {
        pIEngine->SpeedUp();
        pIEngine->GetCurSpeed(&curSp);
        cout << "current speed is: " << curSp << endl;
    }  while (curSp <= maxSp);

    cout << endl << "Report status again: " << endl;
    pIStats->DisplayStats();

     if ( pICF )         pICF->Release();
     if ( pICreateMyCar) pICreateMyCar->Release();
     if ( pIStats )      pIStats->Release();
     if ( pIEngine )     pIEngine->Release();

    cout << endl << "Close the COM runtime ";
    CoUninitialize();
    cout << "success." << endl;

    return 0;
}


方法一:向上面的调用,通过包含#include "../CarLocalServer/CarLocalServerTypeInfo_h.h"和#include "../CarLocalServer/CarLocalServerTypeInfo_i.c"

方法二:使用#import导入tlb

可以使用: CComBSTR ,CComPtr<> 和 CComQIPtr<> 等来简化调用。

五,总结
COM比一般的DLL有很多的优点,COM没有重名问题,因为根本不是通过函数名来调用函数,而是通过虚函数表,自然也不会有函数名修饰的问题。路径问题也不复存在,因为是通过查注册表来找组件的,放在什么地方都可以,即使在别的机器上也可以。也不用考虑和EXE的依赖关系了,它们二者之间是松散的结合在一起,可以轻松的换上组件的一个新版本,而应用程序混然不觉。

但是COM仍然是有问题的,比如说版本控制的问题,.NET将逐步代替COM的使用。

六,参考
1)OLE/COM/COM+/DCOM/ActiveX/ActiveX contorl ( 概念) 
2)用VC进行COM编程所必须掌握的理论知识 
3)COM编程入门(1) 
4)COM编程入门(2) 
5)c++中使用com的方法

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值