How to customize USMT in Horizon Mirage

When doing a Windows 7 migration using Horizon Mirage the User State Migration Tool from Microsoft is used to migrate user settings and data as well as operating system settings.

USMT by itself already supports a lot of settings and additionally Mirage adds support for even more settings which are automatically migrated. Have a look at my previous article to get to know about the settings which are migrated out of the box.

Still in most cases USMT inside Mirage needs to be modified as additional application, user or operating system settings and data need to be transferred during the migration. For example you may want to migrate Google Chrome, Mozilla Firefox or SAP GUI settings.

Extending USMT is just a matter of creating additional XML files which contain the information on what additional settings should be migrated. This can be done manually by following the USMT user guide custom XML examples or using a nice tool called USMT XML Builder GUI.

After the XML file with the needed settings is created (in my example it is called ThinApp.xml) it needs to be copied into the USMT x86 and x64 folder. To get these folders you have to download USMT and update it. I covered how this can be done in my prevoius article on how to import USMT into Mirage.

USMTXMLFile

When the USMT folder is prepared an the custom XML files are added we need to customize the USMT script inside Mirage. During the USMT stage Mirage launches a script called “Launch_USMT.cmd” which is located in the Mirage Client folder under ” C:Program FilesWanovaMirage Service”. This file needs to be copied to the root of the USMT folder.

USMTRoot

After this is done the script needs to be adjusted to include the newly created custom XML file. For this just open the “Launch_USMT.cmd” in a text editor of your choice and modify the variable USMT_MIG_XML to include the custom XML file.

LaunchUSMTCmd

For each new XML file the following parameter needs to be appended to the variable:

/i:%USMT_LOCATION%CustomXMLFile.xml

Of course you have to change the name of the XML to the appropriate one. After you changed the variable it should be look similar to following example:

Before:

set USMT_MIG_XML=/i:%USMT_LOCATION%MigApp.xml /i:%USMT_LOCATION%MigDocs.xml /i:%WallpaperFile_XML_PATH% /i:%KeyboardLayout_XML_PATH% /i:%MigRegionalSettings_XML_PATH% /i:%DefaultPrinter_XML_PATH% /i:%Win7CustomSettings_XML_PATH%

After:

set USMT_MIG_XML=/i:%USMT_LOCATION%MigApp.xml /i:%USMT_LOCATION%MigDocs.xml /i:%WallpaperFile_XML_PATH% /i:%KeyboardLayout_XML_PATH% /i:%MigRegionalSettings_XML_PATH% /i:%DefaultPrinter_XML_PATH% /i:%Win7CustomSettings_XML_PATH% /i:%USMT_LOCATION%ThinApp.xml

After the variable is changed and the updated script is saved the USMT folder needs to be imported to Mirage again.

MirageUSMTCommit

From now on the customized USMT is used for any migration task.

ThinApp Scripting with VBScript

Most of the times when I talk to our customers they want some functionality in ThinApp which isn’t offered out of the box.  For example:

  • Allow to run a virtualized application only until a specific date.
  • Update a configuration file every time the applications is launched.
  • Allow an application only to be executed when user is inside the corporate office.
  • Messure the usage of an application.
  • Enforce licensing restriction.
  • And so on…

While such functionalities aren’t offered by ThinApp out of the box there is something else much more powerful available. With ThinApp you have a fully fledged scripting engine available in each and every ThinApp package. This allows you to implement advanced functionalities like the examples mentioned above.

ThinApp supports running VB-based scripts out of the box. Just place your .vbs files side by side to the package.ini, run build.bat and you have successfully integrated your script in your ThinApp package. While it is really that simple there are some very helpful things you should know about.

Functions

The ThinApp script engine natively supports VBScript. Besides the whole spectrum of VBScript functionalities the ThinApp script engine itself provides some additional functions. For example:

  • ExpandPath
    This function allows us to convert a macro folder to a system folder.
  • ExecuteExternalProcess
    With this function it is possible to launch a process outside the virtual environment.
  • GetBuildOption
    GetBuildOption lets us look inside the package.ini file.
  • GetFileVersionValue
    With this function we are able to check if a file has for example a specific version.
  • GetCommandLine
    This option show the command line argument a entry point was called with.
  • GetCurrentProcessName
    Show the name of the current process which triggered the VBScript launch.
  • SetRegistryIsolation
    With the help of this function you can change the registry isolation mode for a specific key during the runtime.

These are just a few examples of functions the ThinApp script engine supports. All of them are documented in the ThinApp User’s Guide. Using a bit of VBScript and some of the specific ThinApp functions you could for example implement your own little help function to your ThinApp packages which works as follows:

Launch your ThinApp entry point with the parameter -thinstallhelp.
ThinAppHelpCommand

And as a result you see a message box with some helpful information about your ThinApp package.
ThinAppHelpMessage

Here is the code realizing this little helper.

Function OnFirstParentStart</p>
' Check command line input for help parameter (-thinstallhelp)
If InStr(GetCommandLine(), "-thinstallhelp") Then

' Get current entry point / process
strCurrentProcess = GetCurrentProcessName

' Get build options from package.ini
strInventoryName = GetBuildOption("InventoryName")
strSandboxName = GetBuildOption("SandboxName")
strCaptureRuntime = GetBuildOption("CapturedUsingVersion")
strOptAppLinks = GetBuildOption("OptionalAppLinks")
strReqAppLinks = GetBuildOption("RequiredAppLinks")

' Set message box title
strTitle = "ThinApp Help"

' Compose output
strMessage = "Current entry point / process: " & strCurrentProcess & Vbcrlf & Vbcrlf & "InventoryName: " & strInventoryName & Vbcrlf & "SandboxName: " & strSandboxName & Vbcrlf & "Capture Runtime: " & strCaptureRuntime & Vbcrlf & "Optional AppLinks: " & strOptAppLinks & Vbcrlf & "Required AppLinks: " & strReqAppLinks

' Display message box with Ok button and title
Msgbox strMessage, 0, strTitle

' Exit process as we just want to show the help message
ExitProcess 0

End If

End Function

Callback functions

As soon as you add a VBScript to your ThinApp package the script gets executed every time a process gets launched within your ThinApp package. This equally applies to manually launched entry points and automatically launched child processes. As you can imagine this isn’t a very good behavior performance wise. For this reason the ThinApp scripting engine includes so-called callback functions. Callback functions help to control when and how often your VBScript code is executed.

For a better understanding which function is called when try the following example. Just save the code below as example.vbs file in your ThinApp project directory, rebuild your application and start using it. The example script displays a pop up message with the name of the function currently executed and the name of the process triggering the function.

Function OnFirstSandboxOwner

strMessage = "OnFirstSandboxOwner: " & GetCurrentProcessName
MsgBox(strMessage)

End Function

Function OnFirstParentStart

strMessage = "OnFirstParentStart: " & GetCurrentProcessName
MsgBox(strMessage)

End Function

Function OnFirstParentExit

strMessage = "OnFirstParentExit: " & GetCurrentProcessName
MsgBox(strMessage)

End Function

Function OnLastProcessExit

strMessage = "OnLastProcessExit: " & GetCurrentProcessName
MsgBox(strMessage)

End Function

strMessage = "OnEveryProcess: " & GetCurrentProcessName
MsgBox(strMessage)

.vbs files

As you may have discovered in your own testing you can add more than one script (.vbs) file to your project directory. If you add multiple scripts every script gets executed in alphanumerical ascending or descending order. The callback function OnFirstSandboxOwner is using an ascending order while all other callback functions are using a descending order. Have a look at an example using two scripts called A.vbs, B.vbs and C.vbs and the callback functions OnFirstSandboxOwner and OnFirstParentExit:

Execution order OnFirstSandboxOwner: Execution order OnFirstParentExit:
  • A.vbs
  • C.vbs
  • B.vbs
  • B.vbs
  • C.vbs
  • A.vbs
  • As you see it is a bit chaotic and I strongly advice only to work with one VBScript file.

    AppLinks

    Also if you are using AppLinks and you linking two or more ThinApp packages each containing VBScripts you have to be aware of the following circumstances. Let’s suppose you have two ThinApp packages each containing a bunch of scripts. ThinApp package 1 contais A.vbs, B.vbs, D.vbs and Z.vbs while ThinApp package 2 contains A.vbs and C.vbs. ThinApp package 1 has ThinApp package 2 configured as an AppLink. What do you think happens now? Here is an example when the scripts are executed in the callback function OnFirstParentStart:

    1. A.vbs (from ThinApp package 2)
    2. B.vbs (from ThinApp package 1)
    3. C.vbs (from ThinApp package 2)
    4. D.vbs (from ThinApp package 1)
    5. Z.vbs (from ThinApp package 1)

    As you can see the order is still the same as discussed in the .vbs file section with one exception. As both packages contained the A.vbs file only the A.vbs file from the second ThinApp package is executed. This behavior is anticipated as files from a child AppLink package (in this case ThinApp package 2) are always overwriting files from its parent AppLink package (in this case ThinApp package 1).

    Conclusion

    You now know that ThinApp has an integrated script engine which natively supports VBScript, callback  and some addition function. You also know how the ThinApp script engine works. With these insights you should now be able to realize some if not all of the missing functions you always wanted ThinApp to have – assumed you have a little bit of know how in the VBScript corner.

    How to check what ThinApp version a package was built with

    To check what ThinApp runtime version was used to build a single package you can use a simple command line argument.

    MyThinAppPackage.exe -thinstall

    ThinstallVersionCmd

    While this is good enough to check one or two packages it isn’t very practical if you need to check a whole bunch of packages. Also the -thinstallversion command line parameter isn’t very script friendly either. But no worries the ThinApp SDK and PowerShell come to the rescue. The following script allows us to list the ThinApp runtime version of a specific file or all files in a folder (and subfolders):

    Param
    (
    [Parameter(Mandatory=$true)]
    [ValidateScript({ Test-Path $_ })]
    [string]
    $Path
    )
    
    $ThinApp = New-Object -ComObject ThinApp.Management
    $files = Get-ChildItem $Path -Recurse | ForEach-Object {$_.Fullname}
    
    ForEach ($file in $files)
    {
    Try
    {
    $ThinAppType = $ThinApp.GetThinAppType($file)
    }
    Catch
    {
    $ThinAppType = $null
    }
    
    If (($ThinAppType -lt 6) -and ($ThinAppType -gt 0))
    {
    $ThinAppPackage = $ThinApp.OpenPackage($file)
    New-Object -TypeName PSObject -Property @{
    Name = $ThinAppPackage.InventoryName
    Version = $ThinAppPackage.ThinAppVersion
    Path = $file
    }
    }
    }

    Row 1-7 is used to quiz the user for a path or a file. Then in row 9 the ThinApp com object is created and in row 10 a list of fully qualified file names (even of files in subfolders) based on the user provided path or file is created. In row 14 to 21 the script checks if it can get the ThinApp type of the current file and if not it catches the error. Within lines 23 – 30 if the current file is a valid (1-5) ThinApp package it reads out the InventoryName, file name and path and the ThinApp runtime version and creates a new PowerShell object for it.

    In action it looks like this:
    ThinstallVersionPSPath

    In order to use this script you have to register the ThinApp SDK (to learn how to do it visit my “Getting started with ThinApp SDK and PowerShell” article) and then just execute the script in a PowerShell console and provide a folder or file. Here are some examples:

    To check the version of a single file:

    .\Get-ThinAppVersion.ps1 -Path 'Z:\ThinAppApps\Mozilla Firefox.exe'

    To check the version of all files in the provided folder and subfolders:

    .\Get-ThinAppVersion.ps1 -Path Z:\ThinAppApps

    You can even check files and folders on a network share:

    .\Get-ThinAppVersion.ps1 -Path \\server\thinapp

    To execute this script your PowerShell execution policy need to be set to “Unrestricted” (or lower) as this script isn’t signed. More information about the PowerShell execution policy can be found via the following link: http://technet.microsoft.com/en-us/library/ee176961.aspx

    As the output is a fully functional PowerShell object you can do some cool stuff. For example you can create a CSV file listing all your ThinApp packages and the corresponding version or filter for a specific runtime version.

    To export the results to a CSV use this command:

    .\Get-ThinAppVersion.ps1 -Path Z:\ThinAppApps | Export-Csv -Path C:\temp\MyThinAppRuntimeVersions.txt

    To only list ThinApp packages which are using a specific runtime version use this command:

    .\Get-ThinAppVersion.ps1 -Path Z:\ThinAppApps | Where-Object {$_.Version -eq "4.7.0-556613"}

    Download the script using the button below: https://gist.github.com/timarenz/5318cb9fdf3d15a9ef41cdb01ffc2550