使用 C++ 互操作(隐式 PInvoke)
与其他 .NET 语言不同,Visual C++ 具有互操作性支持,使得托管和非托管代码可以存在于同一应用程序甚至同一文件中(使用托管、非托管 pragma)。 这样,Visual C++ 开发人员可将 .NET 功能集成到现有的 Visual C++ 应用程序中,而不会干扰应用程序的其余部分。
还可以使用 dllexport、dllimport 从托管编译器中调用非托管函数。
当不需要指定如何封送函数参数,或不需要显式调用 DllImportAttribute 时可以指定的任何其他详细信息时,隐式 PInvoke 非常有用。
Visual C++ 为托管和非托管函数提供了两种互操作方式:
.NET Framework 支持显式 PInvoke,显式 PInvoke 在大多数 .NET 语言中都可用。 但顾名思义,C++ 互操作是特定于 Visual C++ 的。
C++ 互操作
C++ 互操作提供了更好的类型安全性,并且实现起来通常不那么繁琐。 但是,如果非托管源代码不可用,或者对于跨平台项目,则 C++ 互操作不是一个可用选项。
C++ COM 互操作
在与 COM 组件进行互操作时,Visual C++ 支持的互操作性特性提供了优于其他 .NET 语言的特殊优势。 与受限于 .NET Framework Tlbimp.exe(类型库导入程序) 的限制(例如对数据类型的有限支持和每个 COM 接口的每个成员的强制公开)不同,C++ 互操作允许随意访问 COM 组件,并且不需要单独的互操作程序集。 与 Visual Basic 和 C# 不同,Visual C++ 可以通过常用的 COM 机制(例如 CoCreateInstance 和 QueryInterface)直接使用 COM 对象。 这可能是因为 C++ 互操作功能导致编译器自动插入转换代码,以从托管函数移动到非托管函数并再次返回。
使用 C++ 互操作,COM 组件可以按通常使用的方式使用,也可以包装在 C++ 类中。 这些包装类称为自定义运行时可调用包装器或 CRCW,与直接在应用程序代码中使用 COM 相比,它们有两个优点:
生成的类可用于 Visual C++ 以外的语言。
COM 接口的详细信息可以对托管客户端代码隐藏。 可以使用 .NET 数据类型代替本机类型,并且可以在 CRCW 内透明地执行数据封送处理的细节。
无论是直接使用 COM 还是通过 CRCW 使用 COM,都必须封送除简单的 blittable 类型之外的参数类型。
Blittable 类型
对于使用简单的内在类型(请参阅Blittable 和非 Blittable 类型)的非托管 API,不需要特殊编码,因为这些数据类型在内存中具有相同的表示,但更复杂的数据类型需要显式数据封送处理。 有关示例,请参阅如何:使用 PInvoke 从托管代码调用本机 DLL。
示例
// vcmcppv2_impl_dllimp.cpp
// compile with: /clr:pure user32.lib
using namespace System::Runtime::InteropServices;
// Implicit DLLImport specifying calling convention
extern "C" int __stdcall MessageBeep(int);
// explicit DLLImport needed here to use P/Invoke marshalling because
// System::String ^ is not the type of the first parameter to printf
[DllImport("msvcrt.dll", EntryPoint = "printf", CallingConvention = CallingConvention::Cdecl, CharSet = CharSet::Ansi)]
// or just
// [DllImport("msvcrt.dll")]
int printf(System::String ^, ...);
int main() {
// (string literals are System::String by default)
printf("Begin beep\n");
MessageBeep(100000);
printf("Done\n");
}
Begin beep
Done
本节内容
有关在互操作方案中使用委托的信息,请参阅委托(C++ 组件扩展)。