Saturday, May 04, 2013

WiX: Installing Files to Public Documents Folder

A few days ago I stumbled over the problem of installing files into the “Public Documents” folder using the Windows Installer Toolkit (WiX). Although the different bits a pieces are available somewhere on the internet I couldn’t locate a single source that describes a complete solution.

On Windows based systems each user has their own personal folder but there is also a folder called “Public Documents”. For the task at hand, I wanted to install a few sample text files into a subfolder of the public documents folder using WiX. For this example I used Visual Studio 2012 (Professional Edition or better is sufficient) with the Visual Studio extension installed by WiX 4.0.

Let’s start with the wizard for the WiX setup project:

image

As the name for the setup you can pick anything you like. In this case I chose “Acme.Setup”. The resulting project looks very simple. It has no references and only a single WiX script file in it:

image

The content of Produce.wxs is very basic (Note that the first few digits of all GUIDs in this post are replaced with the string ‘YOURGUID’. You need to replace the GUIDs with your own to make it work):

<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
    <Product Id="*" Name="Acme.Setup" Language="1033" Version="1.0.0.0" Manufacturer="" UpgradeCode="YOURGUID-269e-4795-97d7-a392d75749fb">
        <Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />

        <MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
        <MediaTemplate />

        <Feature Id="ProductFeature" Title="Acme.Setup" Level="1">
            <ComponentGroupRef Id="ProductComponents" />
        </Feature>
    </Product>

    <Fragment>
        <Directory Id="TARGETDIR" Name="SourceDir">
            <Directory Id="ProgramFilesFolder">
                <Directory Id="INSTALLFOLDER" Name="Acme.Setup" />
            </Directory>
        </Directory>
    </Fragment>

    <Fragment>
        <ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
            <!-- TODO: Remove the comments around this Component element and the ComponentRef below in order to add resources to this installer. -->
            <!-- <Component Id="ProductComponent"> -->
                <!-- TODO: Insert files, registry keys, and other resources here. -->
            <!-- </Component> -->
        </ComponentGroup>
    </Fragment>
</Wix>

Obviously, we now need to change this to add some files. So let’s pick a text file from a different project in the same solution. In this case I created a C# library projects called Acme.Lib and added one simple text file. We can ignore the default class ‘Class1’ that is generated in the Acme.Lib project. It is not relevant for this blog post. Here is what the solution looks like now:

image

The content of the file readme.txt and it’s name are irrelevant as well. Choose what works best for your case.

Next we need to make sure that the installer script knows about the text file and where it is located. The first option is to use your knowledge about the location of the two projects, Acme.Lib and Acme.Setup. So you could change the file Product.wxs as follows:

<Fragment>
    <ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
        <!-- TODO: Remove the comments around this Component element and the ComponentRef below in order to add resources to this installer. -->
        <!-- <Component Id="ProductComponent"> -->
            <!-- TODO: Insert files, registry keys, and other resources here. -->
      <Component Id="ReadmeFile" Guid="{YOURGUID-994F-49C0-A7AC-53D1571A295A}">
         <File Id="readme.txt" Name="readme.txt" Source="..\Acme.Lib\readme.txt"></File>
      </Component>
        <!-- </Component> -->
    </ComponentGroup>
</Fragment>

Some points of interest. First of all we need to add a new <Component> to the script. Your installer can consist of thousands of components if you wish. In this case I’m only adding one component with an Id and a GUID. Again, please note that I have replaced the first few digits of the GUID with ‘YOURGUID’. You need to replace all GUIDs in this example with newly generated GUIDs anyways to make it work.

Inside of the component I place a <File> tag with the Id, the name and the Source of the file. The Id needs to be unique within the installer while the name is the name that the file will have once installed. The ‘Source’ attributes tells the WiX toolset where to find the file so it can be included into the installer. Note that in contrast to some other installer tools sets the build of the installer will fail in case the WiX toolset can’t find the file.

At this stage there one additional tweak that we have to do to compile this build script successfully. We need to edit the 'Manufacturer’ attribute at the beginning of Product.wxs. Here are the first four lines (Note that ‘Manufacturer’ has been set to ‘Acme’):

<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
    <Product Id="*" Name="Acme.Setup" Language="1033" Version="1.0.0.0" Manufacturer="Acme" UpgradeCode="YOURGUID-269e-4795-97d7-a392d75749fb">
        <Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />

If you now build your solution it should build without problems.

At this stage all you have is an installer that installs into the into a subfolder in Program Files. It works but it is not what we want. So let’s have a look how we can change that.

The trick is to add some WiX specific extensions. This can be accomplished by adding a reference to the installer project, ‘Acme.Setup"’ in this example. The dialog box looks as follows and we want to add a file called ‘WixUtilExtension.dll’:

image

Adding this reference enables support for functionality that allows installing into the “Public Documents” folder.

The way we can use this is by adding a property reference with the Id ‘WIX_DIR_COMMON_DOCUMENTS’ as follows:

<Product Id="*" Name="Acme.Setup" Language="1033" Version="1.0.0.0" Manufacturer="Acme" UpgradeCode="YOURGUID-269e-4795-97d7-a392d75749fb">
    <Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />

    <MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
    <MediaTemplate />

   <PropertyRef Id="WIX_DIR_COMMON_DOCUMENTS"/>

   <Feature Id="ProductFeature" Title="Acme.Setup" Level="1">
        <ComponentGroupRef Id="ProductComponents" />
    </Feature>
</Product>

WiX will set the property ‘WIX_DIR_COMMON_DOCUMENTS’ to the correct path for the “Public Documents” folder when the installer is executed. We don’t have to worry about that.

Now this property has been referenced we can use it to specify where we would like to install our readme file. For that we first need to change the <Directory> tag content as follows:

<Fragment>
    <Directory Id="TARGETDIR" Name="SourceDir">
      <Directory Id="WIX_DIR_COMMON_DOCUMENTS">
         <Directory Id="ACMEDOCUFOLDER" Name="Acme Documentation">
         </Directory>
      </Directory>
      <Directory Id="ProgramFilesFolder">
            <Directory Id="INSTALLFOLDER" Name="Acme.Setup" />
        </Directory>
    </Directory>
</Fragment>
Note the additional directory tag with the Id ‘WIX_DIR_COMMON_DOCUMENTS’. I left the other directory tag in place. We don’t need it for the example in this blog post. For yuor actual installer you may want to use it to place binary files into a folder inside of “Program Files”.

The next change you have to make is adding a new component group that installs into the WIX_DIR_COMMON_DOCUMENTS folder. Here is the snippet that achieves this:

<Fragment>
    <ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
        <!-- TODO: Remove the comments around this Component element and the ComponentRef below in order to add resources to this installer. -->
        <!-- <Component Id="ProductComponent"> -->
            <!-- TODO: Insert files, registry keys, and other resources here. -->
      <!-- </Component> -->
   </ComponentGroup>
   <ComponentGroup Id="DocuComponents" Directory="ACMEDOCUFOLDER">
      <Component Id="ReadmeFile" Guid="{YOURGUID-994F-49C0-A7AC-53D1571A295A}">
         <File Id="readme.txt" Name="readme.txt" Source="..\Acme.Lib\readme.txt"></File>
      </Component>
   </ComponentGroup>
</Fragment>

Note that again I left the default component group untouched (Id=”ProductComponents”). Instead I created a new component group and gave it the id ‘DocuCompoents’. Then I moved the component ‘ReadmeFile’ from the former to the latter.

We are not quite finished yet. One item is missing. The installer does not yet know that it should include this new component / component group into the installer. So here is what we need to do to accomplish that:

<Product Id="*" Name="Acme.Setup" Language="1033" Version="1.0.0.0" Manufacturer="Acme" UpgradeCode="YOURGUID-269e-4795-97d7-a392d75749fb">
    <Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />

    <MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
    <MediaTemplate />

   <PropertyRef Id="WIX_DIR_COMMON_DOCUMENTS"/>

   <Feature Id="ProductFeature" Title="Acme.Setup" Level="1">
      <ComponentGroupRef Id="ProductComponents" />
      <ComponentGroupRef Id="DocuComponents" />
   </Feature>
</Product>

Once you build and execute the resulting installer then the result will look as follows:

image

And this screenshot demonstrates that we have accomplished what we wanted: We successfully installed a folder and a file into the “Public Documents” folder.

If you’d like the full source code of this solution please send an email to manfredmlange at gmail dot com.

Happy coding!

Sunday, April 07, 2013

Outstanding Support

I had an issue with JIRA/Greenhopper today and contacted Atlassian’s support at 08:12 am (it’s Sunday here). At 08:17 am I had a message from a human being saying that he was investigating. Communication was flowing and at 08:34 am the issue was resolved. Total time spent: 17 minutes from reporting the problem.

The way I have experienced working with Atlassian they will probably say this is just the normal way they operate. And I agree they have always been very easy to work with.

I’m mentioning it here since I think their support can serve as an example for a lot of other companies. I trust that you can easily create your own list of those who can improve.

Bottom line: Today I experienced excellent support from Atlassian … as usual. Thank you, Nick!

Saturday, March 16, 2013

Ubuntu 12.10 in Virtual Box on Windows 7

Today I spent some time to get Ubuntu 12.10 installed as a virtual machine using VirtualBox. That as such wasn’t the challenge. For example a pretty good description can be found at Windows Seven Forums.

Getting the guest additions installed was the problem. Quite a few sources suggested to use VirtualBox’s menu “Devices” – “Install Guest Additions…” However, what was offered as a solution didn’t work in my case. Note that I’m not a Linux or VirtualBox expert, so my lack of knowledge might have been the reason.

After quite some search I found a very good description on Sysprobs. Dinesh did a fantastic job with his blog post. Now I can resize the VM’s window any way I like and the VM’s screen adjusts properly. Almost seamless integration Nice!

Thursday, December 06, 2012

csUnit Interim Release 2.8.4723

We have made available a new release of csUnit. This is an interim release with the version number 2.8.4723 and can be found on GitHub.

This release requires .NET 4.5. It doesn’t have an installer and no Visual Studio plugin. The download is a zip file that you need to extract into a folder then run csUnitRunner from there. If you encounter an issue please log the issue here with detailed info about your environment, the version of csUnit you are using and steps to reproduce.

We intend to create a WiX based installer next.

For more info about csUnit check out csUnit’s homepage.

Saturday, September 15, 2012

Free Azure Storage Explorers

When working with Azure you’ll eventually work with storage (blob, table, …). Wouldn’t it be nice if you had a simple way to browse through the content of your Azure storage? And wouldn’t it be nice if you also use the same tool to access your local Azure storage emulator?

Here are two tools that do exactly that. And in addition they are free.

The first tool is “Azure Storage Explorer” provided by Neudesic. This tool is open source, hosted on Codeplex. It is licensed under the “Common Development and Distribution License (CDDL)”. I’ve used this tool for several months now and was very valuable, allowing moving files, copying files, creating containers, deleting containers, etc. You can set up all your storage accounts, including local development storage and then access it without having to enter credentials again. It remembers your access keys.

The second tool is “CloudBerry Explorer for Windows Azure” provided by CloudBerry. This tools is available as freeware. I just downloaded it and played with it for a little while, so can’t provide any detailed insights yet.

Having a tool for browsing your Azure storage content is a handy tool for developers as well. That’s why I thought it would be worth mentioning here.

Happy coding!

Tuesday, September 11, 2012

NullReferenceException in Html.ValidationSummary()

I was implementing a view using ASP.NET MVC, MVC 4 in this case but I suspect the version is irrelevant. I received a NullReferenceException when any validation failed, ie the model state was invalid.

The stack trace was close to useless and looked similar to the following:

at ASP._Page_Views_Issue_Create_cshtml.Execute() in c:\projects\au\AgileTraxx\Web\Views\Issue\Create.cshtml:line 26
at System.Web.WebPages.WebPageBase.ExecutePageHierarchy()
at System.Web.Mvc.WebViewPage.ExecutePageHierarchy()
at System.Web.WebPages.StartPage.RunPage()
at System.Web.WebPages.StartPage.ExecutePageHierarchy()
at System.Web.WebPages.WebPageBase.ExecutePageHierarchy(WebPageContext pageContext, TextWriter writer, WebPageRenderingBase startPage)
at System.Web.Mvc.RazorView.RenderView(ViewContext viewContext, TextWriter writer, Object instance)
at System.Web.Mvc.BuildManagerCompiledView.Render(ViewContext viewContext, TextWriter writer)
at System.Web.Mvc.ViewResultBase.ExecuteResult(ControllerContext context)
at System.Web.Mvc.ControllerActionInvoker.InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult)
at System.Web.Mvc.ControllerActionInvoker.<>c__DisplayClass1a.<InvokeActionResultWithFilters>b__17()
at System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilter(IResultFilter filter, ResultExecutingContext preContext, Func`1 continuation)

Line 26 which the stack trace referring to was:

Html.ValidationSummary();

This wasn’t much help either. It took me a lot of experimentation to find out that in this particular case the property Model of the view was null. Once I set the model for this view, it all went nicely.

Note that not always it is obvious from scanning the code that one didn’t set the model for a view. Therefore I’m blogging my experience here and to give the recommendation that should you encounter the same or similar behavior, to the following:

  1. Set a breakpoint on the line where the exception is thrown.
  2. Debug the application
  3. When the breakpoint is hit, enter “Model” in the “Immediate Window” in Visual Studio and check whether it outputs “null”.

Happy coding!

Saturday, April 21, 2012

ASP.NET: Base Directory and Developer Tests

Assume that in your ASP.NET application you need to access a file that is part of the deployed web site. On the other hand you would like to also run some developer tests.

The way I like to organize developer tests is putting them in a separate project and a separate assembly. For example for an ASP.NET project “MySite” I would have a second project called “MySite.Tests”. In the test project I then reference the “MySite” project which gives me direct access to all code inside of “MySite”. Typically give all projects a strong name and therefore can make internals of “MySite” visible to code in “MySite.Tests” by placing the InternalsVisibleToAttribute on the “MySite” assembly.

The challenge now with accessing the file is that depending on whether they are run from within an ASP.NET server (IIS or WebDev.WebServer) or within a unit test runner, finding the root folder (or base directory) can be tricky in some cases.

For example we could use a solution based on the MapPath method of the HttpContext.Current.Server object. This is described in more details in an answer on StackOverflow and works when an HttpContext is available. When the same code is run in a unit test runner HttpContext.Current is null (Controller.HttpContext in MVC), so this solution wouldn’t work.

The solution that I have chosen and that works so far is based on the application domain. The following code is all that is required and it works in both IIS, WebDev.WebServer as well as when executed within a unit test runner:

var domainRoot = AppDomain.CurrentDomain.BaseDirectory;
var configFile = Path.Combine(domainRoot, Common.DbSettingsFile);

Happy coding!