Reading registry key fails with MSBuild Extension Pack 4.0
-
01-10-2019 - |
Question
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>
Solution
If you are using MSBuild 4.0 you can as well iuse the built-in property function GetRegistryValueFromView (documented at https://msdn.microsoft.com/en-us/library/dd633440.aspx#BKMK_GetRegistryValueFromView). This functions allows to specify the 64-bits or 32-bits view (or both)
Your call would look like :
<PropertyGroup>
<CrmPath>$([MSBuild]::GetRegistryValueFromView('HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSCRM', 'CRM_Server_InstallDir', null, RegistryView.Registry64, RegistryView.Registry32))</CrmPath>
</PropertyGroup>
OTHER TIPS
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.
I solved this by looking at the new capabilities of .NET 4.0 (as suggested here: 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;
}
}
}
}
With MSBuild Extension Pack 4.0 you also can create, modify and delete registry keys and values. But if you only plan to read you may take a look at @Thierry answer first.
For x64 registry node view
<MSBuild.ExtensionPack.Computer.Registry TaskAction="Get" RegistryView="Registry64" RegistryHive="LocalMachine" Key="SOFTWARE\Microsoft\MSCRM" Value="CRM_Server_InstallDir">
<Output PropertyName="CrmPath" TaskParameter="Data"/>
</MSBuild.ExtensionPack.Computer.Registry>
For x86 registry node view
<MSBuild.ExtensionPack.Computer.Registry TaskAction="Get" RegistryView="Registry32" RegistryHive="LocalMachine" Key="SOFTWARE\Microsoft\MSCRM" Value="CRM_Server_InstallDir">
<Output PropertyName="CrmPath" TaskParameter="Data"/>
</MSBuild.ExtensionPack.Computer.Registry>
For more information see RegistryView on MSDN.