Working with SafeHandles in F#

191 views Asked by At

I'm working with some F# code that uses platform invoke. One of the APIs I'm using returns a handle. Instead of using a nativeint, I've implemented my own SafeHandle (specifically SafeHandleMinusOneIsInvalid.) This makes working with the module containing the pinvoke signature a little clunky. Here is an example:

type MySafeHandle() = 
    inherit SafeHandleZeroOrMinusOneIsInvalid(true)

    override this.ReleaseHandle() =
        NativeMethods.FreeHandle(base.handle)            
        true

module NativeMethods = 
    [<DllImport("mylibrary.dll")>]
    extern void GetHandle([<Out>]MySafeHandle& handle)
    [<DllImport("mylibrary.dll")>]
    extern void FreeHandle(nativeint handle)

This won't compile because the module and the class recursively reference each other, which doesn't work. If I move the module above MySafeHandle, then GetHandle won't see the SafeHandle.

I can't move the platform invoke methods inside of MySafeHandle since it appears that extern methods in F# must be in modules (even though the compiler won't stop you from trying to put them in a class).

It also appears that F#'s recursive types don't work between a module and a class, just classes.

Is there a solution to this problem that does not require declaring two different modules? Ideally I'd like to keep all of my platform invoke code organized into one module.

1

There are 1 answers

0
Nikon the Third On

Well I know of one, because I had the same problem myself.

The thing is, it's kinda ugly I guess:

It involves a static reference that you set to the imported function later in the module.

type MySafeHandle() = 
    inherit SafeHandleZeroOrMinusOneIsInvalid(true)
    static let freeHandle = ref Unchecked.defaultof<_>
    static member internal SetFreeHandleRef value = freeHandle := value

    override this.ReleaseHandle() =
        !freeHandle base.handle            
        true

module NativeMethods = 
    [<DllImport("mylibrary.dll")>]
    extern void GetHandle([<Out>]MySafeHandle& handle)
    [<DllImport("mylibrary.dll")>]
    extern void FreeHandle(nativeint handle)

    MySafeHandle.SetFreeHandleRef FreeHandle