COM step by step

xiaoxiao2021-03-06  37

Author: Saurabh Dasgupta

March 6, 2001

Original link:

Http://www.codeguru.com/cpp/com-tech/activex/tutorials/Article.php/c5567/

Introduction

For me, understand COM (Component Object Model, Component Object Model) is not as long-distance travel. I believe that each programmer who wants to understand COM must write at least one simple COM object using ordinary C , that is, does not rely on any template or macro provided by MFC / ATL. In this article, I will gradually describe how to create a simple COM object from the basic principles. These components can be used for client programs for VC / VB.

As an exercise, we will try to design a COM component, which will implement imaginary quick phase plus algorithm. It must pass the two long data types of parameters and return another long parameter to the user, which is the result of the algorithm. We now start designing interfaces.

interface

The interface of the COM object does not involve the actual implementation, but its method marks the part of the COM object used to communicate with the outside world. We named our interface IADD, and its declaration uses the interface definition language (Interface Definition Language, IDL). IDL is a language used to define a function flag, which is independent of various programming languages, which enables the RPC underlying to pack, loading and unpacking the parameters between different computers. In our IADD interface, we have setFirstNumber and SetSecondNumber methods that are used to pass the parameters of the addition. There is also a method, DOTHEADDITION, which is used to complete the addition and pass the result to the client.

step 1:

Create a new Win32 DLL project (for example, addobj), we will create all the files next in this folder. Create an empty file and type the following. Save it as Iadd.idl. The identifier of the interface can be generated using 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 add;

Step 2:

Use the command line compiler MIDL.exe (Note: MIDL.exe comes with VC , and may include some path issues for MIDL, you may need to modify your path variable settings to compile file Iadd.idl. After compiling, the following files are generated:

Iadd.h contains a C -style interface declaration. DLLDATA.C contains code for proxy DLL. The case used to call the object on a different process or computer. Iadd.tlb binary file describes our IADD interface and all of it. This file can be used by all clients of our COM components. IADD_P.C contains the group code of the agent DLL. The case used to call the object on a different process or computer. Iadd_i.c contains the IID of the interface.

Step 3:

Now let's create this COM object. Create a new file (addObj.h), declare a C class, named CADDOBJ, inherit the self-interface IADD (file Iadd.h). Remember, IADD inherits from iUnknown, it is also an abstract base class. Therefore, we have to declare all methods for the abstract base class Iadd like IUNKNOWN.

///////Addobj.h// contains a C class declaration that implements the IADD interface // # include "Iadd.h" extern long g_ncomobjsinuse; Class Caddobj: public {public: // iUnknown interface hResult __stdcall queryinterface REFIID riid, void ** ppObj); ULONG __stdcall AddRef (); ULONG __stdcall Release (); // iAdd interfaces HRESULT __stdcall SetFirstNumber (long nX1); HRESULT __stdcall SetSecondNumber (long nX2); HRESULT __stdcall DoTheAddition (long * pBuffer); PRIVATE: long m_nx1, m_nx2; // adds operands long m_nrefcount; // management reference count}; ///

Step 4:

We will be implemented for all methods of the IADD interface. Create a new file (addObj.cpp) and implement all the following code.

//// //addobj.cpp//-containing the method of IADD interface Implementation // # 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;} Step 5:

We also need to implement iUnknown methods. We will implement 3 fixed methods (AddRef, Release and QueryInterface) in the same file addobj.cpp. That Private member m_nrefcount is used to maintain the survival of the object. M_nrefcount will not be directly reduced or increased, and we use a thread safe practice, which is an API that uses interlocking and reduced APIs.

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 (); returnif;} // If the control reaches this, let the client know // We do not support the requested interface // * ppobj = null; return e_nointerface;} / / QueryInterfac method ULONG __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;}

Step 6:

We have completed the functional part of the Add COM object. Just like each COM, each COM object must have an interface ICLASSFACTORY separate implementation. The client will use this interface to get an instance of our IADD interface implementation. The iClassFactory interface is just like all other COM interfaces, but also inheritance from iUnknown. Therefore, we will provide an implementation of the iUnknown method and the implementation of the iClassFactory method (LockServer and CreateInstance). Create a new file (name AddObjFactory.h), declare a class CaddFactory, inheriting from ICLASSFACTory. ///////AddObjFactory.h// contains C class declaration IClassFactory implemented // class CAddFactory: public IClassFactory {public: // The method IUnknown interface HRESULT __stdcall QueryInterface (REFIID riid, void ** ppObj); ULONG __stdcall AddRef (); ULONG __stdcall Release (); // IClassFactory interface method HRESULT __stdcall CreateInstance (IUnknown * pUnknownOuter, const IID & iid, void ** ppv); HRESULT __stdcall LockServer (BOOL bLock); private: long m_nRefCount;};

Step 7:

Now you need to implement the CADDFACTORY method. Create a new file (addObjFactory.cpp) to provide entity of all methods of IUNKNOWN and ICLASSFACTORY. AddRef, Release and QueryInterface methods are similar to those of CADDOBJ. The implementation of the CREATEINSTANCE method is the following, which instantiate the CADDOBJ class and posts the requested interface pointer. The LockServer method does not have a specific implementation.

HRESULT __STDCALL CADDFACTORY :: CreateInstance (IUNKNOWN * PUNKNOWNOUTER, Const IID & IID, VOID ** PPV) {/// This method allows all clients to make component // class factories provide a mechanism to control generated components. Methods. // The author of the component may decide to make each license agreement // creation effective or invalidate every license agreement. // // 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 request for interface // Return POBJECT-> Queryinterface (IID, PPV);} HRESULT __STDCALL CADDFAA:: LOCKSERVER (Bool Block) {Return E_NOTIMPL;} Step 8:

A COM object in a process is actually in a simple Win32 DLL, and they all abide by a specific protocol. Each COM DLL must have an export function named DllgetClassObject, and the client will call an instance of the class factory (IUNKNOWN or ICLASSFACTORY), and then the CREATEINSTANCE method. Create a new file (exports.cpp) to implement DllgetClassObject.

StDAPI DLLGETCLASSOBJECT (Const CLSID & CLSID, Const IID & IID, Void ** PPV) {// Checks whether the request COM object can implement multiple COM objects in this DLL // if (CLSID == CLSID_ADDOBJECT ) {// // IID to specify the requested interface // client to request iUnknown, iClassFactory, IclassFactory2 // CaddFactory * paddFactory2 // CaddFactory; if (paddFact == null) Return E_OUTOFMEMORY; Else {Return PaddFact- > QueryInterface (IID, PPV);}} // // If the control reaches this, then this means that the user specified in the DLL // Return Class_e_classNotavailable;}

Step 9:

The client still needs to know when a COM DLL can uninstall from memory. In depth, the COM object within the process can be explicitly loaded by calling the API function loadLibrary, which can load the DLL into the client's process address space. Similarly, call FreeELibrary can uninstall the DLL. COM client must know when DLL can be securely uninstalled, it must confirm that there is no COM object instance in the current DLL. In order to make this calculation simpler, we will add a global variable (g_ncomobjinjinuse) in the C constructor of Caddobj and CaddFactory in COM DLL. Similarly, we will reduce the value of g_ncomobjinuses in their respective destructive functions.

We have to export a specific COM function DllcanunloadNow, which is implemented as follows:

StDAPI DLLCANUNLOADNOW () {// // When there is no object existing in the DLL, it is not in use // (which is 0) // We will check the value of g_ncomobjsinuse // if (g_ncomobjsinuse == 0) {RETURN S_OK;} else {return s_false;}}

Step 10:

The location of the COM object needs to be written to the registry, which can be implemented through an external .reg file, or let the DLL export a DLLREGISTERSERVER function. In order to clear the content of the registry, we also export another DLLunRegisterServer function. The implementation of these two functions is in registry.cpp. You can use some simple tools such as Regsvr32.exe to load a specific DLL and perform DllRegisterServer / DllunregisterServer.

In order for the linker to export these four functions, we also need to create a module definition file (exports.def).

; Contains a list of functions exported in DLL; Description "Simple Com Object" Exports DllgetClassObject Private DllcanunloadNow Private DllRegisterServer Private DllunregisterServer Private

Step 11:

We need to finally handle our Win32 DLL project Addobj. Insert the Iadd.idl file into the workspace of the project.

Set the custom build option for this file.

Insert a command line string in the "POST-Build Step" dialog box to run Regsvr32.exe after each build.

Build this DLL. Inserting the IDL file into the workspace will make the editing after each file is modified easier. Every time our project is successfully compiled, the COM object is also registered.

Step 12:

Now use Addobj this COM object in Visual Basic. Create a simple EXE project and run the following lines of code. Please confirm that you want to add an Iadd.TLB type library project reference.

DIM IADD SET IADD = CreateObject ("CodeGuru.fastaddition") Iadd.SetFirstNumber 100 Iadd.setsecondNumber 200 msgbox "total =" & Iadd.dotHeaddition () Step 13:

We previously used the following files:

Iadd.idl contains interface declarations. AddObj.h contains C category declarations for class Caddobj. AddobjFactory.h includes C category declarations for class CaddFactory. Addobj.cpp contains C class implementation of class Caddobj. AddObjfactory.cpp contains C class implementation of class CaddFactory. Exports.cpp contains the implementation of DllgetClassObject, DllcanunloadNow, and DLLMain. Registry.cpp contains DLLREGISTERSERVER, DLLUNREGISTERSERVER implementation. Addobjguid.h contains our COM object addObj's GUID value.

Step 14:

Type libraries can also be released along with Addobj.dll. To simplify this process, the Iadd.tlb type library can also be embedded as an resource file for addobj.dll. In this way, only the DLL file addobj.dll can only be released to the customer.

Step 15:

A Visual C client can use the COM interface by several ways:

#Impott "Iadd.tlb".

2. Iadd.h header file. In this case, the Seller of the DLL must post the Iadd.h header file with the DLL.

3. Generate C code using some wizard tools such as the MFC Class Wizard.

For the first method, the compiler creates some intermediate files (.tlh, .tli) that contain interface declarations. Furthermore, the compiler also defines the intelligent interface pointer on the basis of the original interface. Intelligent interface pointers make COM programmers to manage the survival of COM objects easier.

In the following example #import directly imported Addobj.dll instead of addobj.tlb because we contained TLB files in the DLL. Otherwise, # import will import TLB files.

Create a new console EXE in VC , enter the following content and compile.

/////Client.cpp/// Client Use the COM object from addobj.dll // # include #include #import "addobj.dll" ///// / Here we use #import to DLL, you can also use #improt. TLB. // # import generates two files (.tlh / .tli) directly in the output folder. // void main () {long n1 = 100, n2 = 200; long noutput = 0; coinitialize (null); codegurumathlib :: Iaddptr pfastaddalgorithm; // // ipdptr is not actual IADD pointer, but a C class template (_COM_PTR_T) // It embeds the original IADD pointer instance // When the destructor, the destructor will call the original interface pointer to Release () // further, it also overloads Operator -> to direct All of the original method of operating an internal interface pointer // pFastAddAlgorithm.CreateInstance ( "CodeGuru.FastAddition"); pFastAddAlgorithm-> SetFirstNumber (n1); // "->" overloaded operation pFastAddAlgorithm-> SetSecondNumber (n2); nOutPut = pFastAddAlgorithm -> dotheaddition (); printf ("/ NOUTPUT AFTER ADDING% D &% D IS% D / N", N1, N2, NOUTPUT);} Click here to download source code

Click here to download the demo project

转载请注明原文地址:https://www.9cbs.com/read-64334.html

New Post(0)