编译器警告(等级 2)C4412

“function”:函数签名包含类型“type”;在纯代码与混合代码或本机代码之间传递 C++ 对象是不安全的

备注

“/clr:pure”编译器选项在 Visual Studio 2015 中已弃用,在 Visual Studio 2017 中不受支持。 如果有需要纯代码的代码,建议将其移植到 C#。

编译器检测到可能导致运行时错误的潜在不安全情况:正在从 /clr:pure 编译单位调用通过 dllimport 导入的函数,并且函数签名包含不安全类型。 如果某个类型包含成员函数或者具有不安全类型或间接不安全类型的数据成员,则该类型是不安全的。

由于纯代码和本机代码(或混合本机代码和托管代码)之间的默认调用约定存在差异,因此这是不安全的。 (通过 dllimport)将函数导入到 /clr:pure 编译单位时,请确保签名中每种类型的声明与导出函数的编译单位中的声明相同(特别注意隐式调用约定之间的差异)

虚成员函数特别容易产生意想不到的结果。 但是,即使是非虚函数也应该进行测试,以确保获得正确的结果。 如果确定将得到正确的结果,则可以忽略此警告。

默认情况下,C4412 处于关闭状态。 有关详细信息,请参阅默认情况下处于关闭状态的编译器警告dllexport、dllimport

要解决此警告,请从类型中删除所有函数。

示例

下面的示例生成 C4412。

// C4412.cpp
// compile with: /c /W2 /clr:pure
#pragma warning (default : 4412)

struct Unsafe {
   virtual void __cdecl Test();
};

struct Safe {
   int i;
};

__declspec(dllimport) Unsafe * __cdecl func();
__declspec(dllimport) Safe * __cdecl func2();

int main() {
   Unsafe *pUnsafe = func();   // C4412
   // pUnsafe->Test();

   Safe *pSafe = func2();   // OK
}

下面的示例是用于声明两种类型的头文件。 Unsafe 类型不安全,因为它有一个成员函数。

// C4412.h
struct Unsafe {
   // will be __clrcall if #included in pure compilation
   // defaults to __cdecl in native or mixed mode compilation
   virtual void Test(int * pi);

   // try the following line instead
   // virtual void __cdecl Test(int * pi);
};

struct Safe {
   int i;
};

此示例使用头文件中定义的类型导出函数。

// C4412_2.cpp
// compile with: /LD
#include "C4412.h"

void Unsafe::Test(int * pi) {
   *pi++;
}

__declspec(dllexport) Unsafe * __cdecl func() { return new Unsafe; }
__declspec(dllexport) Safe * __cdecl func2() { return new Safe; }

/clr:pure 编译中的默认调用约定与本机编译不同。 当包含 C4412.h 时,Test 默认为 __clrcall。 如果编译并运行此程序(不要使用 /c),该程序将引发异常

下面的示例生成 C4412。

// C4412_3.cpp
// compile with: /W2 /clr:pure /c /link C4412_2.lib
#pragma warning (default : 4412)
#include "C4412.h"

__declspec(dllimport) Unsafe * __cdecl func();
__declspec(dllimport) Safe * __cdecl func2();

int main() {
   int n = 7;
   Unsafe *pUnsafe = func();   // C4412
   pUnsafe->Test(&n);

   Safe *pSafe = func2();   // OK
}