![]() |
Languages »
C# »
COM Interop
Intermediate
Collections InteroperabilityBy Meir BechorThis article describes how to move collections between native and managed code. |
C#, VC7.1, .NET, WinXP, COM, VS.NET2003, Dev
|
|
Advanced Search Add to IE Search |
|
|
|
||||||||||||||||
In my work, I am using a COM object as an interface between a managed server and a HW device that has a C DLL interface. Last week, I encountered the need to pass a collection from the HW device to the server. To do that, I had the COM read it from the device and pass it to the server. The problem was that I couldn�t return a collection of my UDT (User Defined Type) from COM to the managed server.
So how do we return a UDT from native to managed code?
The easiest way to achieve interoperability between native and managed code is by putting the unmanaged code in a COM object. So what is left is to return the collection from COM to the managed code.
COM has two ways to return collections:
HRESULT Foo(int Length, [length_is(Length)] SUDTStruct Col[]);
SAFEARRAY with the collection like so:
If you use IDL, the syntax is:
HRESULT Foo(SAFEARRAY(SUDTStruct) **Col)
If you use embedded IDL, the syntax is:
HRESULT Bar([out, satype(struct PTZPresetsInfo))] SAFEARRAY **Col)The first way does not work with .NET. The interop proxy that will be created for it will look like:
void Foo(int Length, ref SUDTStruct Col)
which means that only one struct will be returned and not the whole collection, so the only way is using a safe array.
To do that we need a few simple stages:
[export,
uuid("3DA0FCDB-BAC1-4b13-8808-AB3E43A281BA")]
struct SUDTStruct
{
...
};
HRESULT Bar(([out, satype(struct PTZPresetsInfo)) SAFEARRAY **Col)
SAFEARRAY and returning it: SAFEARRAY *pSafeArrayCol;
unsigned int ndim = 1;
USES_CONVERSION;
SAFEARRAYBOUND rgbounds;
rgbounds.lLbound = 0;
rgbounds.cElements = ColSize;
IRecordInfo* pRecInfo = NULL;
ITypeLib* pTypelib = NULL;
HRESULT hr = LoadTypeLib(A2OLE(�UDT TYPE LIBRARY�),&pTypelib);
if (FAILED(hr))
{
return NULL;
}
ITypeInfo *pTypeInfo;
hr =
pTypelib->GetTypeInfoOfGuid(__uuidof(CollectionType::value_type),
&pTypeInfo);
if (FAILED(hr))
{
return NULL;
}
hr = GetRecordInfoFromTypeInfo(pTypeInfo, &pRecInfo);
if (FAILED(hr))
{
return NULL;
}
pSafeArrayCol = SafeArrayCreateEx(VT_RECORD, 1,
&rgbounds, pRecInfo);
pRecInfo->Release();
CollectionType::value_type *pItem;
hr = SafeArrayAccessData(pSafeArrayCol,
reinterpret_cast<PVOID*>(&pItem));
pItem: SafeArrayUnaccessData(pSafeArrayCol);
A few remarks: if you want to pass a collection of a UDT to COM all you need to do is define a UDT with a GUID and define a function like so:
HRESULT Bar(SAFEARRAY([in] SUDTStruct) *Col)
This function will appear in the .NET interop like so:
void Bar(System.Array Col)
When calling it, pass a SUDTStruct[] as the collection:
In order to ease the process of creating the safe array I created a simple template function that receives a STL collection that has the UDT as its value type and create a safe array out of it with the same data. The source is attached to this article.
For example:
std::vector<SUDTStruct> Col; // init the collection with the data SAFEARRAY *pSafeArrayCol = CreateUDTSafeArrayFromCol(Col);
The CreateSafeArray function will create a safe array and initialize it with the data in the STL collection.
The demo project attached is a COM object and a .NET assembly that sends and gets collections from it.
Microsoft has done a lot of work to make COM a mechanism for interoperability between managed and unmanaged areas, but not all the COM capability is exported through the .NET proxy created for COM.
| You must Sign In to use this message board. | ||||||||
|
||||||||
|
||||||||
|
||||||||
|
||||||||
General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 23 May 2005 Editor: Smitha Vijayan |
Copyright 2005 by Meir Bechor Everything else Copyright © CodeProject, 1999-2009 Web17 | Advertise on the Code Project |