C# Interop:从带有 out 参数的非托管代码返回结构会产生奇怪的结果
C# Interop : Returning struct from unmanaged code with out parameter gives strange results
我正在尝试做一些基本的 C#\C 互操作,结果 returns 奇怪。这些是C端的定义:
typedef struct _RESULT {
BOOL success;
char *err;
} RESULT;
typedef struct _INPUT_DATA {
char *message;
} INPUT_DATA;
API int execute_out(IN INPUT_DATA *input, OUT RESULT *result);
实施简单:
API int execute_out(INPUT_DATA *input, RESULT *result){
result = (RESULT*)malloc(sizeof RESULT);
result->err = (char*)malloc(sizeof 128);
strcpy(result->err, "Result");
result->success = TRUE;
return 0;
}
现在,我的 C# 定义如下:
[StructLayout(LayoutKind.Sequential)]
public struct INPUT_DATA
{
[MarshalAs(UnmanagedType.LPStr)]
public string message;
}
[StructLayout(LayoutKind.Sequential)]
public struct RESULT
{
public bool success;
public IntPtr err;
}
[DllImport("unmanaged.dll", CallingConvention = CallingConvention.Cdecl)]
public extern static int execute_out(INPUT_DATA input, out RESULT result);
当我在托管端设置这样的代码时:
Unmanaged.INPUT_DATA input = new Unmanaged.INPUT_DATA();
input.message = "Test";
Unmanaged.RESULT result;
Unmanaged.execute_out(input, out result);
我收到 RESULT 结构中定义的 err 变量的空数据,而且我的成功标志设置不正确(结果为 false)。谁能告诉我我在这里缺少什么?
另外,与这种情况类似的最佳做法是什么:
调用者(托管代码)应该为 RESULT 结构分配内存然后释放它,还是应该有另一个调用来释放非托管端分配的内存?
根据@HansPassant 和@Olaf 的建议,这是适合我的实际代码:
API int execute_out(INPUT_DATA *input, RESULT *result){
//result = (RESULT*)malloc(sizeof RESULT); //this malloc is redundat which effectively replaces original pointer
result->err = (char*)malloc(sizeof 128);
strcpy(result->err, "Result");
result->success = TRUE;
return 0;
}
Malloc 已注释,因为它替换了从托管代码传递的原始指针。
在托管方面,函数需要这样声明:
[DllImport("unmanaged.dll", CallingConvention = CallingConvention.Cdecl)]
public extern static int execute_out(ref INPUT_DATA input, [Out] out RESULT result);
注意 ref 关键字在 RESULT 指针之前正确传递 INPUT_DATA 指针和 [Out] 属性以告诉编组器我们需要一些回报。
最后,托管端的完整调用如下所示:
Unmanaged.INPUT_DATA input = new Unmanaged.INPUT_DATA();
input.message = "Test";
Unmanaged.RESULT result;
Unmanaged.execute_out(ref input, out result);
string error = Marshal.PtrToStringAnsi(result.err);
希望有人会觉得这有用。
我正在尝试做一些基本的 C#\C 互操作,结果 returns 奇怪。这些是C端的定义:
typedef struct _RESULT {
BOOL success;
char *err;
} RESULT;
typedef struct _INPUT_DATA {
char *message;
} INPUT_DATA;
API int execute_out(IN INPUT_DATA *input, OUT RESULT *result);
实施简单:
API int execute_out(INPUT_DATA *input, RESULT *result){
result = (RESULT*)malloc(sizeof RESULT);
result->err = (char*)malloc(sizeof 128);
strcpy(result->err, "Result");
result->success = TRUE;
return 0;
}
现在,我的 C# 定义如下:
[StructLayout(LayoutKind.Sequential)]
public struct INPUT_DATA
{
[MarshalAs(UnmanagedType.LPStr)]
public string message;
}
[StructLayout(LayoutKind.Sequential)]
public struct RESULT
{
public bool success;
public IntPtr err;
}
[DllImport("unmanaged.dll", CallingConvention = CallingConvention.Cdecl)]
public extern static int execute_out(INPUT_DATA input, out RESULT result);
当我在托管端设置这样的代码时:
Unmanaged.INPUT_DATA input = new Unmanaged.INPUT_DATA();
input.message = "Test";
Unmanaged.RESULT result;
Unmanaged.execute_out(input, out result);
我收到 RESULT 结构中定义的 err 变量的空数据,而且我的成功标志设置不正确(结果为 false)。谁能告诉我我在这里缺少什么?
另外,与这种情况类似的最佳做法是什么:
调用者(托管代码)应该为 RESULT 结构分配内存然后释放它,还是应该有另一个调用来释放非托管端分配的内存?
根据@HansPassant 和@Olaf 的建议,这是适合我的实际代码:
API int execute_out(INPUT_DATA *input, RESULT *result){
//result = (RESULT*)malloc(sizeof RESULT); //this malloc is redundat which effectively replaces original pointer
result->err = (char*)malloc(sizeof 128);
strcpy(result->err, "Result");
result->success = TRUE;
return 0;
}
Malloc 已注释,因为它替换了从托管代码传递的原始指针。
在托管方面,函数需要这样声明:
[DllImport("unmanaged.dll", CallingConvention = CallingConvention.Cdecl)]
public extern static int execute_out(ref INPUT_DATA input, [Out] out RESULT result);
注意 ref 关键字在 RESULT 指针之前正确传递 INPUT_DATA 指针和 [Out] 属性以告诉编组器我们需要一些回报。
最后,托管端的完整调用如下所示:
Unmanaged.INPUT_DATA input = new Unmanaged.INPUT_DATA();
input.message = "Test";
Unmanaged.RESULT result;
Unmanaged.execute_out(ref input, out result);
string error = Marshal.PtrToStringAnsi(result.err);
希望有人会觉得这有用。