Building a COM Component Using Plain C++

Document Sample
Building a COM Component Using Plain C++ Powered By Docstoc
					                                                                                              1


Building a COM Component Using Plain C++
        The fundamental theory of COM (GUIDs, interfaces, class factories, registery
etc..) will be explained in the lecture. To help understand the theory, I will show you all
the steps required in creating a simple COM server, then manually registering it in the
registery, and finally writing a client for it.

        To build a COM component using plain C++, you need to create two classes (a
useful class and the associated factory class), write the four exported dll functions, and
create and compile the IDL file describing the interfaces of the COM component (useful
class). Let us build a small COM component to see the above tasks.
Step 1: Create a Win32 dll type project. Name the project comserverx01 as shown below:




Specify an empty dll in the next wizard screen.

Step 2: Add a text file called comx_IDL.idl to the project by choosing File->New. Then
type the following code in the file.
        Note there are four 128-bit GUIDs in the following IDL code. You will not use
the same GUIDs as shown below, instead for each of the numbers, you should create a
new GUID from the GUID generator tool that comes with Visual Studio. To get a new
GUID, select Project->Add To Project->Components and Controls menu, then double
click on Visual C++ components, then select GUID generator, and click on the Insert
button. Choose registery format, then click on New button and then the Copy button to
copy the new GUID to the clip board. Then you can paste the GUID in the appropriate
place in the following IDL code.
                                                                    2




// comx_IDL.idl
import "oaidl.idl";
[
         object,
         uuid(1C14A131-E248-11D4-9E1C-00105A0E2D6C)
]
interface ISetGet:IUnknown
{
         HRESULT seta([in]int m);
         HRESULT setb([in]int m);
         HRESULT geta([out]int* pVal);
         HRESULT getb([out]int* pVal);
};

[
       object,
       uuid(1C14A133-E248-11D4-9E1C-00105A0E2D6C)
]
interface IStatistics:IUnknown
{
         HRESULT computeavg([out]float* pVal);
         HRESULT computemax([out]int* pVal);
};
[
         uuid(1C14A135-E248-11D4-9E1C-00105A0E2D6C), version(1.0)
]
library comserverx01lib
{
         importlib("stdole32.tlb");
         [
         uuid(1C14A137-E248-11D4-9E1C-00105A0E2D6C) // CLSID
         ]
         coclass x
         {
                    [default] interface ISetGet;
                    interface IStatistics;
                    };
};
                                                                                             3


Save the above IDL file. Then from the file view tab, select the comx_IDL.idl file, then
hit the delete key to remove the IDL file from the project. Note that this will not delete
the IDL file from the project directory.

Step 3: Next we need to compile the IDL file using the MIDL compiler. Go to the DOS
prompt to the project directory, then type the following command to compile the IDL file.
midl comx_IDL.idl




The MIDL compiler will produce 5 output files, as shown below.
comx_IDL.h         C++ interface definitions and associated GUIDs
 comx_IDL.tlb      Type library
 comx_IDL_i.c      interface constants. Needed when not using __uuidof operator.
 comx_IDL_p.c      proxy stub source code
 DLLDATA.C         Some more code needed in building proxy-stub dll.

Step 4: Now back to the comserverx01 dll project. Add a cpp file called
comserverx01.cpp to the project. Then type the following code in it.
// comserverx01.cpp
// COM server DLL has DllMain and 4 exported functions
#include "xfactory.h"
#include "globals.h"
static Cxfactory g_xfactory;
#include "comx_IDL_i.c"

extern "C"
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID)
{
         return TRUE;
}

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
{
      if(rclsid == CLSID_x)
                return g_xfactory.QueryInterface(riid, ppv);
      else
      {
                *ppv = NULL;
                return CLASS_E_CLASSNOTAVAILABLE;
      }
}
                                                                                      4

STDAPI DllCanUnloadNow(void )
{
      return (g_lLockCount == 0) ? S_OK : FALSE;
}

STDAPI DllRegisterServer(void)
{
      return S_OK;
}

STDAPI DllUnregisterServer(void)
{
      return S_OK;
}
Add a header file called “globals.h” to the project. Type the following code in it.
// globals.h

#ifndef GLOBAL_H
#define GLOBAL_H
static long g_lLockCount = 0;

#endif
Add another header file called “x.h” to the project with the following code in it:
// x.h -- header file for the useful class

// class x is derived from ISetGet and IStastistic
// ISetGet and IStatistics are defined in the IDL file comx_IDL.idl
// seta, setb, geta and getb are part of the ISetGet interface
// computeavg and computemax are part of the IStatistics interface
// Every COM interface is derived from IUnknown interface which has
// 3 functions: 1. QueryInterface, 2. AddRef and 3. Release

#ifndef X_H
#define X_H
#include "comx_IDL.h"

class x: public ISetGet, IStatistics
{
          int a, b;
          long m_lRefCount;
public:
          x();
          ~x();
          STDMETHOD (QueryInterface)(REFIID riid, void **ppv);
          STDMETHOD_(ULONG, AddRef)();
          STDMETHOD_(ULONG, Release)();
          STDMETHOD (seta)(int m);
          STDMETHOD (setb)(int m);
          STDMETHOD (geta)(int* pVal);
          STDMETHOD (getb)(int* pVal);
          STDMETHOD (computeavg)(float* pVal);
          STDMETHOD (computemax)(int* pVal);

};
#endif
                                                                                          5


The STDMETHOD() macro is defined in basetypes.h file. It transforms the function
specified in the macro parameter to a virtual function with HRESULT as the return type,
and stdcall as the function calling mechanism. The STDMETHOD_ does not have the
HRESULT as the return type.

Add a cpp file called “x.cpp” to the project with the code shown below:
// x.cpp

#include "x.h"
#include <windows.h>
#include "globals.h"

x::x()
{
           m_lRefCount = 0;

}

x::~x()
{
           InterlockedDecrement(&g_lLockCount);

}

HRESULT x::QueryInterface(REFIID riid, void **ppv)
{
      if(riid == IID_IUnknown)
                 *ppv = static_cast <ISetGet*> (this);
      else if(riid == IID_ISetGet)
                 *ppv = static_cast <ISetGet*> (this);
      else if(riid == IID_IStatistics)
                 *ppv = static_cast <IStatistics*> (this);
      else
      {
                 *ppv = NULL;
                 return E_NOINTERFACE;
      }

           static_cast <IUnknown*> (*ppv)->AddRef();
           return S_OK;
}

ULONG x::AddRef()
{
     return InterlockedIncrement(&m_lRefCount);
}

ULONG x::Release()
{
     InterlockedDecrement(&m_lRefCount);
     if (m_lRefCount == 0)
              delete this;
     return m_lRefCount;
}
                                                                                         6

HRESULT x::seta(int m)
{
      a = m;
      return S_OK;
}

HRESULT x::setb(int m)
{
      b = m;
      return S_OK;
}

HRESULT x::geta(int* pVal)
{
      *pVal = a;
      return S_OK;
}

HRESULT x::getb(int *pVal)
{
      *pVal = b;
      return S_OK;
}

HRESULT x::computeavg(float* pVal)
{
      *pVal = (a + b) / (float)2.0;
      return S_OK;
}

HRESULT x::computemax(int* pVal)
{
      if(a > b)
                *pVal = a;
      else
                *pVal = b;

         return S_OK;
}
Now we need to add the factory class to the project. Add a header file called “xfactory.h”
to the project with the following code:
// xfactory.h

// purpose is to create as many useful class objects as needed
// Each useful object is created on the heap
// Cxfactory is derived from IClassFactory which is a standard COM interface.
// This interface has 2 funcitons in it:
// 1. CreateInstance, 2. LockServer

#ifndef XFACTORY_H
#define XFACTORY_H

#include "comx_IDL.h"

class Cxfactory: public IClassFactory
                                                                                            7

{
          long m_lRefCount;
public:
          Cxfactory();
          STDMETHOD (QueryInterface)(REFIID riid, void **ppv);
          STDMETHOD_(ULONG, AddRef)();
          STDMETHOD_(ULONG, Release)();
          STDMETHOD (CreateInstance)(IUnknown *punkOuter, REFIID riid, void **ppvObject);
          STDMETHOD (LockServer)(BOOL fLock);
};
#endif
Add a cpp file called “xfactory.cpp” to the project with the following code:
// xfactory.cpp

#include "x.h"
#include "xfactory.h"
#include "comx_IDL.h"
#include "globals.h"

Cxfactory::Cxfactory()
{
        m_lRefCount = 0;

}

HRESULT Cxfactory::QueryInterface(REFIID riid, void **ppv)
{
      if(riid == IID_IUnknown)
                 *ppv = static_cast <IUnknown*> (this);
      else if(riid == IID_IClassFactory)
                 *ppv = static_cast <IClassFactory*> (this);
      else
      {
                 *ppv = NULL;
                 return E_NOINTERFACE;
      }

          static_cast <IUnknown*> (*ppv)->AddRef();
          return S_OK;
}

ULONG Cxfactory::AddRef()
{
     return InterlockedIncrement(&m_lRefCount);
}

ULONG Cxfactory::Release()
{
     return InterlockedDecrement(&m_lRefCount);
}

HRESULT Cxfactory::CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppvObject)
{
      *ppvObject = NULL;
      if(punkOuter != NULL)
              return CLASS_E_NOAGGREGATION;
                                                                                              8


       x* px = new x;
       if(px == NULL)
                 return E_OUTOFMEMORY; // out of heap space
       HRESULT hr = px->QueryInterface(riid, ppvObject);
       if(FAILED(hr))
                 delete px;
       return hr;
}

HRESULT Cxfactory::LockServer(BOOL fLock)
{
      if(fLock)
               InterlockedIncrement(&g_lLockCount);
      else
               InterlockedDecrement(&g_lLockCount);
      return S_OK;
}
Add a text file called “comserverx01.def” to the project with the following code in it:
; comserverx01.def
LIBRARY "comserverx01.dll"
EXPORTS
        DllGetClassObject @1 PRIVATE
        DllCanUnloadNow @2 PRIVATE
        DllRegisterServer @3 PRIVATE
        DllUnregisterServer @4 PRIVATE

Build the dll. If there are no errors, then we need to register the comserverx01.dll in the
windows registery.

Registering the Dll:
        From the start menu, choose Run, then type regedit. The COM class needs to
have its class ID (CLSID) registered under the HKEY_CLASSES_ROOT\CLSID section
of the registery. Expand on the HKEY_CLASSES_ROOT, then locate and expand the
CLSID section. Copy the class ID of the useful class to the clip board from the IDL file
(this was the last GUID, shown in bold in the comx_IDL.idl file, note that your number
will be different from this handout). Then right click on the CLSID folder in the
registery, and choose new Key. Paste the class ID. You will need to add the { and } to
make the GUID in registery format as shown below.
                                                                                           9


Right click on the newly added key, and choose new key, give it a name of
InprocServer32. Then from the right hand side, double click on the Default icon, add the
correct location of the dll as the value.




Similarly right click in the right hand side and choose new string value. Give it a name of
ThreadingModel. Then double click on it and give it a value of Apartment as shown
above.


Writing the COM Client:
Create a Win32 Console type application. Give it a project name of comclientx01. Add a
cpp file called comclientx01.cpp to it with the following code:
// comclientx01.cpp

#include <windows.h>
#include "..\comserverx01\comx_IDL.h"
#include "..\comserverx01\comx_IDL_i.c"
#include <iostream.h>

int main()
{

         // create an apartment
         // CoCreateInstance is equivalent to calling
         // CoGetClassObject followed by CreateInstance
         CoInitialize(NULL);
         ISetGet* pIsg = NULL;
//       HRESULT hr = CoCreateInstance(CLSID_x, NULL,
//                 CLSCTX_INPROC_SERVER, IID_ISetGet,
//                 reinterpret_cast <void **> (&pIsg));
         HRESULT hr = CoCreateInstance(__uuidof(x), NULL,
                   CLSCTX_INPROC_SERVER, __uuidof(ISetGet),
                   reinterpret_cast <void **> (&pIsg));
         if(FAILED(hr))
         {
                   cout << "CoCreateInstance failed" << endl;
                   CoUninitialize();
                   return 0;
         }
         pIsg->seta(5);
         pIsg->setb(10);
                                                                                     10

     int a1;

     pIsg->geta(&a1);
     cout << "a1 = " << a1 << endl;

     IStatistics* pIst = NULL;
     hr = pIsg->QueryInterface(IID_IStatistics,
               reinterpret_cast <void **> (&pIst));
     if(FAILED(hr))
     {
               cout << "QueryInterface failed" << endl;
               pIsg->Release();
               CoUninitialize();
               return 0;
     }

     float f1;
     pIst->computeavg(&f1);
     cout << "Computed average = " << f1 << endl;
     pIsg->Release();
     pIst->Release();

     // request a factory object
     IClassFactory *pIcf;
     hr = CoGetClassObject(CLSID_x, CLSCTX_INPROC_SERVER, NULL,
               IID_IClassFactory, reinterpret_cast <void **> (&pIcf));
     if(FAILED(hr))
     {
               cout << "CoGetClassObject failed" << endl;
               CoUninitialize();
               return 0;
     }

     ISetGet* pIsg2;
     pIcf->CreateInstance(NULL, IID_ISetGet, reinterpret_cast <void **> (&pIsg2));
     pIsg2->seta(55);
     int a2;
     pIsg2->geta(&a2);

     cout << "A in 2nd object = " << a2 << endl;

     pIsg2->Release();
//   pIcf->Release();

     // create 3rd object
     ISetGet* pIsg3;
     pIcf->CreateInstance(NULL, IID_ISetGet, reinterpret_cast <void **> (&pIsg3));
     pIsg3->setb(99);

     int b3;
     pIsg3->getb(&b3);
     pIsg3->Release();
     pIcf->Release();
     cout << "b3 returns as " << b3 << endl;
     return 0;
}
                                                                                    11


Build and run the client, the output will look like:




Although, majority of the time, you will be using the ATL library (ATL COM Appwizard
type project in Visual Studio) to build a COM component, sometimes you run into a
situation where you have some existing MFC code that you would like to be converted to
a COM component. MFC uses slightly complex nested class technique to implement the
COM interfaces in the useful class. I am showing you a small example of how you would
create a COM component using MFC.




     Creating COM components using MFC approach
1. . Visual C++ does not directly contain a MFC COM project type, so you need to start
   out by choosing project type as MFC AppWizard(dll).




2. Choose a Regular DLL, and check the Automation support as shown below.
                                                                                     12




3. In the StdAfx.h file, comment the following lines.

   // #include <afxodlgs.h>   // MFC OLE dialog classes
   // #include <afxdisp.h>   // MFC Automation classes
4. Notice that testxcom2.cpp file already has the code for important COM functions
   such as:
   STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
   STDAPI DllCanUnloadNow(void)
   STDAPI DllRegisterServer(void)

   Also read the important note in the testxcom2.cpp file on including the macro
   AFX_MANAGE_STATE(AfxGetStaticModuleState());                  in the beginning of any
   function that will call into the MFC dlls.

5. Add a new file (C++ header file type) to the project. Name it xInterface.h. Type the
   following code in it. You will need to generate 2 GUID’s and insert them into the
   IID_ISetGet and IID_IStatistics parts as shown below. To generate the GUIDs, select
   Project->Add to Project->Componenets and Controls, then choose Visual C++
   components, and then GUID generator. Make sure, the GUID format is 3. Static
   const. struct, as shown below. Hit copy button and then you can paste the GUID into
   your xInterface.h file. Replace the GUID <<name>> by the IID IID_ISetGet after
   you paste.
                                                                           13




The xInterface.h file will look as:

//----xInterface.h-----
#ifndef X_INTERFACE_H
#define X_INTERFACE_H

struct ISetGet : public IUnknown
{
    STDMETHOD_(void,set_a)(int) = 0;
    STDMETHOD_(int&, get_a)() = 0;
    STDMETHOD_(void,set_b)(int) = 0;
    STDMETHOD_(int&, get_b)() = 0;
    STDMETHOD_(void,set_c)(int) = 0;
    STDMETHOD_(int&, get_c)() = 0;
};
struct IStatistics : public IUnknown
{
    STDMETHOD_(void,compute_avg)(float &) = 0;
    STDMETHOD_(void, compute_min)(int &) = 0;
    STDMETHOD_(void,compute_max)(int &) = 0;
};


// {1ECFF0DC-466D-11D3-BABB-0050BAA11068}
static const IID IID_ISetGet =
{ 0x1ecff0dc, 0x466d, 0x11d3,
                    { 0xba, 0xbb, 0x0, 0x50, 0xba, 0xa1, 0x10, 0x68 } };

// {1ECFF0DD-466D-11D3-BABB-0050BAA11068}
static const IID IID_IStatistics =
{ 0x1ecff0dd, 0x466d, 0x11d3,
                                                                                      14


                   { 0xba, 0xbb, 0x0, 0x50, 0xba, 0xa1, 0x10, 0x68 } };

    #endif.


6. From the class wizard, add a new class to the project called xcom2, set the base class
   to CCmdTarget, and make sure you check the createable by type ID.




7. Add the following lines (shown in bold) to the definition of class xcom2 in xcom2.h
   file.

class xcom2 : public CCmdTarget
{
         DECLARE_DYNCREATE(xcom2)

private:
           int a, b, c;
           float avg;
           int min, max;

protected:
                   xcom2();     // protected constructor used by dynamic creation
8. Add the code for interface declarations in the bottom of xcom2.h file as shown below.

           DECLARE_MESSAGE_MAP()
           DECLARE_OLECREATE(xcom2)

           BEGIN_INTERFACE_PART(SetGet, ISetGet)
                 STDMETHOD_(void, set_a)(int m);
                 STDMETHOD_(int&, get_a)();
                 STDMETHOD_(void, set_b)(int m);
                 STDMETHOD_(int&, get_b)();
                 STDMETHOD_(void, set_c)(int m);
                 STDMETHOD_(int&, get_c)();
           END_INTERFACE_PART(SetGet)

           BEGIN_INTERFACE_PART(Statistics, IStatistics)
                                                                                         15

               STDMETHOD_(void, compute_avg)(float &);
               STDMETHOD_(void, compute_min)(int &);
               STDMETHOD_(void, compute_max)(int &);
         END_INTERFACE_PART(Statistics)

         // Generated OLE dispatch map functions
         //{{AFX_DISPATCH(xcom2)
                  // NOTE - the ClassWizard will add and remove member functions here.
         //}}AFX_DISPATCH
         // DECLARE_DISPATCH_MAP()             Comment this line
         DECLARE_INTERFACE_MAP()
   };

9. Include the “xInterface.h” file in the xcom2.cpp file as shown below:

   // xcom2.cpp : implementation file

   #include "stdafx.h"
   #include "testxcom2.h"
   #include "xInterface.h"
   #include "xcom2.h"

10. In the xcom2.cpp file, write the code for the constructor, comment the
    EnableAutomation line.

         xcom2::xcom2()
         {
         //           EnableAutomation();

                      a = 1; b = 2; c = 3;
                      // To keep the application running as long as an OLE automation
                      //       object is active, the constructor calls AfxOleLockApp.

                      AfxOleLockApp();
         }


11. Modify the last pat of xcom2.cpp file to add the information about the two interfaces
    as shown below.
   BEGIN_INTERFACE_MAP(xcom2, CCmdTarget)
      INTERFACE_PART(xcom2, IID_Ixcom2, Dispatch)
         INTERFACE_PART(xcom2, IID_ISetGet, SetGet)
         INTERFACE_PART(xcom2, IID_IStatistics, Statistics)
   END_INTERFACE_MAP()

   // {1ECFF0D6-466D-11D3-BABB-0050BAA11068}
   IMPLEMENT_OLECREATE(xcom2, "testxcom2.xcom2", 0x1ecff0d6, 0x466d, 0x11d3,
                                   0xba, 0xbb, 0x0, 0x50, 0xba, 0xa1, 0x10, 0x68)

   /////////////////////////////////////////////////////////////////////////////
   // xcom2 message handlers
                                                                                           16


12. Delete the Automation related declarations as shown below.
    (delete the following lines from xcom2.cpp file)
    BEGIN_DISPATCH_MAP(xcom2, CCmdTarget)
       //{{AFX_DISPATCH_MAP(xcom2)
              // NOTE - the ClassWizard will add and remove mapping macros here.
       //}}AFX_DISPATCH_MAP
    END_DISPATCH_MAP()

13. Write the code for all member functions of the two interfaces in the xcom2.cpp file.
    Type the following lines at the end of xcom2.cpp file.

// xcom2 message handlers

STDMETHODIMP_(ULONG) xcom2::XSetGet::AddRef()
{
     METHOD_PROLOGUE(xcom2,SetGet)
     return pThis->ExternalAddRef();
}

STDMETHODIMP_(ULONG) xcom2::XSetGet::Release()
{
     METHOD_PROLOGUE(xcom2,SetGet)
     return pThis->ExternalRelease();
}

STDMETHODIMP xcom2::XSetGet::QueryInterface(REFIID iid, LPVOID* ppvObj)
{
     METHOD_PROLOGUE(xcom2,SetGet)
     return pThis->ExternalQueryInterface(&iid,ppvObj);
}

STDMETHODIMP_(void) xcom2::XSetGet::set_a(int m)
{
     METHOD_PROLOGUE(xcom2,SetGet)
     (pThis->a) = m;
}

STDMETHODIMP_(int&) xcom2::XSetGet::get_a()
{
     METHOD_PROLOGUE(xcom2,SetGet)
     return pThis->a;
}

STDMETHODIMP_(void) xcom2::XSetGet::set_b(int m)
{
     METHOD_PROLOGUE(xcom2,SetGet)
     pThis->b = m;
}

STDMETHODIMP_(int&) xcom2::XSetGet::get_b()
{
     METHOD_PROLOGUE(xcom2,SetGet)
     return pThis->b;
}
                                                                              17

STDMETHODIMP_(void) xcom2::XSetGet::set_c(int m)
{
     METHOD_PROLOGUE(xcom2,SetGet)
     pThis->c = m;
}

STDMETHODIMP_(int&) xcom2::XSetGet::get_c()
{
     METHOD_PROLOGUE(xcom2,SetGet)
     return pThis->c;
}

// IStatistics interface functions
STDMETHODIMP_(ULONG) xcom2::XStatistics::AddRef()
{
           METHOD_PROLOGUE(xcom2,Statistics)
           return pThis->ExternalAddRef();
}

STDMETHODIMP_(ULONG) xcom2::XStatistics::Release()
{
     METHOD_PROLOGUE(xcom2,Statistics)
     return pThis->ExternalRelease();
}

STDMETHODIMP xcom2::XStatistics::QueryInterface(REFIID iid, LPVOID* ppvObj)
{
     METHOD_PROLOGUE(xcom2,Statistics)
     return pThis->ExternalQueryInterface(&iid,ppvObj);
}

STDMETHODIMP_(void) xcom2::XStatistics::compute_avg(float & res)
{
     METHOD_PROLOGUE(xcom2,Statistics)
     res = ((pThis->a) + (pThis->b) + (pThis->c)) / 3.0;
}

STDMETHODIMP_(void) xcom2::XStatistics::compute_min(int & mn)
{
     METHOD_PROLOGUE(xcom2,Statistics)
     if((pThis->a) < (pThis->b))
              mn = pThis->a; else mn = pThis->b;
     if ((pThis->c) < mn)
              mn = pThis->c;
}

STDMETHODIMP_(void) xcom2::XStatistics::compute_max(int & mx)
{
     METHOD_PROLOGUE(xcom2,Statistics)
     if((pThis->a) > (pThis->b))
              mx = pThis->a; else mx = pThis->b;
     if ((pThis->c) > mx)
              mx = pThis->c;
}
                                                                                         18


14. Build the testxcom2.dll

15. Register the dll by using regsvr32.exe as:

   Regsvr32 e:/vccprogs/testxcom2/debug/testxcom2.dll

   Examine the registery entries by running regedit program. Then through the
   edit->find menu, search for testxcom2. Then through the edit->findnext to go the
   classid for the testxcom2 class.




                     Creating a test Client Program
1. Create an MFC AppWizard(exe) type application. Choose an SDI application. Name
   the project testxcom2client.

2. Add the following line in StdAfx.h file:


   #include <afxwin.h>    // MFC core and standard components
   #include <afxext.h>   // MFC extensions
   #include <afxdisp.h>   // MFC Automation classes
   #include <afxdtctl.h>       // MFC support for Internet Explorer 4 Common Controls
   #ifndef _AFX_NO_AFXCMN_SUPPORT
   #include <afxcmn.h>                 // MFC support for Windows Common Controls
   #endif // _AFX_NO_AFXCMN_SUPPORT

   #include <afxole.h> // added

3. Add the statement AfxOleInit(); in the beginning of the InitInstance function in
   testxcomclient.cpp file.

4. Add a menu item under view called “testx com” by going through the resource view
   and double clicking on the menu resource.

5. Using the class wizard, write an event handler for testxcom menu in the view class.
   Type the code is shown below:

   void CTestxcom1clientView::OnViewTestxcom()
   {
       // TODO: Add your command handler code here
       CLSID clsid;
       LPCLASSFACTORY pClf;
       LPUNKNOWN pUnk;
       ISetGet * pSG;
       HRESULT hr;

       if ((hr = ::CLSIDFromProgID(L"testxcom2.xcom2",&clsid)) != NOERROR) {
                                                                             19

               AfxMessageBox("Unable to find Prog ID\n in Registery");
               return;
       }

       if ((hr = ::CoGetClassObject(clsid,CLSCTX_INPROC_SERVER,
                   NULL, IID_IClassFactory,(void **)&pClf)) != NOERROR) {
                  AfxMessageBox("Unable to find CLSID ID\n in Registery");
                  return;
       }

       pClf->CreateInstance(NULL,IID_IUnknown,(void **)&pUnk);
       pUnk->QueryInterface(IID_ISetGet,(void**)&pSG);

       pSG->set_a(5);
       int i1;
       i1 = pSG->get_a();
       CString s1;
       s1.Format("a = %d, b = %d, c = %d",i1,pSG->get_b(),pSG->get_c());
       AfxMessageBox(s1);

       IStatistics *pS;
       pSG->QueryInterface(IID_IStatistics,(void **)&pS);
       float f1;
       pS->compute_avg(f1);
       s1.Format("Average = %f",f1);
       AfxMessageBox(s1);

       PS->Release();
       pClf->Release();
       pUnk->Release();
       pSG->Release();
   }

6. Build and run the program.

				
DOCUMENT INFO
Shared By:
Categories:
Tags:
Stats:
views:4
posted:6/14/2012
language:English
pages:19