Tuesday, September 18, 2012

IMAPI version 2 managed wrapper.

Introduction

Microsoft introduced native image mastering support in Vista, going forward. The library itself is not too complex but the latest version of the image mastering API (IMAPI) has some quirks. This is well documented by Eric Haddan in his article describing how to build an interop file and use it from .NET.

Implementation

I wanted to take it a step further and provide a library wrapper that was CLI compliant. The end result is a class that provides 3 methods, called in order, to burn an image. The project can be found on my GitHub account.

Properties

  • Media: A read only property that populates after the LoadMedia call and reports what type of physical media is connected (eg. CD-R, DVD-R, DVD+RW, etc.).
  • MediaStates: A read only collection of states in which the media is under, that populates after the LoadMedia call (eg. is blank, overwriteable, unsupported).
  • MediaCapacity: A read only property that populates after the LoadMedia call and reports the size capacity of the media.
  • Nodes: A root list of file and directory nodes that are to be written to the media.
  • Recorders: A read only selectable collection. A recorder must be selected for the LoadRecorder call.
  • VolumeLabel: A property that sets the volume label of the media to be written to.

Methods

  • LoadRecorder: Verifies a recorder is selected, loads the recording drive resources and verifies that it is working.
  • LoadMedia: Loads the media resource and verifies that it is valid for the recorder. An exception will occur if the media isn't supported (eg. closed session disc, no disc present).
  • FormatMedia: Formats the media of any previous data.
  • WriteImage: The final step to write your files to disc. This is a blocking call.

Events

  • FormatEraseUpdate: Reports progress of FormatMedia call.
  • FormatWriteUpdate: Reports progress of files being written to the media.
  • ImageUpdate: Reports progress of files being added to the media image.

Caveats

I use this library for the simple single purpose of writing finalized images to a blank CD. It has not been fully tested to accomodate all media and recorder state scenarios; I haven't tried it yet with DVD's or multi-session discs. If you would like to extend the library, feel free to push some changes to the GitHub project and I will wrap them in.

I decided to leave the method calls as blocking (synchronous) since there could be many different synchronization models that may use this library. For my purposes, I used this in an WPF application that would call the WriteImage method from a Task and then post back the progress events of the write using the Dispatcher from my WPF window. Below is a code snippet demonstrating this.

private ImageMaster _burner;

...

private void _CreateCd()
{
 // files added to _burner and _burner.WriteImage called
}

private void _burner_FormatWriteUpdate(IMAPI2.ImageMaster sender, IMAPI2.FormatWriteUpdateEventArgs e)
{
 this.Dispatcher.Invoke(() => this.statusProgressBar.Value == (e.ElapsedTime / e.TotalTime) * 100);
}

private void CreateCdWindow_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
 System.Threading.Tasks.Task.Factory.StartNew(_CreateCd);
}

Thursday, September 13, 2012

Implementing a bit reader/writer in C.

Introduction

I was working on implementing a simple compression tool based on naive Huffman coding and initially assumed all of my prefix codes would be at or under a byte long. Although I failed to account for the worst case of Huffman coding, which can yield codes up to 255 bits in length.

This required a bit reading and writing function that would allow for variable length codes. I could have used other methods to balance worst case Huffman trees, but this issue presented a unique challenge. How to write a bit reader/writer in C.

Bit Reader/Writer Specifications

  • Read and write using reader and writer ADT's.
  • Handle any possible bit length given.
  • Remember bits read from file but are outside the requested bit length (eg. 2 bytes read in but only 12 bits requested, leaving 4 bits in the buffer).
  • Flush a partially written block to disk on a free of the bit writer ADT.
  • Return number of bits read and written on the read and write functions.

Design

The underlying structure to the ADT will contain fields for the: file pointer, the byte long buffer and the buffer bit length. The examples given for every case are in steps; what is originally in the buffer, what bits are requested, what bits are read from or written to the file, and finally what bits are left over and buffered.

Reader

There are two main cases to consider when designing the read function.
  1. Empty Buffer: The is simple since the file read bits will already be byte aligned to your output bits. You just need to simply mask out the unneeded bits and buffer them for the next read (eg. no bits buffered, 12 bits requested, 16 bits read in, 4 bits buffered).
  2. Populated Buffer: The can get a little trickier, especially in the last two cases of this.
    1. Requested bits already buffered. This is very easy since it doesn't require any file read and the buffer just needs to shift out the read bits (eg. 7 bits in the buffer and 3 are requested; buffer serves out 3 and truncates to 4).
    2. Not all requested bits buffered; end of read bits not in same byte block as end of output bits. The last output byte block must mask out the unneeded bits, put them in the buffer and then append the additional unneeded read bits from the next byte block (eg. 4 bits buffered, 13 bits requested, 16 bits read in, 8 bits buffered).
    3. Not all requested bits buffered; end of read bits in same byte block as end of output bits. The last output byte block must mask out the unneeded bits and put them in the buffer (eg. 4 bits buffered, 10 bits requested, 8 bits read in, 2 bits buffered).

Writer

There are only two cases to consider when designing the write function.
  1. Full Buffer: Shift in write bits, commit to disk and repeat until all bits are processed. If the last bit to write is not on the buffer byte boundary, it will wait until the next write (eg. 6 bits buffered, 12 bits to write, 16 bits written out, 2 bits buffered).
  2. Partial Buffer: Shift in write bits (eg. 2 bits buffered, 4 bits to write, no bits written out, 6 bits buffered).

Caveats

The code below is purely a demonstration of how a bit reader/writer works in a generic sense. There is room for plenty of optimization if you know what type of architecture your targeting and the domain of your reader/write arguments.

The Code

Header File

Source File

Tuesday, August 14, 2012

Licensing using XML digital signing.

Motivation

One of the main concerns in any licensing implementation is that authored license files are coming from the trusted authority (ie. your company) and not some third party. This can be accomplished in software by encrypting and decrypting the license with a single key, but symmetric key storage can be cumbersome and have added complexity.

Assymetric cryptography, on the other hand, allows for two keys: a private one to author files with and a public one to verify its authenticity. This is well suited for licensing, where we want to ensure that the authored license has not been altered in anyway and the content can be relied on.

Implementation

I ran across a great article that details the use of XML digital signatures in the .NET framework. It describes the type of digital signatures that can be implemented and lays out the classes and methods needed to author and verify license files.

This specific implementation allows for an: authentication key to compare against some unique value tying the license to the system, expiration date to set a time span on the software use and set of available features the user is authorized for.

Base Class

This forms the runtime object representation of the license and will be inherited by the license reader and writer.

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using System.Security;
using System.Security.Cryptography;
using System.Security.Cryptography.Xml;
using System.Xml;

namespace Licensing
{

public abstract class LicenseBase
{ 
 private byte[] _authenticationKey; 
 private DateTime _expiration; 
 private List _features;
    private int _id;

 public byte[] AuthenticationKey {
  get { return _authenticationKey; }
  protected set { _authenticationKey = value; }
 }
 public DateTime Expiration {
  get { return _expiration; }
  protected set { _expiration = value; }
 }
 public int Id {
  get { return _id; }
        protected set { _id = value; }
 }
 protected List Features {
        get { return _features; }
 } 

 public LicenseBase()
 {
  _features = new List();
 }

    public void Clear()
    {
        _id = 0;
        _authenticationKey = null;
        _features.Clear();
    }
}

}

Writer Class

This class will be used internally to author the license files that will be distributed with the released software. Note that the private key is added as an embedded resource in the project and should not be distributed since the public key can be derived from it.

using System;
using System.IO;
using System.Security;
using System.Security.Cryptography;
using System.Security.Cryptography.Xml;
using System.Text;
using System.Xml;

namespace Licensing
{
    class License : LicenseBase
    {

        License(int id, byte[] authenticationKey, DateTime expiration, string[] features)
        {
            this.AuthenticationKey = authenticationKey;            
            this.Expiration = expiration;
            this.Id = id;
            this.Features.AddRange(features);
        }

        /// 
        /// Generates a key pair for digital signing and verification.
        /// 
        /// 
        /// 
        static internal void GenerateAssymetricKeys()
        {
            string datestamp = null;
            StreamWriter output = null;
            RSA key = RSA.Create();
            key.KeySize = 1024;

            datestamp = DateTime.UtcNow.ToString("yyyyMMdd");

            // Generate private key to only be used internally (DO NOT DISTRIBUTE).
            output = File.CreateText("private-" + datestamp + ".key");
            output.Write(key.ToXmlString(true));
            output.Close();

            // Generate public key to be used by customers (distribute).
            output = File.CreateText("public-" + datestamp + ".key");
            output.Write(key.ToXmlString(false));
            output.Close();
        }

        /// 
        /// Digitally sign an XML document.
        /// 
        /// The XML document to sign.
        /// The private key to sign it with.    
        /// 
        private static void _SignXmlDocument(System.Xml.XmlDocument document, RSA privateKey)
        {
            SignedXml signedDocument = new SignedXml(document);
            signedDocument.SigningKey = privateKey;
            signedDocument.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigCanonicalizationUrl;

            // Add reference to XML data
            Reference @ref = new Reference("");
            @ref.AddTransform(new XmlDsigEnvelopedSignatureTransform(false));
            signedDocument.AddReference(@ref);

            // Build the signature.
            signedDocument.ComputeSignature();

            // Attach the signature to the XML document.
            XmlElement signature = signedDocument.GetXml();
            document.DocumentElement.AppendChild(signature);
        }

        /// 
        /// Write the contents and digitally sign the document.
        /// 
        /// The file path to the digitally signed document.
        public void WriteDocument(string filepath)
        {
            XmlDocument document = new XmlDocument();
            document.AppendChild(document.CreateXmlDeclaration("1.0", "UTF-8", null));

            XmlElement root = document.CreateElement("License");
            document.AppendChild(root);

            XmlElement id = document.CreateElement("Id");
            id.InnerText = this.Id.ToString();
            root.AppendChild(id);

            XmlElement authenticationKey = document.CreateElement("AuthenticationKey");            
            authenticationKey.InnerText = Convert.ToBase64String(this.AuthenticationKey);
            root.AppendChild(authenticationKey);

            XmlElement expiration = document.CreateElement("Expiration");
            expiration.InnerText = this.Expiration.ToString();
            root.AppendChild(expiration);

            XmlElement options = document.CreateElement("Features");
            XmlElement featureItem = null;
            foreach (string feature in this.Features)
            {
                featureItem = document.CreateElement("Feature");
                featureItem.InnerText = feature;
                options.AppendChild(featureItem);
            }
            root.AppendChild(options);

            XmlElement publickey = document.CreateElement("Certificate");
            publickey.SetAttribute("DateCode", KeyCreationDateCode);
            publickey.InnerXml = File.ReadAllText(PublicKeyFilename);
            root.AppendChild(publickey);

            RSA privateKey = RSA.Create();
            privateKey.FromXmlString(Licensing.Properties.Resources.privatekey);
            _SignXmlDocument(document, privateKey);

            File.WriteAllText(filepath, document.InnerXml);            
        }

    }
}

Reader Class

This class will be used in the field to verify the license files that will be distributed with the released software. It is best to wrap the license read and authenticate methods in try-catch blocks since they represent exceptional behavior that fall outside the post-conditions of the method. Handling the exceptions will allow for better error reporting in the field so you can decide what information you will share with the end user (eg. bad authentication key, malformed/tampered licenses). Note again that the public key is an embedded resource for the project. It should never be distributed with the license since an attacker could generate their own keypair and envelope their own public key, with signature, in the license.

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using System.Security;
using System.Security.Cryptography;
using System.Security.Cryptography.Xml;
using System.Xml;

namespace Licensing
{
    class LicenseReader : LicenseBase
    {
        LicenseReader()
        {
        }

        public void Authenticate()
        {
            // Check the hardware key value against the AuthenticationKey        
            if (!_AuthenticationKeyMatches(hardwareKey))
                throw new LicenseAuthenticationException("The license failed to authenticate the hardware key.");

            // ... or check some node locking ID (eg. MAC ID, hard drive serial number) against the AuthenticationKey.
            if (!_AuthenticationKeyMatches(nodeLockedId))
                throw new LicenseAuthenticationException("The license failed to authenticate the node locked ID.");

            if (this.Expiration > DateTime.UtcNow)
                throw new LicenseAuthenticationException("The license has expired.");
        }

        /// 
        /// Compare byte array against authentication key byte array.
        /// 
        /// Byte array to compare against.
        /// True if a match, else false.
        private bool _AuthenticationKeyMatches(byte[] compare)
        {
            if (this.AuthenticationKey == null)
                return false;

            int upperBound = Math.Min(this.AuthenticationKey.Length, compare.Length);
            for (int i = 0; i < upperBound; i++)
            {
                if (this.AuthenticationKey[i] != compare[i])
                    return false;
            }
            return true;
        }

        public bool IsFeature(string featureName)
        {
            return this.Features.Contains(featureName);
        }

        /// 
        /// Read the digitally signed document and load its contents.
        /// 
        /// The file path to the digitally signed document.
        protected virtual void ReadDocument(string filepath)
        {
            this.Clear();

            XmlDocument document = new XmlDocument();
            document.Load(filepath);

            RSA publicKey = RSA.Create();
            publicKey.FromXmlString(Licensing.Properties.Resources.publickey);

            if (_VerifyXmlDocument(document, publicKey))
            {
                this.Id = int.Parse(document.SelectSingleNode("//License/Id").InnerText);
                this.AuthenticationKey = Convert.FromBase64String(document.SelectSingleNode("//License/AuthenticationKey").InnerText);
                this.Expiration = DateTime.Parse(document.SelectSingleNode("//License/Expiration").InnerText);

                XmlNodeList features = document.SelectNodes("//License/Features/Feature");
                foreach (XmlNode feature in features)
                {
                    this.Features.Add(feature.InnerText);
                }
            }

        }

        /// 
        /// Verify the digital signature of an XML document.
        /// 
        /// The XML document containing the signature.
        /// The public key to verify signature authenticity.
        /// True if the signature is authentic, else false.
        /// 
        private bool _VerifyXmlDocument(XmlDocument document, RSA publicKey)
        {
            SignedXml signedDocument = new SignedXml(document);
            try
            {
                XmlNode signature = document.GetElementsByTagName("Signature", SignedXml.XmlDsigNamespaceUrl)[0];
                signedDocument.LoadXml((XmlElement)(signature));
            }
            catch
            {
                return false;
            }
            return signedDocument.CheckSignature(publicKey);
        }

    }
}

Additions & Caveats

Symmetric encryption could also be used to hinder casual viewing of the license file. The issue of symmetric key storage must be revisited but will no longer be critical to maintaining the integrity of the license files.

Strong naming of the license reading assembly is encouraged to prevent dissasembly and re-embedding of an attacker's own public key. Although, this is by no means an attack-proof method since strong naming can be removed by changing information in the CLI header of the portable executable (PE). Even if the application was able to secure the public key outside the application in a PKI, there is still the issue of code injection or recompilation once strong naming is stripped. Indirection of the public key or obfuscation of the assembly can also help but is not a reliable security measure.

Monday, July 30, 2012

Automate IIS enable on Windows 7.

Background

There are plenty of sites that will tell you how to enable IIS through the Control Panel. Although, if you are tasked with enabling IIS automatically (eg. for an installer), it can be a nightmare. This is only really needed though in special cases where the installer has more control over the hardware specifications. Determining an upgrade or uninstall scenario with IIS can be very tough since you are not installing your own software but rather enabling a feature of Windows. With that in mind, we can propose a solution for automated enabling of IIS.

Requirements

Windows Editions

The edition of Windows 7 for IIS must be either Ultimate or Professional if you plan on using ASP.NET. You can try this on other editions, or even Vista, but your results may differ since this is only scenario I have tested against. More information can be found at the IIS TechNet site.

DISM Tool

To enable IIS features through the command line, DISM can be used. This can be reliably found on Windows 7 but is inconsistent on Vista, where the older Package Manager is used instead. This was one of the reasons I decided to constrain the specification to Windows 7.

The first step in using DISM, is to find out which features need to be enabled, in order for IIS to work completely. I ran across this MSDN blog entry that lists all the needed features. Next task is to script the DISM commands.

Scripting

Caveats

Iterating over each feature and enabling it with DISM can be very time consuming. The faster approach is to evaluate the enabled state of each feature and turn them all on in one command. PowerShell can be used but the presence of it and its needed modules can be inconsistent on Windows 7. I had to fall back on batch scripting, which is the part that can cause nightmares due to its ugly syntax.

Arrays are need to store the enabled states of each feature and the feature names themselves. In my case, I needed an array of pairs. I was able to fake it using a suggestion on Stack Overflow. Also, special syntax is needed to reference variables inside a loop that are scoped outside the loop.

Last, but not least, remember to register the ASP.NET framework version being used with IIS. This will need to be done once IIS is enabled (reboot is required). Adjust the framework bitness and version below according to the one you are using.

[WindowsFolder]\Microsoft.NET\Framework64\v4.0.30319\aspnet_regiis.exe -i

The Code

The following batch script will discover which features are disabled, concatenate them into the enable command and then execute. You may need to replace SysNative with system32 or the Windows system folder being used by your target system if this is being ran outside the installer.

setlocal enableextensions enabledelayedexpansion
set features=IIS-ApplicationDevelopment IIS-ASPNET IIS-CommonHttpFeatures IIS-HostableWebCore IIS-HttpCompressionDynamic IIS-HttpCompressionStatic IIS-HttpErrors IIS-HttpLogging IIS-HttpRedirect IIS-HttpTracing IIS-Metabase IIS-NetFxExtensibility IIS-Performance IIS-StaticContent IIS-URLAuthorization IIS-WebServerRole WAS-ConfigurationAPI WAS-NetFxEnvironment WAS-ProcessModel WAS-WindowsActivationService
set dism_params=

set n=0
for %%f in (%features%) do (
 set /a n+=1
 set dism_params=!dism_params! /FeatureName:%%f
 set features[!n!]=%%f
)
set n=0
for /f "tokens=3*" %%i in ('%WINDIR%\SysNative\Dism.exe /Online /Get-FeatureInfo %dism_params% ^| findstr /b /r "^State : "') do (
 set /a n+=1
 set states[!n!]=%%i
)

set dism_params=
for /l %%i in (1,1,%n%) do (
 if "!states[%%i]!" == "Disabled" set dism_params=!dism_params! /FeatureName:!features[%%i]!
)

if "%dism_params%" neq "" %WINDIR%\SysNative\Dism.exe /Online /Enable-Feature %dism_params%

Wednesday, July 25, 2012

Converting thermocouple voltages to temperature.

Background

Unlike most sensors used in data acquisition, thermocouples do not have a linear mapping between their voltage and celcius values. This means that instead of factoring some scalar value against the data, in order to scale it to engineering units (eg. volts to acceleration), an experimentally derived equation must be created to approximate the temperature at specific voltages. This non-linearity is primarily due to the materials being used to discover the temperature difference.

The science and engineering behind thermocouples goes beyond the scope of this post but more information can be found below:

K-Type Thermocouples

One of the most general purpose thermocouple types used in data acquisition are K types since their are relatively inexpensive to make. This will also be the thermocouple that I will focus on.

Luckily, NIST provides a table of discrete values, correlating voltages to celcius and vice-versa. In addition, they also provide the exact polynomial equation to use underneath the given tables, depending on the voltage range the value falls in. Following is the exact functions to do the conversion for K-type thermocouples.

Voltage To Celcius

Given some ordered set of coefficients, D, and a voltages value, v, let the voltage to celcius conversion be defined as a function of v as follows.

D is dependent on the voltage value being evaluated:

  • values greater than 206.44 mV will have D = {-131.8058, 48.30222, -1.646031, 0.05464731, -0.0009650715, 0.000008802193, -0.0000000311081, 0.0, 0.0, 0.0 };
  • values geater than 0.0 V will have D = { 0.0, 25.08355, 0.07860106, -0.2503131, 0.0831527, -0.01228034, 0.0009804036, -0.0000441303, 0.000001057734,-0.00000001052755};
  • and values less than 0.0 V will have D = { 0.0, 25.173462, -1.1662878, -1.0833638, -0.8977354, -0.37342377, -0.086632643, -0.010450598, -0.00051920577, 0.0 }.

Celcius To Voltage

Given some ordered set of coefficients, C, an ordered set of exponentials, A, and a celcius value, x, let the celcius to voltage conversion be defined as a function of x as follows.

C is dependent on the celius value being evaluated:

  • positive and zero values will have C = { -0.017600413686, 0.038921204975, 0.000018558770032, -0.000000099457592874, 0.00000000031840945719, -0.00000000000056072844889, 0.00000000000000056075059059, -3.2020720003E-19, 9.7151147152E-23, -1.2104721275E-26};
  • negative values will have C = { 0.0, 0.039450128025, 0.000023622373598, -0.00000032858906784, -0.0000000049904828777, -0.000000000067509059173, -0.00000000000057410327428, -0.0000000000000031088872894, -1.0451609365E-17, -1.9889266878E-20, -1.6322697486E-23};
A is a fixed ordered set of exponentials; A = {0.1185976, -0.0001183432, 126.9686}.

Performance

Since the polynomial equation's number of terms are bounded by a constant (ie. |D| or |C|) the performance hit will be a constant factor to the number of voltage or celcius values to calculate. Also, temperature is sampled at a very low rate, yielding small data sets that usually have redundant values in them. Further optimization can be made by caching previous computations and reusing their results if the same voltage or celcius value is encountered again.

The Code

private static double[] _thermocoupleCoefficientsTypeKneg = {
 0.0,
 0.039450128025,
 2.3622373598E-05,
 -3.2858906784E-07,
 -4.9904828777E-09,
 -6.7509059173E-11,
 -5.7410327428E-13,
 -3.1088872894E-15,
 -1.0451609365E-17,
 -1.9889266878E-20,
 -1.6322697486E-23
};
private static double[] _thermocoupleCoefficientsTypeKpos = {
 -0.017600413686,
 0.038921204975,
 1.8558770032E-05,
 -9.9457592874E-08,
 3.1840945719E-10,
 -5.6072844889E-13,
 5.6075059059E-16,
 -3.2020720003E-19,
 9.7151147152E-23,
 -1.2104721275E-26
};
private static double[] _thermocoupleExponentialsTypeK = {
 0.1185976,
 -0.0001183432,
 126.9686
};
/// 
/// Type K thermocouple inverse coefficient values for voltage to celcius conversions.
/// 
/// Valid values for -200C - 0C / -5.891mV - 0mV.
private static double[] _thermocoupleInverseCoefficientsTypeK0 = {
 0.0,
 25.173462,
 -1.1662878,
 -1.0833638,
 -0.8977354,
 -0.37342377,
 -0.086632643,
 -0.010450598,
 -0.00051920577,
 0.0
};
/// 
/// Type K thermocouple inverse coefficient values for voltage to celcius conversions.
/// 
/// Valid values for 0C - 500C / 0mV - 20.644mV.
private static double[] _thermocoupleInverseCoefficientsTypeK1 = {
 0.0,
 25.08355,
 0.07860106,
 -0.2503131,
 0.0831527,
 -0.01228034,
 0.0009804036,
 -4.41303E-05,
 1.057734E-06,
 -1.052755E-08
};
/// 
/// Type K thermocouple inverse coefficient values for voltage to celcius conversions.
/// 
/// Valid values for 500C - 1372C / 20.644mV - 54.886mV.
private static double[] _thermocoupleInverseCoefficientsTypeK2 = {
 -131.8058,
 48.30222,
 -1.646031,
 0.05464731,
 -0.0009650715,
 8.802193E-06,
 -3.11081E-08,
 0.0,
 0.0,
 0.0
};


public static double CelciusToVoltageTypeK(double value)
{
 double[] a = _thermocoupleExponentialsTypeK;
 double[] c = null;
 double result = 0.0;
 if ((value >= 0.0)) {
  c = _thermocoupleCoefficientsTypeKpos;
 } else {
  c = _thermocoupleCoefficientsTypeKneg;
 }
 for (int index = 0; index <= c.Length - 1; index++) {
  result += (c[index] * Math.Pow(value, index)) + (a[0] * Math.Exp(a[1] * Math.Pow(value - a[2], 2)));
 }
 return result;
}

public static double VoltageToCelciusTypeK(double value)
{
 double[] d = null;
 double result = 0.0;
 if ((value > 0.020644)) {
  d = _thermocoupleInverseCoefficientsTypeK2;
 } else if ((value >= 0.0)) {
  d = _thermocoupleInverseCoefficientsTypeK1;
 } else {
  d = _thermocoupleInverseCoefficientsTypeK0;
 }
 for (int index = 0; index <= d.Length - 1; index++) {
  result += d[index] * Math.Pow(value, index);
 }
 return result;
}

Monday, April 23, 2012

Bit specific builds in Visual Studio 2010.

Using Conditioned Tags

The Visual Studio 2010 UI does not have options to handle conditional references based on what kind of configuration and platform you are running. For example, you may have a project reference that needs to change depending on whether you are doing a 32 or 64 bit build.

Fortunately, the project file syntax does support conditional references. I was able to find this out on stack overflow.

In our case, we have our DLL's in source control under the project's bin directory. For some reason I kept getting the 64-bit version of the DLL and then found out it was because the Content tags also needed to be conditioned.

Example

Lets say you have a project (MyProject) with two DLL's, 32bit.dll and 64bit.dll, that are referenced by the project and kept under the bin directory for the project:

  • MyProject\bin\x86\32bit.dll
  • MyProject\bin\x64\64bit.dll

You will need to hand edit the MyProject\MyProject.vbproj file and add these entries to it (or change it if they are already in there). The first two entries are for the project references and the last two are for their inclusion as a content item in the project.

   <Reference Include="32bit" Condition="'$(Platform)' == 'x86'">
      <HintPath>bin\x86\32bit.dll</HintPath>
   </Reference>  
   <Reference Include="64bit" Condition="'$(Platform)' == 'x64'">
      <HintPath>bin\x64\64bit.dll</HintPath>
   </Reference>
   ...
   <ItemGroup>    
      <Content Include="bin\x86\32bit.dll" Condition="'$(Platform)' == 'x86'"/>
      <Content Include="bin\x64\64bit.dll" Condition="'$(Platform)' == 'x64'"/>
   </ItemGroup>

Now, whenever the platform is changed to x86, it will only use the x86 conditioned tags and likewise when changed to x64. This can also be applied to other conditions too, check the links below for a complete breakdown of the MSBuild schema and conditions.

Wednesday, December 21, 2011

WPF and data multithreading deadlocks.

WPF & Multithreading

In designing applications, it may be advantageous to run data processing methods on a separate thread from the UI. This allows greater responsiveness in the application and takes advantage of concurrent computation.

The cornerstone interface to any binding in WPF is INotifyPropertyChanged, which is implemented on a data structure's different properties. Although this can be the source of deadlocks if the data structure is being processed on a separate thread where locks are explicitly implemented by the developer.

A deadlock could occur if some UI event was being triggered on a data structure (eg. expanding a view, changing a text box entry) while the data thread was processing it with an acquired lock. Internally, the PropertyChanged event will call a Dispatcher.Invoke on the UI. Although the UI could be waiting for the lock to release on that same property that the data thread already has, creating a cyclic dependency or deadlock.

The call stack for the UI thread. Note the wait call.

The call stack for the data thread.

Deadlock Scenario

As an example, suppose you have your standard WPF application UI thread and a separate data thread. The data thread polls a time stamp from a remote database and updates a class property called DataObject.Timestamp, which raises the PropertyChanged event. The property is also bound to a text box in our WPF application window, which is made visible in the window based on the user's preference (ie. collapsing and expanding a view).

In our deadlock example, the data thread puts a lock on the DataObject.Timestamp property while it updates it and other data structures. While this is happening, the view is expanded, causing the text box to start binding to the DataObject.Timestamp property and wait for an acquire of the lock that the data thread is holding. The data thread then sets the DataObject.Timestamp property inside the critical region code and the PropertyChanged event is raised. This causes the new time stamp value to be marshaled back onto the Dispatcher with a blocking/synchronous Dispatcher.Invoke call. Although the data thread now deadlocks in the critical region, waiting for the UI Dispatcher.Invoke to return, which is waiting on the data thread to release the lock.

Solution

To prevent the data thread's deadlock critical region from blocking on the Dispatcher.Invoke, override the OnPropertyChanged method to marshal the value asynchronously to the dispatcher.

protected override void OnPropertyChanged(string propertyName)
{
 Application.Current.Dispatcher.BeginInvoke(new Action(() => { base.OnPropertyChanged(propertyName); }));
}

This will ensure that the critical region doesn't block and the property changes are still serviced by the dispatcher.