Click here to Skip to main content
15,861,168 members
Articles / Programming Languages / C++
Article

COM from scratch - PART TWO

Rate me:
Please Sign up or sign in to vote.
4.85/5 (44 votes)
17 Apr 200414 min read 224.5K   6K   134   48
An article about COM Library.

Introduction

In part one, some background information regarding COM technology was explained, and a simple example showed how a client can use a component's functionality through its interface. In this part, I'll guide the reader to separate the implementation of the component from its client such that the client is no longer bound to the component, and is able to create it through the class factory.

Part two-Breaking the chain

Serving components (Distribution)

Software component servers provide a way so that functionality can be reused more easily, besides they reduce memory overhead when several applications use the same functionality at the same time, because although each application gets its own copy of the data, they can share the code. By putting a component into a DLL, it's possible to make a kind of component distribution and the DLL becomes the component's server and will contain the implementations of interfaces supported by the component.

Building of the component's Server (DLL)

In the example, the client and the component were both in the same file, now they should be separated, the client will be in a .exe file, which loads the component into its address space in order to use it, and the component will be served by a DLL. The client should load the DLL into its process and create the component before it can get an interface pointer. If the client links to the CreateInstance() function in the DLL, all other functions of the component are accessible through an interface pointer. So, the solution is just to export the CreateInstance() function from the DLL such that the client can link explicitly to it on the fly. The DLL will be built from the command prompt using one of Microsoft’s command-line tools.

The following figure shows the Server files which are going to be used in making the DLL:

The Server files

Step 1:

Create a source file (Component.cpp) and put the definition and the implementation of the component's class into it.

Step 2:

Export the CreateInstance() function by adding the following piece of code at the end of the file:

Client and Server files

Step 3:

The linker should be informed that the CreateInstance function will be exported, and that can be done by using a module-definition file. A module-definition file is a file with the "def" extension, which contains information about exports, attributes and other information for linking an .EXE file (which has exports) or DLL. In the .def file, the CreateInstance function's export ordinal is chosen to be 1. The following part shows the contents of this file.

;component.def 
; Component module-definition file
; LIBRARY Component.dll 
DESCRIPTION 'Components windows dynamik library' 
EXPORTS ; Explicit exports can go here 
CreateInstance @1 PRIVATE

Step 4:

The interface identifier and the interface definition should be known for both the client and the component, and they can be put into two separate files, which are shared between the client and the component. Create another source file (GUID.cpp), which can hold the interface ID:

//
// GUID.cpp - Interface ID
//
#include "objbase.h"
extern "C" 
{

extern const IID IID_IComponent = 
{ 0x853b4626, 0x393a, 0x44df, //Data1,Data2,Data3
{ 0xb1, 0x3e, 0x64, 0xca, 0xbe, 0x53, 0x5d, 0xbf } };  //Data4

    // The extern is required to allocate memory for C++ constants.
}

Step 5:

Create a header file (interface.h) with the following content:

//
// Interface.h
//
interface IComponent : IUnknown
{
    virtual void __stdcall Print(const char* msg) = 0 ;
} ;


// Forward references for GUID
extern "C"
{
    extern const IID IID_IComponent ;

}

Step 6:

Create a "make" file containing the options for making the DLL with the following content:

#Makefile 

################################################################################
# Compiler options:
# /c     compile without linking
# CL     cl.exe is a 32-bit tool that controls the Microsoft C 
#        and C++ compilers and linker. 
#        The compilers produce Common Object File Format
#        (COFF) object (.obj) files. 
#        The linker produces executable (.exe) files
#        or dynamic-link libraries (DLLs).
#
##################################
# Linker options:
#
# /DEF     Passes a module-definition (.def) file to the linker
# /DEBUG   Creates debugging information
# /DLL     Builds a DLL


CPP_FLAGS=/c /MTd /Zi /Od /D_DEBUG
EXE_LINK_FLAGS=/DEBUG
DLL_LINK_FLAGS=/DLL /DEBUG

LIBS=UUID.lib

#############################################
# Targets:
# CodeProject is just a pseudotarget 
#
CodeProject :  component

component : Component.dll 


#########################################
# Shared source files:
#

GUID.obj : GUID.cpp 
    Cl $(CPP_FLAGS) GUID.cpp

##########################################
# Component source files:
#

Component.obj : Component.cpp Interface.h
    Cl $(CPP_FLAGS) Component.cpp


########################################
# Link component:
#

Component.dll : Component.obj    GUID.obj Component.def 
    link $(DLL_LINK_FLAGS) Component.obj GUID.obj $(LIBS) /DEF:Component.def

Step 7:

Make the DLL from the Command line by using Microsoft Program Maintenance Utility (NMAKE.EXE). This program is a tool that can build projects based on commands contained in a description file.

  1. Open the Command window (Click Start choose Run menu item and then write cmd in the dialog box).
  2. From the Command line, change to the directory which contains the server files.
  3. From the Command line, enter: nmake /f makefile.

      The NMAKE utility will create the DLL in the same folder:

      The Output window

    Building the Client

    The following figure shows the files which are used in making the Client. The Client will be built within the Visual C++ development environment.

    The Client files

    Step 1:

    Using the AppWizard, create a simple Win32 Console application and choose an empty project.

    Step 2:

    Create a new source file (Create.cpp) and make a function which takes the DLL's name as parameter, loads the "DLL" and then calls the exported function CreateInstance(). The function's return value would be the return value of the CreateInstance() function, which is an IUnknown interface pointer. In order to link explicitly to the "DLL", the function calls the GetProcAddress function to get the address of the exported function. The GetProcAddress function takes two parameters. The first parameter is a handle to the "DLL" module and the second parameter is the "DLL" name. By calling the LoadLibrary function, it would be possible to obtain the module handle.

    // Create.cpp
     #include "iostream.h"
     #include "unknwn.h"//IUnknown definition file.
     #include "Create.h"
    
    typedef IUnknown* (*CREATEFUNCPTR)();
     //////////////////////////////////////////
     IUnknown* CallCreateInstance(char* dllname) 
    { 
     //-----------------------------------------------------------------//
     // Load dynamic link library into client's process.
     //Loadlibrary maps a DLL module and return a handle 
     //that can be used in GetProcAddress 
     //to get the address of a DLL function 
     //-----------------------------------------------------------------// 
        HMODULE hm = ::LoadLibrary(dllname);
        if (hm ==NULL)
            return NULL; 
        // Get  the address of CreateInstance function. 
        CREATEFUNCPTR Function = 
            (CREATEFUNCPTR)::GetProcAddress(hm, "CreateInstance"); 
        if (Function == NULL) 
            return NULL; 
        return Function();
     }

    Step 3:

    Create a new header file (Create.h) with the following contents:

    // Create.h
    IUnknown* CallCreateInstance(char* dllname) ;

    Step 4:

    Create a new header file (interface.h) with the following contents:

    //
    // Interface.h
    //
    interface IComponent : IUnknown
    {
        virtual void __stdcall Print(const char* msg) = 0 ;
    } ;
    
    
    // Forward references for GUID
    extern "C"
    {
        extern const IID IID_IComponent ;
    
    }

    Step 5:

    Create another source file (GUID.cpp), which can hold the interface ID:

    // GUID.cpp - Interface ID
    #include "objbase.h"
    extern "C" 
    {
    extern const IID IID_IComponent = 
    { 0x853b4626, 0x393a, 0x44df, //Data1,Data2,Data3
    { 0xb1, 0x3e, 0x64, 0xca, 0xbe, 0x53, 0x5d, 0xbf } };  //Data4
    // The extern is required to allocate memory for C++ constants.
    }

    Step 6:

    Create a source file (Client.cpp) and implement the main function. Call the function made in step 2, in order to instantiate the component and use its methods:

    //--------//
    // Client
    //--------//
    int main()
    {
        HRESULT hr ;
        
        // Get the name of the component to use.
        char dllname[20];
        cout << "Enter the filename of component's server [component.dll]:";
        cin  >> dllname;
        ...
    
        // calling the CreateInstance function in the
        // DLL in order to create the component.
        TRACE("Getting an IUnknown interface pointer...") ;
        IUnknown* pIUnknown = CallCreateInstance(dllname) ;
       
       ...
       
        IComponent* pIComponent ;
        hr = pIUnknown->QueryInterface(IID_IComponent, (void**)&pIComponent);
        
        if (SUCCEEDED(hr))
        {
           ...
            pIComponent->Print("COM from scratch.") ; 
            //using the component's functionality
    
            pIComponent->Release() ;
           ...
        }
       ...
        
        return 0 ;
    }

    Step 7:

    Put the component's server (Component.dll) into the same directory of the client. Now the client is able to load the DLL into its address space and get the address of the CreateInstance function by using LoadLibrary and GetProcAddress functions. Build and run the client program.

    The following screen shot shows the client application, after loading the DLL and calling the component's Print method:

    The output window

    Conclusion: Distribution of COM components by servers makes it easy for clients to reuse components' functionality.

    Extending component's functionality without rebuilding Clients

    One of the advantages of COM components is that it is easy to extend functionalities of an application without rebuilding. As long as an interface is not changed, the client application can still use the component, although its functionality is extended by new changes to its methods. In order to show this advantage of COM components, it's better to view the problem of rebuilding of client applications by a simple example. In the following, a DLL is linked to a client application, you may notice that whenever changes are made to the DLL (for example, by adding a new member variable to a class in the DLL and modifying a member function), the client application will fail to run if it is not rebuild.

    Step 1: Make the DLL

    1. Using the AppWizard, create a new project with type Win32 Dynamic Link Library:

      AppWizard

    2. In step two from the wizard, choose 'A simple DLL project' and click Finish:

      Step 1

    3. Create a new header file and define a class with a member variable and a member function which can be exported from the DLL:
      //myclass.h
      
      class CMyclass
       {
       long m_cRef; 
       public:
       _declspec(dllexport) void Print(const char*msg);
      };
    4. Create a source file (myclass.cpp) and implement the member function:

      Implementation of Print method

    5. Build the DLL.

    Step 2: Make the Client and load the DLL

    1. Make a new empty project of type Win32 Console Application.
    2. Create a new source file in order to load and test the DLL (client.cpp).
    3. Include the header, which contains the class definition in the DLL:
      //client.cpp
      
      #include"iostream.h"
      #include"..\DLL\myclass.h"
      
      /////////////////////////////////
      void  main()
      { 
       CMyclass  classObj; 
       classObj.Print("COM from scratch.");
      }
    4. Add the DLL.lib file from the DLL project to the Client project (Project->Add to Project->Files, and then choose Library Files (.lib) as the file type):

      Implementation of Print method

    5. Copy the DLL into the same folder of the client's executive file. If you build and run the client application, "COM from scratch." will be written on the screen, and the DLL will be loaded without any problem.

    Step 3: Viewing the Rebuild problem

    1. Return to the implementation of the CMyclass class and add a new member variable:
      //myclass.h
      class CMyclass
      {
      long m_cRef;
      int m_i; // a new member variable
      public:
              _declspec(dllexport) void Print(const char* msg);
      };
    2. Modify the implementation of the Print member function:

      Reimplementation of Print method

    3. Rebuild the DLL and copy it into the same folder of the client's executive file.
    4. Execute the client application with the new version of the DLL without rebuilding, and if you run the client application, you will confront with a problem, and rebuilding of the client application solves this problem. Rebuilding of client applications is a big problem, because it requires source codes. Now if you make the same changes to the component's class from the example in part one and rebuild the DLL and test it with the client application (without rebuilding it), you will notice that the client runs without any problem, although a new member variable is added to the component's class and the implementation of the Print method has been changed. Since method calls in COM components are indirectly and through their interfaces, there will not be any problem if the methods are modified.

    Conclusion: COM extends functionalities of applications without rebuilding.

    Improvement of the example

    In the example, although the client and the component have been separated, the client is closely related to the component's implementation and should know about the DLL's name, and changing the DLL's name will affect the client. An improvement is that we can move the component from one DLL to another or to other directories. The solution is to replace the CallCreateInstance function with a COM Library function called CoCreateInstance. COM Runtime Library is an integral component of the Windows operating system, which provides the means for clients to locate and instantiate COM objects. COM class objects can be identified by CLSIDs (globally unique identifiers), which are used in order to locate and create an instance of an object. Once the CLSID is obtained, a client application submits the CLSID to the COM run-time library to load the COM object and retrieve an interface pointer. Using the CLSID and the registry, CoCreateInstance locates the specified object, creates an instance of that object, and returns an interface pointer to that object. In order to use CoCreateInstance to create an object, the object must be registered with the system.

    CoCreateInstance

    The COM Library contains this function. The easiest way of creating a component is by the use of CoCreateInstance function. CoCreateInstance uses a class factory when it creates a component. It takes a CLSID, creates an instance of the corresponding component, and returns an interface for this instance of the component. CoCreateInstance takes 4 in parameters and 1 out parameter (IUnknown*). By passing an IID to CoCreateInstance, the client doesn't need to call QueryInterface on the component after creating it.

    CoCreateInstance's parameters:

    • The first parameter is the CLSID of the object.
    • The second parameter is used to aggregate the object as part of another object.
    • The third parameter specifies the execution context of the object.
    • The 4th parameter is the IID (Interface ID) of the requested interface.
    • The last parameter which is an out parameter is an interface pointer to the created object.

    Registration of components

    Objects that can be created with CoCreateInstance must also be registered with the system. Registration maps a CLSID to the automation component file (.dll or .exe) in which the object resides. If clients will want to obtain CLSIDs at run-time, there must be a way to dynamically locate and load CLSIDs for accessible objects. Furthermore, there has to be some system-wide method for the COM Library to associate a given CLSID (regardless of how the client obtained it) to the server code that implements that class. In other words, the COM Library requires some persistent store of CLSID-to-server mappings that it uses to implement its locator services. The COM implementation on Microsoft Windows uses the Windows system registry as a store for such information. In that registry, there is a root key called "CLSID" under which servers are responsible to create entries that point to their modules. Usually, these entries are created at installation time by the application's setup code, but can be done at run-time if desired. When a server is installed under Windows, the installation program will create a sub-key under "CLSID" for each class the server supports, using the standard string representation of the CLSID as the key name. So the primary entry for a CLSID is a sub-key under CLSID key, which is the CLSID spelled in hex digits within braces. We may also want to associate a CLSID with what is called a programmatic identifier or ProgID, which effectively identifies the same class. A ProgID is a text string without spaces that can be used instead of the CLSID string. The standard ProgID format is <Vendor>.<Component>.<Version>, such as Codeproject.Cmpnt1.1. This format is reasonably unique, and if everyone follows it, there will generally not be a collision. There is also the "VersionIndependentProgID", which has the same format without the version number. Both the ProgID and the VersionIndependentProgID can be registered, with a human-readable name as a value, below the root key. The VersionIndependentProgID is mapped to the ProgID, which is mapped to the CLSID. To create registry entries, you can either write code or create a REG file and simply run it to merge its entries with the registry. The following image shows the registry entries for the Component1 from the Demo Application.

    Component's CLSID

    The Class Factory

    The CoCreateInstance function does not create COM components directly. Instead, it creates a component called class factory, which then creates the desired component. A class factory is a component that creates other components. A particular class factory creates components that correspond only to a single, specific CLSID. The client uses interfaces supported by the class factory for controlling how the class factory creates each component. The standard interface for creating components is IClassFactory interface. IClassFactory like other COM interfaces is derived from IUnknown interface and has two methods:

    • CreateInstance, which creates an un-initialized object of a specified CLSID.
    • LockServer, which locks the object's server in memory, allowing new objects to be created more quickly.

    In the following, a class factory is defined in order to create the COM component in the example:

    ///////////////////////////////////////////////////////////
    //
    // Class factory
    //
    class CFactory : public IClassFactory
    {
    public:
        // IUnknown
        virtual HRESULT __stdcall QueryInterface(const IID& iid,void** ppv) ;
        virtual ULONG   __stdcall AddRef() ;
        virtual ULONG   __stdcall Release() ;
    
        // IClassFactory
        virtual HRESULT __stdcall CreateInstance(IUnknown* pUnkOuter,
                                                 const IID& iid,
                                                 void** ppv) ;
        virtual HRESULT __stdcall LockServer(BOOL bLock) ; 
    
        // Constructor
        CFactory() : m_cRef(1) {}
       // Destructor
        ~CFactory() {}
    
    private:
        long m_cRef ;
    } ;
    
    
    //
    // Class factory IUnknown implementation
    //////////////////////////////////////////////////////////////////////
    HRESULT __stdcall CFactory::QueryInterface(const IID& iid,LPVOID* ppv)
    {
        if ((iid == IID_IUnknown) || (iid == IID_IClassFactory))
            *ppv = static_cast<IClassFactory*>(this) ; 
        else
        {
            *ppv = NULL ;
            return E_NOINTERFACE ;
        }
        reinterpret_cast<IUnknown*>(*ppv)->AddRef() ;
        return S_OK ;
    }
    
    ///////////////////////////////////
    ULONG __stdcall CFactory::AddRef()
    {
        return ::InterlockedIncrement(&m_cRef) ;
    }
    
    
    ////////////////////////////////////
    ULONG __stdcall CFactory::Release() 
    {
    
        if (::InterlockedDecrement(&m_cRef) == 0)
        {
            delete this ;
            return 0 ;
        }
        return m_cRef ;
    }
    
    
    //
    // IClassFactory implementation
    ///////////////////////////////////////////////////////////////
    HRESULT __stdcall CFactory::CreateInstance(IUnknown* pUnkOuter, 
                                              const IID& iid,void** ppv) 
    {
    
        HRESULT hr;
        if (pUnkOuter != NULL)
        {
            return CLASS_E_NOAGGREGATION ;
        }
        CComponent* pComponent = new CComponent ;
        if (pComponent == NULL)
        {
    
            return E_OUTOFMEMORY ;
        }
    
        // Get the requested interface.
        hr = pComponent->QueryInterface(iid,(void**) ppv) ;
    
      
    if(FAILED(hr))
        pComponent->Release() ;
    
        return hr ;   
    }
    //-----------------------------------------------------------------------//
    // LockServer
    // Called by the client of a class object to keep a server open in memory,
    // allowing instances to be created more quickly.
    //-----------------------------------------------------------------------//
    ///////////////////////////////////////////////////
    HRESULT __stdcall CFactory::LockServer(BOOL bLock) 
    {
        return S_OK ;
    }

    Before going to more details, it's better to have an overview about creation of the component via COM Library:

    1. The client calls CoCreateInstance, which is implemented in COM Library.
    2. CoCreateInstance is implemented using CoGetClassObject function.
    3. CoGetClassObject calls DllGetClassObject, which is implemented in DLL server and its job is to create the class factory for the component.
    4. DllGetClassObject queries the class factory for IClassFactory interface, which is returned to CoCreateInstance function.
    5. CoCreateInstance uses IClassFactory interface to call its CreateInstance method.
    6. The IClassFactory::CreateInstance(...) uses the new operator to create the component and it queries the component for its interface.
    7. After getting the component's interface, CoCreateInstance releases the class factory and returns an interface pointer to the client.
    8. The client uses the interface pointer in order to call the Print method of the component and uses its functionality.

    The following image illustrates these steps:

    Creation of the Component vis COM Library

    So, in order to improve the example, we need to:

    1. Implement the CFactory methods.
    2. Implement the DllGetClassObject in the component server or the DLL instead of CreateInstance function.
    3. Write the necessary code (or use a registration file) in order to register the component in the Windows registry system.

    It's also easier to make the DLL within the Visual C++ development environment. In the following, these steps will be implemented:

    Step 1:

    • Using the AppWizard, create a new project (with the name "Component") for the DLL, and select MFC AppWizard (DLL). Consider that the DLL now will reside in a directory (C:\CodeProject) different form its client:

      AppWizard

    • In step one, select "MFC Extension DLL" and click the "Finish" button:

      Step 1 in AppWizard

    • Open the Component.cpp file and replace its content with the following part:
      // Component.cpp : Defines the initialization routines for the DLL.
      //
      
      #include "stdafx.h"
      #include <afxdllx.h>
      #include "interface.h"
      #include <objbase.h>
      #include "iostream.h"
      #ifdef _DEBUG
      #define new DEBUG_NEW
      #undef THIS_FILE
      static char THIS_FILE[] = __FILE__;
      #endif
      //
      // Component.cpp
      
      
      /////////////////////////////////////////////
       BOOL APIENTRY DllMain(HINSTANCE InsModule, 
                            DWORD dwReason, 
                            void* lpReserved)
      {
      
          return TRUE;
      }
    • Copy and paste the definitions of the component's class and the class factory and their implementations in the source file Component.cpp, and ignore the export of the CreateInstance function, because the class factory is going to create the component.

    Step 2: Getting the Class Factory - DllGetClassObject

    The DllGetClassObject function will be called from within the CoGetClassObject function, when the class context is a DLL, and as mentioned before, its job is to create the class factory for the component. Implement this function in the Component.cpp file:

    /////////////////////////////////////////////////////////////////////////
    STDAPI DllGetClassObject(const CLSID& clsid, const IID& iid, void** ppv)
    
    {
    
     if (clsid != CLSID_Component)
     return CLASS_E_CLASSNOTAVAILABLE;
     // Create class factory.
     CFactory* pFactory = new CFactory ;
     if (pFactory == NULL)
     return E_OUTOFMEMORY;
     // Get requested interface.
     HRESULT hr = pFactory->QueryInterface(iid, ppv); 
     pFactory->Release(); 
     return hr;
    
    }

    Compile and build the DLL (Component.dll).

    Step 3: Registration

    Using the GUIDGEN.EXE, create a CLSID for the Component's class:

    {49BF12F1-5041-48da-9B44-AA2FAA63AEFB}
      static const GUID CLSID_Component = 
      { 0x49bf12f1, 0x5041, 0x48da, 
           { 0x9b, 0x44, 0xaa, 0x2f, 0xaa, 0x63, 0xae, 0xfb } };

    Create a file with ".reg" extension (component.reg) in order to create registry entries for the component (using the CLSID):

    REGEDIT
     HKEY_CLASSES_ROOT\Codeproject.Component.1 = 
                Codeproject Component Version 1.0
     HKEY_CLASSES_ROOT\Codeproject.Component.1\CLSID = 
                {49BF12F1-5041-48da-9B44-AA2FAA63AEFB}
     HKEY_CLASSES_ROOT\Codeproject.Component = Codeproject Component 
     HKEY_CLASSES_ROOT\Codeproject.Component\CurVer = Codeproject.Component.1
     HKEY_CLASSES_ROOT\CLSID\{49BF12F1-5041-48da-9B44-AA2FAA63AEFB} = 
                Codeproject Component 1.0
     HKEY_CLASSES_ROOT\CLSID\{49BF12F1-5041-48da-9B44-AA2FAA63AEFB}\InprocServer32 = 
                c:\codeproject\component.dll
     HKEY_CLASSES_ROOT\CLSID\{49BF12F1-5041-48da-9B44-AA2FAA63AEFB}\ProgID = 
                Codeproject.Component.1
     HKEY_CLASSES_ROOT\CLSID\{49BF12F1-5041-48da-9B44-AA2FAA63AEFB}\
                VersionIndependentProgID = Codeproject.Component

    Activate the registry file by clicking on that. After running the registry file, the entries will be stored in Windows registry system. The following image shows these entries:

    Registry entries for the Component

    That's it; the chain now is totally broken to pieces. Check it out with the client.

    The Client

    Now, although the component's server (component.dll) resides in the directory C:\codeproject, the client can easily load it and use its functionality through the class factory and COM Library, and this is the way COM components usually are created and used by their clients. The following shows how the client uses the component through the COM Library:

    //-----------//
    // Client
    //-----------//
    void main()
    
    {
        HRESULT hr;
        IUnknown* pIUnknown;
        IComponent* pIComponent;
        IClassFactory* pIClassFactory;
    
        ::CoInitialize(NULL);
        /*
        //Once the CoCreateInstance is called, the component 
        //will be created and the client can not 
        //control it, that's why CoCreateInstance is inflexible 
        //and the solution is to call CoGetClassObject function
        hr = ::CoCreateInstance(CLSID_Component,NULL,
                CLSCTX_INPROC_SERVER,IID_IUnknown,(void**)&pIUnknown) ; 
        if (SUCCEEDED(hr))
            {
                hr=pIUnknown->QueryInterface(IID_IComponent,(void**)&pIComponent);
                if(SUCCEEDED(hr))
                   pIComponent->Print("COM from scratch.");
            }
    
         */ 
        //-------------------------------//
        // improvement of the client code
        //------------------------------//
        // By calling the CoGetClassObject function, the client can control
        // creation of the component
        hr=CoGetClassObject(CLSID_Component,CLSCTX_INPROC_SERVER, 
                     NULL,IID_IClassFactory,(void**)&pIClassFactory);
        if (SUCCEEDED(hr))
        {
            hr=pIClassFactory->CreateInstance(NULL,
                    IID_IComponent,(void**)&pIComponent);
            if(SUCCEEDED(hr))
               pIComponent->Print("COM from scratch.");
        }
    
        ::CoUninitialize ();
    
    }

    Part three is explained in the next article.

    License

    This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

    A list of licenses authors might use can be found here


    Written By
    Software Developer
    Denmark Denmark
    This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

    Comments and Discussions

     
    Questionwhere does IID_IClassFactory get its value Pin
    dbrower25617-May-08 13:17
    dbrower25617-May-08 13:17 
    AnswerRe: where does IID_IClassFactory get its value Pin
    Sharjith26-Sep-08 0:54
    professionalSharjith26-Sep-08 0:54 

    General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

    Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.