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!
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.Ansilooks suspicious. All your strings and chars are_tuchar, and I gather theuin there means “Unicode”, doesn’t it? If that’s the case, you needCharSet=CharSet.Unicode.Secondly, (and this is the most likely culprit) why is the
ErrorMessagesfield marshaled asByValArray? You know thatByValhere 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 fieldErrorMessagesmarshaled 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
resultandblocksfields.Fifthly, the
languagefield 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,LPStrmeans “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 outsizeof( 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!