Click here to Skip to main content
11,717,509 members (77,270 online)
Click here to Skip to main content

Tagged as

F#29 : Interop

, 22 May 2014 CPOL 11K 5 2
Rate this:
Please Sign up or sign in to vote.
In this post we will examine how F# can be used to do various tasks that you may have commonly use C#/VB.NET for. It will also show how to interop between F#/C# and vice versa. I have already shown some of the content here in previous posts, so apologies for that in advance. Downloadable Code [&#823

In this post we will examine how F# can be used to do various tasks that you may have commonly use C#/VB.NET for. It will also show how to interop between F#/C# and vice versa. I have already shown some of the content here in previous posts, so apologies for that in advance.

Downloadable Code For This Post

As this post deals with a lot of different things, and concepts, I took pity on you lot, and have uploaded a ZIP file that contains all the code for this post, which is available right here :

https://dl.dropboxusercontent.com/u/2600965/Blogposts/2014/WPFAppDemo.zip

Creating A Console App With F#

One of the things you may like to do with F# is create a console application. This is really simple as Visual Studio comes with a template for this, so it really is just a case of clicking a button. This can be seen in the screen shot below:

image

Using this Visual Studio template will give you this basic Console Application code:

[<EntryPoint>]
let main argv =
    printfn "%A" argv
    0 // return an integer exit code

In fact we covered a lot of ground explaining the inners of a F# console application in the very 1st post in this series: http://sachabarbs.wordpress.com/2014/02/25/f-1-hello-world/

Creating A Simple Windows Form App With F#

You may wish to create Windows Forms Applications in F#. There is no inbuilt template for this so you just need to make sure you

  1. reference the correct Dlls, which for most Windows Forms applications would simply be “System.WIndows.Forms
  2. Change the project type to a Windows Application

Here is a extremely simple WinForms app which simply shows a MessageBox when a button is clicked

open System
open System.Windows.Forms

[<EntryPoint>]
let main argv =

    let bufferData = Array.zeroCreate<byte> 100000000

    let form = new Form(Text = "Test Form")
    let button = new Button(Text = "Start")
    form.Controls.Add(button)
    button.Click.Add(fun args -> MessageBox.Show("button clicked") |> ignore)
    Application.Run(form)
    //return 0 for main method
    0 

Which when runs will give you something like this (providing you clicked the button that is)

image

Creating A Simple WPF App With F#

F# can also be used to create Silverlight/WPF/WinRt apps. Unfortunately there is no inbuilt support for this, but as long as you know the Dlls to use you can get through this hurdle. There is one key difference when using F# compared to C#/VB .NET, which is in both of those language there are lovely nice integrated Visual Studio templates, which allow you to add a new Window/UserControl, which will generate a partial class with 2 parts

  • The XAML designer part
  • The code behind which has the InitialiseComponent() which will result in the correct compiler generated file being created

In F# you MUST create a simple .xaml file which is best made by adding a new Text file to your F# project and then renaming it. You MUST then load the XAML up the XamlReader.Load to create a Window.

This small demo I have put together attempts to follow current XAML development best practices, which are to use the MVVM pattern. As such you can expect to see code for

  • INPC base class that implements the INotifyPropertyChanged interface
  • DelegateCommand which allows you to create a ICommand implementation where the CanExecute()/Execute methods are handled in your own ViewModel code
  • A ViewModel
  • A View

I have taken some of the code here from F# industry leaders like Phil Trelford, from the F# snippets

INPC Base Class

The base class code is Phil Trelfords original code that is available right here : http://fssnip.net/2x and it looks like this

//Phil Trelfords ObservableObject : http://fssnip.net/2x
type ObservableObject () =
    let propertyChanged =
        Event<PropertyChangedEventHandler,PropertyChangedEventArgs>()
    let getPropertyName = function
        | PropertyGet(_,pi,_) -> pi.Name
        | _ -> invalidOp "Expecting property getter expression"
    interface INotifyPropertyChanged with
        [<CLIEvent>]
        member this.PropertyChanged = propertyChanged.Publish
    member this.NotifyPropertyChanged propertyName = propertyChanged.Trigger(this,PropertyChangedEventArgs(propertyName))
    member this.NotifyPropertyChanged quotation = quotation |> getPropertyName |> this.NotifyPropertyChanged

Delegate Command Implementation

This was taken from the following Url : http://geekswithblogs.net/MarkPearl/archive/2010/06/17/yippy-ndash-the-f-mvvm-pattern.aspx, and looks like this

type DelegateCommand (canExec:(obj -> bool), doExec:(obj -> unit)) =
    let canExecuteEvent = new DelegateEvent<EventHandler>()
    interface ICommand with
        [<CLIEvent>]
        member x.CanExecuteChanged = canExecuteEvent.Publish
        member x.CanExecute arg = canExec(arg)
        member x.Execute arg = doExec(arg)

ViewModel

Having these helpers in place, then allows you to create a ViewModel that has your custom properties/commands that the View will bind to. Here is a very simple example ViewModel:

//An example ViewModel
type MainWindowViewModel () =
    inherit ObservableObject()
    let mutable message = "Yo I am from the ViewModel"
    let mutable currentItem = ""

    let items = new List<string>()

    do items.Add("Who laughing now")
    do items.Add("The cat barked")
    do items.Add("Batman is here")

    let showMessage(msg) =
        let msg = MessageBox.Show(msg)
        ()

    member this.Items
       with get () = items

    member this.Message
        with get () = message
        and set value =
            message <- value
            this.NotifyPropertyChanged <@ this.Message @>

    member this.CurrentItem
        with get () = currentItem
        and set value =
            currentItem <- value
            this.NotifyPropertyChanged <@ this.CurrentItem @>

    member this.ShowMessageCommand =
        new DelegateCommand
            (
                (fun d -> true),
            (fun e -> showMessage(this.Message))
            )

    member this.ShowCurrentItemCommand =
            new DelegateCommand
            (
            (fun d -> true),
            (fun e -> showMessage(this.CurrentItem))
            ) 

View

As previously stated the View must be loaded using the XAMLReader.Load() function. This then creates the Window (view) from the XAML. We then create a new ViewModel and set that as the DataContext for the Window, to facilitate binding from the View to the ViewModel. Here is the relevant code to do this:

open System
open System.Reflection
open System.Windows
open System.Windows.Markup
open WPFAppDemo.ViewModels

let mainWindowStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("MainWindow.xaml")
let mainWindow =
    let win = XamlReader.Load(mainWindowStream) :?> Window
    let viewModel = new MainWindowViewModel()
    win.DataContext <- viewModel
    win

let app = new Application()

[<STAThread>]
app.Run mainWindow |> ignore

Where the View that binds to the ViewModel (which is set as the DataContext for the View)

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
    <StackPanel Orientation="Vertical">
         <TextBox FontSize="14" Text="{Binding Message}" Margin="10"/>
             <Button Content="Show Message" Command="{Binding ShowMessageCommand}" Margin="10"/>
             <ListBox ItemsSource="{Binding Items}" SelectedItem="{Binding CurrentItem}" Margin="10"/>
             <Button Content="Show Current Item" Command="{Binding ShowCurrentItemCommand}" Margin="10"/>
    </StackPanel>
</Window> 

Call A C# Method From F#

It should come as no surprise that F# can call a method created in a C# dll. Here is some demo code that proves this.

We have this C# code, which will be called via F#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading.Tasks;

namespace CSharpCommon
{
    public class Class1
    {
         public IEnumerable<string> GetEnumerable(int number, string prefix)
         {
             return Enumerable.Range(0, number)
        .Select(x => string.Format("CSharpCommon.Class1.ListItem : {0}", x.ToString()));
      }
    }
}

And here is the F# code that calls this:

open System

//use the C# Dll
open CSharpCommon

[<EntryPoint>]
let main argv =
    printfn "%A" argv

    //Call the C# function from F#
    let class1 = new CSharpCommon.Class1()
    let list = class1.GetEnumerable(10,"Called from F#") |> Seq.iter (fun x -> printfn "item: %s" x)

    Console.ReadLine() |> ignore

    0 // return an integer exit code

Which when run gives this result:

image

Call A F# Function From C#

Likewise it should come as no surprise that C# can call a method created in a F# dll. Here is some demo code that proves this.

We have this F# code, which will be called via C#

namespace FSharpCommon
module CustomTypes =

open System

    let GetEnumerable(number : int, prefix : string) =
    seq { for i in 1 .. number do yield String.Format("FSharpCommon.CustomTypes.ListItem : {0}", i)}

And here is the C# code that calls this:

using System;

//use the F# Dll
using FSharpCommon;

namespace CSharpConsoleCallsFSharp
{
    class Program
    {
        static void Main(string[] args)
    {
        //Call the F# function from C#
        foreach (var item in CustomTypes.GetEnumerable(10, "Called from C#"))
        {
        Console.WriteLine(String.Format("item: {0}", item));
        }
        Console.ReadLine();
    }
    }
}

Which when run gives this result:

image

Calling A Native Dll From F#

As some of you may know there has long been a way to call native Dlls. Native Dlls are the ones that come with Windows. This technique of calling native Dlls is known as Platform Invoke (or PInvoke for short). It is quite useful occasionally and can get you out of some funny little scrapes at time. As far as I recall it has support all the way back to VB6 (may be even earlier). The problem with calling PInvoke methods is that you are then tying yourself well and truly to ONLY being able to run your code on Windows, as this is the only place the native Dlls exist that you are calling.

That said I still think it is good to cover this technique. I have deliberately chosen a very simple native function to call, as some of the PInvoke signatures can get a little hairy.

The function I have chosen to use is the Kernel32.dll Beep function. You can read more about it using the great PInvoke resource PInvoke.net. The Beep function is detailed here : http://www.pinvoke.net/default.aspx/kernel32.beep

Before we get into the F# code, I just wanted to show what it would look like to call this function from C#, which would be done as follows:

using System;
using System.Runtime.InteropServices;

class BeepSample
{
    [DllImport("kernel32.dll", SetLastError=true)]
    static extern bool Beep(uint dwFreq, uint dwDuration);

    static void Main()
    {
    Beep(100, 5);
    }
}

And here is the same Native code using F#, where we have placed the code within a module. There are some subtle differences between the C# code and the F# code, this is all explained in the comments within the code block below. The PInvoke.net is still a very good starting place when you want to use some PInvoke in F#.

namespace ConsoleApplication1
module CustomTypes =

    open System.Runtime.InteropServices
    open System

    // NOTE : This is how to use a simple P/Invoke example from F#
    // There are a couple of things to note here
    // 1. The Beep function actually declares a return type, this is abnormal for F# function
    // 2. When compared to the C# code shown below (commented out) the F# MarshalAs, MUST appear
    // on the actual return value
    // 3. The argument do NOT follow the F# convention of argName : argType
    [<DllImport("kernel32.dll", SetLastError=true)>]
    extern [<MarshalAs(UnmanagedType.Bool)>]
        bool Beep(System.UInt32 dwFreq, System.UInt32 dwDuration)

Which can then be used like this

open System
open ConsoleApplication1.CustomTypes

[<EntryPoint>]
let main argv =

    printfn "About to beep" |> ignore

    //beep
    ConsoleApplication1.CustomTypes.Beep(2500u, 300u) |> ignore

    Console.ReadLine() |> ignore

    //return 0 for main method
    0 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Sacha Barber
Software Developer (Senior)
United Kingdom United Kingdom
I currently hold the following qualifications (amongst others, I also studied Music Technology and Electronics, for my sins)

- MSc (Passed with distinctions), in Information Technology for E-Commerce
- BSc Hons (1st class) in Computer Science & Artificial Intelligence

Both of these at Sussex University UK.

Award(s)

I am lucky enough to have won a few awards for Zany Crazy code articles over the years

  • Microsoft C# MVP 2015
  • Codeproject MVP 2015
  • Microsoft C# MVP 2014
  • Codeproject MVP 2014
  • Microsoft C# MVP 2013
  • Codeproject MVP 2013
  • Microsoft C# MVP 2012
  • Codeproject MVP 2012
  • Microsoft C# MVP 2011
  • Codeproject MVP 2011
  • Microsoft C# MVP 2010
  • Codeproject MVP 2010
  • Microsoft C# MVP 2009
  • Codeproject MVP 2009
  • Microsoft C# MVP 2008
  • Codeproject MVP 2008
  • And numerous codeproject awards which you can see over at my blog

You may also be interested in...

Comments and Discussions

 
GeneralMy vote of 5 Pin
Volynsky Alex20-May-14 13:22
professionalVolynsky Alex20-May-14 13:22 
GeneralRe: My vote of 5 Pin
Sacha Barber22-May-14 2:21
mvpSacha Barber22-May-14 2:21 
GeneralRe: My vote of 5 Pin
Volynsky Alex22-May-14 7:56
professionalVolynsky Alex22-May-14 7:56 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.150901.1 | Last Updated 22 May 2014
Article Copyright 2014 by Sacha Barber
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid