Using .NET Collections from Native Code
A short example of how to access a managed ArrayList of Strings from Native C++/ATL
Introduction
As projects are migrated to the .NET Framework, there is often a need to integrate newly developed .NET based functionality into existing, not-yet-migrated, legacy COM applications. Ideally, the code being developed on the .NET Framework should take advantage of the rich class libraries that the framework has to offer. This can pose several problems for COM clients that want to use the managed code since COM and .NET have differing object models, memory models and runtime requirements.
Fortunately, many of the .NET Framework classes are exposed to COM clients via mscorlib.tlb. A quick look at mscorlib.tlb in OleView reveals just how much of the basic framework is COM Visible:
There are many more classes that are visible to COM objects. In fact, every class that is marked ComVisible(true)
in the API documentation can be easily accessed from COM clients.
Using the Code
This example shows how to use a System.Collections.ArrayList
from native C++. The C++ client makes use of ATL.
The managed class method Library.GetBooks()
returns an ArrayList
. A single string
was added to this list in the Library()
constructor. The following snippet prints a count
of the number of items in the list (in this example, there is only 1):
ILibraryPtr ptrLibrary(__uuidof(Library));
IUnknown *ptrUnkBooks = NULL;
// ManagedCSharpDemoLibrary::_ArrayList is a stub struct
// created by InteropServices. Since everything in COM
// derives from IUnknown, it's safe to cast from an IUnknown *
ptrLibrary->GetBooks((ManagedCSharpDemoLibrary::_ArrayList **)&ptrUnkBooks);
// ICollection is defined in mscorlib.tlb. It corresponds
// to System.Collections.ICollection
ICollection *ptrCol = NULL;
ptrUnkBooks->QueryInterface(__uuidof(ICollection), (void **)&ptrCol);
long count = -1;
ptrCol->get_Count(&count);
cout << "Number of books: " << count << endl;
The System.Collections.ICollection.Count
property contains the number of items in the ArrayList
. ICollection
is only one of many interfaces implemented by ArrayList
. Each of these interfaces is declared as a dual interface so it can be accessed by early bound clients (like this C++ client) as well as late bound clients (e.g. VBscript).
To access the individual items in the ArrayList
, the IList
interface is required. The IList.Item
property in the managed world provides zero based indexed access to the elements of the list. This property can be accessed as follows:
IList *ptrList = NULL;
ptrCol->QueryInterface(__uuidof(IList), (void **)&ptrList);
// ArrayLists store objects. These are marshalled as
// VARIANTs. Since this list is storing strings, the
// VARIANTs are of type BSTR;
VARIANT vObj;
_variant_t vtObj; // provides a convenient way to convert to _bstr_t
for (int i=0; i < count; i++)
{
ptrList->get_Item(i, &vObj);
vtObj.Attach(vObj);
wcout << "Found book: " << ((_bstr_t)vtObj) << endl;
}
ptrCol->Release();
This example prints out each element in the list. It can be easily adapted for lists that are not string
s. The VT_* VARIANT
macros can be used to determine the type of VARIANT
in the list.
Points of Interest
The COM C++ compiler extensions can greatly simplify COM programming in C++. Attributed programming also provides another way to easily create and consume COM types from C++ but there are still a lot of legacy projects out there using versions of VC++ that predate attributed programming.
History
- 29th May, 2008: First version submitted