您的位置:首页 > 编程语言 > C语言/C++

C++ COM

2016-04-01 16:28 369 查看
前言:为了介绍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, NULL, NULL);




    //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的方法
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: