韩国首尔电影院:COM逐步教程

来源:百度文库 编辑:九乡新闻网 时间:2024/04/28 22:57:50

中文版:http://blog.csdn.net/titilima/article/details/268505

Step by Step COM Tutorial



1234567891011121314151617181920
Login to rate.
  • March 6, 2001
  • By Saurabh Dasgupta
  • Send Email ?
  • More Articles ?
Font Size
  • Introduction
  • Interfaces
  • Step 1:Create the IDL file
  • Step 2:Generating the type library
  • Step 3:Derive from IAdd
  • Step 4:Implement the methods of IAdd
  • Step 5:Implementing IUnkown
  • Step 6:Factory
  • Step 7:Implementing the methods of IClassFactory
  • Step 8:Implementing DllGetClassObject
  • Step 9:Implementing DllCanUnloadNow
  • Step 10:DllRegisterServer-UnregisterServer
  • Step 11:Inserting the IDL file into the Workspace
  • Step 12:Using the COM object from Visual Basic
  • Step 13:Analysis of all the files that were created by us .
  • Step 14:Embedding the type library into the ActiveX DLL
  • Step 15:Using the COM object from Visual C++ client

top

Introduction

For me , understanding COM (Component Object Model) has been no less than an odyssey. I believe that every programmer who wishes to understand the basic principles behind COM, must write atleast one simple COM object using plain C++ , i.e. without the aid of templates and macros that comes along with MFC/ATL. In this article I present the guidelines for creating simple COM objects from first principles. The components should be usable by both VC/VB clients.

As an exercise we will attempt to design a COM component that will implement a hypothetical super-fast addition algorithm. The component must take in two parametes of long data type, and return to the user another long parameter that will be an outcome of our addition algorithm.We will begin with designing the interface.

top

Interface

The interface of a COM object does not talk about the actual implementation, but the method signatures that will be used by others to communicate with the COM object. We will name our interface as IAdd. The declaration for this interface will be done using the Interface Definition Language (IDL). IDL is a language used for defining function signatures, in a format that is independent of the programming language, and this helps the RPC infrastructure to pack, ship and unpack parameters from one computer to another. In our interface IAdd, we will have methods SetFirstNumber and SetSecondNumber which will be used for passing the parameters for addition. There will be another method , DoTheAddition, that will actually do the addition and give back to the client the result.

top

Step 1:
Create a new Win32 DLL project (AddObj say). Create all the subsequent files in this folder. Create a blank file and keyin the following contents. Save it as IAdd.idl. The interface identifiers have been generated using the tool uuidgen.exe.

import "unknwn.idl";[object,uuid(1221db62-f3d8-11d4-825d-00104b3646c0),helpstring("interface  IAdd is used for implementing a super-fast addition Algorithm")]interface IAdd : IUnknown    {    HRESULT     SetFirstNumber(long nX1);    HRESULT     SetSecondNumber(long nX2);        HRESULT     DoTheAddition([out,retval] long *pBuffer);    };[uuid(3ff1aab8-f3d8-11d4-825d-00104b3646c0),helpstring("Interfaces for Code Guru algorithm implementations .")]library CodeGuruMathLib    {    importlib("stdole32.tlb");    importlib("stdole2.tlb");    interface IAdd;    }
top
  • Post a comment
  • Print Article

Step 2:
Compile the file IAdd.idl using the command line compiler MIDL.exe (note:midl.exe ships with VC++ and incase of any path problems for midl, you may need to fix your path variable settings )
Upon compilation the following files will be generated:

IAdd.hContains the C++ style interface declarations.dlldata.cContains code for proxy DLL. Useful when invoking the object on a different process/computer.IAdd.tlbBinary file , with a well defined format that completely describes our interface IAdd along with all it's methods. This file is to be distributed to all the clients of our COM component.IAdd_p.cContains marshalling code for proxy DLL. Useful while invoking the object on a different process/computer.IAdd_i.cContains the interface IIDs

top

Step 3:
We will create the COM object. Create a new file (AddObj.h), delclare a C++ class , name this class CAddObj. Derive this class from the interface IAdd (file IAdd.h). Remember that , IAdd derives from IUnknown, which is also a abstract base class. Therefore we will have to declare all the methods for the abstract base classes IAdd as well as IUnknown.

///////////////////////////////////////////////////////////////AddObj.h//Contains the C++ class declarations for implementing the IAdd//interfaces//#include    "IAdd.h"extern long g_nComObjsInUse;class CAddObj :         public IAdd    {    public:    //IUnknown interface     HRESULT __stdcall QueryInterface(                                REFIID riid ,                                 void **ppObj);    ULONG   __stdcall AddRef();    ULONG   __stdcall Release();    //IAdd interface    HRESULT __stdcall SetFirstNumber( long nX1);    HRESULT __stdcall SetSecondNumber( long nX2);    HRESULT __stdcall DoTheAddition( long *pBuffer);    private:    long m_nX1 , m_nX2; //operands for addition    long m_nRefCount;   //for managing the reference count    };///////////////////////////////////////////////////////////
top

Step 4:
We will provide implementations for the all methods of the IAdd interface. Create a new file (AddObj.cpp) and implement the method code here.

///////////////////////////////////////////////////////////////AddObj.cpp//Contains the method  implementations of the IAdd interface//interfaces//#include    #include    "AddObj.h"#include    "IAdd_i.c"HRESULT __stdcall CAddObj::SetFirstNumber( long nX1)    {    m_nX1=nX1;    if (m_bIsLogEnabled) WriteToLog("Junk");    return S_OK;    }HRESULT __stdcall CAddObj::SetSecondNumber( long nX2)    {    m_nX2=nX2;    return S_OK;    }HRESULT __stdcall CAddObj::DoTheAddition( long *pBuffer)    {    *pBuffer =m_nX1 + m_nX2;    return S_OK;    }
top

Step 5:
IUnknown methods need to be implemented. We will implement the 3 mandatory methods (AddRef, Release and QueryInterface) in the same file AddObj.cpp. The private member m_nRefCount is used for maintainig the object life time. m_nRefCount is not decremented/incremented directly, instead we do it in a thread safe way, using the API InterlockedIncrement and InterlockedDecrement

HRESULT __stdcall CAddObj::QueryInterface(                                    REFIID riid ,                                     void **ppObj)    {    if (riid == IID_IUnknown)        {        *ppObj = static_cast(this) ;         AddRef() ;        return S_OK;        }    if (riid == IID_IAdd)        {        *ppObj = static_cast(this) ;        AddRef() ;        return S_OK;        }    //    //if control reaches here then , let the client know that    //we do not satisfy the required interface    //    *ppObj = NULL ;    return E_NOINTERFACE ;    }//QueryInterface methodULONG   __stdcall CAddObj::AddRef()    {    return InterlockedIncrement(&m_nRefCount) ;    }    ULONG   __stdcall CAddObj::Release()    {         long nRefCount=0;    nRefCount=InterlockedDecrement(&m_nRefCount) ;    if (nRefCount == 0) delete this;    return nRefCount;    }
top

Step 6:
We have finished with the functionality part of the Add COM object. As per COM guide lines, every COM object must have a separate implementation of the interface IClassFactory. Clients will use this interface to get an instance of our IAdd interface implementation. The interface IClassFactory, like all other COM interfaces, derives from IUnknown. Therefore we will have to provide an implementation of the IUnknown methods, as well as the IClassFactory methods (LockServer and CreateInstance). Create a new file (name it AddObjFactory.cpp) , declare a class CAddFactory here and make this class derive from IClassFactory.

///////////////////////////////////////////////////////////////AddObjFactory.h//Contains the C++ class declarations for the IClassFactory implementations//class CAddFactory : public IClassFactory    {    public:    //interface IUnknown methods     HRESULT __stdcall QueryInterface(                                REFIID riid ,                                 void **ppObj);    ULONG   __stdcall AddRef();    ULONG   __stdcall Release();    //interface IClassFactory methods     HRESULT __stdcall CreateInstance(IUnknown* pUnknownOuter,                                         const IID& iid,                                         void** ppv) ;    HRESULT __stdcall LockServer(BOOL bLock) ;     private:    long m_nRefCount;    };
top

Step 7:
The CAddFactory methods need to be implemented. Create a new file (AddObjFactory.cpp) and provide the method bodies for all the IUnknown and IClassFactory methods. The AddRef, Release and QueryInterface methods have implementations similar to that of class CAddObj. The method CreateInstance is the place, where the class CAddObj is instantiated and and the requested interface pointer is passed back. The method LockServer has not be given any specific implementation.

HRESULT __stdcall CAddFactory::CreateInstance(IUnknown* pUnknownOuter,                                           const IID& iid,                                           void** ppv)     {    //    //This method lets the client manufacture components en masse    //The class factory provides a mechanism to control the way    //the component is created. Within the class factory the     //author of the component may decide to selectivey enable    //or disable creation as per license agreements     //    //    // Cannot aggregate.    if (pUnknownOuter != NULL)        {        return CLASS_E_NOAGGREGATION ;        }    //    // Create an instance of the component.    //    CAddObj* pObject = new CAddObj ;    if (pObject == NULL)        {        return E_OUTOFMEMORY ;        }    //    // Get the requested interface.    //    return pObject->QueryInterface(iid, ppv) ;    }HRESULT __stdcall CAddFactory::LockServer(BOOL bLock)     {    return E_NOTIMPL;    }
top

Step 8:
An inprocess COM object is nothing more than a simple Win32 DLL, that obeys a certain protocol. Every COM DLL must have an exported function by the name DllGetClassObject. Clients will invoke this function to get an instance of the class factory (either IUnknown or IClassFactory), followed by invocation of the CreateInstance method. Create a new file (Exports.cpp). Implement DllGetClassObject here.

STDAPI DllGetClassObject(const CLSID& clsid,                         const IID& iid,                         void** ppv)    {    //    //Check if the requested COM object is implemented in this DLL    //There can be more than 1 COM object implemented in a DLL    //    if (clsid == CLSID_AddObject)        {        //        //iid specifies the requested interface for the factory object        //The client can request for IUnknown, IClassFactory,        //IClassFactory2        //        CAddFactory *pAddFact = new CAddFactory;        if (pAddFact == NULL)            return E_OUTOFMEMORY;        else            {            return pAddFact->QueryInterface(iid , ppv);            }        }        //    //if control reaches here then that implies that the object    //specified by the user is not implemented in this DLL    //    return CLASS_E_CLASSNOTAVAILABLE;    }
top

Step 9:
Clients need to know when a COM DLL can be unloaded from memory. Deep down,an inprocess COM object gets explicitly loaded by a call to the API LoadLibrary, which brings the specified DLL into the client's process space. An explicitly loaded DLL can be unloaded by a call to FreeLibrary.
COM clients must know when a DLL can be safely unloaded. A client must make sure that there are no instances of any COM object alive , from a particular DLL. To make this accounting simpler , within the COM DLL, we will increment a global variable (g_nComObjsInUse) in the C++ constructors of CAddObj & CAddFactory. Similarly, we will decrement g_nComObjsInUse in their respective destructors.
We will export another COM specified function ,DllCanUnloadNow. The implementation is as follows:

STDAPI DllCanUnloadNow()    {    //    //A DLL is no longer in use when it is not managing any existing objects    // (the reference count on all of its objects is 0).     //We will examine the value of g_nComObjsInUse     //    if (g_nComObjsInUse == 0)        {        return S_OK;        }    else        {        return S_FALSE;        }    }
top

Step 10:
The location of a COM object has to be entered into the registry. This can be done through an external .REG file or make the DLL export a function DllRegisterServer. To erase the registry contents, we will export another function DllUnregisterServer. The implementations of these two functions are in the file Registry.cpp . A simple tool like regsvr32.exe can be used to load a specified DLL and then execute DllRegisterServer/DllUnregisterServer.

To make the linker export the 4 functions, we will create a module definition file (Exports.def)

;;contains the list of functions that are being exported from this DLL;DESCRIPTION     "Simple COM object"EXPORTS                DllGetClassObject   PRIVATE                DllCanUnloadNow     PRIVATE                DllRegisterServer   PRIVATE                DllUnregisterServer PRIVATE
top

Step 11:
We have to give the finishing touches to our AddObj Win32 DLL project. Insert the file IAdd.idl into the project work space. 


Set the custom build options for this file.


Insert a command line string in the "Post-build step" dialog box for executing regsvr32.exe at the end of every build. 

Build the DLL. Inserting the IDL file into the workspace alleviates the need for external compilation, every time the file is modified. Every time we successfuly build our project, the COM object is registered.

top

Step 12:
To use the AddObj COM object from Visual Basic, creat a simple EXE project and run the following lines of code. Make sure to add a project reference to the IAdd.tlb typelibrary.

    Dim iAdd As CodeGuruMathLib.iAdd    Set iAdd = CreateObject("CodeGuru.FastAddition")    iAdd.SetFirstNumber 100    iAdd.SetSecondNumber 200    MsgBox "total = " & iAdd.DoTheAddition()
top

Step 13:
The following files were used by us:

IAdd.idlContains the interface declarations.AddObj.hContains the C++ class declaration for the class CAddObjAddObjFactory.hContains the C++ class declaration for the class CAddFactoryAddObj.cppContains the C++ class implementation for the class CAddObjAddObj.cppContains the C++ class implementation for the class CAddFactoryExports.cppContains the implementations for DllGetClassObject,DllCanUnloadNow & DllMainRegistry.cppContains the implementations for DllRegisterServer,DllUnregisterServerAddObjGuid.hContains the GUID value for our AddObj COM object.

top

Step 14:
Along with the AddObj.dll, the type library can also be distributed. To simplify the process, the type library IAdd.tlb can also be embedded as a binary resource file in the AddObj DLL . Henceforth, only the DLL file AddObj.dll needs to be distributed to the clients. 




top

Step 15:
A Visual C++ client can use use COM interfaces through any of the following:

  • #import "IAdd.tlb" .
  • IAdd.h header file. In this case the DLL vendor must ship the IAdd.h header file along with the DLL.
  • Generate C++ code using some wizard sort of a tool(eg: MFC's Class Wizard)
In Case 1, the compiler creates some intermediate files (.tlh, tli) that contain the expanded interface declarations. Further, the compiler also declares smart interface pointer classes built around the raw interfaces. Smart interface pointer classes make life easier for the COM programmer by properly managing the lifetime of the COM object.
In the following example #import has been done on the file AddObj.dll and not on the IAdd.tlb, because we are shipping the TLB file within the DLL. Otherwise, #import should have been done on the TLB.
Create a new console EXE project in VC++. Type the following contents and compile.

/////Client.cpp////Demo of client using the COM object from AddObj.dll //#include    #include    #import     "AddObj.dll"////Here we do a #import on the DLL ,you can also do a #import on the .TLB //The #import directive generates two files (.tlh/.tli) in the output folders.//void main()    {    long n1 =100, n2=200;            long nOutPut = 0;    CoInitialize(NULL);    CodeGuruMathLib::IAddPtr pFastAddAlgorithm;    //    //IAddPtr is not the actual interface IAdd, but a template C++ class (_com_ptr_t)    //that contains an embedded instance of the raw IAdd pointer    //While destructing , the destructor makes sure to invoke Release() on the internal    //raw interface pointer. Further, the operator -> has been overloaded to direct all    //method invocations to the internal raw interface pointer.    //    pFastAddAlgorithm.CreateInstance("CodeGuru.FastAddition");    pFastAddAlgorithm->SetFirstNumber(n1);//"->" overloading in action    pFastAddAlgorithm->SetSecondNumber(n2);    nOutPut = pFastAddAlgorithm->DoTheAddition();    printf("\nOutput after adding %d & %d is %d\n",n1,n2,nOutPut);        }
top

Downloads

Download source - 114 Kb
Download demo project - 101 Kb