C#Interop:使用out参数从非托管代码返回struct会产生奇怪的结果

时间:2017-04-23 16:49:34

标签: c# c marshalling

我正在尝试做一些基本的C#\ C interop,它会返回奇怪的结果。这些是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结构中定义的错误变量的空数据,并且我的成功标志设置不正确(产生错误)。谁能告诉我我在这里失踪了什么? 此外,与此案例相似的最佳做法是什么:
调用者(托管代码)应该为RESULT结构分配内存,然后释放它,还是应该在另一个调用中释放非托管端的已分配内存?

1 个答案:

答案 0 :(得分:0)

根据@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);

希望有人会觉得这很有用。