C# P/Invoke 损坏的内存
C# P/Invoke Corrupt Memory
我在使用 C# P/Invoke 时遇到一个奇怪的问题,当从 C# 编组到 C++ 时,结构已损坏,除非通过 ref 传递结构,但不会发生在同一调用和其他调用中的所有其他结构.
第一次调用 100% 没有问题,即使使用数组也是如此。第二次调用仅在我将 Struct8 作为 ref 传递时才起作用,如果我不这样做,那么当它命中 C++ 代码时内存已损坏,这没有意义,因为为什么所有其他结构都可以工作而不必通过引用传递?
我的结构是 1:1(要用 MarshalAs 等字符的字符串)
C++ 代码大部分使用 C 风格的数组
我正在拨打以下电话
var struct1 = new Struct1() { Name = @"TEST" };
Create(
struct1,
ref struct2,
new Struct3() { Channel = string.IsNullOrEmpty(request.Channel) ? "TESTCHANNEL" : request.Channel },
Array.Empty<Struct4>(),
new Struct5() { Something = 0 },
out var struct6,
out var struct7,
out var struct2Detail
);
var struct8 = new Struct8() { Number = struct2.Number, TrySomething = 1 };
Initialize(struct1, struct8, out struct6, out struct2Detail);
只有当我这样称呼它时才有效:
Initialize(struct1, ref struct8, out struct6, out struct2Detail);
C# 编组 + 结构的伪代码
public static void Create(
Struct1 struct1
,ref Struct2 struct2
,Struct3 struct3
,Struct4[] struct4
,Struct5 struct5
,out Struct6 struct6
,out Struct7 struct7
,out Struct2Detail struct2Detail
)
{
int numProcFields = struct4.Length;
Net5_Create(
struct1
,ref struct2
,struct3
,ref numProcFields
,struct4
,struct5
,out struct6
,out struct7
,out struct2Detail
);
}
[DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern void Net5_Create(
Struct1 struct1
,[In, Out] ref Struct2 struct2
,Struct3 struct3
,[In, Out] ref int numProcFields
,Struct4[] struct4
,Struct5 struct5
,out Struct6 struct6
,out Struct7 struct7
,out Struct2Detail struct2Detail
);
public static void Initialize(
Struct1 struct1
,Struct8 struct8
,out Struct6 struct6
,out Struct2Detail struct2Detail
)
{
Net5_Initialize(
struct1
,struct8
,out struct6
,out struct2Detail
);
}
[DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern void Net5_Initialize(
Struct1 struct1
,Struct8 struct8
,out Struct6 struct6
,out Struct2Detail struct2Detail
);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct Struct1
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 33)]
public string Name;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct Struct2
{
public int Number;
public int Reference;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)]
public string ReferenceValue;
public int ReferenceYear;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
public string DateCreated;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct Struct4
{
public int ObjectType;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)]
public string Name;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 201)]
public string Value;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct Struct6
{
public int Number1;
public int Number2;
public int Number3;
public int Number4;
public int Number5;
public int Number6;
public int Number7;
public int Number8;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct Struct7
{
public int Number1;
public int Number2;
public int Number3;
public int Number4;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)]
public string String1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 257)]
public string String2;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct Struct8
{
public int Number;
public int TrySomething;
}
C++ 端的代码
#ifdef __cplusplus
extern "C" {
#endif
#ifdef _WIN32
# define MODULE_API __declspec(dllexport)
#else
# define MODULE_API
#endif
MODULE_API void Net5_Initialize(
Struct1* struct1
,Struct8* struct8
,Struct6* struct6
,Struct2Detail* struct2Detail
);
void Net5_Initialize(
Struct1* struct1
,Struct8* struct8
,Struct6* struct6
,Struct2Detail* struct2Detail
)
{
dotnetInstance->Initialize(
struct1
, struct8
, struct6
, struct2Detail
);
}
#ifdef __cplusplus
}
#endif
#endif
// typedef __int32 int32;
struct Struct1
{
char Name[33];
Struct1()
{
Name[0] = 0;
}
void Function1() { ... }
}
struct Struct2
{
int32 Number;
int32 Reference;
char ReferenceValue[65];
int32 ReferenceYear;
char DateCreated[16];
Struct2()
{
Number = 0;
Reference = 0;
ReferenceValue[0] = 0;
ReferenceYear = 0;
DateCreated[0] = 0;
}
void Function1() { ... }
}
struct Struct4
{
int32 ObjectType;
char Name[65];
char Value[201];
Struct4()
{
ObjectType = 0;
Name[0] = 0;
Value[0] = 0;
}
void Function1() { ... }
}
struct Struct6
{
int32 Number1;
int32 Number2;
int32 Number3;
int32 Number4;
int32 Number5;
int32 Number6;
int32 Number7;
int32 Number8;
Struct6()
{
Number1 = 0;
Number2 = 0;
Number3 = 0;
Number4 = 0;
Number5 = 0;
Number6 = 0;
Number7 = 0;
Number8 = 0;
}
void Function1() { ... }
}
struct Struct7
{
int32 Number1;
int32 Number2;
int32 Number3;
int32 Number4;
char String1[65]
char String2[257];
Struct7()
{
Number1 = 0;
Number2 = 0;
Number3 = 0;
Number4 = 0;
String1[0] = 0;
String2[0] = 0;
}
void Function1() { ... }
}
struct Struct8
{
int32 Number;
int32 TrySomething;
Struct8()
{
Number = 0;
TrySomething = 0;
}
void Function1() { ... }
}
void Class::Create(Struct1* struct1, Struct2* struct2, Struct3* struct3, int32* numProcFields, Struct4* struct4, Struct5* struct5, Struct6* struct6, Struct7* struct7, Struct2Detail* struct2Detail)
{
...
}
void Class::Initialize(Struct1* struct1, Struct8* struct8, Struct6* struct6, Struct2Detail* struct2Detail)
{
lprintf("Initialize - Started (%d)", struct8->Number);
...
}
事实证明问题不在于 Initialize,如果 Create 的输入数组内存也是 byref
工作创建
[DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern void Net5_Create(
ref Struct1 struct1
,ref Struct2 struct2
,ref Struct3 struct3
,ref int numProcFields
,[MarshalAs(UnmanagedType.LPArray)] Struct4[] struct4
,ref Struct5 struct5
,ref Struct6 struct6
,ref Struct7 struct7
,ref Struct2Detail struct2Detail
);
我在使用 C# P/Invoke 时遇到一个奇怪的问题,当从 C# 编组到 C++ 时,结构已损坏,除非通过 ref 传递结构,但不会发生在同一调用和其他调用中的所有其他结构.
第一次调用 100% 没有问题,即使使用数组也是如此。第二次调用仅在我将 Struct8 作为 ref 传递时才起作用,如果我不这样做,那么当它命中 C++ 代码时内存已损坏,这没有意义,因为为什么所有其他结构都可以工作而不必通过引用传递?
我的结构是 1:1(要用 MarshalAs 等字符的字符串) C++ 代码大部分使用 C 风格的数组
我正在拨打以下电话
var struct1 = new Struct1() { Name = @"TEST" };
Create(
struct1,
ref struct2,
new Struct3() { Channel = string.IsNullOrEmpty(request.Channel) ? "TESTCHANNEL" : request.Channel },
Array.Empty<Struct4>(),
new Struct5() { Something = 0 },
out var struct6,
out var struct7,
out var struct2Detail
);
var struct8 = new Struct8() { Number = struct2.Number, TrySomething = 1 };
Initialize(struct1, struct8, out struct6, out struct2Detail);
只有当我这样称呼它时才有效:
Initialize(struct1, ref struct8, out struct6, out struct2Detail);
C# 编组 + 结构的伪代码
public static void Create(
Struct1 struct1
,ref Struct2 struct2
,Struct3 struct3
,Struct4[] struct4
,Struct5 struct5
,out Struct6 struct6
,out Struct7 struct7
,out Struct2Detail struct2Detail
)
{
int numProcFields = struct4.Length;
Net5_Create(
struct1
,ref struct2
,struct3
,ref numProcFields
,struct4
,struct5
,out struct6
,out struct7
,out struct2Detail
);
}
[DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern void Net5_Create(
Struct1 struct1
,[In, Out] ref Struct2 struct2
,Struct3 struct3
,[In, Out] ref int numProcFields
,Struct4[] struct4
,Struct5 struct5
,out Struct6 struct6
,out Struct7 struct7
,out Struct2Detail struct2Detail
);
public static void Initialize(
Struct1 struct1
,Struct8 struct8
,out Struct6 struct6
,out Struct2Detail struct2Detail
)
{
Net5_Initialize(
struct1
,struct8
,out struct6
,out struct2Detail
);
}
[DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern void Net5_Initialize(
Struct1 struct1
,Struct8 struct8
,out Struct6 struct6
,out Struct2Detail struct2Detail
);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct Struct1
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 33)]
public string Name;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct Struct2
{
public int Number;
public int Reference;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)]
public string ReferenceValue;
public int ReferenceYear;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
public string DateCreated;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct Struct4
{
public int ObjectType;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)]
public string Name;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 201)]
public string Value;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct Struct6
{
public int Number1;
public int Number2;
public int Number3;
public int Number4;
public int Number5;
public int Number6;
public int Number7;
public int Number8;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct Struct7
{
public int Number1;
public int Number2;
public int Number3;
public int Number4;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)]
public string String1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 257)]
public string String2;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct Struct8
{
public int Number;
public int TrySomething;
}
C++ 端的代码
#ifdef __cplusplus
extern "C" {
#endif
#ifdef _WIN32
# define MODULE_API __declspec(dllexport)
#else
# define MODULE_API
#endif
MODULE_API void Net5_Initialize(
Struct1* struct1
,Struct8* struct8
,Struct6* struct6
,Struct2Detail* struct2Detail
);
void Net5_Initialize(
Struct1* struct1
,Struct8* struct8
,Struct6* struct6
,Struct2Detail* struct2Detail
)
{
dotnetInstance->Initialize(
struct1
, struct8
, struct6
, struct2Detail
);
}
#ifdef __cplusplus
}
#endif
#endif
// typedef __int32 int32;
struct Struct1
{
char Name[33];
Struct1()
{
Name[0] = 0;
}
void Function1() { ... }
}
struct Struct2
{
int32 Number;
int32 Reference;
char ReferenceValue[65];
int32 ReferenceYear;
char DateCreated[16];
Struct2()
{
Number = 0;
Reference = 0;
ReferenceValue[0] = 0;
ReferenceYear = 0;
DateCreated[0] = 0;
}
void Function1() { ... }
}
struct Struct4
{
int32 ObjectType;
char Name[65];
char Value[201];
Struct4()
{
ObjectType = 0;
Name[0] = 0;
Value[0] = 0;
}
void Function1() { ... }
}
struct Struct6
{
int32 Number1;
int32 Number2;
int32 Number3;
int32 Number4;
int32 Number5;
int32 Number6;
int32 Number7;
int32 Number8;
Struct6()
{
Number1 = 0;
Number2 = 0;
Number3 = 0;
Number4 = 0;
Number5 = 0;
Number6 = 0;
Number7 = 0;
Number8 = 0;
}
void Function1() { ... }
}
struct Struct7
{
int32 Number1;
int32 Number2;
int32 Number3;
int32 Number4;
char String1[65]
char String2[257];
Struct7()
{
Number1 = 0;
Number2 = 0;
Number3 = 0;
Number4 = 0;
String1[0] = 0;
String2[0] = 0;
}
void Function1() { ... }
}
struct Struct8
{
int32 Number;
int32 TrySomething;
Struct8()
{
Number = 0;
TrySomething = 0;
}
void Function1() { ... }
}
void Class::Create(Struct1* struct1, Struct2* struct2, Struct3* struct3, int32* numProcFields, Struct4* struct4, Struct5* struct5, Struct6* struct6, Struct7* struct7, Struct2Detail* struct2Detail)
{
...
}
void Class::Initialize(Struct1* struct1, Struct8* struct8, Struct6* struct6, Struct2Detail* struct2Detail)
{
lprintf("Initialize - Started (%d)", struct8->Number);
...
}
事实证明问题不在于 Initialize,如果 Create 的输入数组内存也是 byref
工作创建
[DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern void Net5_Create(
ref Struct1 struct1
,ref Struct2 struct2
,ref Struct3 struct3
,ref int numProcFields
,[MarshalAs(UnmanagedType.LPArray)] Struct4[] struct4
,ref Struct5 struct5
,ref Struct6 struct6
,ref Struct7 struct7
,ref Struct2Detail struct2Detail
);