4

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.

DevDevDev
  • 5,107
  • 7
  • 55
  • 87

1 Answers1

4

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
  • 733,204
  • 149
  • 1,241
  • 1,454
  • Why do I need to do that? I asked this question earlier and people were all saying it would "just work". – DevDevDev Aug 03 '09 at 17:57
  • @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 Aug 03 '09 at 17:59
  • (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 Aug 03 '09 at 18:00