Platform Invocation Services

Platform Invocation Services

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

Architecture

Overview

Two variants of P/Invoke in use currently 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]. To perform this, the CLR:

  • Locates the DLL containing the function.
  • Loads the DLL into memory.
  • Locates the address of the function in memory and pushes its arguments onto the stack, marshaling data as required.

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. [1]

Other pitfalls include:

  • Incorrect data alignment of user-defined types in the managed language: there are different ways data can be aligned depending on compilers or compiler directives in C and care must be taken to explicitly tell the CLR how to align data for non-blittable types. A common example of this is when trying to define a data type in .NET to represent a union in C. Two different variables overlap in memory, and defining these two variables in a type in .NET would cause them to be in different locations in memory, so special attributes must be used to correct the issue.
  • Interference with the location of data by the managed language's garbage collector: if a reference is local to a method in .NET and is passed to a native function, when the managed method returns, the garbage collector may reclaim that reference. Care should be taken that the object reference is pinned, preventing it from being collected or moved by the garbage collector, which would result in an invalid access by the native module.

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 magic comes with new challenges:

  • Code is prone to Double Thunking[2] if not specifically addressed
  • The Loader Lock issue [3]

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:

HRESULT DllGetVersion
(
    DLLVERSIONINFO* pdvi
)

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

[DllImport("shell32.dll")]
static extern int DllGetVersion(ref DLLVERSIONINFO pdvi);


The second example shows how to extract an Icon in a File:

ExtractIcon function signature in the Windows API:

HICON ExtractIcon
(      
    HINSTANCE hInst,
    LPCTSTR lpszExeFileName,
    UINT nIconIndex
);

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

[DllImport("shell32.dll")]
static extern IntPtr ExtractIcon(
    IntPtr hInst, 
    [MarshalAs(UnmanagedType.LPStr)] string lpszExeFileName, 
    uint nIconIndex);

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

CreateEvent function signature:

 HANDLE CreateEvent(
     LPSECURITY_ATTRIBUTES lpEventAttributes,
     BOOL bManualReset,
     BOOL bInitialState,
     LPCTSTR lpName
 );

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

[DllImport("kernel32.dll", SetLastError=true)]
static extern IntPtr CreateEvent(
    IntPtr lpEventAttributes, 
    bool bManualReset,
    bool bInitialState, 
    [MarshalAs(UnmanagedType.LPStr)] string lpName);

A more complex example

// native declaration
typedef struct _PAIR 
{ 
        DWORD Val1; 
        DWORD Val2; 
} 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<>
inline CLR_PAIR^ marshal_as<CLR_PAIR^, PAIR> (const PAIR&Src) {    // note use of de/referencing, must match your use
        CLR_PAIR^ Dest = gcnew CLR_PAIR;
        Dest->Val1 = Src.Val1;
        Dest->Val2 = Src.Val2;
        return Dest;
};
CLR_PAIR^ mgd_pair1;
CLR_PAIR^ mgd_pair2;
PAIR native0,*native1=&native0;
 
native0 = NativeCallGetRefToMemory();
 
// using marshal_as, makes sense for large or frequently used types
mgd_pair1 = marshal_as<CLR_PAIR^>(*native1);
 
// direct field use
mgd_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: The P/Invoke Problem.

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

__declspec(dllexport) void MyFunction(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.

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 abiguity 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 has two parts:

  • A converter which takes small sections of native C++ header file code containing struct and method definitions. It then produces C# p/invoke code for you to copy and paste into your applications.
  • A searchable database of converted Windows API constant, method and struct definitions.

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).

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. ^ http://msdn.microsoft.com/en-us/library/ms235292(VS.80).aspx
  3. ^ http://msdn.microsoft.com/en-us/library/ms173266(vs.80).aspx

See also

External links


Wikimedia Foundation. 2010.

Игры ⚽ Нужно решить контрольную?

Look at other dictionaries:

  • Platform Invocation Services — P/Invoke P/Invoke (aussi appelé Platform Invocation Services) de Microsoft est une spécification de l implémentation de CLI, qui permet au managed code d appeler du code natif dans les DLL. Le code natif est référencé via les meta données qui… …   Wikipédia en Français

  • Web Services Invocation Framework — Infobox Software name = Apache WSIF logo = caption = developer = Apache Software Foundation latest release version = 2.0 latest release date = January 27, 2003 operating system = Cross platform genre = Web Services license = Apache License 2.0… …   Wikipedia

  • Web Services — Dieser Artikel oder Abschnitt bedarf einer Überarbeitung. Näheres ist auf der Diskussionsseite angegeben. Hilf mit, ihn zu verbessern, und entferne anschließend diese Markierung. Ein Webservice oder Webdienst ist eine Software Anwendung, die mit… …   Deutsch Wikipedia

  • Java Platform, Standard Edition — or Java SE is a widely used platform for programming in the Java language. It is the Java Platform used to deploy portable applications for general use.In practical terms, Java SE consists of a virtual machine, which must be used to run Java… …   Wikipedia

  • Remote Desktop Services — Developer(s) Microsoft Stable release 7.0 (6.1.7600) / October 27, 2009 …   Wikipedia

  • Terminal Services — Infobox Software name = Terminal Services developer = Microsoft operating system = Microsoft Windows, Mac OS X latest release version = 6.0.6001.18000 latest release date = February 4, 2008 use = Remote Desktop Access license = MS EULA website =… …   Wikipedia

  • Open Mobile Terminal Platform — Type Industry Forum Industry Wireless Services Founded 2004 Products Industry recommendations , BONDI initiative Website http://ww …   Wikipedia

  • P/Invoke — (aussi appelé Platform Invocation Services) de Microsoft est une spécification de l implémentation de CLI, qui permet au managed code d appeler du code natif dans les DLL. Le code natif est référencé via les meta données qui décrivent les… …   Wikipédia en Français

  • Java Native Interface — (JNI)  стандартный механизм для запуска кода, под управлением виртуальной машины Java (JVM), который написан на языках С/С++ или Ассемблера, и скомпонован в виде динамических библиотек, позволяет не использовать статическое связывание. Это… …   Википедия

  • Java Native Access — (JNA) ist eine Java Programmbibliothek für den Zugriff auf plattformspezifische ( native ) dynamische Programmbibliotheken (DLL in Windows). Hierbei muss im Unterschied zu JNI kein plattform spezifischer Code geschrieben werden. JNA ist in der… …   Deutsch Wikipedia

Share the article and excerpts

Direct link
Do a right-click on the link above
and select “Copy Link”