19 Dezember 2009

AssemblyAttributes

This is a cute little helper class that is useful for almost every .NET or Silverlight application that displays some information about itself (think “About Box”). It retrieves the values of the following assembly attributes in an easy and consistent manner:
  • Title
  • Product
  • Copyright
  • Company
  • Description
  • Trademark
  • Configuration
  • Version
  • FileVersion
  • InformationalVersion

Doing so is a piece of cake for every experienced developer but getting these information for the 100th time manually through reflection is quite cumbersome. And junior developers sometimes don’t know where to start and get lost in learning about reflection and attributes.
With the help of generics and lambdas you can code an elegant class that solves this problem one and for all. Usage is like this:

AssemblyAttributes assembly = new AssemblyAttributes();

string title = assembly.Title;
string version = assembly.Version;

And this is the full source:

/// <summary>
/// Easy access to common Assembly attributes.
/// </summary>
public class AssemblyAttributes
{
    readonly Assembly _assembly;

    public AssemblyAttributes() : this(Assembly.GetCallingAssembly())
    {}

    public AssemblyAttributes(Assembly assembly)
    {
        _assembly = assembly;
    }

    public string Title
    {
        get { return GetValue<AssemblyTitleAttribute>(a => a.Title); }
    }

    public string Product
    {
        get { return GetValue<AssemblyProductAttribute>(a => a.Product); }
    }

    public string Copyright
    {
        get { return GetValue<AssemblyCopyrightAttribute>(a => a.Copyright); }
    }

    public string Company
    {
        get { return GetValue<AssemblyCompanyAttribute>(a => a.Company); }
    }

    public string Description
    {
        get { return GetValue<AssemblyDescriptionAttribute>(a => a.Description); }
    }    
    
    public string Trademark
    {
        get { return GetValue<AssemblyTrademarkAttribute>(a => a.Trademark); }
    }   
    
    public string Configuration
    {
        get { return GetValue<AssemblyConfigurationAttribute>(a => a.Configuration); }
    }

    public string Version
    {
        get
        {
#if !SILVERLIGHT
            return _assembly.GetName().Version.ToString();
#else
            return _assembly.FullName.Split(',')[1].Split('=')[1]; // workaround for silverlight
#endif
        }
    }

    public string FileVersion
    {
        get { return GetValue<AssemblyFileVersionAttribute>(a => a.Version); }
    }

    public string InformationalVersion
    {
        get { return GetValue<AssemblyInformationalVersionAttribute>(a => a.InformationalVersion); }
    }

    /// <summary>
    /// Returns the value of attribute T or String.Empty if no value is available.
    /// </summary>
    string GetValue<T>(Func<T, string> getValue) where T : Attribute
    {
        T a = (T)Attribute.GetCustomAttribute(_assembly, typeof(T));
        return a == null ? "" : getValue(a);
    }

}

The real workhorse of this class is the GetValue<T> method. It gets an arbitrary custom attribute from an assembly. If it exists, it returns the result of applying the getValue delegate on it. If the attributes does not exist, it returns the empty string.


The full article with downloads is posted at CodeProject.

14 Dezember 2009

Improve performance through using FileAccess.Write

At work I’m implementing a little app that copies video files from cameras to network folders. The size of a video file is usually several hundred megabytes. The target file is written with the standard .NET FileStream class. The strange thing I noticed was that calling Close on the stream took between 15 and 20 seconds. Even if I flushed the stream Close call took ages. I created the stream like this

var output = new FileStream(path, FileMode.Create);

After playing with the available constructor overloads I found a better solution:

var output = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read);

Using this overload, the Close call took only 1 millisecond! That means a performance boost of factor 20000 just by using the right FileAccess Options.

Tip: Always use the “Read” or “Write” if you don’t need “ReadWrite”. This will give you a performance boost on network files.

12 Dezember 2009

Open Xaml file in Xml view without loading the Designer

I use Expression Blend almost always as a design tool for my WPF projects. If I want to do quick modifications inside the xaml code from Visual Studio, I find the designer loading time quite annoying. But VS offers you the possibility to open xaml files in the xml/xaml view by default. Just open Tools –> Options –> Text Editor –> XAML –> Miscellaneous and tick “Always open documents in full XAML view” check. 
full_xaml_view_vs2008

08 Dezember 2009

WiX Lessons Part 2

Today I did some localization of my WiX based installer and learned, that Windows Installer must have a very old code base. While localization with WiX is really easy it inherits some limitations that you don’t expect nowadays. First you cannot use UTF8 or Unicode to define your string tables. No, you need to specify windows codepages that should be used for a concrete language. Unicode is not supported. So you have to dig in old documentation for appropriate codepages (Luckily I can use Windows-1252 for West European Languages). Second you cannot simply build one msi for all languages. You must build a msi package for every language. Then you can either use a bootstrapper to decide which msi to start, or you can use some black magic on the command line which uses transformations at runtime to morph one msi into the appropriate language. Multi Language packages are not built into Windows Installer.

I had another strange behavior today regarding the usage of WiX library. I made a simple library with some very minimalistic installer gui which needs three images. projectThe three images are located inside an Images folder. See attached picture for the project structure. The files are referenced like this:

<Binary Id="Banner" SourceFile="Images\banner.bmp" />

First build succeeded than it always failed, saying that it could not find “Images\banner.bmp”. It seems some tool in the chain needs an absolute path because I fixed the build by adding the $(sys.CURRENTDIR) preprocessor directives:

SourceFile="$(sys.CURRENTDIR)Images\banner.bmp"

And you need to check “Bind files into the library file” on the Build page of the project properties. Else the library contains only the filenames and every project using the library must have access to the absolute path of the images. By default this is unchecked which imho corrupts the meaning of a redistributable library.