解决错误C3821:托管类型或功能不能在非托管功能中使用

时间:2014-06-22 20:07:15

标签: .net windows c++-cli interop

我正在编写一个C ++ / CLI层来处理一些互操作。

本机API填充了涉及固定数组,联合,匿名结构等的复杂结构:

typedef struct DECLSPEC_ALIGN(16) _FOO {
    union {
        BAR Bar;
        struct {
           POP   Array[8];
           DWORD More;
        };
    };
} FOO, *PFOO;

我正在尝试将此数据结构转换为更“理智”的.NET类,供C#使用。问题是,我不能使用这个遗留结构,gcnew我的新类在同一个函数中:

Foo^ Test::GetFoo(HANDLE h)
{
    FOO foo;                              // Necessarily unmanaged

    if (!::GetFoo(h, &foo))
        throw gcnew Exception("GetFoo failed");

    Foo^ result = gcnew Foo();            // Necessarily managed

    // populate result

    return result;
}

这样做会出错:

  

错误2错误C3821:'void Test :: GetFoo(HANDLE)':托管类型或函数不能在非托管函数中使用

如果本机结构和gcnew不能存在于同一个函数中,那么人们怎么能希望(甚至手动)在两者之间编组数据呢?

这里有许多Q / A涉及包装非托管类,这似乎与此无关。

1 个答案:

答案 0 :(得分:11)

  

托管代码中不支持对齐的数据类型

这是真正的错误消息,遗憾的是它没有显示在错误列表窗口中。您只能在“输出”窗口中看到它。编译器错误消息看起来很奇怪时要记住的事情。

是的,它是准确的,托管代码不会运行堆栈对齐保证,以使这个结构对齐。 32位代码运行,对齐为4,64位代码可以提供8.不够好得到16.编译器也无法做到这一点,通常的堆栈指针操作在IL中不可用,这搞砸了抖动生成的元数据,告诉垃圾收集器当它走过堆栈时对象的引用位置。

所以,没有办法,你不能把它变成局部变量。您已经有了选择,最直接的方式是分配它:

#include <malloc.h>
....

    FOO* value = (FOO*)_aligned_malloc(sizeof(FOO), __alignof(FOO));
    try {
        // etc...
    }
    finally {
        _aligned_free(value);
    }