“function”:函数签名(function signature)包含类型“type”; C++对象在纯粹代码和混合或原生代码之间传递时是不安全的。
备注
/clr:pure 编译器选项在 Visual Studio 2015 中已被弃用,并且从 Visual Studio 2017 开始不再受支持。 如果代码需要完全符合 CLR 标准,我们建议将其移植到 C#。
编译器检测到可能导致运行时错误的潜在不安全情况:使用 /clr:pure 编译的代码调用通过 dllimport导入的函数,且函数签名包含不安全的类型。 如果某个类型包含成员函数或者具有不安全类型或间接不安全类型的数据成员,则该类型是不安全的。
此模式不安全,因为纯代码和本机代码(或混合本机和托管代码)之间的默认调用约定存在差异。 通过 dllimport 将函数导入用 /clr:pure 编译的代码时,请确保签名中每种类型的声明与导出该函数的编译单元中的签名相同(特别要注意隐式调用约定的差异)。
虚成员函数特别容易产生意想不到的结果。 但是,甚至应测试非虚拟函数,以确保获得正确的结果。 确保结果正确后,可以忽略此警告。
默认情况下,C4412 处于关闭状态。 有关详细信息,请参阅默认关闭的编译器警告,以及dllexportdllimport。
要解决此警告,请从类型中删除所有函数。
示例
以下示例生成 C4412:
// 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.
以下示例生成 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
}