F#29 : Interop





5.00/5 (1 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.
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:
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
- reference the correct Dlls, which for most Windows Forms applications would simply be “
System.WIndows.Forms
” - 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)
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 aICommand
implementation where theCanExecute()
/Execute
methods are handled in your ownViewModel
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:
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:
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