Click here to Skip to main content
11,647,659 members (74,863 online)
Click here to Skip to main content

C++/CLI Library classes for interop scenarios

, 20 Jun 2006 Ms-PL 82.8K 43
Rate this:
Please Sign up or sign in to vote.
The article takes a brief look at some not so commonly used classes such as auto_handle, lock, and ptr.

Introduction

Visual C++ 2005 comes with a bunch of helper classes that provide support for smart pointers, synchronization support, and COM wrappers. Most of them are very handy for interop scenarios, and this article will show examples on using these classes.

The auto_handle class

C++/CLI has syntactic support for deterministic disposal - and you can seemingly declare managed objects on the stack. Now, while this is extremely handy for most scenarios, sometimes, you end up with methods (mostly those in the BCL) that return handles to objects. This forces us to use the ^ syntax and to manually remember to call delete once the object is not required any more. The following code shows an example of one such method.

void Demo1A()
{
    StreamWriter^ sw;
    try
    {
        sw = File::CreateText("d:\\temp.txt");
        sw->WriteLine("This is a line of text");
    }
    finally
    {
        delete sw;
    }
}

This is where the auto_handle class comes in handy. Add the following #include declaration to your code.

#include <msclr\auto_handle.h>

Now, you can rewrite the previous method as follows.

void Demo1B()
{
    msclr::auto_handle<StreamWriter> sw = File::CreateText("d:\\temp.txt");
    sw->WriteLine("This is a line of text");        
}

The auto_handle class overloads the -> operator and returns the underlying handle. So we can call StreamWriter methods just as if we were using a StreamWriter object. And when the destructor is called, delete is called on the underlying handle. If you need to persist the StreamWriter beyond the present scope, you can free the handle from the auto_handle object by calling release on it, as shown below.

sw.release();

Other operator overloads include those on =, !, and bool, so you can treat it like a pointer. And there's also a swap helper function that swaps handles between two auto_handle objects.

The gcrootand auto_gcrootclasses

These two are probably quite well known and very commonly used, specially gcroot, since it existed in the old syntax too. But for the sake of completion, I include these here too. The gcroot template class wraps the BCL GCHandle class and allows us to declare and use a managed object as a member of a non-CLI class. The following code snippet shows a demonstration of the use of this class.

class Demo2A
{
    msclr::gcroot<StreamWriter^> m_sw;
public:
    Demo2A()
    {
        m_sw = File::CreateText("d:\\temp.txt");
    }
    ~Demo2A()
    {
        delete m_sw;
    }
};

Notice how, I had to manually delete the StreamWriterobject in the destructor. Using the auto_gcrootclass, we can actually avoid having to do that on our own. Here's a modified class that uses auto_gcroot.

class Demo2B
{
    msclr::auto_gcroot<StreamWriter^> m_sw;
public:
    Demo2B()
    {
        m_sw = File::CreateText("d:\\temp.txt");
    }
};

Well, now the destructor is gone. For most purposes, you should use the auto_gcrootclass instead of the gcrootclass, for obvious reasons. Remember to #include <msclr\gcroot.h> or <msclr\auto_gcroot.h> appropriately.

The _safe_bool class

_safe_boolis a value classthat can be used in place of boolwhen providing an operator overload for bool. It prevents an implicit conversion to any integral type. Consider the following class.

ref class Demo3A
{
public:
    operator bool() 
    {
        return true;
    }
};

Now the following code would compile fine, even when it may not have been intended to do so.

Demo3A a;
    int y = a;

Here's how you would rewrite the class using a _safe_bool.

ref class Demo3B
{
public:
    operator msclr::_detail_class::_safe_bool() 
    {
        return msclr::_detail_class::_safe_true;
    }
};

Now the following code will not compile.

Demo3B a;
    int y = a;

You can also treat it like a bool, for most purposes.

if(a == msclr::_detail_class::_safe_false)
    {
    }

Both auto_handleand auto_gcrootuse _safe_boolfor their booloperator overloads. You need to #include <msclr\safebool.h> to use this class.

The ptr template - a COM wrapper class

This class is quite handy when you need to use a COM object as a member of a CLI class. The following #include is needed for this class.

#include <msclr\com\ptr.h>

The below code sample shows how to use the class.

ref class ManLink
{
private:
    msclr::com::ptr<ILink> lnkptr;
public:
    ManLink()
    {
        lnkptr.CreateInstance(__uuidof(Link)); // Create COM object
    }
    void Resolve()
    {
        lnkptr->ResolveLink(. . .); // Invoke COM method
    }
};

The destructor will release the COM object (so you don't need to worry about it). There are several methods provided such as Attach which allows you to associate a COM pointer with a com::ptr object, Detach which makes the com::ptr object give up ownership of the COM object, and Releasewhich releases the COM object. Note that Detach, increases the ref count before returning the interface pointer, and you are then responsible for releasing the object, once you are done using it.

The lock class

C# has the lockkeyword which is internally implemented using the BCL Monitorclass. C++/CLI does not have equivalent syntactic support, but the support library includes the lockclass which (like the C# lock keyword) internally uses the Monitor class. You need to #include the following header file.

#include <msclr\lock.h>

Now, here's a sample showing how the lock class can be used.

ref class Demo4
{
public:
    void DoSafeStuff()
    {
        msclr::lock lk(this);
        Console::WriteLine("Starting work on thread {0}", 
            Thread::CurrentThread->ManagedThreadId);
        Thread::Sleep(300); // simulate complex task
        Console::WriteLine("Work ended on thread {0}", 
                Thread::CurrentThread->ManagedThreadId);
    }
};

The lock class also comes with methods such as is_locked which returns true if a lock is held, a release method that releases the lock, an acquire method that gets a lock (and throws an exception if the lock could not be obtained), and a try_acquire method which is similar except that it does not throw an exception (instead it returns a bool). The following code shows how an instance of the above class is used from multiple threads, and how try_acquireis used to wait until all the threads are done executing.

int main(array<System::String ^> ^args)
{
    Demo4 d;
    msclr::lock lk(%d,msclr::lock_later); // defers locking
    for(int i=0; i<5; i++,(
        gcnew Thread(
        gcnew ThreadStart(%d, &Demo4::DoSafeStuff)))->Start()); 
    while(!lk.try_acquire(300));
    Console::WriteLine("Exiting main");
    return 0;
}

I think it's fantastic that C++/CLI allows us to smoothly write library implementations where a language feature does not directly exist.

Mapping delegates to non-CLI class methods

There's a delegate_proxy_factoryclass declared in <msclr\event.h>that allows you to map an event handler to a non-CLI class's member function. If you've done Windows Forms - MFC interop, then you presumably already know how to do this. But it's very useful even outside MFC Forms interop scenarios, and is also trivial to implement. Here's some code that shows how to map the FileSystemWatcherclass's Renamedevent to a native class method (albeit with managed arguments).

class Demo5
{
    msclr::auto_gcroot<FileSystemWatcher^> m_fsw;
public:
    // Step (1)
    // Declare the delegate map where you map
    // native method to specific event handlers
    BEGIN_DELEGATE_MAP(Demo5)
        EVENT_DELEGATE_ENTRY(OnRenamed, Object^, RenamedEventArgs^)
    END_DELEGATE_MAP()

    Demo5()
    {
        m_fsw = gcnew  FileSystemWatcher("d:\\tmp");
        // Step (2)
        // Setup event handlers using MAKE_DELEGATE
        m_fsw->Renamed += MAKE_DELEGATE(RenamedEventHandler, OnRenamed);
        m_fsw->EnableRaisingEvents = true;
    }

    // Step (3)
    // Implement the event handler method
    void OnRenamed(Object^, RenamedEventArgs^ e)
    {
        Console::WriteLine("{0} -> {1}",e->OldName, e->Name);
    }
};

Conclusion

Essentially, these support library classes, make up for missing syntactic support in C++/CLI. Why add compiler overhead, when library implementations are trivial and equally effective? As usual, feedback (both critical and otherwise) is welcome.

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)

Share

About the Author

Nish Nishant
United States United States
Nish Nishant is a Software Architect/Consultant based out of Columbus, Ohio. He has over 15 years of software industry experience in various roles including Lead Software Architect, Principal Software Engineer, and Product Manager. Nish is a recipient of the annual Microsoft Visual C++ MVP Award since 2002 (13 consecutive awards as of 2014). Nish is an industry acknowledged expert in the Microsoft technology stack. He authored C++/CLI in Action for Manning Publications in 2005, and had previously co-authored Extending MFC Applications with the .NET Framework for Addison Wesley in 2003. In addition, he has over 140 published technology articles on CodeProject.com and another 250+ blog articles on his WordPress blog. Nish is vastly experienced in team management, mentoring teams, and directing all stages of software development. Contact Nish : You can reach Nish on his google email id voidnish. Website and Blog

You may also be interested in...

Comments and Discussions

 
GeneralGreat Article Pin
Tomer Noy27-Jun-06 20:49
memberTomer Noy27-Jun-06 20:49 
GeneralRe: Great Article Pin
Nishant Sivakumar28-Jun-06 4:07
staffNishant Sivakumar28-Jun-06 4:07 

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

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

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.150804.3 | Last Updated 20 Jun 2006
Article Copyright 2006 by Nish Nishant
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid