喂! 我刚开始摆弄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");
希望我没有遗漏任何东西。 谢谢你的帮助!
答案 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个字节,这里就有明显的不匹配。
第三和第四,同一点适用于result
和blocks
字段。
第五,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,但在MyFunc
上Unicode
...是使用Unicode编译的非托管dll还是安西?如果它使用Unicode,那么我认为你应该在编组字符串时使用LPWStr
,而使用Ansi它应该是LPStr
。