tags:

views:

135

answers:

2

I've got existing C# code with signature of Foo(long[][] longs) which I need to call from Unmanaged C++ (not C++/CLI). I just can't seem to figure out the right combination of __gc[] and __gc* to make the compiler happy.

With C++/CLI, this is straight-forward:

std::vector<__int64> evens;
evens.push_back(2); evens.push_back(4); evens.push_back(6); evens.push_back(8);
std::vector<__int64> odds;
odds.push_back(1); odds.push_back(3); odds.push_back(5); odds.push_back(7);
std::vector<std::vector<__int64> > ints;
ints.push_back(evens); ints.push_back(odds);

array<array<__int64>^>^ mgdInts = gcnew array<array<__int64>^>(ints.size());
for (size_t i = 0; i<ints.size(); ++i)
{
 const std::vector<__int64>& slice = ints[i];
 mgdInts[i] = gcnew array<__int64>(slice.size());
 for (size_t j=0; j<slice.size(); ++j)
  mgdInts[i][j] = slice[j];
}

Edit: As I'm using Visual Studio 2008, the "simple" solution is to put the C++/CLI code in its own file and compile with /clr; of course, it would be easier if I didn't have to do this (e.g., other .h files with Managed C++). The C# code can't change as it's auto-generated from a web reference.

+1  A: 

Change the signature from this Foo(long[][] longs) to this: Foo(Array longs)

Then when you look at the resulting type library in OleView.exe, you should see:

    HRESULT Foo([in] SAFEARRAY(int64) longs);

From C++, that's fairly straight forward to call. You can just Win32 to create a SAFEARRAY, or I suggest include and then use the CComSafeArray wrapper class in ATL.

Even though both C# and C++ have richer array definitions, the interoperability between the two is typically done though the Type Library marshaller, 99% of which is legacy and based on what's "VB Compatible". The only array types that the Type Library marshaller supports is SAFEARRAY, so that's what you get when you follow the normal way of doing all this.

However, COM supports a richer array system (conformant arrays), which C# understands, it's harder to do, and you can't simply regasm your C# DLL and use the resulting type library in your unmanaged C++ program. Some of the techniques require tweaking the C# code with ILDASM. Others require you to keep two definitions of the interface, one in C++ and one in C#, and make sure they're in sync (no way to convert one to the other), then in the IDL for C++ adorn the parameter with size_is, and in C# with MarshalAs. It's kind of a mess and really the only type people do that is if they have an already published legacy interface that they cannot change. If this is your C# code, and you can define the interface, I wouldn't go there. Still, the technique is available. Here's a refernece: http://support.microsoft.com/kb/305990 Expect about a week or so to get through this if you've never done anything like this before.

zumalifeguard
The C# code "can't" change, and I think compiling one file with /CLR (VS 2008) is likely more straight-forward than this.
Dan
Yes it is.If for some reason you simply do not want any C++/CLR code, then another way to solve it is to create another C# object that you *can* control, that has the type library compliant interface and wrap the original code.
zumalifeguard
A: 

The solution I came up with is to use List<>.ToArray():

System::Collections::Generic::List<__int64 __gc[]>* mgdInts = new System::Collections::Generic::List<__int64 __gc[]>(ints.size());
for (size_t i = 0; i<ints.size(); ++i)
{
 const std::vector<__int64>& slice = ints[i];
 __int64 mgdSlice __gc[] = new __int64 __gc[slice.size()];
 for (size_t j=0; j<slice.size(); ++j)
  mgdSlice[j] = slice[j];

 mgdInts->Add(mgdSlice);
}

ClassLibrary1::Class1::Foo(mgdInts->ToArray());
Dan