Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pinvoke struct marshalling help needed - System.AccessViolationException

Hey! I've just begun fiddling with pinvoke and have encountered a problem. I'm getting the AccessViolationException. First of all, is there some way to debug or trace out which field is causing this error? The only thing being written to is the result struct.

The c++ call looks like:

MyFunc(int var1, _tuchar *var2, _tuchar *var3, _tuchar *var4, MyStruct *Result,
       _tuchar *var5, _tuchar *var6);

The c++ struct:

typedef struct MyStruct 
{
   _tuchar *id;
   _tuchar *ErrorMessages;
   int int1; 
   _tuchar language[3]; 
   _tuchar *result;
   int type;
   int number;
   int *type2; 
   _tuchar **blocks;
}

The C# struct:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct MyStruct
{
    [MarshalAs(UnmanagedType.LPStr)]
    public string Id;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst=500)]
    public char[] ErrorMessages;

    public int int1;

    [MarshalAs(UnmanagedType.LPStr)]
    public string language;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 15)]
    public char[] result;

    public int type;

    public int number;

    public int type2;

    [MarshalAs(UnmanagedType.ByValArray)]
    public string[] blocks;

The C# method declaration:

[DllImport(MyPath, EntryPoint = "MyEntryPoint", SetLastError = true,
           CharSet = CharSet.Unicode)]
internal static extern int MyFunc(int var1, string var2, string var3,
      string var4, ref MyStruct Result, string var5, string var6);

The C# Call:

var result = new MyStruct();
MyFunc(0, "var2", "var3", "var4", ref result, "var5", "var6");

Hope I haven't left anything out. Thanks for any help!

like image 865
Dashu Avatar asked Oct 12 '22 04:10

Dashu


1 Answers

Ooooh, man! You've picked quite a complex case for your first fiddling experience. I recommend doing something simpler first, and then moving on to the real stuff.

Firstly, CharSet=CharSet.Ansi looks suspicious. All your strings and chars are _tuchar, and I gather the u in there means "Unicode", doesn't it? If that's the case, you need CharSet=CharSet.Unicode.

Secondly, (and this is the most likely culprit) why is the ErrorMessages field marshaled as ByValArray? You know that ByVal here means "by value", don't you? As in, not by reference. And you know that little asterisk thing in C++ means "reference", don't you? So why does your reference field ErrorMessages marshaled as a by-value array? In case you don't know, an array is generally said to be passed "by value" when all of it's content is being passed, instead of just passing a reference (pointer) to a memory location where all that content is stored. In C++ struct definition, you specify _tuchar*, which means "a reference (pointer) to some memory containing one or more of _tuchars", whereas in C# you specify [MarshalAs(UnmanagedType.ByValArray, SizeConst=500)], which means "500 _tuchars are supposed to be here, no more and no less". Seeing how a reference (pointer) usually takes 4 bytes (or 8 bytes on 64bit machines), and 500 unicode characters take 1000 bytes, you have an obvious mismatch right here.

Thirdly and fourthly, same point goes for result and blocks fields.

Fifthly, the language field is exactly reverse situation: the C++ code says "there are 3 _tuchars here", while C# code says "there is a reference (pointer) to a string here" (in case you don't know, LPStr means "Long Pointer to STRing")

And finally, after you have fixed all those problems, I recommend you execute your program and print out the result of call to Marshal.SizeOf( typeof( MyStruct ) ). That will give you exactly how big your struct is, in .NET's opinion. The go on the C++ side and print out sizeof( MyStruct ). That will give you what C++ thinks about the size.

If they turn out different, see what's wrong. Try to remove fields one by one, until they become same. This will give you the culprit field(s). Work with them.

Overall, I suggest you need a better understanding of how things work first. This case is waaaay too complex for a beginner.

Good luck!

like image 133
Fyodor Soikin Avatar answered Nov 03 '22 01:11

Fyodor Soikin