Restoring Clients from Windows Server 2012 Essentials with missing network card drivers

by Tobias Hertkorn on May 9th, 2013

Last night the laptop’s hard drive die. Just like that. Fortunately I do have my Windows Server 2012 Essentials set up and running on my LAN, so that way my home computers are backed up at least once a day. So no biggie.

I downloaded the restore disc from MSDN – but then the trouble kinda began. First I did not find an appropriate USB stick to turn it into a bootable device. When I finally had moved all files off of my regular USB stick (all 7GB of it) and had reconfigured the partition on it and copied the CD onto it, I was ready to get moving with the restore.

Unfortunately I got stuck at the “now please load your drivers” screen. Yes, the Windows Server 2012 Essentials does store your network drivers in a separate place, and I had copied the drivers to the USB stick – but the restore disk wasn’t able to load the driver. I can only guess why, but I figure it was either the wrong bit-ness or the wrong version of Windows (pretty old laptop with exotic hardware, so of course there were no recent drivers to be found anywhere…)

So what to do. I really wanted that laptop restored ASAP. Then I remembered that Win8 has Hyper-V built in now. So why not boot up the rescue system within a Hyper-V instance and attach the new drive to it? Configuring it was a breeze and the more powerful and newer laptop did a restore in record time – all while I was still able to continue working with it. No driver hassle and with my Sharkoon Pro plugged into the e-SATA port, just as easy as can be to set up. I guess from now on I will not even bother trying to restore using the physical hardware. Restore via Hyper-V is just so much easier.

The initial setup though is not without pitfalls, so I will walk you through it:

First of all create a new VM within Hyper-V, name and network connectivity doesn’t matter. Just make sure to select “no hard drive” in the initial setup wizard and connect the restore ISO to the virtual DVD drive.

All the while Hyper-V is setting up, we have to make sure that the connected drive that should be the target of our restore is in offline mode. To check that we have to open the disk management, right click on the drive and select “offline”.

Now we can open the more detailed settings page for our new virtual machine. We have to make some alterations in order to have a proper setup for a restore.

  1. Remove the network card and replace it with a “legacy network interface”. This will help us avoid driver issues. Make sure the network card is connected to an external virtual network switch. This is important so that the rescue disc and find the windows server 2012 essentials for the actual restore.
  2. Go to the (still empty) disk configuration and add a virtual ide disk. Then select “physical disk”. The disk you took offline moments ago should be listed in the drop-down.

That’s it. If everything is setup correctly booting up should take you to the restore. Navigating through all the steps of the wizard should lead you to a valid installation on the attached disk drive.

After the restore is finished and before the VM reboot, shut down the VM, remove the hard drive and plug it into your broken laptop. The system should now come online as expected and you should find yourself at the exact place where the last backup occurred.

Never worry about missing network card drivers during restore from an Home Server or Essential ever again.

May 9th, 2013 8:52 pm | Comments Off

Creating an ad-hoc WLAN on Win8

by Tobias Hertkorn on February 6th, 2013

Sharing your internet connection from a windows phone is just super easy. It’s all I need for a quick research or edit session on the road, enjoying the brilliant Touch Cover of my Surface, instead of typing on my tiny cell phone screen.

For longer sessions, e.g. if the hotel WLAN connection becomes flaky during evening hours, I’d rather not use precious traffic from my cell phone. Instead I want to use the way more generous plan I have for my built in mobile connection on my laptop.

As I have pointed out before, network options have changed with the switch to Windows 8. And that holds true to the setup of an ad-hoc WLAN connection as well. As far as I could tell the option to start a new ad-hoc wireless network was removed from the standard GUI.

Fortunately it is possible to create one from the command line. All we need is a little help from netsh.

Just add this to a start.bat:


REM start
netsh wlan set hostednetwork mode=allow ssid=TSharpAdHoc key=123RandomKey
netsh wlan start hostednetwork

and a stop.bat:


REM stop
netsh wlan stop hostednetwork

Now you can even go as far as adding those as tiles as a convenient way to start the ad-hoc connection and share your mobile network via ICS. Done!

Now there are two little icons sitting in my new start menu for easy connection sharing enabling and disabling. So, please excuse me, I will go sit over there on the bed, proof-reading this blog post, all thanks to SkyDrive and my new shared connection.

February 6th, 2013 8:52 pm | Comments (1)

Success: Headless installation of Windows Server 2012 Essentials

by Tobias Hertkorn on January 2nd, 2013

TL;DR;

Do the first basic steps using Hyper-V on a second PC; then after the first reboot throw the disk into the designated headless server.

The trick

Headless installation can be cumbersome and nerve-wrecking. “Did it boot from my USB stick correctly?”, “Is it hanging somewhere waiting for input?”, … Those are the kind of questions going through my head every time I did a headless installation in the past and waited, often for hours, until the server finally gave me some meaningful feedback.

No more, because a.) The new setup experience for Windows Server 2012 Essentails takes headless installations into account and b.) I now know a nice little trick that makes the installation experience an even better one.

The first part of the installation can be done on any PC running Hyper-V. Up to the first reboot of the setup there are no device specific things going on, so why not doing them with the benefit of full visual control?

Hyper-V makes everything easier

Who would have thought Hyper-V even makes installing hardware server easier? Not me.

But by making the soon-to-be server drive the main disk within a hyper-v session actually lets you start the installation process right up to the point where it is ready to be popped into the server to finish the installation.

To do that connect the new server drive to a second PC. I did this using the Sharkoon SATA QuickPort PRO HDD docking station. It’s a great piece of hardware, especially when you are experience hardware problems. And then it’s usually too late to order one, so my advice: Every house hold should have one anyway, so go out there and buy one now. I’ll wait here.

Did you buy one? Good.

For me the QuickPort PRO is best suited, because it has eSATA support, which from my experience will greatly speed up both restores and headless installs.

Mark the newly attached disk as offline and add it to Hyper-V

In order for the new drive to be consumed by Hyper-V it must be marked as offline for the host OS. For that go into the disk management MMC snap-in. I usually do that by right-clicking on “Computer” and selecting “Manage”. Finding the server disk, I right-click it and select “Offline”. In this screenshot Disk 2 is the server disk and it is now marked as offline.

With this done, I can now create a new virtual machine using the Hyper-V manager.

Going through the wizard, I select 2048MB of RAM, select no connection yet and select “Attach a virtual hard disk later”.

Now I open the settings dialog for the virtual machine, because I need to change a couple of things, in order for the installation to go through smoothly. First of all, I upgrade the number of processors to 2. Then I remove the Network Adapter and add a Legacy Network Adapter instead. Strictly speaking this is not necessary for this particular installation. But it helps and it is vital for restores. So I do it anyways. For the network I choose my external network that shares the network interface.

For the VM to boot into the installation I attach the ISO of the installation disk to the DVD drive on IDE Controller 1.

Finally I select the IDE Controller 0 and add a hard drive. Then I select “Physical hard disk” and in the dropdown I choose the server disk.

That’s it. Now the settings dialog should look like this:

Start the installation and watch out for the reboot

Now I start the VM in order to begin the installation. For the rest to work flawlessly it is important that I shut down the VM right before or at least during the very first reboot the installation initiates. In case of the Windows Server 2012 Essentials installation it is right after the installation screen looks this way:

Finishing the installation

Next thing to do is put the server disk as the primary disk into the server. And power on.

After a short while I am able to connect to the IP the DHCP server assigned to the new server and continue the installation via browser.

Here the installation for 2012 Essentials especially shines, since it lets me supervise the process of the final installation.

Finally, after the installation is done I am presented with a website that lets me connect my clients to my freshly baked server.

Final words

I hope you enjoyed this little trick. For me it became absolutely helpful, not just for the installation of the server, but especially during restores of client backups. Using the Hyper-V it is super easy to do the restore to a new drive without worrying about network drivers or flaky hardware.

January 2nd, 2013 2:04 pm | Comments (2)

Howto choose a network location in Windows 8

by Tobias Hertkorn on November 24th, 2012

Windows 8 changed a bit compared to Windows 7 and Windows Vista. We all know that. It also changed the place, where you can choose what kind of network type you want to apply to the connection. Private, public, work or domain are no longer chosen via Network and Sharing Center.

Instead open the networks charm and right-click (or touch and hold) the network you want to change and select “Turn sharing on or off”.

Then choose the network type – it is an indirect choice. You no longer choose private or public directly, instead you choose the kind of sharing behavior the network should have.

As a final step you can verify that your selection is reflected in the right way using Control Panel\Network and Internet\Network and Sharing Center\Advanced sharing settings. Just look for the profile marked with “(current profile)”

November 24th, 2012 12:58 pm | Comments (2)

Writing a Task-based IHttpAsyncHandler

by Tobias Hertkorn on October 17th, 2012

Download the source at: Com.Hertkorn.TaskBasedIHttpAsyncHandler

If you want to serve up files or other content that has not much in common with the webforms or mvc engine, you should serve them using http handlers. The corresponding interface is IHttpHandler. They are light-weight and give great performance, especially when combined with IIS 7.

The basics - registering a custom handler

Http handler can be registered on multiple levels within IIS. One of the most common one is within the confines of the web application. That is done using web.config. If you have a handler defined in the class Com.Hertkorn.TaskBasedIHttpAsyncHandler.Www.Handlers.SampleAwaitHttpAsyncHandler which is in the dll Com.Hertkorn.TaskBasedIHttpAsyncHandler.Www use the following syntax:

XML:
  1. <configuration>
  2.   <system.webServer>
  3.     <handlers>
  4.       <add name="testAwait" path="testAwait.axd" type="Com.Hertkorn.TaskBasedIHttpAsyncHandler.Www.Handlers.SampleAwaitHttpAsyncHandler, Com.Hertkorn.TaskBasedIHttpAsyncHandler.Www" verb="*"/>
  5.     </handlers>
  6.   </system.webServer>
  7. </configuration>

Make sure you understand the ramifications of the verb setting, before you copy and paste the above code.

Now the http handler listens under the URL testAwait.axd for incoming requests.

Taking it up a notch - async

Using async handlers, when done correctly, will offload IO bound work to IO completion ports and therefore will free up the http pipeline threads to work on other requests. In high-stress situations this is crucial in order to achieve high throughput. IHttpAsyncHandler though is not for every situation, a introduction into the advantages and disadvantages can be found on msdn.

The original interface for an async IHttpHandler is called IHttpAsyncHandler and is implementing like this:

C#:
  1. public abstract class HttpAsyncHandler : IHttpAsyncHandler
  2. {
  3.     public abstract bool IsReusable { get; }
  4.  
  5.     IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
  6.     {
  7.         // ...
  8.     }
  9.  
  10.     void IHttpAsyncHandler.EndProcessRequest(IAsyncResult result)
  11.     {
  12.         // ...
  13.     }
  14.  
  15.     void IHttpHandler.ProcessRequest(HttpContext context)
  16.     {
  17.         // ...
  18.     }
  19. }

Of course this is not an easy and "modern" interface into the async world. Nowadays we are trying to expose async operations using the Task class. So ideally an asynchronous handler would look something like this:

C#:
  1. public class SampleTaskHttpAsyncHandler : HttpAsyncHandler
  2. {
  3.     public override Task ProcessRequestAsync(HttpContextBase context)
  4.     {
  5.         return new Task(() =>
  6.         {
  7.             var data = DateTime.Now.ToString("hh:MM:ss,fff");
  8.             Thread.Sleep(1000);
  9.             context.Response.Output.Write("<html><body><p>start: ");
  10.             context.Response.Output.Write(data);
  11.             context.Response.Output.Write("<br/>done: ");
  12.             context.Response.Output.Write("{0:hh:MM:ss,fff}", DateTime.Now);
  13.             context.Response.Output.WriteLine("</p>");
  14.             context.Response.Output.Write("</body></html>");
  15.         });
  16.     }
  17.  
  18.     public override bool IsReusable
  19.     {
  20.         get
  21.         {
  22.             return true; // Not keeping any state in this handler
  23.         }
  24.     }
  25. }

or even better using the new async await syntax introduced in .NET 4.5:

C#:
  1. public class SampleTaskHttpAsyncHandler : HttpAsyncHandler
  2. {
  3.     public override async Task ProcessRequestAsync(HttpContextBase context)
  4.     {
  5.         var data = DateTime.Now.ToString("hh:MM:ss,fff");
  6.         await Task.Delay(1000);
  7.         context.Response.Output.Write("<html><body><p>start: ");
  8.         context.Response.Output.Write(data);
  9.         context.Response.Output.Write("<br/>done: ");
  10.         context.Response.Output.Write("{0:hh:MM:ss,fff}", DateTime.Now);
  11.         context.Response.Output.WriteLine("</p>");
  12.         context.Response.Output.Write("</body></html>");
  13.     }
  14.  
  15.     public override bool IsReusable
  16.     {
  17.         get
  18.         {
  19.             return true; // Not keeping any state in this handler
  20.         }
  21.     }
  22. }

In order to achieve that we will implement an abstact HttpAsyncHandler base class that will handle all the ugly continuation handling for us and will present us with a simple Task based mechanism.

The implementation

C#:
  1. public abstract class HttpAsyncHandler : IHttpAsyncHandler
  2. {
  3.     // In very high performance situations you might want to change the argument to HttpContext
  4.     // and save one object allocation per request.
  5.     public abstract Task ProcessRequestAsync(HttpContextBase context);
  6.  
  7.     public abstract bool IsReusable { get; }
  8.  
  9.     IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
  10.     {
  11.         if (cb == null)
  12.         {
  13.             throw new InvalidOperationException("cb is null");
  14.         }
  15.  
  16.         var task = CreateTask(context);
  17.  
  18.         task.ContinueWith((ar, state) => (state as AsyncCallback)(ar), cb);
  19.  
  20.         if (task.Status == TaskStatus.Created)
  21.         {
  22.             // Got a cold task -> start it
  23.             task.Start();
  24.         }
  25.  
  26.         return task;
  27.     }
  28.  
  29.     void IHttpAsyncHandler.EndProcessRequest(IAsyncResult result)
  30.     {
  31.         var task = result as Task;
  32.  
  33.         // Await task --> Will throw pending exceptions
  34.         // (to avoid an exception being thrown on the finalizer thread)
  35.         task.Wait();
  36.  
  37.         task.Dispose();
  38.     }
  39.  
  40.     void IHttpHandler.ProcessRequest(HttpContext context)
  41.     {
  42.         var task = CreateTask(context);
  43.  
  44.         if (task.Status == TaskStatus.Created)
  45.         {
  46.             task.RunSynchronously();
  47.         }
  48.         else
  49.         {
  50.             task.Wait();
  51.         }
  52.     }
  53.  
  54.     private Task CreateTask(HttpContext context)
  55.     {
  56.         var task = ProcessRequestAsync(new HttpContextWrapper(context));
  57.  
  58.         if (task == null)
  59.         {
  60.             throw new InvalidOperationException("ProcessRequestAsync must return a Task");
  61.         }
  62.  
  63.         return task;
  64.     }
  65. }

The trick is to use the ContinueWith in order to complete the request after the actual task is done.

Download the source with sample at: Com.Hertkorn.TaskBasedIHttpAsyncHandler

October 17th, 2012 8:48 pm | Comments Off

Uploading a file to Azure blob storage from powershell

by Tobias Hertkorn on October 12th, 2012

I am in no way a powershell user, so uploading a simple file to blog storage was surprisingly non-intuitive for me. After I found out how to do it, it is painfully obvious - but not intuitive. So in order for me to have it written down - and for you to use, here is the code:

XML:
  1. [System.Reflection.Assembly]::LoadFrom("C:\Program Files\Microsoft SDKs\Windows Azure\.NET SDK\2012-06\bin\Microsoft.WindowsAzure.StorageClient.dll")
  2. $account = [Microsoft.WindowsAzure.CloudStorageAccount]::Parse("DefaultEndpointsProtocol=https;AccountName=<youraccountname>;AccountKey=<youraccountkey>")
  3. $client = [Microsoft.WindowsAzure.StorageClient.CloudStorageAccountStorageClientExtensions]::CreateCloudBlobClient($account)
  4. $blob = $client.GetBlockBlob(<blobname>)
  5. $blob.UploadFile(<localpath>)

Make sure to execute these commands within the azure powershell command line.

Replace <youraccountname> with the name of your blob storage account, and <youraccountkey> with the corresponding access key.
<blobname> is the name of the file you want to create in storage. Here is a nice tip for you: If you want to make it look and feel as if there are subdirectories, just name the blob appropriately: "subdirectory/name" would create a blob "name" in the subdirectory "subdirectory". It's more of an convention, but most tools follow this convention.
<localpath> is of the form "c:\temp\testfile.txt"

Hope that helps!

October 12th, 2012 7:29 am | Comments Off

Programmatically change power options using C#

by Tobias Hertkorn on May 8th, 2012

During the development of my little Set Power Scheme daemon, I did a dive into the Power Options API of Windows. Basically I needed a way to programmatically set the active power scheme. The power management functions are located in the PowrProf.dll. Unfortunately there is no managed wrapper to said functionality, but it is a pretty easy API to program against. Specifically we will look at PowerEnumerate, PowerReadFriendlyName, PowerGetActiveScheme and PowerSetActiveScheme.

The first thing one notices is that all power schemes have a unique Guid assigned, which is used to identify the available schemes. So, let’s just get all available Guids. For that we will use PowerEnumerate.

C#:
  1. internal static class WinAPI
  2. {
  3.     [DllImport("PowrProf.dll")]
  4.     public static extern UInt32 PowerEnumerate(IntPtr RootPowerKey, IntPtr SchemeGuid, IntPtr SubGroupOfPowerSettingGuid, UInt32 AcessFlags, UInt32 Index, ref Guid Buffer, ref UInt32 BufferSize);
  5.  
  6.     public enum AccessFlags : uint
  7.     {
  8.         ACCESS_SCHEME = 16,
  9.         ACCESS_SUBGROUP = 17,
  10.         ACCESS_INDIVIDUAL_SETTING = 18
  11.     }
  12. }

Calling the function once will yield back the Guid of the power scheme at the specified schemeIndex. So, in order to get all we simply call the function in a while loop until no more schemes are found (= the return value of the function is not 0).

C#:
  1. public IEnumerable<Guid> FindAll()
  2. {
  3.     var schemeGuid = Guid.Empty;
  4.  
  5.     uint sizeSchemeGuid = (uint)Marshal.SizeOf(typeof(Guid));
  6.     uint schemeIndex = 0;
  7.  
  8.     while (WinAPI.PowerEnumerate(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, (uint)WinAPI.AccessFlags.ACCESS_SCHEME, schemeIndex, ref schemeGuid, ref sizeSchemeGuid) == 0)
  9.     {
  10.         yield return schemeGuid;
  11.         schemeIndex++;
  12.     }
  13. }

Of course only reading the Guid is not very nice, so we will translate the Guid into a friendly name using the PowerReadFriendlyName call.

C#:
  1. [DllImport("PowrProf.dll")]
  2. public static extern UInt32 PowerReadFriendlyName(IntPtr RootPowerKey, ref Guid SchemeGuid, IntPtr SubGroupOfPowerSettingGuid, IntPtr PowerSettingGuid, IntPtr Buffer, ref UInt32 BufferSize);

This API is not the easiest to use for us that are not using COM Interop on a daily basis. In order to call this API, we have to allocate memory for the friendly name to be written to and when we are done free said memory.

C#:
  1. private static string ReadFriendlyName(Guid schemeGuid)
  2. {
  3.     uint sizeName = 1024;
  4.     IntPtr pSizeName = Marshal.AllocHGlobal((int)sizeName);
  5.  
  6.     string friendlyName;
  7.  
  8.     try
  9.     {
  10.         WinAPI.PowerReadFriendlyName(IntPtr.Zero, ref schemeGuid, IntPtr.Zero, IntPtr.Zero, pSizeName, ref sizeName);
  11.         friendlyName = Marshal.PtrToStringUni(pSizeName);
  12.     }
  13.     finally
  14.     {
  15.         Marshal.FreeHGlobal(pSizeName);
  16.     }
  17.  
  18.     return friendlyName;
  19. }

Notice that the free call is executed in a finally block, therefore guaranteeing that it is even called in any exception case.

Finally we can now use the Guid to either get or set the active power scheme.

C#:
  1. [DllImport("PowrProf.dll")]
  2. public static extern uint PowerGetActiveScheme(IntPtr UserRootPowerKey, ref IntPtr ActivePolicyGuid);
  3.  
  4. [DllImport("PowrProf.dll")]
  5. public static extern uint PowerSetActiveScheme(IntPtr UserRootPowerKey, ref Guid SchemeGuid);

C#:
  1. public void SetActive(Guid powerSchemeId)
  2. {
  3.     var schemeGuid = powerSchemeId;
  4.  
  5.     WinAPI.PowerSetActiveScheme(IntPtr.Zero, ref schemeGuid);
  6. }
  7.  
  8. public Guid GetActive()
  9. {
  10.     IntPtr pCurrentSchemeGuid = IntPtr.Zero;
  11.  
  12.     WinAPI.PowerGetActiveScheme(IntPtr.Zero, ref pCurrentSchemeGuid);
  13.  
  14.     var currentSchemeGuid = (Guid)Marshal.PtrToStructure(pCurrentSchemeGuid, typeof(Guid));
  15.  
  16.     return currentSchemeGuid;
  17. }

All this functionally is wrapped in a nice repository pattern in Set Power Scheme:

PowerSchemeRepository.cs

Happy coding.

May 8th, 2012 11:21 am | Comments Off

The trouble of making a project public

by Tobias Hertkorn on March 7th, 2012

I just wrote a little helper program that lets you track your currently set power scheme. And reset it, if any external sources (like Windows group policies) change said power options. I wrote this, because I have sometimes long running tasks that collide head-on with our IT departments wise decision to force a power scheme that puts my laptop to sleep after an hour. A laptop. What's the power consumption of that thing? And most of the time it's not even plugged in on site, spending company money.

Writing the tool was pretty easy, done in about 45 minutes of coding to get a basic sweet little exe that does nothing but parse a command-line argument, and force-set a power scheme. Which was more than fine by me, since I used the task scheduler to run this program every 5 minutes, knew how to correctly pass a guid to the program, was not surprised that it did not give any feedback, ...

I started showing the tool to a couple of friends, who - not surprisingly - had the same problems. How come all IT departments of all major companies have the same stupid ideas? ;-) This is when the suggestions and the feedback started rolling in. So, at first I wrote "documentation" on how to use it. Then I added more elaborate command line parsing, because it seems people do not read manuals. Strange.
Then I set up a git repository on github, so I could start saying "is there a pull request for what you are suggesting" and be all hip and stuff. This did not work out so great, because I ended up adding a daemon mode. Some poor individuals apparently do not even have admin rights on their laptop - and could not set up a task in task scheduler.
And finally I added the possibility to copy and paste the power scheme guids from within the program because some people can't be bothered to use powercfg.exe.

And I feel like it is still not done. What are the todos? Write a blog post about the tool and set up a dedicated website for it.

So, going public meant going from a 45 min solution that worked brilliantly, to working more than 10 hours for it. And suddently feel like I am not done.

Is it always that hard to give back to the community? What is your experience?

March 7th, 2012 2:27 pm | Comments (1)

Signaling IIS that the app pool is still busy – Running Tasks in IIS

by Tobias Hertkorn on February 8th, 2012

Unfortunately IIS is really not at all nice during app pool shutdown to threads running in its context. They simply are killed hard when a shutdown is initiated e.g. during an app pool recycle.

Of course this is by no means a desired behavior and can lead to serious data corruption. Phill Haack already did an excellent blog post on this subject, so please go read The Dangers of Implementing Recurring Background Tasks In ASP.NET now. I’ll wait here.

Today I want to talk about an addition to RegisterObject. The IIS does check if an app pool is busy or idle and may initiate a shutdown simply because it wants to save resources by shutting down long idle app pools. So simply put, even though one correctly uses RegisterObject, the app pool still does not know that there is stuff going on, which is relevant and should mark the app pool as busy.

Like RegisterObject this is done using the System.Web.Hosting.HostingEnvironment class, specifically IncrementBusyCount() and DecrementBusyCount(). With a busy count larger than zero the app pool is marked as busy.

When running task I would recommend that any given tasks does two things before starting its work: Register an object as managed by the hosting environment using RegisterObject and increment the busy counter.

It goes without saying that it must be guaranteed that a task, even a failed one, does a proper cleanup, unregistering and decrementing the busy count.

Final side-note: Please do realize that the IIS will still initiate app pool shutdowns even on busy app pools. So there is still the need to use RegisterObject.

February 8th, 2012 11:53 am | Comments Off

Using conditional references to influence which assemblies are used for a specific project configuration or platform

by Tobias Hertkorn on November 22nd, 2011

Introduction

When using specific third party assemblies it is necessary to distinguish between platforms. Best example are e.g. log4net, sqlite, … which come in both x86 and x64 but not in the general Any Cpu flavor. A typical development scenario could be that an ASP.NET application is developed using the IIS Express and deployed on a 64bit IIS in production.
A different development scenario could be that a Silverlight application uses Design Time data in order to create a richer design time experience for the developer. In this situation a best practice is to put the classes containing the design time data into a separate assembly that is only referenced during development and is not deployed in production. Here one needs to distinguish between solution configuration Debug and Release.
In order to support these scenarios the project files allow for conditional references.
Unfortunately Visual Studio does not expose the conditional references feature in its UI. Therefore one has to edit the corresponding .csproj file by hand. This can be simply done using Visual Studio using the following steps:

  1. Right click the project one wants to edit
  2. Select "Unload Project" from the popup menu
  3. Right click the unloaded project
  4. Select "Edit "

Basic project file layout (Visual Studio 2010)

A project file (.csproj) is an XML file containing an XML header and a root element Project. The Project can contain multiple elements of different types, most notably the PropertyGroup, ItemGroup, Import and Target elements. In this post I will not describe the specific use of each of the elements, but will concentrate on conditional assemblies. Documentation for these elements can be found on msdn.
Assembly or project references are found in an ItemGroup using either Reference or ProjectReference elements.
A typical .csproj producing either an x86 or x64 executable looks like this:

XML:
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  3.   <PropertyGroup>
  4.     <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
  5.     <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
  6.     <ProductVersion>8.0.30703</ProductVersion>
  7.     <SchemaVersion>2.0</SchemaVersion>
  8.     <ProjectGuid>{5793FBD7-6122-484B-AD86-AA211162D0EB}</ProjectGuid>
  9.     <OutputType>Exe</OutputType>
  10.     <AppDesignerFolder>Properties</AppDesignerFolder>
  11.     <RootNamespace>Com.Hertkorn.DotNet.ConditionalAssemblyReferences</RootNamespace>
  12.     <AssemblyName>Com.Hertkorn.DotNet.ConditionalAssemblyReferences</AssemblyName>
  13.     <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
  14.     <TargetFrameworkProfile>
  15.     </TargetFrameworkProfile>
  16.     <FileAlignment>512</FileAlignment>
  17.   </PropertyGroup>
  18.   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
  19.     <PlatformTarget>x86</PlatformTarget>
  20.     <DebugSymbols>true</DebugSymbols>
  21.     <DebugType>full</DebugType>
  22.     <Optimize>false</Optimize>
  23.     <OutputPath>bin\Debug\</OutputPath>
  24.     <DefineConstants>DEBUG;TRACE</DefineConstants>
  25.     <ErrorReport>prompt</ErrorReport>
  26.     <WarningLevel>4</WarningLevel>
  27.   </PropertyGroup>
  28.   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
  29.     <PlatformTarget>x86</PlatformTarget>
  30.     <DebugType>pdbonly</DebugType>
  31.     <Optimize>true</Optimize>
  32.     <OutputPath>bin\Release\</OutputPath>
  33.     <DefineConstants>TRACE</DefineConstants>
  34.     <ErrorReport>prompt</ErrorReport>
  35.     <WarningLevel>4</WarningLevel>
  36.   </PropertyGroup>
  37.   <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
  38.     <DebugSymbols>true</DebugSymbols>
  39.     <OutputPath>bin\x64\Debug\</OutputPath>
  40.     <DefineConstants>DEBUG;TRACE</DefineConstants>
  41.     <DebugType>full</DebugType>
  42.     <PlatformTarget>x64</PlatformTarget>
  43.     <CodeAnalysisLogFile>bin\Debug\Com.Hertkorn.DotNet.ConditionalAssemblyReferences.exe.CodeAnalysisLog.xml</CodeAnalysisLogFile>
  44.     <CodeAnalysisUseTypeNameInSuppression>true</CodeAnalysisUseTypeNameInSuppression>
  45.     <CodeAnalysisModuleSuppressionsFile>GlobalSuppressions.cs</CodeAnalysisModuleSuppressionsFile>
  46.     <ErrorReport>prompt</ErrorReport>
  47.     <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
  48.     <CodeAnalysisRuleSetDirectories>;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets</CodeAnalysisRuleSetDirectories>
  49.     <CodeAnalysisIgnoreBuiltInRuleSets>true</CodeAnalysisIgnoreBuiltInRuleSets>
  50.     <CodeAnalysisRuleDirectories>;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules</CodeAnalysisRuleDirectories>
  51.     <CodeAnalysisIgnoreBuiltInRules>true</CodeAnalysisIgnoreBuiltInRules>
  52.   </PropertyGroup>
  53.   <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
  54.     <OutputPath>bin\x64\Release\</OutputPath>
  55.     <DefineConstants>TRACE</DefineConstants>
  56.     <Optimize>true</Optimize>
  57.     <DebugType>pdbonly</DebugType>
  58.     <PlatformTarget>x64</PlatformTarget>
  59.     <CodeAnalysisLogFile>bin\Release\Com.Hertkorn.DotNet.ConditionalAssemblyReferences.exe.CodeAnalysisLog.xml</CodeAnalysisLogFile>
  60.     <CodeAnalysisUseTypeNameInSuppression>true</CodeAnalysisUseTypeNameInSuppression>
  61.     <CodeAnalysisModuleSuppressionsFile>GlobalSuppressions.cs</CodeAnalysisModuleSuppressionsFile>
  62.     <ErrorReport>prompt</ErrorReport>
  63.     <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
  64.     <CodeAnalysisRuleSetDirectories>;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets</CodeAnalysisRuleSetDirectories>
  65.     <CodeAnalysisIgnoreBuiltInRuleSets>false</CodeAnalysisIgnoreBuiltInRuleSets>
  66.     <CodeAnalysisRuleDirectories>;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules</CodeAnalysisRuleDirectories>
  67.     <CodeAnalysisIgnoreBuiltInRules>false</CodeAnalysisIgnoreBuiltInRules>
  68.   </PropertyGroup>
  69.   <ItemGroup>
  70.     <Reference Include="CustomMarshalers" />
  71.     <Reference Include="System" />
  72.     <Reference Include="System.Core" />
  73.     <Reference Include="System.Xml.Linq" />
  74.     <Reference Include="System.Data.DataSetExtensions" />
  75.     <Reference Include="Microsoft.CSharp" />
  76.     <Reference Include="System.Data" />
  77.     <Reference Include="System.Xml" />
  78.   </ItemGroup>
  79.   <ItemGroup>
  80.     <Compile Include="Program.cs" />
  81.     <Compile Include="Properties\AssemblyInfo.cs" />
  82.   </ItemGroup>
  83.   <ItemGroup>
  84.     <None Include="app.config" />
  85.   </ItemGroup>
  86.   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
  87.   <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
  88.        Other similar extension points exist, see Microsoft.Common.targets.
  89.   <Target Name="BeforeBuild">
  90.   </Target>
  91.   <Target Name="AfterBuild">
  92.   </Target>
  93.   -->
  94. </Project>

Conditionals

In the above project one can already spot the mechanism used to distinguish between e.g. configurations and platforms.

XML:
  1. <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">

An XML node annotated with the Condition attribute is only included when parsing the file if the condition holds true. This mechanism can be applied to any element in the project file's XML structure.

Using conditions for assembly references

If one uses an assembly that is specifically designed for x86 or x64 one can use the conditional attribute to include the correct dll for the specified build platform. One of the best known dll of that type is SQLite:

Without the conditional a dll reference looks like this:

XML:
  1. <Reference Include="System.Data.SQLite">
  2.   <HintPath>..\libs\sqlite\System.Data.SQLite.dll</HintPath>
  3. </Reference>

To distinguish between the two states of 32bit and 64bit one simply doubles theReference element and applies mutually exclusive Condition attributes to both:

XML:
  1. <Reference Include="System.Data.SQLite" Condition="'$(PlatformTarget)' == 'x86'">
  2.   <HintPath>..\libs\sqlite\x86\System.Data.SQLite.dll</HintPath>
  3. </Reference>
  4. <Reference Include="System.Data.SQLite" Condition="'$(PlatformTarget)' == 'x64'">
  5.   <HintPath>..\libs\sqlite\x64\System.Data.SQLite.dll</HintPath>
  6. </Reference>

Note that the HintPath differs depending on the platform.

Using conditions for project references

If you have a project that should not get deployed as part of the final released product, why not use the Condition attribute to only include the project during debugging. One good example for these kinds of projects is Design-time data. Simply distinguish on the Configuration setting.

XML:
  1. <ProjectReference
  2.       Include="..\Com.Hertkorn.DotNet.ConditionalAssemblyReferences.Design\Com.Hertkorn.DotNet.ConditionalAssemblyReferences.Design.csproj"
  3.       Condition="'$(Configuration)' == 'Debug'">
  4.   <Project>{C7EAA6CA-1AF6-4D72-929B-D7D1177868D2}</Project>
  5.   <Name>Com.Hertkorn.DotNet.ConditionalAssemblyReferences.Design</Name>
  6. </ProjectReference>

Hint

Keep in mind that the Visual Studio UI does not reflect conditional references within the "References" folder of a project. Therefore all references are visible in any given configuration or platform selection. The compiler and Intellisense on the other hand are aware of conditional references, honoring the correct settings both with visual feedback and error notification during builds.

Summary

Today we looked at the possibility to customize which assemblies or projects are referenced in each individual configuration or platform selection. The customization cannot be done using the UI, but is accomplished by manually editing the project file.

November 22nd, 2011 8:52 pm | Comments Off
Tobi + C# = T# - Blogged blogoscoop