Update 2014-04-16:  I forgot to include that fact that a locationModify rule is required when using the %HklmWowSoftware% variable.  The post has been updated to reflect this.

 

One challenge with capturing the settings for a 32-bit applications with USMT is that some file and Registry paths will be different on 32-bit operating systems and 64-bit operating systems.  On a 32-bit operating system 32-bit programs typically get installed to C:\Program Files and local machine Registry entries are written to a subkey of HKLM\Software.  However, on a 64-bit operating system , 32-bit programs get installed to C:\Program Files (x86) and HKLM\Software Registry entries are redirected to HKLM\SOFTWARE\Wow6432Node.  (I have oversimplified this here for brevity's sake.  See this section of the Programming Guide for 64-bit Windows for more details: http://msdn.microsoft.com/en-us/library/aa384249(v=vs.85).aspx.)

Because of this, you might think you would need to different components to migrate 32-bit application settings depending on the source and destination operating system architecture.  For example, I had a customer that was using three different XML files with components for 32-bit applications.  I’ll illustrate this using a fictitious 32-bit application called MyApp.  This application is installed by default to C:\Program Files\MyApp and creates machine-based settings in HKLM\Software\MyApp on a 32-bit OS.  For the sake of simplicity for this example, lets say that the desired way to migrate this app is to capture all the files in the C:\Program Files\MyApp\Config folder and to capture all of HKLM\Software\MyApp (or the equivalent locations on a 64-bit OS).

The customer had three different migration scenarios:

  • Windows XP 32-bit to Windows XP 32-bit (break/fix rebuilds, etc.)
  • Windows XP 32-bit to Windows 7 64-bit (OS migration)
  • Windows 7 64-bit to Windows 7 64-bit (break/fix rebuilds, etc.)

For Windows XP 32-bit to Windows XP 32-bit migrations they had a component like this for MyApp.

<component type="Application" context="System">
    <displayName>Migrate MyApp - XP to XP</displayName>
    <role role="Settings">
        <rules>
            <include>
                <objectSet>
                    <pattern type="File">%CSIDL_PROGRAM_FILES%\MyApp\Config\* [*]</pattern>
                    <pattern type="Registry">HKLM\Software\MyApp\* [*]</pattern>
                </objectSet>
            </include>
        </rules>
    </role>
</component>

For Windows XP 32-bit to Windows 7 64-bit migrations they had a component like this for MyApp.  This has locationModify rules to move the migrated items to the redirected locations for 32-bit apps on 64-bit Windows.

<component type="Application" context="System">
    <displayName>Migrate MyApp - XP to Win7</displayName>
    <role role="Settings">
        <rules>
            <include>
                <objectSet>
                    <pattern type="File">%CSIDL_PROGRAM_FILES%\MyApp\Config\* [*]</pattern>
                    <pattern type="Registry">HKLM\Software\MyApp\* [*]</pattern>
                </objectSet>
            </include>
            <locationModify script="MigXmlHelper.RelativeMove('%CSIDL_PROGRAM_FILES%\MyApp\Config','%CSIDL_PROGRAM_FILESX86%\MyApp\Config')">
                <objectSet>
                    <pattern type="File">%CSIDL_PROGRAM_FILES%\MyApp\Config\* [*]</pattern>
                </objectSet>
            </locationModify>
            <locationModify script="MigXmlHelper.RelativeMove('HKLM\Software\MyApp','HKLM\SOFTWARE\Wow6432Node\MyApp')">
                <objectSet>
                    <pattern type="Registry">HKLM\Software\MyApp\* [*]</pattern>
                </objectSet>
            </locationModify>
        </rules>
    </role>
</component>

For Windows 7 64-bit to Windows 7 64-bit migrations they had a component like this for MyApp which directly captured/restored the redirected locations for the 32-bit app on 64-bit Windows.

<component type="Application" context="System">
    <displayName>Migrate MyApp - Win7 to Win7</displayName>
    <role role="Settings">
        <rules>
            <include>
                <objectSet>
                    <pattern type="File">%CSIDL_PROGRAM_FILESX86%\MyApp\Config\* [*]</pattern>
                    <pattern type="Registry">HKLM\SOFTWARE\Wow6432Node\MyApp\* [*]</pattern>
                </objectSet>
            </include>
        </rules>
    </role>
</component>

Of course, this makes it complicated to call USMT with the correct XML file depending on what source and destination operating systems were involved.  Fortunately, there is a way to avoid this.  It involves using a technique I lifted directly from MigApp.xml.  MigApp.xml contains a namedElements node that defines a bunch of global items.  Some of these items define single variables representing the file and local machine Registry locations for 32-bit applications independent of the operating system architecture.  You can copy this into your own custom XML files and use those variables in the same way.  Copy the XML content below into your custom XML files (just after the migration node at the top)

<library prefix="MigSysHelper">MigSys.dll</library>

<_locDefinition>
    <_locDefault _loc="locNone"/>
    <_locTag _loc="locData">displayName</_locTag>
</_locDefinition>

<namedElements>
    <!-- Global -->
    <environment name="GlobalEnvX64">
        <conditions>
            <condition>MigXmlHelper.IsNative64Bit()</condition>
        </conditions>
        <variable name="HklmWowSoftware">
            <text>HKLM\SOFTWARE\Wow6432Node</text>
        </variable>
        <variable name="ProgramFiles32bit">
            <text>%ProgramFiles(x86)%</text>
        </variable>
        <variable name="CommonProgramFiles32bit">
            <text>%CommonProgramFiles(x86)%</text>
        </variable>
    </environment>
    <environment name="GlobalEnv">
        <conditions>
            <condition negation="Yes">MigXmlHelper.IsNative64Bit()</condition>
        </conditions>
        <variable name="HklmWowSoftware">
            <text>HKLM\Software</text>
        </variable>
        <variable name="ProgramFiles32bit">
            <text>%ProgramFiles%</text>
        </variable>
        <variable name="CommonProgramFiles32bit">
            <text>%CommonProgramFiles%</text>
        </variable>
    </environment>

    <!-- Global USER -->
    <environment context="USER" name="GlobalEnvX64User">
        <conditions>
            <condition>MigXmlHelper.IsNative64Bit()</condition>
        </conditions>
        <variable name="VirtualStore_ProgramFiles32bit">
            <text>%CSIDL_VIRTUALSTORE_PROGRAMFILES(X86)%</text>
        </variable>
        <variable name="VirtualStore_CommonProgramFiles32bit">
            <text>%CSIDL_VIRTUALSTORE_COMMONPROGRAMFILES(X86)%</text>
        </variable>
    </environment>
    <environment context="USER" name="GlobalEnvUser">
        <conditions>
            <condition negation="Yes">MigXmlHelper.IsNative64Bit()</condition>
        </conditions>
        <variable name="VirtualStore_ProgramFiles32bit">
            <text>%CSIDL_VIRTUALSTORE_PROGRAMFILES%</text>
        </variable>
        <variable name="VirtualStore_CommonProgramFiles32bit">
            <text>%CSIDL_VIRTUALSTORE_COMMONPROGRAMFILES%</text>
        </variable>
    </environment>
</namedElements>

Once you add this, you can now define one component to migrate MyApp that will work in all three scenarios:

<component type="Application" context="System">
    <displayName>Migrate MyApp</displayName>
    <environment name="GlobalEnv"/>
    <environment name="GlobalEnvX64"/>
    <environment name="GlobalEnvUser"/>
    <environment name="GlobalEnvX64User"/>
    <role role="Settings">
        <rules>
            <include>
                <objectSet>
                    <pattern type="File">%ProgramFiles32bit%\MyApp\Config\* [*]</pattern>
                    <pattern type="Registry">%HklmWowSoftware%\MyApp\* [*]</pattern>
                </objectSet>
            </include>
            <locationModify script="MigXmlHelper.RelativeMove('%HklmWowSoftware%','%HklmWowSoftware%')">
                <objectSet>
                    <pattern type="Registry">%HklmWowSoftware%\MyApp\* [*]</pattern>
                </objectSet>
            </locationModify>
        </rules>
    </role>
</component>

The variable %ProgramFiles32bit% will resolve correctly to C:\Program Files on a 32-bit OS and C:\Program Files (x86) on a 64-bit OS.  The variable %HklmWowSoftware% will resolve correctly to HKLM\Software on a 32-bit OS and HKLM\SOFTWARE\Wow6432Node on a 64-bit OS.

Note that you need to add four environment nodes shown in the above example into your components that will use these variables.  This is essentially adding a “reference” to the namedElements items that define the variables.

You also need a locationModify rule like the one show above when using the %HklmWowSoftware% variable.  It may seem odd to need a location modify that has the same source and destination location.  However, this is needed because USMT does not automatically redirect Registry locations.  It will try to write them back to the original location.  Fortunately, the locationModify RelativeMove function will expand the environment variables of the first parameter in the context of the source machine and the second parameter in the context of the destination machine.  This will cause the Registry entries to be redirected properly.

You may be wondering if you need to worry about redirection of HKCU Registry entries for 32-bit applications.  You don’t.  For most of HKCU, 32-bit apps write to HKCU without redirection (See http://msdn.microsoft.com/en-us/library/aa384232(v=vs.85).aspx for details).

 

Disclaimer: The information on this site is provided "AS IS" with no warranties, confers no rights, and is not supported by the authors or Microsoft Corporation. Use of included script samples are subject to the terms specified in the Terms of Use.

This post was contributed by Michael Murgolo, a Senior Consultant with Microsoft Services - U.S. East Region