Native and Managed Interoperability: Platform Invoke

When I first began learning how to write software it was on a Windows 95 box using Visual Basic 3. Visual Basic provided a great learning tool for getting into development. It doesn’t have a high price for entry, as just about anyone with a little inclination can pick it up and use the RAD tools it provides. However, I eventually grew of it and moved to learning C and the working with the Win32 API directly. Ever since, I have been absolutely hooked. I really enjoy working with the native Windows API (yes, I’m one of the few), so naturally when I started picking up managed languages I was immensely curious about the power of interoperability.

The Power of P/Invoke

There are generally two (Managed C++ has its own built-in Interop features, but that is out of scope for this post) specific types of interoperability when dealing with Native and Managed code, Platform Invoke (P/Invoke) and COM Interop. P/Invoke is amazingly useful when dealing with a simple flat API (static exported functions exposed through a Dynamic Link Library) because the .NET Common Language Runtime (CLR) does all the required DLL loading and type marshaling.

All you need to do is declare the prototypes for the functions you’re wanting to import – and through the “DllImport” attribute argument list let the CLR know the library name, calling convention the callee uses, if the function returns a complex type you need to specify how to marshal it, and if you deal with string arguments you need to specify what character set the callee will be expecting. It may sound like a lot, but when you compare this to the traditional Win32 C method, I think you’ll agree .NET Interop is pretty impressive.

The Native Candidate

In my post on Unique Identifiers I included a sample library that generates Unique Identifiers. Building on the information provided there, I’m going to walk-though invoking this library from a traditional Win32 C native application and two Managed applications – C# and F# specifically. The intent here is to show the dynamics of Managed and Native code, along with the power and simplicity the .NET framework provides developers at relatively little cost.

Method Invocation Underpinnings

The native C library used in these samples exposes 4 functions for working with unique identifiers, here are the external prototypes:

#if defined DLL_EXPORT
    #define EXPORT extern "C" __declspec( dllexport )
    #define EXPORT extern "C" __declspec( dllexport )

EXPORT GUID __stdcall Empty();
EXPORT GUID __stdcall Generate();
EXPORT GUID __stdcall FromStringA( const char * );
EXPORT GUID __stdcall FromStringW( const wchar_t * );

There are a few things to note here.

First is the “EXPORT” definition. This simply tells the compiler to use “C” name declarations so that the linker can provide information about the function addresses in the compiled binary, which means we don’t need the .def file to invoke our functions correctly. We can simply load the library into memory and get the address of the functions by their name (which depends on the calling convention – more on this in a second).

Next is the explicit calling convention, stdcall. A calling convention is simply a contract between the function caller and callee. The calling convention specifies a few things: where parameters and return values are placed (stack, registers or both), the order in which parameters are passed and who is responsible for cleaning up the stack after the function has executed. We explicitly stated stdcall which is the standard Win32 calling convention (and the simplest, our prototypes are pretty bland). There are 3 commonly used x86 calling conventions:

  • cdecl – This calling convention pushes parameters on the stack in a right-to-left order and return values are placed in the EAX register. With cdecl the caller is responsible for cleaning up the stack – this allows functions with variable argument lists to execute properly. Functions using the cdecl calling convention are name decorated with an underscore prefix, i.e. a function “Foo()” will be decorated as “_Foo”.
  • stdcall – This is the standard Win32 calling convention. It is basically identical to cdecl with one exception. Parameters are placed on the stack in a right-to-left order and return values are placed in EAX, exactly the same as cdecl. However, with stdcall the callee is responsible for clearing the stack which is slightly more efficient because the caller does not need to unwind the stack – however, function argument lists must be known at compile time. Functions using stdcall are decorated with an underscore prefix as with cdecl, however you must also append an @ sign followed by the number of bytes in the argument list, i.e. a function “Foo( int a, double b)” will be decorated as “_Foo@12”.
  • fastcall – This calling convention is, not surprisingly, designed to be the fastest of the three. The first two 32 bit (or smaller) parameters are placed in the ECX and EDX registers (by Microsoft convention – although this varies by compiler), respectively while each additional parameter will be placed on the stack (so, ideally to get the most out of the fastcall convention keep function signatures as small as possible). The called function is responsible for balancing the stack with fastcall. The naming declaration for functions using fastcall are prefixed with an @ sign followed by the number of bytes in the argument list, i.e. a function “Foo( int a, int b )” would be decorated as “@Foo@8”.

The final point of interest in the exposed function prototypes is the functions that take string arguments, FromStringA and FromStringW. Each of these functions perform exactly the same processing, parsing an Id from a string, the only difference is one takes an Ansi string (8 bits per character) and one takes a Wide-character string (or Unicode) which is 16 bits per character. The reason for exposing both of these is really just for demonstration purposes with the Managed projects which I’ll get to in a bit.

Traditional Native Implementation

Let’s take a look at the interesting bits of code for invoking this library from a traditional Win32 C application:

// ----------[Global Constants]----------
const wchar_t * GeneratorDll = TEXT( "Generator.dll" );
const char * GenerateGuidFunc = "_Generate@0";
const char * EmptyGuidFunc = "_Empty@0";
const char * FromStringAFunc = "_FromStringA@4";
const char * FromStringWFunc = "_FromStringW@4";

// ----------[External Prototypes]----------

typedef GUID ( __stdcall *GetGUID ) ();
typedef GUID ( __stdcall *ParseWideGUID ) ( const wchar_t * );
typedef GUID ( __stdcall *ParseAsciiGUID ) ( const char * );

// ----------[Global Variables]----------

HINSTANCE hLibInst = 0;

// ----------[Functions]----------

void Initialize()
    hLibInst = LoadLibrary( GeneratorDll );

void ShutDown()
    if( hLibInst != NULL )
        FreeLibrary( hLibInst );

    exit ( EXIT_SUCCESS );

void DisplayGeneratedGuid()
    GetGUID _newGuid = ( GetGUID ) GetProcAddress( hLibInst, GenerateGuidFunc );
    GUID newGuid = _newGuid();
    DisplayGuid( newGuid );

void DisplayEmptyGuid()
    GetGUID _emptyGuid = ( GetGUID ) GetProcAddress( hLibInst, EmptyGuidFunc );
    GUID newGuid = _emptyGuid();
    DisplayGuid( newGuid );

void DisplayWideParsedGuid()
    ParseWideGUID _parseGuid = ( ParseWideGUID ) GetProcAddress( hLibInst, FromStringWFunc );
    GUID newGuid = _parseGuid( GuidWideStringToConvert );
    DisplayGuid( newGuid );

void DisplayAsciiParsedGuid()
    ParseAsciiGUID _parseGuid = ( ParseAsciiGUID ) GetProcAddress( hLibInst, FromStringAFunc );
    GUID newGuid = _parseGuid( GuidAsciiStringToConvert );
    DisplayGuid( newGuid );

Now, let’s dissect the interesting parts.

In the global constants section we declare the name of the library we’re going to load, “Generator.dll”. The path isn’t specified because it’s assumed the library will be in the current process executing directory – this is passed to the “LoadLibrary” function which will give us a handle to the loaded module.

Then we declare the functions we’re going to be executing with their fully qualified naming declaration. Since we’re using the stdcall calling convention each function is prefixed with an underscore followed by the size of the function arguments. Note that both “FromStringA” and “FromStringW” have 4 bytes because we pass a 32 bit pointer to the starting address of the string we’re going to parse, so there is nothing distinguishing the decorated name for these two functions (other than the function name obviously).

Next we have to declare the external prototypes for the functions we’re going to be calling. Since “Empty()” and “Generate()” have identical signatures (return a “GUID” take no arguments) they share the same external prototype. The two “FromString” functions take different arguments, one takes a constant pointer (a constant pointer means the callee doesn’t have permission to modify memory at that address – only read from it) the to a char array, the other takes a constant pointer to a wide-character array – so they have unique prototypes.

The rest of the application is very straight-forward. We simply call “LoadLibrary” with our library name, store the handle to this module in a global variable, then in each function that invokes this library we declare a local variable of the corresponding function prototype we’re expecting to get back to store the result of “GetProcAddress” which will take our decorated function name and give us back a pointer to that functions address in our loaded module.

From this point, the local variable is essentially a first-class function we can invoke like any other function we would normally use in our applications.

There is an additional routine in this application that’s not required in the others due to built in functionality by the .NET framework, converting a “Guid” to its string representation. In .NET we simply need to invoke the “ToString” method on a “Guid” object to get this.

CLR to the Rescue

Now, for a stark contrast let’s look at how simple this exact same functionality can be achieved with .NET, starting with C#.

[DllImport( "Generator.dll", CallingConvention = CallingConvention.StdCall )]
static extern Guid Empty();

[DllImport( "Generator.dll", CallingConvention = CallingConvention.StdCall )]
static extern Guid Generate();

[DllImport( "Generator.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode )]
static extern Guid FromStringW( string idString );

[DllImport( "Generator.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi )]
static extern Guid FromStringA( string idString );

That’s essentially it.

In C# all we need to do is declare the method signature for each function we’re going to import, then place a “DllImport” attribute above the function signature where we specify our calling convention and if necessary the character set the function is expecting (Ansi or Unicode in our case) when dealing with string arguments. From this point we’re free to call these functions at will.

Here is the full F# application since it’s only 26 lines – with generous spacing.


open System
open System.Runtime.InteropServices

    GuidLibrary =
        [<DllImport( "Generator.dll", CallingConvention=CallingConvention.StdCall )>] extern Guid Empty()
        [<DllImport( "Generator.dll", CallingConvention = CallingConvention.StdCall )>] extern Guid Generate()
        [<DllImport( "Generator.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode )>] extern Guid FromStringW( string )
        [<DllImport( "Generator.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi )>] extern Guid FromStringA( string )

    Constants =
        let GuidStringToConvert = "{FADDEC2A-B4C1-47A4-8065-14C038E4AD67}"

ignore [ Console.WriteLine( GuidLibrary.Empty() ) ]
ignore [ for i in 0 .. 99 -> Console.WriteLine( GuidLibrary.Generate() ) ]
ignore [ Console.WriteLine( GuidLibrary.FromStringW( Constants.GuidStringToConvert ) ) ]
ignore [ Console.WriteLine( GuidLibrary.FromStringA( Constants.GuidStringToConvert ) ) ]

ignore [ Console.ReadKey() ];

Source Code and Sample Applications

The accompanying source code for this post can be found on my Interop repository on GitHub.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s