Monthly Archives: June 2016

Programmatically editing the hosts file

If you deal with multiple web servers, all with the same service addresses, but in different locations, you’ll find you’re constantly in your hosts file. It is not exactly an onerous task, changing the hosts file; typically, it resides in

C:\Windows\System32\drivers\etc

You have to be running as admin to make any changes, but other than that, it’s just a text file; mine looks like this:

# Copyright (c) 1993-2009 Microsoft Corp.
#
# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.
#
# This file contains the mappings of IP addresses to host names. Each
# entry should be kept on an individual line. The IP address should
# be placed in the first column followed by the corresponding host name.
# The IP address and the host name should be separated by at least one
# space.
#
# Additionally, comments (such as these) may be inserted on individual
# lines or following the machine name denoted by a ‘#’ symbol.
#
# For example:
#
# 102.54.94.97 rhino.acme.com # source server
# 38.25.63.10 x.acme.com # x client host

# localhost name resolution is handled within DNS itself.
# 127.0.0.1 localhost
# ::1 localhost
127.0.0.1 http://www.myuri.test
#192.168.1.1 http://www.myuri.test

The important part for me are the last two lines. As you can see, I have two potential IP addresses for the same URL. I obviously could simply change this in the hosts file; which, as I said, is not exactly a difficult task. But what if there are another 25 possible I.P. addresses? You could certainly have comments, or you could use the following code to create a small utility to change this for you.

I’ve put all the code for this into a single XAML window (using the code behind); this means that I’ve tightly coupled the logic to the UI. I’m a big fan of the MVVM pattern, but there is such a thing as over thinking a problem, and given that this is changing a Windows text file, putting the code into some form of MVVM framework seems overkill.

The XAML looks like this:

<Window x:Class="ChangeHosts.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:ChangeHosts"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525"
        DataContext="{Binding RelativeSource={RelativeSource Self}}">
    <Grid>
        <StackPanel>
            <Button x:Name="LoadHosts" Click="LoadHosts_Click" Content="Load File"/>
            <ComboBox x:Name="comboHosts" ItemsSource="{Binding HostSelections}" SelectedIndex="{Binding HostSelectedIdx}"/>
            <Button x:Name="SetActiveHost" Click="SetActiveHost_Click" Content="Save Selection"/>
        </StackPanel>
    </Grid>
</Window>

As you can see, the first thing that I’ve done is set the data context to the code behind. Obviously, this means that the code-behind will need to implement INotifyPropertyChanged:

    public partial class MainWindow : Window, INotifyPropertyChanged
    {

We’re going to need a collection called HostSelections and an index to represent the selected item; here they are:

        private List<string> _hostSelections = new List<string>();
        public List<string> HostSelections
        {
            get { return _hostSelections; }
            set
            {
                _hostSelections = value;
                NotifyPropertyChanged();
            }
        }

        private int _previousSelectedIdx = -1;
        private int _hostSelectedIdx;
        public int HostSelectedIdx
        {
            get { return _hostSelectedIdx; }
            set
            {
                if (_hostSelectedIdx != _previousSelectedIdx || _previousSelectedIdx == -1)
                {
                    // Update previous value
                    _previousSelectedIdx = _hostSelectedIdx;

                    // Now update the selected index
                    _hostSelectedIdx = value;
                    NotifyPropertyChanged();
                }
            }
        }

The reason for tracking the previous value will become apparent in a minute. Other than that, there’s nothing really to see here. There are two event handlers required by the XAML; the first loads the hosts file:


        private async void LoadHosts_Click(object sender, RoutedEventArgs e)
        {
            await LoadHostFile();
        }

        private async Task LoadHostFile()
        {
            using (StreamReader sr = File.OpenText(System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "drivers/etc/hosts")))
            {
                string hosts = await sr.ReadToEndAsync();
                string[] entries = hosts.Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries).ToArray();
                HostSelections = entries.ToList();

                int idxSel = entries.Select((str, idx) => new { str, idx }).FirstOrDefault(e => !e.str.StartsWith("#")).idx;
                HostSelectedIdx = idxSel;
            }
        }

So, we parse the text and then set the bound collection to the list. Because the file is reloaded every time, there’s no need for an observable collection here. The next event handler sets the new active entry:


        private async void SetActiveHost_Click(object sender, RoutedEventArgs e)
        {
            await ChangeSelecteHost();
        }

        private async Task ChangeSelecteHost()
        {
            if (HostSelections[HostSelectedIdx].StartsWith("#"))
            {
                HostSelections[HostSelectedIdx] = HostSelections[HostSelectedIdx].TrimStart('#');
                HostSelections[_previousSelectedIdx] = $"#{HostSelections[_previousSelectedIdx]}";

                WriteHostsFile();
                await LoadHostFile();
            }
            else
            {
                MessageBox.Show("The selected line is already selected");
            }
        }

        private void WriteHostsFile()
        {
            string updatedHosts = string.Join(Environment.NewLine, HostSelections);

            File.WriteAllText(System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), 
                "drivers/etc/hosts"), updatedHosts);
        }

As you can see, setting the previous index was needed here.

That’s it – the code works as is (I’m omitted the NotifyPropertyChanged boiler plate code).

Conclusion

I’ve uploaded this project to GitHub. Feel free to make suggestions, or submit a pull request.

TaskCompletionSource

I’ve had a couple of problems recently, where I’ve had tasks or asynchronous methods and they don’t quote fit into the architecture that I find myself in. I’d come across the TaskCompletionSource before, but hadn’t realised how useful it was. Basically, a TaskCompletionSource allows you to control when a task finishes; and allows you to do so in a synchronous, or asynchronous fashion. What this gives you is precise control over when an awaited task finishes.

UWP

Consider the following code in UWP. Basically, what this does is execute an anonymous function on the UI thread:


await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High, async () => 
{
    await MyAyncFunc();
}
System.Diagnostics.Debug.WriteLine("After MyAsyncFunc");

The problem here is that executing an anonymous async function in the above scenario doesn’t work. However, using the TaskCompletionSource, we can bypass that whole conversation:

TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();

await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High, async () => 
{
    await MyAyncFunc();
    System.Diagnostics.Debug.WriteLine("After MyAsyncFunc");

    tcs.SetResult(true);
});
await tcs.Task;

Now the function will return when the the TaskCompletionSource.SetResult has been called.

Event based

The second scenario where this is useful is where you are trying to use an event based architecture within an async / await scenario. The following example is a little contrived, but it does illustrate the point:

    class Program
    {
        private static Timer _tmr = new Timer();
        private static TaskCompletionSource<bool> _tcs;

        static void Main(string[] args)
        {
            var tmr = StartTimer();

            Console.WriteLine("Before wait...");
            tmr.Wait();

            Console.WriteLine("After wait...");
        }        

        private static async Task StartTimer()
        {            

            _tmr.Interval = 3000;
            _tmr.Elapsed += _tmr_Elapsed;
            _tmr.Start();

            _tcs = new TaskCompletionSource<bool>();
            await _tcs.Task;
        }

        private static void _tmr_Elapsed(object sender, ElapsedEventArgs e)
        {
            _tcs.SetResult(true);
        }
    }

Potentially, a more real world example of this is when you might want to wrap an API in an async/await.

Control over exactly when a task finishes, and the ability to await async void methods

The final scenario where this can be useful is where you either want to await an `async void` method, or where you have a specific part of a method or process that you want to await.

The following code illustrates how to effectively await an async void method:

    class Program
    {        
        private static TaskCompletionSource<bool> _tcs;

        static void Main(string[] args)
        {
            _tcs = new TaskCompletionSource<bool>();
            BackgroundFunction();

            _tcs.Task.Wait();

            Console.WriteLine("Done");
        }        

        private static async void BackgroundFunction()
        {
            for (int i = 1; i <= 10; i++)
            {
                Console.WriteLine($"Processing: {i}");
                await DoStuff();
            }
            _tcs.SetResult(true);
        }

        private static async Task DoStuff()
        {
            await Task.Delay(500);
            
        }

    }

Finally, here is a parallel for loop:

        static void Main(string[] args)
        {
            Parallel.For(1, 3, (i) =>
            {
                BackgroundFunction();
            });

            Console.WriteLine("Done");
        }        

Imagine that BackgroundFunction is performing a long running task where a specific condition needs to return control. There are obviously combinations of functions in the TPL (WaitAll, WhenAll, WhenAny and WhenAll), however, these rely on the whole task, or a set of tasks, completing. Again, the below example is contrived, but it illustrates the granular control over the task that you have.

        static void Main(string[] args)
        {
            _tcs = new TaskCompletionSource<bool>();

            for (int i = 1; i <= 2; i++)
            {
                BackgroundFunction();
            }

            _tcs.Task.Wait();            

            Console.WriteLine("Done");
        }        

        private static async void BackgroundFunction()
        {
            for (int i = 1; i <= 10; i++)
            {
                Console.WriteLine($"Processing: {i}");
                await DoStuff();

                if (i == 7)
                {
                    _tcs.TrySetResult(true);
                    return;                    
                }
            }            
        }

I will re-iterate again, I realise that in the above example, there are better ways to achieve this, and the example is purely for illustration.

Conclusion

Generally speaking, the simplest and most robust code comes from using the task architecture in the way it was designed: that is, use async / await inside a method that returns a Task. I’m not suggesting in this post that the methods I’ve described should replace that; but there are situations where that might not fit.

Aknowledgements

I used the following posts heavily while writing this:

Awaiting the CoreDispatcher
The Nature of TaskCompletionSource
Real life scenarios for using TaskCompletionSource?
Task Parallelism