views:

28

answers:

3

I am using MSBuild Extension Pack 4.0 to do my local development deployment. When using the class MSBuild.ExtensionPack.Computer.Registry to read a registry key (to get an installation directory) it fails saying the path is invalid. I believe this is due to msbuild being a 32-bit process, so it can only see:

HKEY_LOCAL_MACHINE\Software\SysWow6432\*

and not

HKEY_LOCAL_MACHINE\Software\*

Has anyone found a way around this without reverting to developing a custom tool?

My actual script:

<MSBuild.ExtensionPack.Computer.Registry TaskAction="Get" RegistryHive="LocalMachine" Key="SOFTWARE\Microsoft\MSCRM" Value="CRM_Server_InstallDir">
  <Output PropertyName="CrmPath" TaskParameter="Data"/>
</MSBuild.ExtensionPack.Computer.Registry>
+1  A: 

Did you already try MSBuilds builtin support for reading the registry?

<PropertyGroup>
    <CrmPath>$(registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSCRM@CRM_Server_InstallDir</CrmPath>
</PropertyGroup>

Learned this myself from this blog posting.

Furthermore you can run MSBuild in both x86 and x64:

%WINDIR%\Microsoft.NET\Framework\v3.5\MSBuild.exe

and

%WINDIR%\Microsoft.NET\Framework64\v3.5\MSBuild.exe

Edit

Even if you're dealing with a multitarget environment you could solve this with builtin means.

<!-- MSBuild 3.5 x86 / AnyCPU -->
<PropertyGroup Condition=" '$(MSBuildToolsPath)' == '$(windir)\Microsoft.NET\Framework\v3.5' AND '$(Platform)' == 'AnyCPU' ">
    <CrmPath>$(registry:HKEY_LOCAL_MACHINE\SOFTWARE\SysWow64\*)</CrmPath>
</PropertyGroup>

<!-- MSBuild 3.5 x64 -->
<PropertyGroup Condition=" '$(MSBuildToolsPath)' == '$(windir)\Microsoft.NET\Framework64\v3.5' AND '$(PLatform)' == 'x64' ">
    <CrmPath>$(registry:HKEY_LOCAL_MACHINE\SOFTWARE\*)</CrmPath>
</PropertyGroup>

MSBuild is usually able to tell what environment it is dealing with so you can cater for every possible combination and use the same script on all kinds of machines.

Filburt
Thanks. This put me on the right path along with http://stackoverflow.com/questions/2197086/create-64-bit-registry-key-non-wow64-from-a-32-bit-application
Henrik
@Henrik: You're welcome ... but I think you're overdoing it a little with creating a custom task for this.
Filburt
@Filburt I totally agree that this is over-engineered if I had control over the version of msbuild that runs across the developer machines. Unfortunately I don't (yet), so this seemed the quickest and most compatible way to go.
Henrik
A: 

I solved this by looking at the new capabilities of .NET 4.0 (as suggested here: http://stackoverflow.com/questions/2197086/create-64-bit-registry-key-non-wow64-from-a-32-bit-application)

I can now specify in the lookup if I need a 32-bit or 64-bit value:

<GetWindowsRegistryValue Key="SOFTWARE\Microsoft\MSCRM" Value="CRM_Server_InstallDir" Hive="LocalMachine" View="Registry64">
  <Output PropertyName="CrmPath" TaskParameter="Setting"/>
</GetWindowsRegistryValue>

And the custom (quick and dirty) task:

namespace Utilities.CustomBuildTasks
{
    using System;
    using Microsoft.Build.Framework;
    using Microsoft.Win32;

    /// <summary>
    /// Defines the custom task to retrieve registry values.
    /// </summary>
    public class GetWindowsRegistryValue : ITask
    {
        /// <summary>
        /// Gets or sets the build engine associated with the task.
        /// </summary>
        /// <value></value>
        /// <returns>The build engine associated with the task.</returns>
        public IBuildEngine BuildEngine
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets any host object that is associated with the task.
        /// </summary>
        /// <value></value>
        /// <returns>The host object associated with the task.</returns>
        public ITaskHost HostObject
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the key.
        /// </summary>
        /// <value>The registry key.</value>
        [Required]
        public string Key
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the value.
        /// </summary>
        /// <value>The value.</value>
        [Required]
        public string Value
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the hive.
        /// </summary>
        /// <value>The registry hive.</value>
        [Required]
        public string Hive
        {
            get
            {
                return this.hive.ToString();
            }

            set
            {
                this.hive = (RegistryHive)Enum.Parse(typeof(RegistryHive), value);
            }
        }

        /// <summary>
        /// The hive enumeration value.
        /// </summary>
        private RegistryHive hive;

        /// <summary>
        /// Gets or sets the view.
        /// </summary>
        /// <value>The view (64-bit/32-bit).</value>
        [Required]
        public string View
        {
            get
            {
                return this.view.ToString();
            }

            set
            {
                this.view = (RegistryView)Enum.Parse(typeof(RegistryView), value);
            }
        }

        /// <summary>
        /// The view enumeration value.
        /// </summary>
        private RegistryView view;

        /// <summary>
        /// Gets or sets the setting.
        /// </summary>
        /// <value>The setting.</value>
        [Output]
        public string Setting
        {
            get;
            set;
        }

        /// <summary>
        /// Executes a task.
        /// </summary>
        /// <returns>
        /// true if the task executed successfully; otherwise, false.
        /// </returns>
        public bool Execute()
        {
            try
            {
                var baseKey = RegistryKey.OpenBaseKey(this.hive, this.view);

                var subKey = baseKey.OpenSubKey(this.Key);

                if (subKey == null)
                {
                    return false;
                }

                this.Setting = subKey.GetValue(this.Value).ToString();

                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }
    }
}
Henrik
+1  A: 

The built in registry support is fairly basic. I quite like the idea of being able to set the view without having to call out to 32 or 64 bit msbuild. I'm adding RegistryView as a property in the next release.

Update: It's now checked in if you want to compile it yourself.

Mike

Thanks Mike. I'll be using this in the next release when we move towards the test environment and I need to refactor the deployment scripts slightly.
Henrik