views:

488

answers:

2

I have a C++ struct

struct UnmanagedStruct
{
   char* s;
   // Other members
};

and a C# struct

struct ManagedStruct {
   [MarshalAs(UnmanagedType.LPStr)]
   string s;
   // Other members
}

the C++ library exposes

extern "C" UnmanagedStruct __declspec(dllexport) foo( char* input );

And it is imported like

  [DllImport("SomeDLL.dll", CharSet = CharSet.Ansi)]
  static extern ManagedStruct foo( string input );

However when I call this function I get

MarshalDirectiveException was unhandled

Method's type signature is not PInvoke compatible.

The thing is, this function call works if I remove the char* s and the string s from the structs.

+2  A: 

For this type of scenario, do not use a String directly. Instead switch the type to be an IntPtr value and use Marshal.PtrToStringAuto/Ansi/Uni as appropriate. In this case, since your native code uses char*, PtrToStringAnsi is the best choice.

struct ManagedStruct {
  IntPtr s;
  public string sAsString { get { return Marshal.PtrToStringAnsi(s); } }
}
JaredPar
Why do I need to do that? I asked this question earlier and people were all saying it would "just work".
DevDevDev
@SteveM, strings + PInvoke = difficult. Strings just work in many scenarios but just fail in many others :). Strings as fields of a struct are particularly challenging because there is a huge issue of memory management. In particular, what should the CLR do with the memory used to create the managed string? Should it free it or do nothing? In general if the CLR sees something like this it will assume it has to free the data and end up calling CoTaskMemFree on the value which is probably wrong in your scenario.
JaredPar
(cont). In general if you are ever confused about how a string should be marshalled (and it's not an inlined array), use IntPtr and hand marshal the string. This has a much better chance of working
JaredPar