Platform Invocation Services

Last updated

Platform Invocation Services, commonly referred to as P/Invoke, is a feature of Common Language Infrastructure implementations, like Microsoft's Common Language Runtime, that enables managed code to call native code.

Contents

Managed code, such as C# or VB.NET, provides native access to classes, methods, and types defined within the libraries that make up the .NET Framework. While the .NET Framework provides an extensive set of functionality, it may lack access to many lower level operating system libraries normally written in unmanaged code or third party libraries also written in unmanaged code. P/Invoke is the technique a programmer can use to access functions in these libraries. Calls to functions within these libraries occur by declaring the signature of the unmanaged function within managed code, which serves as the actual function that can be called like any other managed method. The declaration references the library's file path and defines the function parameters and return in managed types that are most likely to be implicitly marshaled to and from the unmanaged types by the common language run-time (CLR). When the unmanaged data types become too complex for a simple implicit conversion from and to managed types, the framework allows the user to define attributes on the function, return, and/or the parameters to explicitly refine how the data should be marshaled so as not to lead to exceptions in trying to do so implicitly.

There are many abstractions of lower-level programming concepts available to managed code programmers as compared to programming in unmanaged languages. As a result, a programmer with only managed code experience will need to brush up on programming concepts such as pointers, structures, and passing by reference to overcome some of the obstacles in using P/Invoke.

Architecture

Overview

Two variants of P/Invoke currently in use are:

Explicit

  • Native code is imported via dynamic-linked libraries (DLLs)
  • Metadata embedded in the caller's assembly defines how the native code is to be called and data accessed (usually requires attributed source specifiers to aid the compiler in generating marshal glue)
    • This definition is the "Explicit" part

Implicit

  • By using C++/CLI, an application may simultaneously use the managed heap (by way of tracking pointers) and any native memory region, without the explicit declaration. (Implicit)
  • A primary benefit in this case being, if underlying native data structures change, so long as the naming is compatible, a breaking change is avoided.
    • i.e. Adding/removing/re-ordering structures in a native header will be transparently supported so long as the structure member names did not also change.

Details

When using P/Invoke, the CLR handles DLL loading and conversion of the unmanaged previous types to CTS types (also referred to as parameter marshalling). [1] [ citation needed ]To perform this, the CLR:

P/Invoke is useful for using standard (unmanaged) C or C++ DLLs. It can be used when a programmer needs to have access to the extensive Windows API, as many functions provided by the Windows libraries lack available wrappers. When a Win32 API is not exposed by the .NET Framework the wrapper to this API must be written manually.

Pitfalls

Writing P/Invoke wrappers can be difficult and error prone. Using native DLLs means that the programmer can no longer benefit from type safety and garbage collection as is usually provided in the .NET environment. When they are used improperly this may cause problems such as segmentation faults or memory leaks. Getting the exact signatures of the legacy functions for use in the .NET environment can be hard, which can result in such problems. For this purpose tools and websites exist to obtain such signatures, helping to prevent signature problems.

Other pitfalls include:

When using C++/CLI, emitted CIL is free to interact with objects located on the managed heap and simultaneously any addressable native memory location. A managed heap resident object may be called, modified or constructed, using simple "object->field;" notation to assign values or specify method calls. Significant performance gains result from having eliminated any needless context switching, memory requirements are reduced (shorter stacks).

This comes with new challenges:

These references specify solutions for each of these issue if they are encountered. A primary benefit is the elimination of the structure declaration, the order of field declaration and alignment issues are not present in the context of C++ Interop.

Examples

Basic examples

This first simple example shows how to get the version of a particular DLL:

DllGetVersion function signature in the Windows API:

HRESULTDllGetVersion(DLLVERSIONINFO*pdvi)

P/Invoke C# code to invoke the DllGetVersion function:

[StructLayout(LayoutKind.Sequential)]privatestructDLLVERSIONINFO{publicintcbSize;publicintdwMajorVersion;publicintdwMinorVersion;publicintdwBuildNumber;publicintdwPlatformID;}[DllImport("shell32.dll")]staticexternintDllGetVersion(refDLLVERSIONINFOpdvi);

The second example shows how to extract an icon in a file:

ExtractIcon function signature in the Windows API:

HICONExtractIcon(HINSTANCEhInst,LPCTSTRlpszExeFileName,UINTnIconIndex);

P/Invoke C# code to invoke the ExtractIcon function:

[DllImport("shell32.dll")]staticexternIntPtrExtractIcon(IntPtrhInst,[MarshalAs(UnmanagedType.LPStr)]stringlpszExeFileName,uintnIconIndex);

This next complex example shows how to share an Event between two processes in the Windows platform:

CreateEvent function signature:

HANDLECreateEvent(LPSECURITY_ATTRIBUTESlpEventAttributes,BOOLbManualReset,BOOLbInitialState,LPCTSTRlpName);

P/Invoke C# code to invoke the CreateEvent function:

[DllImport("kernel32.dll", SetLastError=true)]staticexternIntPtrCreateEvent(IntPtrlpEventAttributes,boolbManualReset,boolbInitialState,[MarshalAs(UnmanagedType.LPStr)]stringlpName);

A more complex example

// native declarationtypedefstruct_PAIR{DWORDVal1;DWORDVal2;}PAIR,*PPAIR;
// Compiled with /clr; use of #pragma managed/unmanaged can lead to double thunking;// avoid by using a stand-alone .cpp with .h includes.// This would be located in a .h file.template<>inlineCLR_PAIR^marshal_as<CLR_PAIR^,PAIR>(constPAIR&Src){// Note use of de/referencing. It must match your use.CLR_PAIR^Dest=gcnewCLR_PAIR;Dest->Val1=Src.Val1;Dest->Val2=Src.Val2;returnDest;};
CLR_PAIR^mgd_pair1;CLR_PAIR^mgd_pair2;PAIRnative0,*native1=&native0;native0=NativeCallGetRefToMemory();// Using marshal_as. It makes sense for large or frequently used types.mgd_pair1=marshal_as<CLR_PAIR^>(*native1);// Direct field usemgd_pair2->Val1=native0.Val1;mgd_pair2->val2=native0.val2;return(mgd_pair1);// Return to C#

Tools

There are a number of tools which are designed to aid in the production of P/Invoke signatures.

Writing a utility application that would import C++ header files and native DLL files and produce an interface assembly automatically turns out to be quite difficult. The main problem with producing such an importer/exporter for P/Invoke signatures is the ambiguity of some C++ function call parameter types.

Brad Abrams has this to say on the subject: [4]

The problem lies with C++ functions like the following:

__declspec(dllexport)voidMyFunction(char*params);

What type should we use for the parameter params in our P/Invoke signature ? This could be either a C++ null terminated string, or could be a char array or could be an output char parameter. So should we use string, StringBuilder, char [] or ref char ?

Regardless of this issue, there are a few tools available to make the production of P/Invoke signatures simpler.

One of the tools listed below, xInterop C++ .NET Bridge has resolved this issue by implementing multiple overrides of the same C++ method in .NET world, developers can then pick the correct one to make the call.

PInvoke.net

PInvoke.net is a wiki containing P/Invoke signatures for a large number of standard Windows APIs. It is owned by Redgate Software and has around 50000 hits per month.

The signatures are manually produced by users of the wiki. They can be searched using a free addin to Microsoft Visual Studio.

PInvoker

PInvoker is an application which imports native DLLs and C++ .h files and exports fully formed and compiled P/Invoke interop DLLs. It overcomes the ambiguity problem by wrapping native pointer function parameters in PInvoker specific .NET interface classes. Instead of using standard .NET parameter types in P/Invoke method definitions (char[], string, etc.) it uses these interface classes in the P/Invoke function calls.

For instance, if we consider the above example code, PInvoker would produce a .NET P/Invoke function accepting a .NET interface class wrapping the native char * pointer. The construction of this class could be from a string or from a char [] array. The actual native memory structure for both is the same, but the respective interface class constructors for each type will populate the memory in different ways. The responsibility for deciding what .NET type needs to be passed into the function is therefore passed to the developer.

Microsoft Interop Assistant

Microsoft Interop Assistant is a free tool available with binaries and source code available for download on CodePlex. It is licensed under the Microsoft Limited Public License (Ms-LPL).

It has two parts:

Because this tool produces C# source code rather than a compiled dll the user is free to make any changes necessary to the code before use. So the ambiguity problem is solved by the application picking one particular .NET type to use in the P/Invoke method signature and if necessary the user can change this to the required type.

P/Invoke Wizard

The P/Invoke Wizard uses a similar method to the Microsoft Interop Assistant in that it accepts native C++ .h file code and produces C# (or VB.NET) code for you to paste into your .NET application code.

It also has options for which framework you wish to target: .NET Framework for the desktop or .NET Compact Framework for Windows Mobile smart devices (and Windows CE).

xInterop C++ .NET Bridge

xInterop C++ .NET Bridge is a windows application to created C# wrapper for native C++ DLLs and C++ bridge to access .NET assemblies, it comes with a C#/.NET library which wraps the standard C++ classes, such as string, iostream, etc., C++ classes and objects can be accessed from .NET.

This tool generates C# wrapper DLLs with source code from existing native C++ DLLs and the associated header files which are required by the tool to build a C# wrapper DLL. The P/Invoke signatures and data marshaling are generated by the application. The resulting C# wrapper has the similar interface of the C++ counterpart with the parameter type converted to the .NET code.

This tool recognizes template classes which is not exported from the C++ DLL and instantiates the template class and export it in a supplement DLL and the corresponding C++ interface can be used in .NET.

See also

Related Research Articles

Common Intermediate Language (CIL), formerly called Microsoft Intermediate Language (MSIL) or Intermediate Language (IL), is the intermediate language binary instruction set defined within the Common Language Infrastructure (CLI) specification. CIL instructions are executed by a CLI-compatible runtime environment such as the Common Language Runtime. Languages which target the CLI compile to CIL. CIL is object-oriented, stack-based bytecode. Runtimes typically just-in-time compile CIL instructions into native code.

<span class="mw-page-title-main">Windows API</span> Microsofts core set of application programming interfaces on Windows

The Windows API, informally WinAPI, is Microsoft's core set of application programming interfaces (APIs) available in the Microsoft Windows operating systems. The name Windows API collectively refers to several different platform implementations that are often referred to by their own names. Almost all Windows programs interact with the Windows API. On the Windows NT line of operating systems, a small number use the Native API.

In software design, the Java Native Interface (JNI) is a foreign function interface programming framework that enables Java code running in a Java virtual machine (JVM) to call and be called by native applications and libraries written in other languages such as C, C++ and assembly.

<span class="mw-page-title-main">Pointer (computer programming)</span> Object which stores memory addresses in a computer program

In computer science, a pointer is an object in many programming languages that stores a memory address. This can be that of another value located in computer memory, or in some cases, that of memory-mapped computer hardware. A pointer references a location in memory, and obtaining the value stored at that location is known as dereferencing the pointer. As an analogy, a page number in a book's index could be considered a pointer to the corresponding page; dereferencing such a pointer would be done by flipping to the page with the given page number and reading the text found on that page. The actual format and content of a pointer variable is dependent on the underlying computer architecture.

In computer programming, a callback or callback function is any reference to executable code that is passed as an argument to another piece of code; that code is expected to call back (execute) the callback function as part of its job. This execution may be immediate as in a synchronous callback, or it might happen at a later point in time as in an asynchronous callback. They are also called blocking and non-blocking.

This article compares two programming languages: C# with Java. While the focus of this article is mainly the languages and their features, such a comparison will necessarily also consider some features of platforms and libraries. For a more detailed comparison of the platforms, see Comparison of the Java and .NET platforms.

Managed Extensions for C++ or Managed C++ is a now-deprecated set of language extensions for C++, including grammatical and syntactic extensions, keywords and attributes, to bring the C++ syntax and language to the .NET Framework. These extensions were created by Microsoft to allow C++ code to be targeted to the Common Language Runtime (CLR) in the form of managed code, as well as continue to interoperate with native code.

C++/CLI is a variant of the C++ programming language, modified for Common Language Infrastructure. It has been part of Visual Studio 2005 and later, and provides interoperability with other .NET languages such as C#. Microsoft created C++/CLI to supersede Managed Extensions for C++. In December 2005, Ecma International published C++/CLI specifications as the ECMA-372 standard.

Metadata, in the Common Language Infrastructure (CLI), refers to certain data structures embedded within the Common Intermediate Language (CIL) code that describes the high-level structure of the code. Metadata describes all classes and class members that are defined in the assembly, and the classes and class members that the current assembly will call from another assembly. The metadata for a method contains the complete description of the method, including the class, the return type and all of the method parameters.

Dynamic-link library (DLL) is Microsoft's implementation of the shared library concept in the Microsoft Windows and OS/2 operating systems. These libraries usually have the file extension DLL, OCX, or DRV . The file formats for DLLs are the same as for Windows EXE files – that is, Portable Executable (PE) for 32-bit and 64-bit Windows, and New Executable (NE) for 16-bit Windows. As with EXEs, DLLs can contain code, data, and resources, in any combination.

In computer programming, the term hooking covers a range of techniques used to alter or augment the behaviour of an operating system, of applications, or of other software components by intercepting function calls or messages or events passed between software components. Code that handles such intercepted function calls, events or messages is called a hook.

The Microsoft Windows operating system supports a form of shared libraries known as "dynamic-link libraries", which are code libraries that can be used by multiple processes while only one copy is loaded into memory. This article provides an overview of the core libraries that are included with every modern Windows installation, on top of which most Windows applications are built.

windows.h is a Windows-specific header file for the C and C++ programming languages which contains declarations for all of the functions in the Windows API, all the common macros used by Windows programmers, and all the data types used by the various functions and subsystems. It defines a very large number of Windows specific functions that can be used in C. The Win32 API can be added to a C programming project by including the <windows.h> header file and linking to the appropriate libraries. To use functions in xxxx.dll, the program must be linked to xxxx.lib. Some headers are not associated with a .dll but with a static library.

Blittable types are data types in the Microsoft .NET Framework that have an identical presentation in memory for both managed and unmanaged code. Understanding the difference between blittable and non-blittable types can aid in using COM Interop or P/Invoke, two techniques for interoperability in .NET applications.

COM Interop is a technology included in the .NET Framework Common Language Runtime (CLR) that enables Component Object Model (COM) objects to interact with .NET objects, and vice versa.

Java Native Access (JNA) is a community-developed library that provides Java programs easy access to native shared libraries without using the Java Native Interface (JNI). JNA's design aims to provide native access in a natural way with a minimum of effort. Unlike JNI, no boilerplate or generated glue code is required.

In computer science, marshalling or marshaling is the process of transforming the memory representation of an object into a data format suitable for storage or transmission. It is typically used when data must be moved between different parts of a computer program or from one program to another.

Component Object Model (COM) is a binary-interface standard for software components introduced by Microsoft in 1993. It is used to enable inter-process communication object creation in a large range of programming languages. COM is the basis for several other Microsoft technologies and frameworks, including OLE, OLE Automation, Browser Helper Object, ActiveX, COM+, DCOM, the Windows shell, DirectX, UMDF and Windows Runtime. The essence of COM is a language-neutral way of implementing objects that can be used in environments different from the one in which they were created, even across machine boundaries. For well-authored components, COM allows reuse of objects with no knowledge of their internal implementation, as it forces component implementers to provide well-defined interfaces that are separated from the implementation. The different allocation semantics of languages are accommodated by making objects responsible for their own creation and destruction through reference-counting. Type conversion casting between different interfaces of an object is achieved through the QueryInterface method. The preferred method of "inheritance" within COM is the creation of sub-objects to which method "calls" are delegated.

<span class="mw-page-title-main">.NET Framework</span> Software platform developed by Microsoft

The .NET Framework is a proprietary software framework developed by Microsoft that runs primarily on Microsoft Windows. It was the predominant implementation of the Common Language Infrastructure (CLI) until being superseded by the cross-platform .NET project. It includes a large class library called Framework Class Library (FCL) and provides language interoperability across several programming languages. Programs written for .NET Framework execute in a software environment named the Common Language Runtime (CLR). The CLR is an application virtual machine that provides services such as security, memory management, and exception handling. As such, computer code written using .NET Framework is called "managed code". FCL and CLR together constitute the .NET Framework.

Windows Runtime (WinRT) is a platform-agnostic component and application architecture first introduced in Windows 8 and Windows Server 2012 in 2012. It is implemented in C++ and officially supports development in C++, Rust/WinRT, Python/WinRT, JavaScript-TypeScript, and the managed code languages C# and Visual Basic .NET (VB.NET).

References

  1. Parameter marshaling is not to be confused with the general term marshalling, meaning Serialization. Marshaled parameters are copied in the CLR stack after their conversion to CTS types, but are not serialized.
  2. "Double Thunking (C++)".
  3. "Initialization of Mixed Assemblies".
  4. "The PInvoke problem". learn.microsoft.com. February 6, 2004.