Managing Pointers, or F#’s Platform-Invoke “Gotcha”

I love how well F# “plays” with other languages. This is obviously true where its in-the-box .NET siblings are concerned. However, over the past few years, I’ve come to find it is just as seamless when mixed with good old-fashioned C code.

Well, it’s nearly as seamless.

For the most part, when using P/Invoke, one may simply copy-and-paste the signatures from a C header file (sans-semi-colons, of course). However, there is at least one scenario where naïvely doing so produces code which is not verifiably type-safe. Let’s look at a specific example. Given the follow function prototype in C:

__declspec(dllexport) void getVersion (int* major, int* minor, int* patch);

One might use the following P/Invoke signature (and associated call) in F#:

[<DllImport("somelib",CallingConvention=CallingConvention.Cdecl)>]
extern void getVersion (int* major, int* minor, int* patch)

// ...

let mutable major,minor,patch = 0,0,0
getVersion(&&major,&&minor,&&patch)
printfn "Version: %i.%i.%i" major minor patch

At this point, everything will compile and run just fine. So where’s the problem? To find out, we have to turn to an under-utilised tool in the .NET developer’s arsenal — PEVerify.



SIDEBAR — What the heck is PEVerify?

According to MSDN:

The PEVerify tool helps developers… to determine whether their MSIL code and associated metadata meet type safety requirements. Some compilers generate verifiably type-safe code only if you avoid using certain language constructs.

That’s great. And the reason we want “verifiably type-safe code” is?

Also, according to MSDN:

The common language runtime relies on the type-safe execution of application code to help enforce security and isolation mechanisms. Normally, code that is not verifiably type-safe cannot run, although you can set security policy to allow the execution of trusted but unverifiable code.

and elsewhere:

Type-safe code accesses only the memory locations it is authorized to access. (For this discussion, type safety specifically refers to memory type safety and should not be confused with type safety in a broader respect.) For example, type-safe code cannot read values from another object’s private fields. It accesses types only in well-defined, allowable ways.

So, clearly, “verifiably type-safe” is a distinction worth having. And PEVerify is a tool worth knowing.



And now back to our program (already in progress)…

Running the tool gives us the following output (reformatted for readability):

Microsoft (R) .NET Framework PE Verifier. Version 4.0.30319.1
 Copyright (c) Microsoft Corporation. All rights reserved.

[IL]: Error:
 [C:\dev\somelibfs.dll : .$Constants::.cctor]
 [mdToken=0x600008f][offset 0x0000000D]
 [found address of Int32][expected unmanaged pointer] 
 Unexpected type on the stack.

[IL]: Error:
 [C:\dev\somelibfs.dll : .$Constants::.cctor]
 [mdToken=0x600008f][offset 0x0000000D]
 [found address of Int32][expected unmanaged pointer]
 Unexpected type on the stack.

[IL]: Error:
 [C:\dev\somelibfs.dll : .$Constants::.cctor]
 [mdToken=0x600008f][offset 0x0000000D]
 [found address of Int32][expected unmanaged pointer]
 Unexpected type on the stack.

3 Error(s) Verifying C:\dev\somelibfs.dll

Clearly, something isn’t right.

It’s not very obvious, but the big clue is where PEVerify tells us it was expecting an unmanaged pointer. Turns out, when dealing with the CLR, there are two types of pointers: unmanaged and managed. The later are what you use when passing around CLR types by-reference (i.e. “byref<’T>“ in F#, or “ref“ in C#, or “ByRef“ in VB). It also happens that you should use the managed variety if you want your F# code to be verifiably type-safe — and this includes P/Invoke calls. If you think about it, this makes sense. The runtime can only guarantee the bits it can control (i.e. the parts which are “managed”). So here’s what the F# code looks like using managed pointers instead:

[<DllImport("somelib",CallingConvention=CallingConvention.Cdecl)>]
extern void getVersion (int& major, int& minor, int& patch)

// ...

let mutable major,minor,patch = 0,0,0
getVersion(&major,&minor,&patch)
printfn "Version: %i.%i.%i" major minor patch

And, if we run PEVerify on the updated code, we get the following report:

Microsoft (R) .NET Framework PE Verifier.  Version  4.0.30319.1
Copyright (c) Microsoft Corporation.  All rights reserved.

All Classes and Methods in C:\dev\somelibfs.dll

That’s much better!

So, to recap, there are two types of pointers, as summarized in the following table:

Pointer F# Type Declaration Invocation
Unmanaged nativeint <type>* &&<type>
Managed byref <type> <type>& &type

In nearly all cases, a .NET developer should prefer the managed pointer. Leave the unmanaged risks with your C code.



I’d like to give special thanks to Jack Pappas, for finding (and helping me to understand and vanquish) this issue in fszmq.

About these ads

2 thoughts on “Managing Pointers, or F#’s Platform-Invoke “Gotcha”

  1. Pingback: Managing Pointers, or F#'s Platform-Invoke &quo...

  2. Pingback: Managing Pointers, or F#’s Platform-Invoke “Gotcha” | Functional programming | Scoop.it

Leave a Reply

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

WordPress.com Logo

You are commenting using your WordPress.com 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 )

Google+ photo

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

Connecting to %s