使用 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 机制(例如 CoCreateInstanceQueryInterface)直接使用 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++ 组件扩展)

另请参阅