Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

COM Interop using managed C++

0.00/5 (No votes)
17 Oct 2001 1  
A demoonstration of COM Interop, showing early and late binding to a COM component

Abstract

This sample demonstrates COM Interop using managed C++. Both early and late binding are demonstrated, and the utility TlbImp is used to import the type library from an unmanaged COM object

Introduction.

One of the design goals of .NET is 100% compatibility with COM clients and servers.

This is a good news for developers, because many companies have a lot of software based on COM technologies. Migrating to .NET still allows you to reuse all your COM objects without modifying them.

.NET interoperability supports both early and late binding.

1. Early binding.

With early binding you import complete type information about COM class at compile time. Compiler takes this information from metadata assembly, that is generated for COM class. The easest way to create metadata assembly from the exist COM type library is to use utility called TlbImp.exe.

TlbImp has the following command line syntax (some of the options are not shown, refer to MSDN for complete syntax):

TlbImp TlbFile /out: file 
  • TlbFile can be the name of any file containing a COM type library. It can be .tlb, .dll, .odl, .ocx, .exe file or any other file format with a type library embedded as a resource.
  • /out: file - The name of the output file that the metadata definitions will be written to.

This demo uses the DClock COM object from the TimeServer.dll ATL server (do not forget to register dll using regsvr32.exe utility). This class has only one method GetTime that takes one parameter bDate of type short and returns a string with current time. The time format is: hh:mm:ss. If bDate is not 0, then return string has date information as well, and has a format: yy/mm/dd hh/mm/ss.

To create a metadata assembly for DClock class, use the following command line:

tlbimp.exe timeserver.dll /out:timeserverlib.dll

In our sample we use a managed C++ class as the client. To create the client you can use the Visual Studio AppWizard. Choose 'C++ managed Console application' as the target, I called the project DClockClient, so the main source is called DClockClient.cpp

Clients that use COM objects should import metadata. In C++ you should use #using directive. The syntax is:

#using "filename"

where filename is the name of the metadata assembly.

Metadata should be accessible at compile time as well as at run time, so for simplicity lets place timeserverlib.dll in the folder where your client code is located, as well as in the debug and release subfolders. As an alternative you can put the assembly in the folder with the client executable and reference it from source using a relative path.

Add the following line to DClockClient.cpp file

#using "timeserverlib.dll"

At runtime the program finds the metadata assembly, uses this information to create a Runtime Callable Wrapper (RCW) object that is responsible for:

  • Preserving the objects identity
  • Maintaining the objects lifetime
  • Proxying custom interfaces
  • Marshaling method calls
  • Consuming selected interfaces

RCW itself is a managed object so it is garbage collected. To import the library namespace, you should use the using namespace directive. Add the following line to your code

using namespace TimeServerLib;

Now you can use object the same way you use any managed CLR class.

The following code demonstrates this technique.

#using <mscorlib.dll>
#using "timeserverlib.dll"

using namespace System;
using namespace TimeServerLib;

int main(void)
{
    DClock *pClock;
    String *strTime;

    pClock = new DClock;
    strTime = pClock->GetTime(0);

    Console::WriteLine(strTime);
    
    return 0;
}

Let's create a managed C++ class to wrap the COM object. From a functionality point of view the wrapper class in this example does nothing and you can achieve the same result by instantiating the COM object directly in the main function. This just shows you another possible way of using COM objects.

To create a managed C++ class to wrap com object, add following code to DClockClient.cpp file:

__gc class CTimeEB
{
public:
    // Constructor.

    CTimeEB()
    {
        // Create instance of DClock COM object.

        // Realy we create Runtime Callable Wrapper (RCW)

        // which is responsible for DClock instaniating.

        pClock = new DClock;
    }

    ~CTimeEB()
    {
        // Our object is an instance of managed, means garbage collected

        // class, so it is not neccessary to call delete for our object.

        // It is just demonstrates that we can release its resources at

        // a well-defined point. 

        delete pClock;
    }

    // Returns string that has current time.

    // If bAddDate is not 0, it returns date and time,

    // if bAddDate is 0, returns time only.

    String* PrintCurrentTime(short bAddDate)
    {
        // Get current time from COM server and concatinate it

        // with predefined string.

            return String::Concat(S"Current time is ",  
                                  pClock->GetTime(bAddDate));
    }

private:
    DClock *pClock;
};

CTimeEB is a managed C++ class so there is nothing special in using it in the code.

2. Late binding.

Late binding is implementing by using the Namespace Reflection mechanism.

To import metadata at runtime the Type class from the System namespace is used. The Type class has a static method GetTypeFromProgID("ProgID") that returns a Type object for a COM object based on its ProgID.

To obtain an instance of a COM object we use the static member CreateInstance(Type type) of the Activator class from System namespace. We pass the Type object that we got at the previous step as a parameter.

Now we can call the methods of our COM object using the InvokeMethod member of our Type object.

The following code demonstrates this technique:

#using <mscorlib.dll>
#using "timeserverlib.dll"

using namespace System;
using namespace TimeServerLib;

int main(void)
{
    Type *typ;
    Object *obj;
    Object* args[];
    String *strTime;

    using namespace Reflection;

    // Get Type object for COM server. It uses ProgID of DClock class.

    typ = Type::GetTypeFromProgID("TimeServer.DClock");

    // obtain instance of COM object.

    obj = Activator::CreateInstance(typ);

    // Create array of parameters.

    args = new Object*[1];


    // Set parameter.

    args[0] =__box(1);

    // Call COM object method by its name.

    strTime = (String*)typ->InvokeMember("GetTime", 
                           BindingFlags::InvokeMethod, 
                           Type::DefaultBinder, obj, args);  

    Console::WriteLine(strTime);

    return 0;
}

The TimeLB class demonstrates reusing COM objects in a managed C++ classes.

// Class to demonstrate late binding.

__gc class CTimeLB
{
public:
    // Constructor

    CTimeLB()
    {
        // Get Type object for COM server. It uses ProgID of DClock class.

        typ = Type::GetTypeFromProgID("TimeServer.DClock");

        // obtain instance of COM object.

        obj = Activator::CreateInstance(typ);

        // Create array of parameters.

        args = new Object*[1];
    }

    // Returns string that has current time.

    // If bAddDate is not 0, it returns date and time,

    // if bAddDate is 0, returns time only.

    String* PrintCurrentTime(short bAddDate)
    {
        String *strTime;

        // Set parameter.

        args[0] =__box(1);

        // Call COM object method by its name.

        strTime = (String*)typ->InvokeMember("GetTime",
                                             BindingFlags::InvokeMethod, 
                                             Type::DefaultBinder, obj, args);  

        // Concatinate current time with predefined string.

        return String::Concat(S"Current time is ", strTime);
    }

private:
    Type *typ;
    Object *obj;
    Object* args[];
};

History

Oct 18 2001 - updated for .NET beta 2

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