CHAPTER 13

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.


Caution 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#, 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 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#.

So, 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 summarize these rules:

  • Always use a signature .fsi file 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 System.Collections.Generic.List 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 fslib.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

let hourAndMinute (time : DateTime) = time.Hour, time.Minute

To call this from C#, you will need to follow the next example. Although this 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.

static void HourMinute()
{
    Tuple<int, int> t = DemoModule.hourAndMinute(DateTime.Now);
    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

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:

#light
open System
open System.Collections.Generic

let filterStringList f (l : List<string>) = l |> Seq.filter f
let filterStringListDelegate
    (del : Predicate<string>)
    (l : List<string>) =
        let f x = del.Invoke(x)
        new List<string>(l |> 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 fslib.dll that the user probably didn't want.

static void MapOne()
{
    List<string> l = new List<string>(
        new string[] { "Stefany", "Oussama",
            "Sebastien", "Frederik" });
    Converter<string, bool> pred =
        delegate (string s) { return s.StartsWith("S");};
    FastFunc<string, bool> ff =
        FuncConvert.ToFastFunc<string, bool>(pred);
    IEnumerable<string> ie =
         DemoModule.filterStringList(ff, l);
    foreach (string s in ie)
    {
        Console.WriteLine(s);
    }
}

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 fslib.dll.

static void MapTwo(
{
    List<string> l = new List<string>(
        new string[] { "Aurelie", "Fabrice",
            "Ibrahima", "Lionel" });
    List<string> l2 =
        DemoModule.filterStringListDelegate(
            delegate(string s) { return s.StartsWith("A"); }, l);
    foreach (string s in l2)
    {
        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.

#light
open System

type Quantity =
| Discrete of int
| Continuous of float

let rand = new Random()

let getRandomQuantity() =
    match rand.Next(1) with
    | 0 -> Quantity.Discrete (rand.Next())
    | _ ->
        Quantity.Continuous
            (rand.NextDouble() * float_of_int (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#:

static void GetQuantityZero()
{
    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. So 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:

static void GetQuantityOne()
{
    DemoModule.Quantity q = DemoModule.getRandomQuantity();
    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:

static void GetQuantityTwo()
{
    DemoModule.Quantity q = DemoModule.getRandomQuantity();
    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.getRandomEasyQuantity();
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:

#light
open System

let rand = new Random()

type EasyQuantity =
| Discrete of int
| Continuous of float
    with
        member x.ToFloat() =
            match x with
            | Discrete x -> float_of_int x
            | Continuous x -> x
        member x.ToInt() =
            match x with
            | Discrete x -> x
            | Continuous x -> int_of_float x
    end

let getRandomEasyQuantity() =
    match rand.Next(1) with
    | 0 -> EasyQuantity.Discrete (rand.Next())
    | _ ->
        EasyQuantity.Continuous
            (rand.NextDouble() * float_of_int (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:

static void GetQuantityThree()
{
    DemoModule.EasyQuantity q = DemoModule.getRandomEasyQuantity();
    Console.WriteLine("Value as a float: {0}", q.ToFloat());
}

Using F# Lists

It is entirely possible 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.to_array function, to a System.Collections.Generic.List using the List.to_ResizeArray function, or to a System.Collections.Generic.IEnumerable using the List.to_seq 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.

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

let getList() =
    [1; 2; 3]

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

static void GetList()
{
    Microsoft.FSharp.Collections.List<int> l = DemoModule.getList();
    foreach (int i in l)
    {
        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 using only the using statement, so if a type is inside a module, it must always be prefixed with the module name when used from C#.

I'll now show you an example of doing this. The following example defines the class 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 TheModule to hold the function values.

#light
namespace Strangelights
open System.Collections.Generic

type TheClass = class
    val mutable TheField : int
    new(i) = { TheField = i }
    member x.Increment() =
        x.TheField <- x.TheField + 1
    member x.Decrement() =
        x.TheField <- x.TheField − 1
end

module TheModule = begin
    let incList (l : List<TheClass>) =
        l |> Seq.iter (fun c -> c.Increment())
    let decList (l : List<TheClass>) =
        l |> 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:

static void UseTheClass()
{
    List<TheClass> l = new List<TheClass>();
    l.Add(new TheClass(5));
    l.Add(new TheClass(6));
    l.Add(new TheClass(7));
    TheModule.incList(l);
    foreach (TheClass c in l)
    {
        Console.WriteLine(c.TheField);
    }
}

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.

type DemoClass = class
    val Z : int
    new(z) = { Z = z}
    member this.CurriedStyle x y = x + y + this.Z
    member this.TupleStyle (x, y) = x + y + this.Z
end

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

So if you wanted to use both methods from C#, you would end up with code that looked as follows:

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);
}

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:

type IDemoInterface = interface
    abstract CurriedStyle : int -> int -> int
    abstract OneArgStyle : (int * int) -> int
    abstract MultiArgStyle : int * int -> int
    abstract NamedArgStyle : x : int * y : int -> int
end

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 signature as this:

int OneArgStyle(Tuple<int, int>);

With the latter, you see the following signature:

int MultiArgStyle(int, int);

The latter is a good bit friendlier for the C# user. However, you can take it 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# code 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.

class DemoImplementation : IDemoInterface
{
    public FastFunc<int, int> CurriedStyle(int x)
    {
        Converter<int, int> d =
            delegate (int y) {return x + y;};
        return FuncConvert.ToFastFunc(d);
    }

    public int OneArgStyle(Tuple<int, int> t)
    {
        return t.Item1 + t.Item2;
    }

    public int MultiArgStyle(int x, int y)
    {
        return x + y;
    }

    public int NamedArgStyle(int x, int y)
    {
        return x + y;
    }
}

Using F# with the .NET Framework Versions 1 and 1.1

Using F# with the .NET Framework versions 1 and 1.1 is surprisingly straightforward, because all you need to do is use the compiler switch --cli-version, discussed in more detail in Chapter 12. However, there are some small differences in both the F# code that you can write and the resulting assembly that you need to be aware of, so if at all possible, I recommend using the .NET Framework 2.

Readers who are familiar with the differences between the .NET Framework versions 1, 1.1, and 2 may have expected that any code that uses type parameterization, or generics as it more commonly known, would not compile using the .NET Framework versions 1 and 1.1. However, this is not the case, because these differences can be compiled away by the F# compiler.

Consider the following simple example:

let doNothing x = x

The function doNothing is generic because the parameters are of any type, and you can guarantee that the returned value will be the same as the input parameter. If you check the function's signature, using the Visual Studio tooltips or the compiler's -i switch, you see the following:

val doNothing : 'a -> 'a

meaning that doNothing takes a parameter of type 'a and returns a value of type 'a where 'a is a type parameter. Unsurprisingly, in the .NET Framework 2, this compiles down to have the following signature in C#:

public static T doNothing<T>(T x)

However, when compiled under the .NET Framework version 1 or 1.1, the function will have the following signature:

public static object doNothing(object x)

This means if you are creating a library for use with the .NET Framework version 1 or 1.1, the users of your functions from other .NET languages will have to cast the object returned to its original type. They would not have to do this if using a version of the library compiled for the .NET Framework 2.

The other problem area is arrays, because these were pseudogeneric in the .NET Framework versions 1 and 1.1 and fully generic in the .NET Framework 2. For those of you not familiar with the .NET Framework version 1 or 1.1, pseudogeneric means that arrays in the .NET Framework version 1 or 1.1 could have a type parameter, but nothing else could. For example, the method GetFiles from the System.IO.Directory class has the following signature, meaning that it returns an array of type string:

public static string[] GetFiles(string path);

These pseudogeneric arrays are trouble for F# because its array type is fully generic. Effectively this means that if you are using "pure F#" (that is, not calling methods from libraries written in C#), then it is OK to use the F# Array module when using the .NET Framework version 1 or 1.1. You will notice that arrays will always appear as object arrays (Object[]) when viewed from other .NET languages, but they seamlessly keep their types when used from F# code. When calling methods that accept arrays, or return arrays, like the aforementioned GetFiles method, then you will need to use the CompatArray module located in the Microsoft.FSharp.Compatibility namespace.

This will all be clearer when you see an example. Consider the following F# code that creates an array and then maps it to create a new array:

#light
let ones = Array.create 1 3
let twos = ones |> Array.map (fun x -> x + 1)

This code will compile under the .NET Framework versions 1, 1.1, and 2; however, the signatures of these values when viewed from other .NET languages would be as follows:

public static object[] ones { get; }
public static object[] twos { get; }

If you are designing a library and interoperation is important to you, you could replace the calls to the array module with calls to the CompatArray module, and you get the signatures typed using pseudogeneric arrays, just as your clients from other .NET code would probably want and expect. This means under the .NET Framework versions 1 and 1.1, you should use the following code:

#light
open Microsoft.FSharp.Compatibility
let ones = CompatArray.create 1 3
let twos = ones |> CompatArray.map (fun x -> x + 1)

This would mean that your module would have the signatures as shown here when viewed from other .NET languages:

public static int[] ones { get; }
public static int[] twos { get; }

Similarly, when using the .NET Framework versions 1 and 1.1, calls to methods from assemblies not written in F#, including all assemblies in the BCL, will generally use pseudogeneric arrays. This means when using the .NET Framework versions 1 and 1.1, it's important to use the CompatArray module and not the Array module. For example, the following will compile without a problem in the .NET Framework 2, but in both 1 and 1.1, it will not compile.

#light
open System.IO
open Microsoft.FSharp.Compatibility
let paths = Directory.GetFiles(@"c:")
let files = paths |> Array.map (fun path -> new FileInfo(path))

When compiled using the --cli-version 1.1 switch, it will give the following error:


prog.fs(5,13): error: FS0001: Type mismatch. Expecting a
        string [] -> 'c
but given a
        'a array -> 'b array.
The type string [] does not match the type 'a array

This is easily corrected by replacing functions from the Array module with their equivalents in ComptArray, as shown in the following example:

#light
open System.IO
open Microsoft.FSharp.Compatibility
let paths = Directory.GetFiles(@"c:")
let files = paths |> CompatArray.map (fun path -> new FileInfo(path))

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. This is quite common; for example, if you want to automatically manipulate programs from Microsoft Office 2003, then you need to use the COM APIs they provide, but there is no need to use TlbImp.exe to create a new wrapper, because Office already ships a series of managed wrappers contained in assemblies prefixed with Microsoft.Office.Interop.

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. So if you wanted 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 I find two command-line switches to be 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 post-fixed 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:

#light
open SpeechLib

let voice = new SpVoiceClass()
voice.Speak("Hello world", SpeechVoiceSpeakFlags.SVSFDefault)



Note More managed .NET APIs are becoming available all the time; the latest version of Office, Office 2007, ships with a fully managed .NET API, and Windows Vista includes a managed version of the Speech API. Although the basic calling of COM components is straightforward if you do a lot of COM-based programming in F#, you will soon find there are subtleties. You can find more information about COM programming at http://strangelights.com/FSharp/Foundations/default.aspx/FSharpFoundations.COM.


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 convention. 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:

#light
open System.Runtime.InteropServices

[<DllImport("User32.dll")>]
extern bool MessageBeep(uint32 beepType)

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



#light
open System.Runtime.InteropServices

[<DllImport("Advapi32.dll")>]
extern bool FileEncryptionStatus(string filename, uint32* status)

let main() =
    let mutable status = 0ul
    FileEncryptionStatus(@"C: est.txt", && status) |> ignore
    print_any 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 can be one of the trickiest things you can do in .NET because of the need to marshal data between the managed and unmanaged worlds, often requiring you to define structures that represent the data to be marshaled. You can find more information about marshaling and other aspects of P/Invoke at http://strangelights.com/FSharp/Foundations/default.aspx/FSharpFoundations.PInvoke.


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

Table 13-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# libraries fslib.dll and mllib.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.


Caution To use inline IL effectively, you really need to have a good understanding of IL. Some of the passages in this section will not make sense if you do not have at least a basic knowledge of IL. You can find resources to help you learn IL at http://strangelights.com/FSharp/Foundations/default.aspx/FSharpFoundations.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 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; say you want to replace them with your own functions. So, you define two functions, add and sub, whose bodies are defined using IL:

#light
let add (x:int) (y:int) = (# "add" x y : int #)
let sub (x:int) (y:int) = (# "sub" x y : int #)

let x = add 1 1
let y = sub 4 2

printf "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.

#light
let add (x:int) (y:int) = (# "ret" x y : int #)

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 One tool distributed with .NET SDK can help you detect these kinds of errors. The tool is called peverify.exe, and you can find more information about peverify.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, sometimes you might 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++.


Caution Using COM in C++ is a huge topic, and it is advisable that any programmer wanting to call F# in this way is already experienced in C++/COM. If you need more information about this topic, you can find some starting points at http://strangelights.com/FSharp/Foundations/default.aspx/FSharpFoundations.CPPCOM.


To expose functions through 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 "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 with the System.Runtime.InteropServices.Guid attribute and also 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.

I'll now show an example of doing this; suppose you want to expose two functions called Add and Sub to your unmanaged client. So, 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


[<Guid("6180B9DF-2BA7-4a9f-8B67-AD43D4EE0563")>]
type IMath = interface
    abstract Add : x: int * y: int -> int
    abstract Sub : x: int * y: int -> int
end


[<Guid("B040B134-734B-4a57-8B46-9090B41F0D62");
ClassInterface(ClassInterfaceType.None)>]
type Math = class
    new () = {}
    interface IMath with
        member this.Add(x, y) = x + y
        member this.Sub(x, y) = x - y
    end
end

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 needed to break them down into other helper functions outside the class, then this would not have been a problem; it is fine to implement your class members in 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 appears after the next list; the development environment and how you set up the 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.

Here's the code:

#include "stdafx.h"
#import "ComLibrary.tlb" named_guids raw_interfaces_only

int _tmain(int argc, _TCHAR* argv[])
{
    CoInitialize(NULL);
    comlibrary::IMathPtr pDotNetCOMPtr;

    HRESULT hRes = pDotNetCOMPtr.CreateInstance(comlibrary::CLSID_Math);
    if (hRes == S_OK)
    {
        long res = 0L;
        hRes = pDotNetCOMPtr->Add(1, 2, &res);
        if (hRes == S_OK)
        {
            printf("The result was: %ld", res);
        }

        pDotNetCOMPtr.Release();
    }

    CoUninitialize ();
    return 0;
}

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.


Note Another option for calling F# code from unmanaged code is by the custom hosting of the CLR. This is even more complicated than calling F# methods via COM; however, it can give you fine-grained control over the behavior of the CLR. If you want to investigate this option, please see http://strangelights.com/FSharp/Foundations/default.aspx/FSharpFoundations.CLRHosting.


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