c# - System.AccessViolationException,同时通过实现 ICustomMarshaler 从 native 编码(marshal)为托管

标签 c# c++ pinvoke

不知何故 question 的延续我最近发布了一个令人讨厌的 System.AccessViolationException,同时尝试从 native 编码到托管(通过使用 ICustomMarshaler),我不明白。这是重现错误 (**) 的示例代码。 C++ 方面:

typedef struct Nested1{
    int32_t         n1_a; // (4*)
    char*           n1_b;
    char*           n1_c;
} Nested1;
typedef struct Nested3{
    uint8_t         n3_a;
    int64_t         n3_b;
    wchar_t*        n3_c;
} Nested3;
typedef struct Nested2{
    int32_t         n2_a;
    Nested3         nest3;
    uint32_t        n2_b;
    uint32_t        n2_c;
} Nested2;
typedef struct TestStruct{
    Nested1     nest1; // (2*)
    Nested2     nest2;
} TestStruct;

void ReadTest(TestStruct& ts)
{
    ts.nest2.n2_c = 10; // (3*)
}

在 C# 端,一个伪造的 TestStruct 只是为了显示错误和 ICustomMarshaler 实现:

class TestStruct{};
[DllImport("MyIOlib.dll", CallingConvention = CallingConvention.Cdecl)]
extern static void ReadTest([Out, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(CustomMarshaler))]TestStruct ts);

class CustomMarshaler : ICustomMarshaler
{
    public static ICustomMarshaler GetInstance(string Cookie) { return new CustomMarshaler(); }

    public object MarshalNativeToManaged(IntPtr pNativeData)
    {
        return new TestStruct();
    }
    public void CleanUpNativeData(IntPtr pNativeData) 
    { 
    } // (1*) 
    public int GetNativeDataSize() { return 40; }
    public IntPtr MarshalManagedToNative(object ManagedObj)
    {
        TestStruct ts = (TestStruct)ManagedObj;
        IntPtr intPtr = Marshal.AllocHGlobal(GetNativeDataSize());

        return intPtr;
    }
 }

private void Form1_Load(object sender, EventArgs e)
{
     TestStruct ts = new TestStruct();
     ReadTest(ts);
}

现在,我有以下内容:

  • 正是使用这段代码,我在 (1*) 行之后得到了一个 System.AccessViolationException
  • 如果我注释掉第 (2*) 行 行 (3*),我不会出现异常,一切正常;
  • 如果我评论其他几个结构字段中的一个,例如第 (3*) 行我收到“托管调试助手‘FatalExecutionEngineError’检测到问题 [...] 此错误可能是 CLR 中的错​​误或用户代码的不安全或不可验证部分中的错误。此错误的常见来源错误包括 COM 互操作或 PInvoke 的用户编码错误,这可能会破坏堆栈”

(**) 我对我的原始帖子进行了大量编辑,因为我认为我找到了一种更简单的方法来显示问题并且保留我以前的文字会使读者感到困惑。希望不是问题,但是如果以前的读者愿意,我可以重新发布我的原文。

最佳答案

您需要 In 属性以及 Out。没有 In 属性,永远不会调用 MarshalManagedToNative。并且没有分配非托管内存。因此访问冲突。

extern static void ReadTest(
    [In, Out, MarshalAs(UnmanagedType.CustomMarshaler, 
        MarshalTypeRef = typeof(CustomMarshaler))]
    TestStruct ts
);

严格来说,非托管代码应该使用指向结构的指针而不是引用参数。您可以从托管代码传递 null,但它作为 C++ 引用参数是无效的。

void ReadTest(TestStruct* ts)
{
    ts->nest2.n2_c = 10;
}

关于c# - System.AccessViolationException,同时通过实现 ICustomMarshaler 从 native 编码(marshal)为托管,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24023728/

相关文章:

c# - 我什么时候需要在 C# 中调用 base()?

c# - 在 C# 中更改本地管理员密码

c# - 隐藏代码中的 WPF 命令未触发

c++ - 我可以为后续的 itoa() 调用重用相同的缓冲区吗?

c++ - Quicksort 没有做任何事情,现在陷入了永远的循环

c++ - Qt 模块错误 - QtOpenGL 中的 LNK 1112

c# - ASP.NET MVC 电子邮件

c# - Delphi DLL 从 C# 返回字符串 ... .NET 4.5 堆损坏但 .NET 4.0 有效?请解释?

c# - 编码包含字符串的结构

c# - pinvoke将ascii字符数组返回给c#