Chapter 14. Compatibility and Advanced Interoperation

In this chapter, you will look at everything you need to make F# interoperate well with other languages, not just within the .NET Framework but also using unmanaged code from F# and using F# from unmanaged code.

Warning

Throughout this book, I have made every effort to make sure the only language you need to understand is F#. However, in this chapter, it will help if you know a little C# (version 2.0), C++, or .NET Common IL, although I've kept the code in these languages to the minimum necessary.

Calling F# Libraries from C#

You can create two kinds of libraries in F#: libraries that are just designed to be used from F# only and libraries that are designed to be used from any .NET language. This is because F# utilizes the .NET type system in a rich and powerful way, so some types can look a little unusual to other .NET languages. However, these types will always look like they should when viewed from F#.

Although you could use any library written in F# from any .NET language, you need to follow a few rules if you want to make the library as friendly as possible. Here is how I would summarize these rules:

  • Always use a signature .fsi file or the private and internal to hide implementation details and document the API expected by clients.

  • Avoid public functions that return tuples.

  • If you want to expose a function that takes another function as a value, expose the value as a delegate.

  • Do not use union types in the API, but if you absolutely must use these types, add members to make them easier to use.

  • Avoid returning F# lists, and use the array, System.Collections.Generic.IEnumerable or better yet Collection or ReadOnlyCollection from the System.Collections.ObjectModel namespace instead.

  • When possible, place type definitions in a namespace, and place only value definitions within a module.

  • Be careful with the signatures you define on classes and interfaces; a small change in the syntax can make a big difference.

I will illustrate these points with examples in the following sections.

Returning Tuples

First, I'll talk about why you should avoid tuples. If you return a tuple from your function, you will force the user to reference FSharp.Core.dll. Also, the code needed to use the tuple just doesn't look that great from C#. Consider the following example where you define the function hourAndMinute that returns the hour and minute from a DateTime structure:

#light
module Strangelights.DemoModule
open System

// returns the hour and minute from the give date as a tuple
let hourAndMinute (time: DateTime) = time.Hour, time.Minute

// returns the hour from the given date
let hour (time: DateTime) = time.Hour
// returns the minutes from the given date
let minute (time: DateTime) = time.Minute

To call this from C#, you will need to follow the next example. If you are a Visual Studio user you'll need to create a C# project alongside your F# solution. To do this choose File

Returning Tuples
How to create a new C# project file

Figure 14.1. How to create a new C# project file

Next, you'll need to add a project reference from the C# project to the F# project. Then add the following C# class to the newly created project.

// !!! C# Source !!!
using System;
using Strangelights;
using Microsoft.FSharp.Core;

static class PrintClass {
        internal static void HourMinute() {
                // call the "hourAndMinute" function and collect the
                // tuple that's returned
                Tuple<int, int> t = DemoModule.hourAndMinute(DateTime.Now);
                // print the tuple's contents
                Console.WriteLine("Hour {0} Minute {1}", t.Item1, t.Item2);
        }
}

The results of this example, when compiled and executed, are as follows:

Hour 16 Minute 1

Although the C# in the previous example isn't too ugly, it would be better if the function had been split in two, one to return the hour and one to return the minute.

Exposing Functions That Take Functions As Parameters

If you want to expose functions that take other functions as parameters, the best way to do this is using delegates. Consider the following example that defines one function that exposes a function and one that exposes this as a delegate:

module Strangelights.DemoModule
open System

/// a function that provides filtering
let filterStringList f ra =
    ra |> Seq.filter f

// another function that provides filtering
let filterStringListDelegate (pred: Predicate<string>) ra =
        let f x = pred.Invoke(x)
        new ResizeArray<string>(ra |> Seq.filter f)

Although the filterStringList is considerably shorter than filterStringListDelegate, the users of your library will appreciate the extra effort you've put in to expose the function as a delegate. When you look at using the functions from C#, it's pretty clear why. The following example demonstrates calling filterStringList. To call your function, you need to create a delegate and then use the FuncConvert class to convert it into a FastFunc, which is the type F# uses to represent function values. As well as being pretty annoying for the user of your library, this also requires a dependency on FSharp.Core.dll that the user probably didn't want.

// !!! C# Source !!!
using System;
using System.Collections.Generic;
using Strangelights;
using Microsoft.FSharp.Core;
class MapOneClass {
        public static void MapOne() {
                // define a list of names
                List<string> names = new List<string>(
                        new string[] { "Stefany", "Oussama",
        "Sebastien", "Frederik" });

                // define a predicate delegate/function
                Converter<string, bool> pred =
                        delegate(string s) { return s.StartsWith("S"); };
                // convert to a FastFunc
                FastFunc<string, bool> ff =
                        FuncConvert.ToFastFunc<string, bool>(pred);

                // call the F# demo function
                IEnumerable<string> results =
                         DemoModule.filterStringList(ff, names);

                // write the results to the console
                foreach (var name in results) {
                        Console.WriteLine(name);
                }
        }
}

The results of this example, when compiled and executed, are as follows:

Stefany
Sebastien

Now, compare and contrast this to calling the filterStringListDelegate function, shown in the following example. Because you used a delegate, you can use the C# anonymous delegate feature and embed the delegate directly into the function call, reducing the amount of work the library user has to do and removing the compile-time dependency on FSharp.Core.dll.

// !!! C# Source !!!
using System;
using System.Collections.Generic;
using Strangelights;
class MapTwoClass {
        public static void MapTwo() {
                // define a list of names
                List<string> names = new List<string>(
                        new string[] { "Aurelie", "Fabrice",
            "Ibrahima", "Lionel" });

                // call the F# demo function passing in an
                // anonymous delegate
                List<string> results =
                        DemoModule.filterStringListDelegate(
                                delegate(string s) { return
s.StartsWith("A"); }, names);

                // write the results to the console
                foreach (var s in results) {
                        Console.WriteLine(s);
                }
        }
}

The results of this example, when compiled and executed, are as follows:

Aurelie

Using Union Types

You can use union types from C#, but because C# has no real concept of a union type, they do not look very pretty when used in C# code. In this section, you will examine how you can use them in C# and how you as a library designer can decide whether your library will expose them (though personally I recommend avoiding exposing them in cross-language scenarios).

For the first example, you will define the simple union type Quantity, which consists of two constructors, one containing an integer and the other a floating-point number. You also provide the function getRandomQuantity() to initialize a new instance of Quantity.

module Strangelights.DemoModule
open System

// type that can represent a discrete or continuous quantity
type Quantity =
| Discrete of int
| Continuous of float
// initalize random number generator
let rand = new Random()
// create a random quantity
let getRandomQuantity() =
    match rand.Next(1) with
    | 0 -> Quantity.Discrete (rand.Next())
    | _ ->
        Quantity.Continuous
            (rand.NextDouble() * float (rand.Next()))

Although you provide getRandomQuantity() to create a new version of the Quantity type, the type itself provides static methods for creating new instances of the different constructors that make up the type. These static methods are available on all union types that are exposed by the assembly by default; you do not have to do anything special to get the compiler to create them. The following example shows how to use these methods from C#:

using System;
using Strangelights;

static class GetQuantityZeroClass {
        public static void GetQuantityZero() {
                // initialize both a Discrete and Continuous quantity
                DemoModule.Quantity d = DemoModule.Quantity.Discrete(12);
                DemoModule.Quantity c = DemoModule.Quantity.Continuous(12.0);
        }
}

Now you know how to create union types from C#, so the next most important task is being able to determine the constructor to which a particular Quantity value belongs. You can do this in three ways. I cover the first two in the next two code examples, and I cover the third at the end of this section.

The first option is that you can switch on the value's Tag property. This property is just an integer, but the compiled version of the union type provides constants, always prefixed with tag_, to help you decode the meaning of the integer. If you want to use the Tag property to find out what kind of Quantity you have, you would usually write a switch statement, as shown in the following example:

// !!! C# Source !!!
using System;
using Strangelights;

static class GetQuantityOneClass {
        public static void GetQuantityOne() {
                // get a random quantity
                DemoModule.Quantity q = DemoModule.getRandomQuantity();
// use the .Tag property to switch over the quatity
                switch (q.Tag) {
                        case DemoModule.Quantity.tag_Discrete:
                                Console.WriteLine("Discrete value: {0}", q.Discrete1);
                                break;
                        case DemoModule.Quantity.tag_Continuous:
                                Console.WriteLine("Continuous value: {0}",
q.Continuous1);
                                break;
                }
        }
}

The results of this example, when compiled and executed, are as follows:

Discrete value: 65676

If you prefer, the compiled form of the union type also offers a series of methods, all prefixed with Is. This allows you to check whether a value belongs to a particular constructor within the union type. For example, on the Quantity union type, two methods, IsDiscrete() and IsContinuous(), allow you to check whether the Quantity is Discrete or Continuous. The following example demonstrates how to use them:

// !!! C# Source !!!
using System;
using Strangelights;

static class GetQuantityTwoClass {
        public static void GetQuantityTwo() {
                // get a random quantity
                DemoModule.Quantity q = DemoModule.getRandomQuantity();
                // use if ... else chain to display value
                if (q.IsDiscrete()) {
                        Console.WriteLine("Discrete value: {0}", q.Discrete1);
                }
                else if (q.IsContinuous()) {
                        Console.WriteLine("Continuous value: {0}", q.Continuous1);
                }
        }
}

The results of this example, when compiled and executed, are as follows:

Discrete value: 2058

Neither option is particularly pleasing because the code required to perform the pattern matching is quite bulky. There is also a risk that the user could get it wrong and write something like the following example where they check whether a value is Discrete and then mistakenly use the Continuous1 property. This would lead to a NullReferenceException being thrown.

DemoModule.EasyQuantity q = DemoModule.getRandomQuantity();
if (q.IsDiscrete()) {
    Console.WriteLine("Discrete value: {0}", q.Continuous1);
}

To give your libraries' users some protection against this, it is a good idea to add members to union types that perform the pattern matching for them. The following example revises the Quantity type to produce EasyQuantity, adding two members to transform the type into an integer or a floating-point number:

module Strangelights.ImprovedModule
open System

// type that can represent a discrete or continuous quantity
// with members to improve interoperability
type EasyQuantity =
| Discrete of int
| Continuous of float
    // convert quantity to a float
    member x.ToFloat() =
        match x with
        | Discrete x -> float x
        | Continuous x -> x
    // convert quantity to a integer
    member x.ToInt() =
        match x with
        | Discrete x -> x
        | Continuous x -> int x

// initalize random number generator
let rand = new Random()

// create a random quantity
let getRandomEasyQuantity() =
    match rand.Next(1) with
    | 0 -> EasyQuantity.Discrete (rand.Next())
    | _ ->
        EasyQuantity.Continuous
            (rand.NextDouble() * float (rand.Next()))

This will allow the user of the library to transform the value into either an integer or a floating-point without having to worry about pattern matching, as shown in the following example:

// !!! C# Source !!!
using System;
using Strangelights;

class GetQuantityThreeClass {
        public static void GetQuantityThree() {
                // get a random quantity
                ImprovedModule.EasyQuantity q = ImprovedModule.get
RandomEasyQuantity();
                // convert quantity to a float and show it
                Console.WriteLine("Value as a float: {0}", q.ToFloat());
        }
}

Using F# Lists

It is entirely possibly to use F# lists from C#, but I recommend avoiding this since a little work on your part will make things seem more natural for C# programmers. For example, it is simple to convert a list to an array using the List.toArray function, to a System.Collections.Generic.List using the new ResizeArray<_>() constructor, or to a System.Collections.Generic.IEnumerable using the List.toSeq function. These types are generally a bit easier for C# programmers to work with, especially System.Array and System.Collections.Generic.List, because these provide a lot more member methods. You can do the conversion directly before the list is returned to the calling client, making it entirely feasible to use the F# list type inside your F# code. MSDN recommends using the yet Collection or ReadOnlyCollection from the System.Collections.ObjectModel namespace to expose collections. Both of these classes have a constructor that accepts an IEnumerable, and so can be constructed from an F# list.

If you need to return an F# list directly, you can do so, as shown in the following example:

module Strangelights.DemoModule

// gets a preconstructed list
let getList() =
    [1; 2; 3]

To use this list in C#, you typically use a foreach loop:

using System;
using Strangelights;
using Microsoft.FSharp.Core;
using Microsoft.FSharp.Collections;
class Program {
        static void Main(string[] args) {
                // get the list of integers
                List<int> ints = DemoModule.getList();

                // foreach over the list printing it
                foreach (int i in ints) {
                        Console.WriteLine(i);
                }
        }
}

The results of this example, when compiled and executed, are as follows:

1
2
3

Defining Types in a Namespace

If you are defining types that will be used from other .NET languages, then you should place them inside a namespace rather than inside a module. This is because modules are compiled into what C# and other .NET languages consider to be a class, and any types defined within the module become inner classes of that type. Although this does not present a huge problem to C# users, the C# client code does look cleaner if a namespace is used rather than a module. This is because in C# you can open namespaces only using the using statement, so if a type is inside a module, it must always be prefixed with the module name when used from C#.

Let's take a look at an example of doing this. The following example defines TheClass, which is defined inside a namespace. You also want to provide some functions that go with this class. These can't be placed directly inside a namespace because values cannot be defined inside a namespace. In this case, you define a module with a related name, such as TheModule, to hold the function values.

namespace Strangelights
open System.Collections.Generic

// this is a counter class
type TheClass(i) =
    let mutable theField = i
    member x.TheField
        with get() = theField
    // increments the counter
    member x.Increment() =
        theField <- theField + 1
// decrements the count
    member x.Decrement() =
        theField <- theField - 1

// this is a module for working with the TheClass
module TheModule = begin
    // increments a list of TheClass
    let incList (theClasses: List<TheClass>) =
        theClasses |> Seq.iter (fun c -> c.Increment())
    // decrements a list of TheClass
    let decList (theClasses: List<TheClass>) =
        theClasses |> Seq.iter (fun c -> c.Decrement())
end

Using the TheClass class in C# is now straightforward because you do not have to provide a prefix, and you can also get access to the related functions in TheModule easily:

// !!! C# Source !!!
using System;
using System.Collections.Generic;
using Strangelights;

class Program {
        static void UseTheClass() {
                // create a list of classes
                List<TheClass> theClasses = new List<TheClass>() {
                        new TheClass(5),
                        new TheClass(6),
                        new TheClass(7)};

                // increment the list
                TheModule.incList(theClasses);

                // write out each value in the list
                foreach (TheClass c in theClasses) {
                        Console.WriteLine(c.TheField);
                }
        }
        static void Main(string[] args) {
                UseTheClass();
        }
}

Defining Classes and Interfaces

In F# there are two ways you can define parameters for functions and members of classes: the "curried" style where members can be partially applied and the "tuple" style where all members must be given at once. When defining classes, your C# clients will find it easier to use your classes if you use the tuple style.

Consider the following example in which you define a class in F#. Here one member has been defined in the curried style, called CurriedStyle, and the other has been defined in the tuple style, called TupleStyle.

namespace Strangelights

type DemoClass(z: int) =
    // method in the curried style
    member this.CurriedStyle x y = x + y + z
    // method in the tuple style
    member this.TupleStyle (x, y) = x + y + z

When viewed from C#, the member CurriedStyle has the following signature:

public FastFunc<int, int> CurriedStyle(int x)

whereas the TupleStyle will have the following signature:

public int TupleStyle(int x, int y);

If you want to use both methods from C#, you would end up with code that looks like the following:

// !!! C# Source !!!
using System;
using Strangelights;
using Microsoft.FSharp.Core;

class Program {
        static void UseDemoClass() {
                DemoClass c = new DemoClass(3);
                FastFunc<int, int> ff = c.CurriedStyle(4);
                int result = ff.Invoke(5);
                Console.WriteLine("Curried Style Result {0}", result);
                result = c.TupleStyle(4, 5);
                Console.WriteLine("Tuple Style Result {0}", result);
        }
        static void Main(string[] args) {
                UseDemoClass();
        }
}

It is clear from this sample that users of your library will be much happier if you use the tuple style for the public members of your classes.

Specifying abstract members in interfaces and classes is slightly more complicated because you have a few more options. The following example demonstrates this:

namespace Strangelights

type IDemoInterface =
    // method in the curried style
    abstract CurriedStyle: int -> int -> int
    // method in the tupled style
    abstract TupleStyle: (int * int) -> int
    // method in the C# style
    abstract CSharpStyle: int * int -> int
    // method in the C# style with named arguments
    abstract CSharpNamedStyle: x : int * y : int -> int

Note that the only difference between OneArgStyle and MultiArgStyle is that the latter is not surrounded by parentheses. This small difference in the F# definition has a big effect on the signature as seen from C#. With the former, you see the following signature:

int OneArgStyle(Tuple<int, int>);

whereas the latter is seen as the following signature:

int MultiArgStyle(int, int);

The latter is a good bit friendlier for the C# user. However, you can take it a bit further and add names to each of your parameters. This won't change the signature the C# user will use when implementing the method, but it will change the names they see when using Visual Studio tools to implement the interface. Furthermore, some other .NET languages treat argument names as significant. This may sound like a small difference, but it will make implementing your interface a lot easier, because the implementer will have a much better idea of what the parameters of the method actually mean.

The following example shows the C# for implementing the interface IDemoInterface defined in the previous example. It makes it clear that C# users will be happier with interfaces containing methods specified using either MultiArgStyle or NamedArgStyle.

// !!! C# Source !!!
using System;
using Strangelights;
using Microsoft.FSharp.Core;

// shows how to implement an interface
// that has been created in F#
class DemoImplementation : IDemoInterface {
// curried style implementation
        public FastFunc<int, int> CurriedStyle(int x) {
                // create a delegate to return
                Converter<int, int> d =
                        delegate(int y) { return x + y; };
                // convert delegate to a FastFunc
                return FuncConvert.ToFastFunc(d);
        }

        // tuple style implementation
        public int TupleStyle(Tuple<int, int> t) {
                return t.Item1 + t.Item2;
        }

        // C# style implementation
        public int CSharpStyle(int x, int y) {
                return x + y;
        }

        // C# style implementation, named arguments
        // make no difference here
        public int CSharpNamedStyle(int x, int y) {
                return x + y;
        }
}

Calling Using COM Objects

Most programmers who work with the Windows platform will be familiar with the Component Object Model (COM). To a certain extent the .NET Framework was meant to replace COM, but the system remains popular and is likely to be with us for some time. Many of the APIs in Windows are exposed as COM objects, and although more and more now have managed equivalents within the .NET Framework, there are still some without managed equivalents. Also, there are still some vendors that sell software that exposes its APIs via COM.

The .NET Framework was designed to interoperate well with COM, and calling COM components is generally quite straightforward. Calling COM components is always done through a managed wrapper that takes care of calling the unmanaged code for you. You can produce these wrappers using a tool called TlbImp.exe, the Type Library Importer, that ships with the .NET SDK.

Note

You can find more information about the TlbImp.exe tool at the following site: http://msdn2.microsoft.com/en-us/library/tt0cf3sx(VS.80).aspx.

However, despite the existence of TlbImp.exe, if you find yourself in a situation where you need to use a COM component, first check whether the vendor provides a managed wrapper for it, called Primary Interop Assemblies. For more information on Primary Interop Assemblies, see the next section "Using COM Style APIs."

However, sometimes it is necessary to use TlbImp.exe directly. Fortunately, this is very straightforward. Normally all that is necessary is to pass TlbImp.exe the location of the .dll that contains the COM component, and the managed wrapper will be placed in the current directory. If you want to create a managed wrapper for the Microsoft Speech API, you would use the following command line:

tlbimp "C:Program FilesCommon FilesMicrosoft SharedSpeechsapi.dll"

Note

There are two command-line switches that I find useful with TlbImp.exe. These are /out:, which controls the name and location of the resulting manage wrapper, and /keyfile:, which can provide a key to sign the output assembly.

The resulting .dll is a .NET assembly and can be used just like any .NET assembly, by referencing it via the fsc.exe command-line switch -r. A useful side effect of this is if the API is not well documented, you can use an assembly browser, such as Reflector discussed in Chapter 12, to find out more about the structure of the API.

After that, the worst thing I can say about using managed wrappers is you might find the structure of these assemblies a little unusual since the COM model dictates structure and therefore they do not share the same naming conversions as most .NET assemblies. You will notice that all classes in the assembly are postfixed with the word Class and each one is provided with a separate interface: this is just a requirement of COM objects. The following example shows the wrapper for the Microsoft Speech API that you created in the previous example being used:

open SpeechLib

let main() =
    // create an new instance of a com class
    // (these almost always end with "Class")
    let voice = new SpVoiceClass()
    // call a method Speak, ignoring the result
    voice.Speak("Hello world", SpeechVoiceSpeakFlags.SVSFDefault) |> ignore

do main()

Using COM Style APIs

Rather than using COM libraries directly, creating your own wrappers, it's more likely you'll have to use COM style API's. This is because many vendors now distribute their applications with Primary Interop Assemblies. These are pre-created COM wrappers, so generally you won't need to bother creating wrappers with TlbImp.exe yourself.

Note

More information about Primary Interop Assemblies can be found on MSDN: http://msdn.microsoft.com/en-us/library/aax7sdch.aspx.

Although Primary Interop Assemblies are just ordinary .NET assemblies, there are typically a few quirks you have to watch out for, which the following outlines:

  • Some arrays and collections often start at one rather than zero.

  • There are often methods that are composed of large numbers of optional arguments. Fortunately, F# supports optional and named arguments to make interacting with these more natural and easier to understand.

  • Many properties and methods have a return type of object. The resulting object needs to be cast to its true type.

  • COM classes contain unmanaged resources that need to be disposed of. However, these classes do not implement the standard .NET IDisposable interface, meaning they cannot be used in an F# use binding. Fortunately, you can use F# object expressions to easily implement IDisposable.

A key difference from C# when interacting with COM in F# is you must always create instances of objects not interfaces. This may sound strange, but in COM libraries each object typically has an interface and a class that implements it. In C# if you try and create an instance of a COM interface using the new keyword in C#, the compiler will automatically redirect the call to appropriate class, but this is not the case in F#.

Interacting with Microsoft Office is probably the most common reason for interacting with COM style libraries. Here is a listing that reads information from an excel spreadsheet.

open System
open Microsoft.Office.Interop.Excel

let main() =
    // initalize an excel application
    let app = new ApplicationClass()

    // load a excel work book
    let workBook = app.Workbooks.Open(@"Book1.xls", ReadOnly = true)
// ensure work book is closed corectly
    use bookCloser = { new IDisposable with
                        member x.Dispose() = workBook.Close() }

    // open the first worksheet
    let worksheet = workBook.Worksheets.[1] :?> _Worksheet

    // get the A1 ceel and all surround cells
    let a1Cell = worksheet.Range("A1")
    let allCells = a1Cell.CurrentRegion
    // load all cells into a list of lists
    let matrix =
        [ for row in allCells.Rows ->
            let row = row :?> Range
            [ for cell in row.Columns ->
                let cell = cell :?> Range
                cell.Value2 ] ]

    // close the workbook
    workBook.Close()

    // print the matrix
    printfn "%A" matrix

do main()

Notice how this sample deals with some of the quirks I mentioned earlier. You implement IDisposable and bind it to bookCloser to ensure the work book is closed, even in the case of an error. The Open method actually has 15 arguments, though you only use two: .Open(@"Book1.xls", ReadOnly = true). The first worksheet is an index one: workBook.Worksheets.[1]. Finally, each row must be upcast in order to use it: let row = row :?> Range

Using P/Invoke

P/Invoke, or platform invoke to give its full name, is used to call unmanaged flat APIs implemented in DLLs and is called using the C or C++ calling conventions. The most famous example of this is the Win32 API, a vast library that exposes all the functionality built into Windows.

To call a flat unmanaged API, you must first define the function you want to call; you can do this in two parts. First, you use the DllImport attribute from the System.Runtime.InteropServices namespace, which allows you to define which .dll contains the function you want to import, along with some other optional attributes. Then, you use the keyword, extern, followed by the signature of the function to be called in the C style, meaning you give the return type, the F# type, the name of the function, and finally the types and names of the parameters surrounded by parentheses. The resulting function can then be called as if it were an external .NET method.

The following example shows how to import the Windows function MessageBeep and then call it:

open System.Runtime.InteropServices

// declare a function found in an external dll
[<DllImport("User32.dll")>]
extern bool MessageBeep(uint32 beepType)

// call this method ignoring the result
MessageBeep(0ul) |> ignore

Note

The trickiest part of using P/Invoke can often be working out what signature to use to call the function. The web site http://pinvoke.net contains a list of signatures for common APIs in C# and VB .NET, which are similar to the required signature in F#. The site is a wiki, so feel free to add the F# signatures as you find them.

The following code shows how to use P/Invoke when the target function expects a pointer. You need to note several points about setting up the pointer. When defining the function, you need to put an asterisk (*) after the type name to show that you are passing a pointer. You need to define a mutable identifier before the function call to represent the area of memory that is pointed to. This may not be global, in the top level, but it must be part of a function definition. This is why you define the function main, so the identifier status can be part of the definition of this. Finally, you must use the address of operator (&&) to ensure the pointer is passed to the function rather than the value itself.

Tip

This compiled code will always result in a warning because of the use of the address of operator (&&). This can be suppressed by using the compiler flag --nowarn 51 or the command #nowarn 51.

open System.Runtime.InteropServices

// declare a function found in an external dll
[<DllImport("Advapi32.dll")>]
extern bool FileEncryptionStatus(string filename, uint32* status)

let main() =
    // declare a mutable idenifier to be passed to the function
    let mutable status = 0ul
    // call the function, using the address of operator with the
    // second parameter
    FileEncryptionStatus(@"C:	est.txt", && status) |> ignore
// print the status to check it has be altered
    printfn "%d" status

main()

The results of this example, when compiled and executed (assuming you have a file at the root of your C: drive called test.txt that is encrypted), are as follows:

1ul

Note

P/Invoke also works on Mono while in F# the syntax is exactly the same. The tricky bit is ensuring the library your invoking is available on all the platforms you're targeting and following the different naming conventions of libraries on all the different platforms. For a more detailed explanation see the article available at: http://www.mono-project.com/Interop_with_Native_Libraries.

The DllImport attribute has some useful functions that can be set to control how the unmanaged function is called. I summarize them in Table 14-1.

Table 14.1. Useful Attributes on the DllImport Attribute

Attribute Name

Description

CharSet

This defines the character set to be used when marshaling string data. It can be CharSet.Auto, CharSet.Ansi, or CharSet.Unicode.

EntryPoint

This allows you to set the name of the function to be called. If no name is given, then it defaults to the name of the function as defined after the extern keyword.

SetLastError

This is a Boolean value that allows you to specify whether any error that occurs should be marshaled and therefore available by calling the Marshell.GetLastWin32Error() method.

Note

As with COM components, the number of flat unmanaged APIs that have no .NET equivalent is decreasing all the time. Always check whether a managed equivalent of the function you are calling is available, which will generally save you lots of time.

Using Inline IL

Inline IL allows you to define your function's body directly in intermediate language (IL), the language into which F# is compiled. This was mainly added to the language to implement certain low operators and functions such as addition and box and not. It is rare that you will need to use this feature because the F# library FSharp.Core.dll already expose all of the functionality built into IL that you are likely to need. However, for those rare occasions where you need to do something that you can't do in F# but you can in IL, it's nice to know you have the option of inline IL.

Using inline IL is simple. You just place the IL instructions you would like between parentheses with pound signs, as in (# #). The IL instructions are placed inside a string and use the standard notation that can be compiled with ilasm.exe. This must be a correctly formed IL, or you will get a compiler error. You can then pass parameters to your IL instruction. They are pushed onto the IL evaluation stack. You must also use the standard colon notation to tell the compiler what the return type will be. This is placed inside the parentheses. You will also need to be explicit about the types of the parameters since the compiler has no way of inferring their types.

You'll now look at an example of using inline IL. Imagine for whatever reason that you do not want to use the add and subtract operators defined in the F# base library fslib.dll, because you may want to replace them with your own functions. You define two functions, add and sub, whose bodies are defined using IL:

// declare add function using the IL add instruction
let add (x:int) (y:int) = (# "add" x y : int #)
// declare sub function using the IL sub instruction
let sub (x:int) (y:int) = (# "sub" x y : int #)

// test these functions
let x = add 1 1
let y = sub 4 2

// print the results
printfn "x: %i y: %i" x y

The results of this example, when compiled and executed, are as follows:

x: 2 y: 2

The programmer should be careful when using this technique because it is trivial to write a program that does not make any sense, and the compiler is unable to warn you about this. Consider the following program where you revise your previous example to replace the "add" instruction with a "ret" instruction, which means "return a value" and makes no sense in this context. This example will compile without error or warning; on execution, you will get an error.

// create a faulty add function
let add (x:int) (y:int) = (# "ret" x y : int #)

// attempt to use fault function
let x = add 1 1

The results of this example, when compiled and executed, are as follows:

Unhandled Exception: System.InvalidProgramException: Common Language Runtime
detected an invalid program.
   at Error.add(Int32 x, Int32 y)

Note

There is a tool distributed with .NET SDK that can help you detect these kinds of errors. The tool is called peverify.exe, and you can find more information about peverfiy.exe at http://msdn2.microsoft.com/en-us/library/62bwd2yd(vs.80).aspx.

Using F# from Native Code via COM

Although it is more likely that you will want to call native code from F# code, there may be some circumstances that you would want to call F# library functions from native code. For example, suppose you have a large application written in C++, and perhaps you are happy for the user interface to remain in C++ but want to migrate some logic that performs complicated mathematical calculations to F# for easier maintenance. In this case, you would want to call F# from native code. The easiest way to do this is to use the tools provided with .NET to create a COM wrapper for your F# assembly. You can then use the COM runtime to call the F# functions from C++.

To expose functions though COM, you need to develop them in a certain way. First, you must define an interface that will specify the contract for your functions, the members of the interface must be written using named arguments (see the section on "Calling F# Libraries from C#" earlier in the chapter), and the interface itself must be marked with the System.Runtime.InteropServices.Guid attribute. Then you must provide a class that implements the interface. This too must be marked the System.Runtime. InteropServices.Guid attribute and also the System.Runtime.InteropServices.ClassInterface, and you should always pass the ClassInterfaceType.None enumeration member to the ClassInterface attribute constructor to say that no interface should be automatically generated.

Let's look at an example of doing this. Suppose you want to expose two functions to your unmanaged client called Add and Sub. Create an interface IMath in the namespace Strangelights, and then create a class Math to implement this interface. You then need to ensure that both the class and the interface are marked with the appropriate attributes. The resulting code is as follows:

namespace Strangelights
open System
open System.Runtime.InteropServices
// define an interface (since all COM classes must
// have a seperate interface)
// mark it with a freshly generated Guid
[<Guid("6180B9DF-2BA7-4a9f-8B67-AD43D4EE0563")>]
type IMath =
    abstract Add : x: int * y: int -> int
    abstract Sub : x: int * y: int -> int


// implement the interface, the class must:
// - have an empty constuctor
// - be marked with its own guid
// - be marked with the ClassInterface attribute
[<Guid("B040B134-734B-4a57-8B46-9090B41F0D62");
ClassInterface(ClassInterfaceType.None)>]
type Math() =
    interface IMath with
        member this.Add(x, y) = x + y
        member this.Sub(x, y) = x - y

The functions Add and Sub are of course simple, so there is no problem implementing them directly in the body of the Math class. If you need to break them down into other helper functions outside of the class, then this would not have been a problem. It is fine to implement your class members any way you see fit. You simply need to provide the interface and the class so the COM runtime has an entry point into your code.

Now comes arguably the most complicated part of the process, registering the assembly so the COM runtime can find it. To do this, you need to use a tool called RegAsm.exe. Suppose you compiled the previous sample code into a .NET .dll called ComLibrary.dll, then you would need to call RegAsm.exe twice using the following command lines:

regasm comlibrary.dll /tlb:comlibrary.tlb
regasm comlibrary.dll

The first time is to create a type library file, a .tlb file, which you can use in your C++ project to develop against. The second registers the assembly itself so the COM runtime can find it. You will also need to perform these two steps on any machine to which you deploy your assembly.

The C++ to call the Add function is as follows. The development environment and how you set up C++ compiler will also play a large part in getting this code to compile. In this case, I created a Visual Studio project, choosing a console application template and activated ATL. Notice the following about this source code:

  • The #import command tells the compiler to import your type library. You may need to use the full path to its location. The compiler will also automatically generate a header file, in this case comlibrary.tlh, located in the debug or release directory. This is useful because it lets you know the functions and identifiers that are available as a result of your type library.

  • You then need to initialize the COM runtime. You do this by calling the CoInitialize function.

  • You then need to declare a pointer to the IMath interface you created. You do this via the code comlibrary::IMathPtr pDotNetCOMPtr;. Note how the namespace comes from the library name rather than the .NET namespace.

  • Next, you need to create an instance of your Math class. You achieve this by calling the CreateInstance, method passing it the GUID of the Math class. Fortunately, there is a constant defined for this purpose.

  • If this was successful, you can call the Add function. Note how the result of the function is actually an HRESULT, a value that will tell you whether the call was successful. The actual result of the function is passed out via an out parameter.

// !!! C++ Source !!!
#include "stdafx.h"
// import the meta data about out .NET/COM library
#import "..ComLibraryComLibrary.tlb" named_guids raw_interfaces_only

// the applications main entry point
int _tmain(int argc, _TCHAR* argv[])
{
        // initialize the COM runtime
        CoInitialize(NULL);
        // a pointer to our COM class
    comlibrary::IMathPtr pDotNetCOMPtr;

        // create a new instance of the Math class
        HRESULT hRes = pDotNetCOMPtr.CreateInstance(comlibrary::CLSID_Math);
        // check it was created okay
        if (hRes == S_OK)
        {
                // define a local to hold the result
        long res = 0L;
                // call the Add function
                hRes = pDotNetCOMPtr->Add(1, 2, &res);
                // check Add was called okay
            if (hRes == S_OK)
            {
                        // print the result
            printf("The result was: %ld", res);
        }

                // release the pointer to the math COM class
        pDotNetCOMPtr.Release();
        }

        // uninitialise the COM runtime
        CoUninitialize ();

}

The results of this example, when compiled and executed, are as follows:

The result was: 3

When you execute the resulting executable, you must ensure that ComLibrary.dll is in the same directory as the executable, or the COM runtime will not be able to find it. If you intend that the library be used by several clients, then I strongly recommend that you sign the assembly and place it in the GAC. This will allow all clients to be able to find it without having to keep a copy in the directory with them.

Hosting the CLR

An alternative option to using COM to integrate for integrating F# code into existing C/C++ applications is to custom host the CLR. The CLR is just a C++ application and there are some .lib files available that allow you to link to it in a standard C++ application. The code required to host the CLR is slightly more complex than the code required to load a COM library, but the complexity of registering COM libraries is removed. You can also get very fine grain control over the behavior of the CLR using these techniques, although you'll probably find the default behavior is fine for most situations. This technique is not suitable for high performance fine grain calls between C++ and F# since you have less control over the signatures used and the CLR method invocation is done using the reflection, meaning the module and method are found using string comparison which can be quite slow. However, if you are calling quite significant portions of F# code will probably find the cost of invocation is amortized quickly.

Let's have a look at the code required to invoke an F# method using custom hosting of the CLR. The code is based on a Visual Studio C++ console project. You need to notice the following about this source code:

  • The #include <mscoree.h> tells the C++ compiler to import the header files that contains the functions and interfaces for loading the CLR.

  • You then need to load and initialize the CLR. You do this by calling CorBindToRuntimeEx followed by the Start method on the resulting object.

  • You can call the method ExecuteInDefaultAppDomain to invoke a method in a CLR assembly.

The full C++ listing is as follows:

// !!! C++ Source !!!
#include "stdafx.h"
// the head file that exposes the C++ methods and interfaces
#include <mscoree.h>

// the applications main entry point
int _tmain(int argc, _TCHAR* argv[])
{
    // pointer to the CLR host object
    ICLRRuntimeHost *pClrHost = NULL;
// invoke the method that loads the CLR
    HRESULT hrCorBind = CorBindToRuntimeEx(
        NULL,   // CLR version - NULL load the latest available
        L"wks", // GC Type ("wks" = workstation or "svr" =  Server)
        0,
        CLSID_CLRRuntimeHost,
        IID_ICLRRuntimeHost,
        (PVOID*)&pClrHost);

    // Start the CLR.
    HRESULT hrStart = pClrHost->Start();

        // Define the assembly, type, function to load,
        // as well as the parameter and variable for the return value
    LPCWSTR pwzAssemblyPath = L"fslib.dll";
    LPCWSTR pwzTypeName = L"Strangelights.TestModule";
    LPCWSTR pwzMethodName = L"print";
    LPCWSTR pwzMethodArgs = L"Hello world!";
    DWORD retVal;

    // Load an assembly and execute a method in it.
    HRESULT hrExecute = pClrHost->ExecuteInDefaultAppDomain(
        pwzAssemblyPath, pwzTypeName,
        pwzMethodName, pwzMethodArgs,
        &retVal);

        // print the result
    printf("retVal: %i", retVal);
}

In addition to this code, you need to link to mscoree.lib, which is available in Windows Platform SDK. The only "special" thing you need to know on the F# side of things is that the function invoked must have the signature string -> int. Here is very simple example of an F# function that will work with the C++ listing:

module Strangelights.TestModule

// function will be invoked
let print s =
    printfn "%s" s
    0

The results of this example, when compiled and executed, are as follows:

Hello world!
retVal: 0

Note

For more information on custom CLR hosting see Alessandro Catorcini and Piotr Puszkiewicz's excellent MSDN article: http://msdn.microsoft.com/en-us/magazine/cc163567.aspx.

Summary

In this chapter, you saw some advanced techniques in F# for compatibility and interoperation. Although these techniques are definitely some of the most difficult to master, they also add a huge degree of flexibility to your F# programming.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset