1

I have defined a struct MyStructure at IDL of unmanaged library SomeLibrary. I need to call managed code (C#) MyManagedLibPtr->RetrieveStuff from unmanaged library (C++) to fill and retrieve an array of these structs back for caller. The problem is that I haven't been able to figure out the signature of RetrieveStuff at managed side. I guess some custom marshalling is required? Here's what I have:

IDL of "SomeLibrary":

[
uuid(xxxxx-xxxx-xxxx-xxxx-xxxxx)
]
struct MyStructure
{
  [helpstring("Some string values")] SAFEARRAY(BSTR) moValues;   
  [helpstring("Some other value")] BSTR moValue; 
};

Unmanaged code (caller):

SAFEARRAY* saArray = NULL;
MyManagedLibPtr->RetrieveStuff(&saArray);  // <--This is the key part

// The rest is just parsing the results.
// Using SafeArray -wrapper class that handles access/unaccess/etc..
SafeArray<SomeLibrary::MyStructure, VT_RECORD> oResults(saArray); 
for (int i =0; i < oResults.GetSize(0); i++)
{
  SomeLibrary::MyStructure oStruct = oResults[i];
  // Etc......
}

At C# side, I've tried a few different solutions but none of them have been correct. This one would have been the sweetest, but obviously the mashalling automation wasn't sweet enough:

// Interface
[DispId(123)]
void RetrieveStuff(ref SomeLibrary.MyStructure[] roResultArray);

The error I get is some HRESULT -code. Haven't checked which one in particular, but obviously it's caused by incorrect signature or marshalling. Any help?

Simo Erkinheimo
  • 1,347
  • 9
  • 17
  • 1
    VT_RECORD is not a good idea for .NET/COM interop. Check out a previous discussion about this here: http://stackoverflow.com/questions/16994495/c-interop-embedding-an-array-in-a-udt – Simon Mourier Jan 03 '14 at 14:23
  • Yup. The C++ code is wrong as well, using IRecordInfo is mandatory. Change the struct to an interface with two properties and all problems disappear. – Hans Passant Jan 03 '14 at 14:37
  • You're both probably right, but in my application (about 1,000,000 lines of code) changing commonly used structure to something else doesn't feel too good. So, even if it wasn't the best idea, I'd still like to know if the problem above is even solvable with custom marshalling. – Simo Erkinheimo Jan 03 '14 at 15:10
  • Have you tried using the MarshalAs attribute on the parameter? Or importing the TLB into the C# code? – Medinoc Jan 03 '14 at 21:32
  • @Medinoc "SomeLibrary" is correctly referenced at managed side since I can pass and use SomeLibrary.MyStrucure at both sides. The problem is passing an (safe)array of those structures. I've tried some different variations of MarshalAs attribute but haven't been able to use it correctly yet. Not sure at all but I guess I need to "MarshalAs SafeArray" and use either "ref object" or "ref Array" as parameter type. But I'm just guessing... this is what I'm asking here. – Simo Erkinheimo Jan 04 '14 at 15:54
  • I don't understand why you even have to choose that yourself. Can't you define the interface in the type library too, so .Net's type library importer will generate the managed interface for you with the right type? – Medinoc Jan 04 '14 at 23:45
  • @Medinoc See the answer I posted. – Simo Erkinheimo Jan 05 '14 at 14:46

1 Answers1

0

So, I found an answer that's ALMOST as requested. For whatever reason I couldn't get it working with ref parameter but I was able to do the marshalling with out. So this isn't 100% answer for the problem but damn close to it and will probably help others as well.

The correct interface declaration at managed side:

// Interface
[DispId(123)]
void RetrieveStuff([Out, MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_RECORD)] out SomeLibrary.MyStructure[] roResultArray);
Simo Erkinheimo
  • 1,347
  • 9
  • 17