Using C++ Interop (Implicit PInvoke)

Unlike other .NET languages, Visual C++ has interoperability support that allows managed and unmanaged code to exist in the same application and even in the same file (with the managed, unmanaged pragmas). This allows Visual C++ developers to integrate .NET functionality into existing Visual C++ applications without disturbing the rest of the application.

You can also call unmanaged functions from a managed compiland using dllexport, dllimport.

Implicit PInvoke is useful when you do not need to specify how function parameters will be marshaled, or any of the other details that can be specified when explicitly calling DllImportAttribute.

Visual C++ provides two ways for managed and unmanaged functions to interoperate:

Explicit PInvoke is supported by the .NET Framework and is available in most .NET languages. But as its name implies, C++ Interop is specific to Visual C++.

C++ Interop

C++ Interop provides better type safety, and it is typically less tedious to implement. However, C++ Interop is not an option if the unmanaged source code is not available, or for cross-platform projects.

C++ COM Interop

The interoperability features supported by Visual C++ offer a particular advantage over other .NET languages when it comes to interoperating with COM components. Instead of being limited to the restrictions of the .NET Framework Tlbimp.exe (Type Library Importer), such as limited support for data types and the mandatory exposure of every member of every COM interface, C++ Interop allows COM components to be accessed at will and does not require separate interop assemblies. Unlike Visual Basic and C#, Visual C++ can use COM objects directly using the usual COM mechanisms (such as CoCreateInstance and QueryInterface). This is possible due to C++ Interop features that cause the compiler to automatically insert the transition code to move from managed to unmanaged functions and back again.

Using C++ Interop, COM components can be used as they are normally used or they can be wrapped inside C++ classes. These wrapper classes are called custom runtime callable wrappers, or CRCWs, and they have two advantages over using COM directly in application code:

  • The resulting class can be used from languages other than Visual C++.

  • The details of the COM interface can be hidden from the managed client code. .NET data types can be used in place of native types, and the details of data marshaling can be performed transparently inside the CRCW.

Regardless of whether COM is used directly or through a CRCW, argument types other than simple, blittable types must be marshaled.

Blittable Types

For unmanaged APIs that use simple, intrinsic types (see Blittable and Non-Blittable Types), no special coding is required because these data types have the same representation in memory, but more complex data types require explicit data marshaling. For an example, see How to: Call Native DLLs from Managed Code Using PInvoke.

Example

// 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

In This Section

For information on using delegates in an interop scenario, see delegate (C++ Component Extensions).

See also