Using ConfigureAwait

By default code after an await continues on the calling thread (i.e. the thread prior to the await keyword). In many cases this is what we want to happen. Remember async methods are not necessarily run on another task/thread, but in those instances where we know that they are truly asynchronous, we might actually want our code after the await to also run in the same context (i.e. on the same background thread of the async method).

So, for the sake of argument we’ll assume we’re awaiting a Task which we know is run on a background thread. Upon completion we intend to process some results from this Task, also on a background thread. Now obviously we could do something like

private async Task Process()
{
   var results = await SomeBackgroundThreadMethod();
   await Task.Run(() => ProcessOnBackgroundThread(results);
}

We can see that if SomeBackgroundThreadMethod runs on a background thread then after the await we continue on the calling thread before again spinning up another Task to run our processing code. So we’re potentially using two background threads when in reality, if we know SomeBackgroundThreadMethod actually is running on a background thread then we could simply continue to process on this same thread.

Okay, so this is a little contrived because, as you know, the SomeBackgroundThreadMethod would itself return a Task and we could simply ContinueWith on this Task. But an alternative to ContinueWith is to call ConfigureAwait on the SomeBackgroundThreadMethod Task, such as

private async Task Process()
{
   var results = await SomeBackgroundThreadMethod().ConfigureAwait(false);
   ProcessOnBackgroundThread(results)
}

Gotchas?

  • Now the code which uses ConfigureAwait seems quite obvious, but ConfigureAwait does not magically create a Task or background thread if none existed on the return from SomeBackgroundThreadMethod. For example if SomeBackgroundThreadMethod simply returns a TaskCompletionSource, ConfigureAwait would simply end up being on the calling thread.
  • ConfigureAwait only affects code following it and within the scope of the method using it, hence in the above code once we exit the Process method all async/await calls will return to their default behaviour, it’s only code within the Process method after the await which continues on the same thread as SomeBackgroundThreadMethod.

References
Task.ConfigureAwait

Xamarin and Visual Studio 2015

I’ve used Xamarin Studio in the past, which is a nice enough environment, but I’ve far more experience with Visual Studio as well as having all my dev tools integrated with it (such as Resharper). Now with Microsoft’s purchase of Xamarin I’ve finally got access to Xamarin’s Visual Studio integrations which also means I can target more platforms in a single solution.

Unfortunately things didn’t “just work” out of the box.

I’m going to go through a few of the issues I found getting started with the Visual Studio integration and hopefully provide solutions and where I can I shall detail how I got everything up and running (assuming I actually do get everything up and running).

What versions am I using?

So I’m going to begin by stating what versions I’m using

  • Visual Studio Professional 2015 (version 14.0.25123.00 Update 2) on Windows 10. This version of Visual Studio includes the Xamarin integration.
  • On the Mac I’m running El Capitan and Xamarin Studio Community edition version 5.10.3 (build 51).

Creating a project

I wanted to jump straight into trying out a Cross-Platform Xamarin.Forms project, so from New Project select Cross-Platform, then Blank App (Xamarin.Forms Portable). I named the project XamarinTest1, obviously name yours whatever you like, but I may refer to this project name within this post.

Visual Studio asks me to choose the target and minimum platform versions for my Universal Windows application – I leave the defaults and press OK.

The project template supplies the following

  • Portable class library – uses for the shared/portable code
  • An Android targeted project
  • An iOS targeted project
  • A UWP/Universal Windows targeted project
  • A Windows 8.1 targeted project
  • A Windows phone 8.1 targeted project

Now my normal inclination is to press the Run button and see what happens…

Getting the Android project up and running

The Android project is selected as the Start-up project, but the first problem occurs.

A message box stating, “There were deployment errors. Continue?” appears. It’s only when I check the Output window that I see a more useful message “Please select a valid device before running the application”. It appears that Visual Studio does not know about the Android virtual devices.

Clicking on the Open Android Emulator Manager button showed I did have emulators set-up (if you hit this problem and no emulators exist, add some).

I found that if I restarted Visual Studio and reloaded the project, it then found the emulators and was able to run correctly.

Getting the iOS project up and running

Select the iOS project and right mouse click on it, then select Set as StartUp project.

To run the iOS application we need to have a Mac running with Xamarin Studio installed. Also we need to set the Sharing System Preferences on the Mac.

Invoke using Cmd-Space and search for Remote Login and then Sharing System Preferences. From here we tick the Remote Login option and set access to specific users.

Now run the iOS application in Visual Studio. If all goes well the iOS emulator will run on the Mac via the Xamarin agent and you can now see what it looks like on your chosen iOS device emulator.

Getting UWP project up and running

Select the UWP project and right mouse click on it, then select Set as StartUp project.

If you now run the UWP application you might get a message box with the following error message: “The project ‘XamarinTest1’ needs to be deployed before it can be started.” etc.

As the message box goes onto say, select the configuration manager and locate the UWP project, check the Build and Deploy options.

If all goes well the application should now run correctly.

Getting Windows 8.1 project up and running

Select the Windows 8.1 project and right mouse click on it, then select Set as StartUp project.

This project should simply work, without any changes required.

Getting Windows Phone 8.1 project up and running

Select the Windows Phone 8.1 project and right mouse click on it, then select Set as StartUp project.

You will need the Windows 8.1 Phone emulator running on your machine (if using Windows 10) and this in turn requires the Hyper V virtualization option on the computer’s BIOS turned on.

Running the application will result in you being asked to download/install the emulator if it’s not on your machine. It’s likely you’ll need to reboot upon completion and/or may be asked if your login user should be part of the Hyper V group – which it should. Once everything is installed correctly then you’ll see a Windows phone emulator appear when you run the application.

Conclusions

It’s unfortunate this stuff doesn’t “just work” immediately out of the box, but with a little tweaking/configuring you can get five different projects running against five different device types and now the world of cross platform C# awaits.

Addendum

Part way through writing this document I foolishly decided to update the Android SDK only to find it failed and left my installation in a non-working state. So here’s what I had to do to get the system working again.

  • Android SDK didn’t appear to be available as a separate download, so I had to install Android Studio and download the NDK seperately
  • Once installed I found the folders of the installations very different from my original install so I needed to option Visual Studio’s Tools | Options
  • Select Xamarin | Android Settings
  • Change the JDK, SDK and NDK paths

So it’s not without its pain points, but eventually I had each of the platform targeted projects running.

Creating a Mobile App on Azure

If you’re wanting a highly scalable cloud based service for mobile applications, you could use the Mobile Apps service in Azure. Mobile Apps offer authentication, data access, offline sync capabilities and more.

Creating the Mobile Apps service

Let’s jump in and simply create our Mobile Apps service.

  • Log into the Azure portal on https://portal.azure.com
  • Select New
  • Select Web + Mobile
  • Select Mobile App
  • Enter a name for your app (which needs to be unique on the azurewebsites.net domain)
  • Supply a new resource group name

Once the new application has been deployed, go to the App Services section in Azure to see the current status of your new service.

Let’s add a database

Let’s take this a little further now – a good possibility is that we need to store data in the cloud for our mobile device. So from your previously created App service, scroll down the Settings tab until you come across the “MOBILE” section and then follow these instructions to create a data connection (which includes creating and SQL Server DB or connecting to an existing one).

  • Select Data Connections from the MOBILE section
  • Press the Add button
  • Leave the default SQL Database for the Type
  • Press on the SQL Database/Configure required settings
  • If you already have an SQL database setup you can select an existing one to use or click the Create a new data base option
  • Assuming you’re creating a new database, supply a database name
  • Click the Configure required settings and supply a server name, admin login, password and set the location of the server

When completed the data connections should list your new database connection.

Easy tables

We can create simple tables and offer CRUD like API’s against our data by using an Azure feature called Easy Tables. This will allow us to use REST and JSON to carry out CRUD operations against our SQL database with no coding on the server. To create Easy Tables select your mobile app service, select the Settings tab and scroll down to the “MOBILE” section, if not already there and then do the following

  • Select the Easy tables option
  • Press the Add button and give your table a name, for example for a blog reader we might start with the list of feeds, hence the table can be Feeds, for now you might leave all the permissions as “Allow anonymous access” just to get up and running
  • You can the click “Manage schema” to add new columns

The URL to you table becomes

https://<your service name>.azurewebsites.net/tables/<your table name>

This now allows us (out of the box, with no extra coding by us on the server) get access to REST API’s to the data, so let’s assume our service name is Mobile and our table name is Feeds, we can use the following to handle basic CRUD operations

  • Create: Use a HTTP POST operation against the URL https://mobile.azurewebsites.net/tables/Feeds
  • Retreive (single item): Use a HTTP GET operation against the URL https://mobile.azurewebsites.net/tables/Feeds/id where id is an identifier which Easy Tables creates on our table by default
  • Retreive (all items): Use a HTTP GET operation against the URL https://mobile.azurewebsites.net/tables/Feeds
  • Update: Use a HTTP method “PATCH” against the URL https://mobile.azurewebsites.net/tables/Feeds/id where id is an identifier which Easy Tables creates on our table by default
  • Delete: Use a HTTP DELETE operation against the URL https://mobile.azurewebsites.net/tables/Feeds/id where id is an identifier which Easy Tables creates on our table by default

Adding authentication

Initially we created the Easy table as “Allow anonymous access”, this ofcourse would allow us to test the API quickly, but obviously leaves the data unsecured. So now let’s put in place the authentication settings.

  • If not already selected, select you Mobile App and from the settings locate the “MOBILE” section, then select Easy tables and finally select you table
  • Click the Change permissions option
  • Simply change the various permissions to Authenticated access only
  • Don’t forget to press the save button

Now if you previously wrote some code to access the table without any form of authentication, you should receive a 401 error from requests to those same services now.

At this point we will need to provide a valid authentication token from an identity provider. Azure mobile apps can be set up to use Facebook, Twitter, Microsoft Account, Google or Azure Active Directory as a trusted identity provider. Along with these providers you could also roll your own custom identity provider.

To use one of the existing providers you’re going to need to get an app and client id plus an app secret, these are usually obtained from the provider’s dev portals.

Once you have this information you can do the following

  • Go to your Azure mobile app and select the Settings tab
  • Locate the Authentication/Authorization option
  • Set the App service Authentication to On
  • Now configure the Authentication provider, in the case of the social media providers simply insert your app id and app secret (or similar) as requested

Once completed you’ll need to use a slightly different URL to access your tables (for example). As you’ll need an OAuth redirect, so we’ll have something like

https://<your service name>.azurewebsites.net/.auth/login/<your identity provider>/callback

where your identity provider would be facebook, twitter, microsoftaccount, google or aad for example.

References

What are Mobile Apps?

The saga of the Oracle client and the “Attempted to read or write protected memory” exception

On one of the project’s I worked on recently, we had a strange problem whereby the application was deployed to multiple servers and on all but one, ran perfectly. On this one server we were seeing, in the log file, that we were getting exceptions with the message “Attempted to read or write protected memory”.

Let’s face it if the code works on all other boxes, the likelihood was a configuration or installation issue on the one anomaly machine. So I knocked together a very simple Oracle client (sample code listed below) to prove that the issue was unrelated to the software I maintained (you know how it is, you might be pretty sure where the problem is but sometimes you have to also prove it to others).

try
{
   var connection = new OracleConnection(connectionString);
   connection.Open();

   var command = connection.CreateCommand();
   command.CommandType = CommandType.Text;
   command.CommandText = queryString;

   var reader = command.ExecuteReader();

   while (reader.Read())
   {
      Console.WriteLine(reader.GetString(0));
   }
}
catch (Exception e)
{
   Console.WriteLine(e.Message);
   Console.WriteLine(e.StackTrace);
}

Obviously you’ll need to supply your own connectionString and queryString if using this snippet of code.

Indeed this simple client code failed with the same issue and hence it was definitely nothing to do with my application (and yes the same sample code was also tested on the working servers and worked perfectly).

We also connected to Oracle via sqlplus and all seemed fine, using the same connection details and query that we were using in the failing application.

Upon further investigation it became clear that multiple (well two) Oracle client installs existed on the machine and therefore it seemed likely our app was somehow trying to connect via a newer version of the Oracle client.

Luckily the web came to the rescue, with this following configuration code which allows us to point our .NET client code to a specific version of an Oracle client installation.

<configSections>
   <section name="oracle.dataaccess.client"
    type="System.Data.Common.DbProviderConfigurationHandler, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
</configSections>
  
<oracle.dataaccess.client>
   <settings>
      <add name="DllPath" value="c:\oracle\product\10.2.0\client_2\BIN"/>
   </settings>
</oracle.dataaccess.client>

C# 6 features

A look at some of the new C# 6 features (not in any particular order).

The Null-conditional operator

Finally we have a way to reduce the usual

if(PropertyChanged != null)
   PropertyChanged(sender, propertyName);

to something a little more succinct

PropertyChanged?.Invoke(sender, propertyName);

Read-only auto properties

In the past we’d have to supply a private setter for read only properties but now C# 6 allows us to do away with the setter and we can either assign a value to a property within the constructor or via a functional like syntax, i.e.

public class MyPoint
{
   public MyPoint()
   {
      // assign with the ctor
      Y = 10;
   }

   // assign the initial value via the initializers
   public int X { get; } = 8;
   public int Y { get; }
}

Using static members

We can now “open” up static class methods and enums using the

using static System.Math;

// Now instead of Math.Sqrt we can use
Sqrt(10);

String interpolation

Finally we have something similar to PHP (if I recall my PHP from so many years back) for embedding values into a string. So for example we might normally write String.Format like this

var s = String.Format("({0}, {1})", X, Y);

Now we can instead write

var s = $"({X}, {Y})";

Expression-bodied methods

A move towards the way F# might write a single line method we can now simplify “simple” methods such as

public override string ToString()
{
   return String.Format("({0}, {1})", X, Y);
}

can now be written as

public override string ToString() => String.Format("({0}, {1})", X, Y);

// or using the previously defined string interpolation
public override string ToString() => $"({X}, {Y})";

The nameof expression

Another improvement to remove aspects of magic strings, we now have the nameof expression. So for example we might have something like this

public void DoSomething(string someArgument)
{
   if(someArgument == null)
      throw new ArgumentNullException(nameof(someArgument));

   // do something useful
}

Now if we change the someArgument variable name to something else then the nameof expression will correctly pass the new name of the argument to the ArgumentNullException.

However nameof is not constrained to just argument in a method, we can apply nameof to a class type, or a method for example.

References

What’s new in C# 6

Filtering listbox data in WPF

Every now and then we’ll need to display items in a ListBox (or other ItemsControl) which we can filter.

One might simply create two ObservableCollections, one containing all items and the other being the filtered items. Then bind the ItemsSource from the ListBox to the filtered items list.

A simple alternate would be to use a CollectionView, for example

public class MyViewModel
{
   public MyViewModel()
   {
      Unfiltered = new ObservableCollection<string>();
      Filtered = CollectionViewSource.GetDefaultView(Unfiltered);
   }

   public ObservableCollection<string> Unfiltered { get; private set; }
   public ICollectionView Filtered { get; private set; }
}

Now to filter the collection view we can use the following (this is a silly example which will filter to show only strings larger than 3 in length)

Filtered.Filter = i => ((string)i).Length > 3;

to remove the filter we can just assign null to it, thus

Filtered.Filter = null;

In use, all we need to do is bind our Filtered property, for example in a ListBox control’s ItemsSource property and then apply a filter or remove a filter as required.

Up and running with Modern UI (mui)

So I actually used this library a couple of years back but didn’t blog about it at the time. As I no longer have access to that application’s code I realized I needed a quick start tutorial for myself on how to get up and running with mui.

First steps

It’s simple enough to get the new styles etc. up and running, just follow these steps

  • Create a WPF application
  • Using NuGet install Modern UI
  • Change the default Window to a ModernWindow (both in XAML and derive you code behind class from ModernWindow
  • Add the following to your App.xaml resources
    <ResourceDictionary>
       <!-- WPF 4.0 workaround -->
       <Style TargetType="{x:Type Rectangle}" />
       <!-- end of workaround -->
       <ResourceDictionary.MergedDictionaries>
          <ResourceDictionary Source="/FirstFloor.ModernUI;component/Assets/ModernUI.xaml" />
          <ResourceDictionary Source="/FirstFloor.ModernUI;component/Assets/ModernUI.Light.xaml"/>
       </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
    

So that was easy enough, by default a grayed out back button is shown, we can hide that by setting the window style to

Style="{StaticResource BlankWindow}"

You can show/hide the window title by using the property

IsTitleVisible="False"

to the ModernWindow.

The tab style navigation

In these new UI paradigms we may use the equivalent of a tab control to display the different views in the main window, we achieve this in mui using

<mui:ModernWindow.MenuLinkGroups>
   <mui:LinkGroup DisplayName="Pages">
      <mui:LinkGroup.Links>
         <mui:Link DisplayName="Page1" Source="/Views/Page1.xaml" />
         <mui:Link DisplayName="Page2" Source="/Views/Page2.xaml" />
      </mui:LinkGroup.Links>
   </mui:LinkGroup>
</mui:ModernWindow.MenuLinkGroups>

This code should be placed within the ModernWindow element (not within a Grid element) and in this example I created a Views folder with two UserControls, Page1 & Page2 (in my case I placed a TextBlock in each with Page1 and Page 2 Text respectively to differentiate the two).

Running this code we now have a UI with the tab like menu and two pages, the back button also now enables (if you are using it) and allows navigation back to the previous selected tab(s).

One thing you might notice, when the app starts no “page”, by default, is selected. There’s a ContentSource property on a ModernWindow and we can set this to the page we want dispalyed, but if you do this you’ll also need to updated the LinkGroup to tell it what the current pages is.

The easiest way to do this is using code behind, in the MainWindow ctor, simply type

ContentSource = MenuLinkGroups.First().Links.First().Source;

Colour accents

By default the colour accents used in mui are the subtle blue style (we’ve probably seen elsewhere), to change the accent colours we can add the following

AppearanceManager.Current.AccentColor = Colors.Red;

to the MainWindow ctor.

Okay that’s a simple starter guide, more (probably) to follow.

References

https://github.com/firstfloorsoftware/mui/wiki

Getting RESTful with Suave

I wanted to implement some microservices and thought, what’s more micro than REST style functions, executing single functions using a functional language (functional everywhere!). So let’s take a dip into the world of Suave using F#.

Suave is a lightweight, non-blocking, webserver which can run on Linux, OSX and Windows. It’s amazingly simple to get up and running and includes routing capabilities and more. Let’s try it out.

Getting Started

Create an F# application and the run Install-Package Suave via Package Manager Console.

Now, this code (below) is taken from the Suave website.

open Suave
open Suave.Filters
open Suave.Operators
open Suave.Successful

[<EntryPoint>]
let main argv = 

    let app =
        choose
            [ GET >=> choose
                [ path "/hello" >=> OK "Hello GET"
                  path "/goodbye" >=> OK "Good bye GET" ]
              POST >=> choose
                [ path "/hello" >=> OK "Hello POST"
                  path "/goodbye" >=> OK "Good bye POST" ] ]

    startWebServer defaultConfig app

    0 // return an integer exit code

Run your application and then from you favourite web browser, type in either http://localhost:8083/hello and/or http://localhost:8083/goodbye and you should see “Hello GET” and/or “Good bye GET”. From the code you can see the application also supports POST.

Let’s test this using Powershell’s Invoke-RestMethod. Typing Invoke-RestMethod -Uri http://localhost:8083/hello -Method Post and you will see “Hello POST”.

Passing arguments

Obviously invoking a REST style method is great, but what about passing arguments to the service. We’re going to need to add the import open Suave.RequestErrors to support errors. We can read parameters from the commands using HttpRequest queryParam

 let browse =
        request (fun r ->
            match r.queryParam "a" with
            | Choice1Of2 a -> 
                match r.queryParam "b" with
                | Choice1Of2 b -> OK (sprintf "a: %s b: %s" a b)
                | Choice2Of2 msg -> BAD_REQUEST msg
            | Choice2Of2 msg -> BAD_REQUEST msg)

    let app =
        choose
            [ GET >=> choose
                [ path "/math/add" >=> browse ]
            ]

    startWebServer defaultConfig browse

Disclaimer: This is literally my first attempt at such code, there may be a better way to achieve this, but I felt the code was worth recording anyway. So from our preferred web browser we can type http://localhost:8083/math/add?a=10&b=100 and you should see a: 10 b:100.

Passing JSON to our service

We can also pass data in the form of JSON. For example, we’re now going to pass JSON contain two integers to our new service. So first off add the following

open Suave.Json
open System.Runtime.Serialization

Now we’ll create the data contracts for sending and receiving our data using, these will be serialized automatically for us through the function mapLson which will see in use soon. Notice we’re also able to deal with specific types, such as integers in this case (instead of just strings everywhere).

[<DataContract>]
type Calc =
   { 
      [<field: DataMember(Name = "a")>]
      a : int;
      [<field: DataMember(Name = "b")>]
      b : int;
   }

[<DataContract>]
type Result =
   { 
      [<field: DataMember(Name = "result")>]
      result : int;
   }

Finally let’s see how we startup our server. Here we use the mapJson to map our JSON request into the type Calc from here we carry out some function (in this case addition) and the result is returned (type inference turns the result into a Result type).

startWebServer defaultConfig (mapJson (fun (calc:Calc) -> { result = calc.a + calc.b }))

Let’s test this using our Invoke-RestMethod Powershell code. We can create the JSON body for this method in the following way.

Invoke-RestMethod -Uri http://localhost:8083/ -Method Post -Body '{"a":10, "b":20}'

References

Suave Music Store
Invoke-RestMethod
Building RESTful Web Services
Building REST Api in Fsharp Using Suave

Setup Powershell to use the Visual Studio paths etc.

This one’s straight off of How I can use PowerShell with the Visual Studio Command Prompt? and it works a treat.

So I amend the $profile file with the following (updated to include VS 2015)

function Set-VsCmd
{
    param(
        [parameter(Mandatory=$true, HelpMessage="Enter VS version as 2010, 2012, 2013, 2015")]
        [ValidateSet(2010,2012,2013,2015)]
        [int]$version
    )
    $VS_VERSION = @{ 2010 = "10.0"; 2012 = "11.0"; 2013 = "12.0"; 2015 = "14.0" }
    if($version -eq 2015)
    {
        $targetDir = "c:\Program Files (x86)\Microsoft Visual Studio $($VS_VERSION[$version])\Common7\Tools"
        $vcvars = "VsMSBuildCmd.bat"
    }
    else
    {
        $targetDir = "c:\Program Files (x86)\Microsoft Visual Studio $($VS_VERSION[$version])\VC"
        $vcvars = "vcvarsall.bat"
    }
 
    if (!(Test-Path (Join-Path $targetDir $vcvars))) {
        "Error: Visual Studio $version not installed"
        return
    }
    pushd $targetDir
    cmd /c $vcvars + "&set" |
    foreach {
      if ($_ -match "(.*?)=(.*)") {
        Set-Item -force -path "ENV:\$($matches[1])" -value "$($matches[2])"
      }
    }
    popd
    write-host "`nVisual Studio $version Command Prompt variables set." -ForegroundColor Yellow
}

The previous version (non-VS 2015) is listed below in case it’s still needed

function Set-VsCmd
{
    param(
        [parameter(Mandatory=$true, HelpMessage="Enter VS version as 2010, 2012, or 2013")]
        [ValidateSet(2010,2012,2013)]
        [int]$version
    )
    $VS_VERSION = @{ 2010 = "10.0"; 2012 = "11.0"; 2013 = "12.0" }
    $targetDir = "c:\Program Files (x86)\Microsoft Visual Studio $($VS_VERSION[$version])\VC"
    if (!(Test-Path (Join-Path $targetDir "vcvarsall.bat"))) {
        "Error: Visual Studio $version not installed"
        return
    }
    pushd $targetDir
    cmd /c "vcvarsall.bat&set" |
    foreach {
      if ($_ -match "(.*?)=(.*)") {
        Set-Item -force -path "ENV:\$($matches[1])" -value "$($matches[2])"
      }
    }
    popd
    write-host "`nVisual Studio $version Command Prompt variables set." -ForegroundColor Yellow
}

Another user on the same question of stackoverlow also put forward the idea of simply changing the shortcut that Visual Studio supply to add the & powershell, like this

%comspec% /k ""C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\Tools\VsDevCmd.bat" & powershell"

Creating a Powershell function

We can create commands/cmdlets using C# or combine commands in ps1 files (for example) but we can also implement Powershell functions, which give is the ability to combine existing Powershell commands but wrap them in their own function with their own help etc.

Let’s look at a simple implementation of a tail-like command

Get-Content -Path c:\logs\logfile.log -Wait 

Ofcourse the first thing we might like to do is wrap this in a simple function, like this

function Get-Tail 
{ 
   Get-Content -Path $args[0] -Wait 
}

This works fine, but now maybe we’d like to make it more obvious what argument(s) Get-Tail expects. So we’ll add a parameter which better illustrates the intent via it’s name etc. So now we might have

function Get-Tail 
{ 
   param (
      [Parameter(Mandatory=$true, 
       Position=0, 
       HelpMessage="Path of file to tail")]
      [ValidateNotNullOrEmpty()]
      [string]$Path
   )

   Get-Content -Path $Path -Wait 
}

Here we’ve named the parameter that we expect as Path and we’ve stipulated it’s type. We’ve also added a Parameter attribute to ensure it’s obvious that this is a mandatory field and if the user forgets to supply it, Powershell will ask for it, to that purpose we’ve also supplied a help message so if the user types !? into the prompt for Path, the user will get the help message telling them what’s expected.

This is looking good, but running Get-Help tells us very little about the function so now we can extend this further and make it look like this

function Get-Tail 
{ 
<#
.SYNOPSIS
   Lists the file to standard out and waits 
   for any change which are then also output
.DESCRIPTION
   Get-Tail works a little like the tail 
   command, it allows the user to write a 
   file to standard out and then waits for
   any further changes, these to are written to
   standard out until the user exist the command.
.PARAMETER Path
   The Path to the file to be tailed
.EXAMPLE
   Get-Tail c:\logs\logfile.log
#>
param
(
[Parameter(Mandatory=$true, 
 Position=0, 
 HelpMessage="Path of file to tail")]
[ValidateNotNullOrEmpty()]
[string]$Path
)
 
Get-Content -Path $Path -Wait 
}

Now we have a command written in Powershell which looks and acts like any of the common commands might work, with help.

Let’s quickly review the lines we’ve added – the <# #> is the comment block for Powershell, within it we’ve headings along the lines of .HEADING and below that we’ve just some text to describe the command. The .PARAMETER is more interesting in that we write the parameter name after it (we can have multiple .PARAMETER blocks for multiple params).

To find out what options are available for the help block, type

Get-Help about_Comment_Based_Help

References

Documenting Your PowerShell Binary Cmdlets