需要Pinvoke struct marshalling帮助 - System.AccessViolationException

时间:2011-04-28 05:02:58

标签: c# pinvoke marshalling

喂! 我刚开始摆弄pinvoke并遇到了问题。我收到了AccessViolationException。首先,有没有办法调试或追踪哪个字段导致此错误?唯一要写的是结果结构。

c ++调用如下:

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

c ++ struct:

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

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;

C#方法声明:

[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);

C#致电:

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

希望我没有遗漏任何东西。 谢谢你的帮助!

2 个答案:

答案 0 :(得分:4)

噢,伙计!你已经选择了一个非常复杂的案例,为你的第一次小小的体验。我建议先做一些简单的事情,然后再转向真实的东西。

首先CharSet=CharSet.Ansi看起来很可疑。你的所有字符串和字符都是_tuchar,我收集u就意味着“Unicode”,不是吗?如果是这种情况,则需要CharSet=CharSet.Unicode

其次,(这是最可能的罪魁祸首)为什么ErrorMessages字段封送为ByValArray?你知道ByVal这里的意思是“按价值”,不是吗?通过引用,。你知道C ++中的小星号意味着“引用”,不是吗?那么为什么你的引用字段ErrorMessages被编组为一个按值数组呢?如果您不知道,通常会说一个数组在传递所有内容时“按值”传递,而不是仅仅将引用(指针)传递到存储所有内容的内存位置。在C ++结构定义中,指定_tuchar*,表示“包含一个或多个_tuchars的某个内存的引用(指针)”,而在C#中指定[MarshalAs(UnmanagedType.ByValArray, SizeConst=500)],这意味着“500个_tuchars应该是来到这里,不多也不少“。看看引用(指针)通常占用4个字节(或64位机器上的8个字节),500个unicode字符占用1000个字节,这里就有明显的不匹配。

第三第四,同一点适用于resultblocks字段。

第五language字段正好相反:C ++代码说“这里有3个_tuchars”,而C#代码说“有一个引用(指针)到一个字符串在这里“(如果你不知道,LPStr的意思是”指向STRING的长指针“)

最后,在您解决了所有这些问题之后,我建议您执行程序并将调用结果打印到Marshal.SizeOf( typeof( MyStruct ) )。在.NET看来,这将为您提供结构的确切大小。转到C ++端并打印出sizeof( MyStruct )。这将为您提供C ++对大小的看法。

如果结果不同,请查看错误。尝试逐个删除字段,直到它们变为相同。这将给你罪魁祸首字段。与他们合作。

总的来说,我建议您需要更好地了解事情的工作方式。对于初学者来说,这种情况太复杂了。

祝你好运!

答案 1 :(得分:0)

在黑暗中这是一个镜头,但你尝试用MarshalAs(UnmanagedType.LPWStr)装饰字符串参数:

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

我认为为字符串选择的默认编组是BStr_tuchar应该扩展为wchar_t所以我猜测LPWStr是正确的编组方法(指针)到一个宽字符串)。


更新: MyStruct上的各种内容看起来不太正确:

ErrorMessages被标记为ByValArray,因此.Net互操作可能期望MyStruct看起来像这样:

typedef struct MyStruct 
{
   _tuchar *id;
   _tuchar ErrorMessages[500];
   // Rest of MyStruct

这可能会导致问题 - result同样如此。

此外,我认为language 应该使用大小为3的ByValArray

最后blocks可能应该使用LPArray传递 - ByValArray似乎不正确。

(这大部分都是猜测btw - 我希望这指向你正确的方向,但我没有 对P / Invoke互操作有很多经验)


另一个更新:MyStruct上,您将charset声明为Ansi,但在MyFuncUnicode ...是使用Unicode编译的非托管dll还是安西?如果它使用Unicode,那么我认为你应该在编组字符串时使用LPWStr,而使用Ansi它应该是LPStr