Occasionally when I’m sequencing App-V 5 applications I find that some of the files that I have included inside the virtual file system cannot be read at run time, even though they exist in the virtual file system!

One example that exhibited this behaviour was when the main shortcut pointed to an executable on a remote share –  when the shortcut was launched, it couldn’t ‘see’ the files that were captured in the virtual file system.

Another example was when the shortcut pointed to a .hta file (The resultant shortcut created a local instance of mshta.exe and as such any calls made couldn’t see ‘inside’ the virtual file system)!

An easy way to circumvent these issues is to use my ExeLauncher.  It does exactly what it says on the tin – it launches executables!  But crucially you will include ExeLauncher.exe inside your App-V 5 package!

You can run it like so:

ExeLauncher.exe "[Path To Executable]" "[Arg 1]" "[Arg 2]" "[Arg x]"...

It will accept an unlimited number of arguments – remember to enclose the path to the executable and any arguments inside quotes, just in case they contain spaces.

To continue on from my examples above, to fix my App-V 5 issues instead of capturing a shortcut to:


I placed a copy of ExeLauncher.exe inside my virtual package and then created a shortcut to:

C:\[PathToExeLauncher]\ExeLauncher.exe "\\[remoteshare]\alkane.exe"

and instead of capturing a shortcut to:


I placed a copy of ExeLauncher.exe inside my virtual package and then created a shortcut to:

C:\[PathToExeLauncher]\ExeLauncher.exe "c:\windows\system32\mshta.exe" "c:\[pathToHta]\alkane.hta"

Then give your shortcut whatever icon and name you want, and voila! Since you’re targetting an executable that resides inside the virtual file system, any process it calls will now be able to see ‘inside’ the virtual file system and your App-V applications will now work correctly!

App-V 5 Connection Group Manager


I’ve been testing a new tool developed by Howard over at HRP Consultancy called App-V 5 Connection Group Manager.  It’s very handy indeed.  There are dozens of scripts out there which do a similar job (keyword – ‘similar’), but most importantly they rely on adding each package to a machine and using Powershell CMDLets to query the package cache and generate the connection group XML file.  Considering an example where we may want to connect half a dozen apps (of varying file sizes!), this in itself may take me 20-30 minutes or more to prepare my virtual machine!

Contrast the aforementioned cumbersome approach with this tool, whereby all we have to do is simply point to the .appv file itself (stored on the file system in an unpublished state) and bang….it extracts the relevant DisplayName, PackageId and VersionId’s to add to the connection group.  What’s more is that this tool provides a GUI which enables us to order and prioritise our connected packages, specify connection group names and GUIDs whilst providing support for the more recent schema’s VersionId and isOptional attributes.

Virtualisr – FREE App-V 4.6, App-V 5 and App-V 5.1 Automated Sequencing

Virtualisr is a tool used for App-V 4.6, App-V 5 and App-V 5.1 automated sequencing/virtualisation.  It can convert scripted installs (VBS, BAT, CMD) to App-V, convert MSI to App-V and convert executables/legacy installers to App-V.  It can rapidly accelerate application migrations and save your company hundreds of man-hours and thousands of pounds.  Other virtualisation technologies can be supported upon request.

PLEASE NOTE: There seems to be a bug with the New-AppvSequencerPackage cmdlet in the Windows ADK version of the sequencer.  PSR mode may not work with this version.

PLEASE ALSO NOTE: I generally only use Virtualisr when I have a batch of pre-configured (or default) apps that I need to quickly convert, or if I’m performing a  migration for a client.  In reality this is probably every few months since most of the time I will package ad-hoc requests.  During this period Oracle tend to update VirtualBox and as part of these updates they usually alter the command line syntax!  This may stop Virtualisr from functioning correctly.  If this is the case please contact me and I will attempt to resolve any issues as soon as possible.



  • FREE!  (for a limited period – licenses will be provided in batches of 10).  License key must be obtained from us first.  Contact us here


  • App-V 4.6, App-V 5 and App-V 5.1 Automated Sequencing/VirtualisationOracle VirtualBox
  • Sequence on your own custom virtual machines
  • Keep your installation files local and secure
  • Utilise industry leading Oracle virtualisation software
  • Bulk import multiple applications to convert, and perform batch conversion whilst you make a brew!
  • Perform batch automated* conversions of .EXE (legacy installers), .MSI, .VBS, .BAT and .CMD to App-V 4.6 and App-V 5 formats
  • Supply command-line arguments for your installation target
  • Apply one or many transforms (MSTs) to MSI installations
  • Manually specify package names (auto-generated from imported MSI/MST name as default)
  • Perform MNT/VFS installations (App-V 4.6)
  • Manually specify PVAD, or automatically set PVAD to actual installation directory (App-V 5/MSI only)
  • Specify App-V templates, Full Load, Mount Drives etc
  • Record Problem Step Recorder screenshots to use as part of your discovery inventory
  • Output conversions to a logical folder structure
  • Compatible with Oracle VirtualBox and VMWare Workstation** virtual machines
  • License not used for failed conversions***
  • Unlimited remote support

* scripted installations can only be automatically converted if the scripts themselves are automated with no human interaction required.

** VMWare Virtual Machine needs to be hosted inside Oracle VirtualBox

*** sufficient error handling required in scripts

Missing any functionality?  Contact us here to request it.


Why Virtualisr:

There are products such as Autonoware ConversionBox and Flexera AdminStudio Virtualization Pack which already provide automated App-V virtualisation and do a decent job of it.  However these are costly (certainly when we’re talking about multiple application portfolios containing several hundred applications), cumbersome installations with often complex licensing agreements.  Virtualisr is a lightweight (under 500kb) executable used alongside Oracle VirtualBox and is completely free.

System Requirements:

  • 6GB Memory (minimum)
  • 2.5 GHz dual-core Intel or AMD processor
  • Powershell 2
  • Microsoft .Net 3.5
  • Oracle VirtualBox (https://www.virtualbox.org/wiki/Downloads)


The Virtualisr executable does not install anything.  The program is run directly.  It works by launching virtual machines that are hosted in Oracle VirtualBox (since VirtualBox is free).  You can also mount your VMWare virtual machines in VirtualBox in order to use Virtualisr.

Step 1 – Install Oracle VirtualBox

You can download it from this location: https://www.virtualbox.org/wiki/Downloads

Step 2 – Create your Sequencing Virtual Machine base image

NOTE: If you already have VMWare virtual machines set up you can use these by importing them into Oracle VirtualBox (Just select ‘Use an existing virtual hard drive file’ and then specify the VMDK/VDI when asked to configure the hard drive).  If you already have VirtualBox virtual machines configured you can use those too.  In both cases, just ensure you have the latest Guest Additions (see below) and you can then ignore this step and step 3.

Get hold of an ISO of the Windows operating system that you want to sequence applications on.  Open Oracle VirtualBox.  Create a new Virtual Machine, specifying memory amount and hard disk configurations. Be wary that there are some large applications out there. I’ve created mine as the default 25GB.

Once you’ve created it, start the VM and point to your Windows ISO when it prompts to select a start-up disk. Follow the usual instructions to install your copy of Windows. Ensure that you give the admin account a password and don’t just leave it blank.

Once this is complete and you’re logged in to the operating system, install the latest Guest Additions (I installed 4.3.12 at the time of writing this).  You can obtain it from this location:

VirtualBox Guest Additions (choose version and downoad the .iso, for example VBoxGuestAdditions_4.3.8.iso)

and instructions are here:


Reboot your VM. Configure the base image as required (service packs, windows updates etc).  Once done, disable Windows Updates, stop the Windows Defender and Windows Search services.  Disable Action Center messages. And disable User Account Control.

Create a snapshot.  I called mine ‘base’

Step 3 – Create your App-V 4.6 and App-V 5.0 snapshots

With our base snapshot running, install the App-V 4.6 sequencer with all the relevant service pack and hotfixes. Once done, create a snapshot called ‘App-V 4.6 <Service Pack/Hotfix etc>’.  For example, mine is called ‘App-V 4.6 SP3’.

Now revert back to the base snapshot. Install the App-V 5 sequencer, again with any service packs and hotfixes. You may also need to install the relevant prerequisites for the App-V 5 Sequencer. Once done create another snapshot.  I called mine ‘App-V 5.0 SP2 HF5’.

You should now be ready to sequence!  It’s pretty self explanatory (i think!), but just in case it’s not, here are a few instructions:

How it Works:

Before you start, create a folder and dump all of your installation source in there.  I created a folder on the root of C: called ‘ToConvert’.  I also organised each application into their own folder for clarity.

Step 1 – Launch Virtualisr.exe

Step 2 – Import Packages to Virtualise

Specify your license key.  You can check how many licenses you have available by clicking ‘Get Status’.

Click ‘Import Source Files’ and point to your source dump location.  In my case, as mentioned above, my source dump is C:\ToConvert.  Click Ok and all of the .MSI, .MST, .CMD, .BAT and .VBS files will populate in the DataGrid.  You can filter which file type you want to view.  From the screenshot above, you can see there are 11 columns.  They should all be self explanatory but you can hover over the header for more information.

  • Dark grey denotes a disabled cell
  • Select zero to many transforms.  If you need them to apply in a specific order I suggest you name them alphabetically (or prefix with a number) to order them as they will install in the order shown in the GUI
  • The Package Name column is editable.  By default it will give this the name of the MSI/First MST/Script provided as install source.
  • The PVAD columns are also editable
  • The MNT checkbox will (in the case of and MSI) attempt to install the MSI to the PVAD location.  Otherwise it will perform a VFS installation.
  • The O/R (Override) checkbox will attempt to get the INSTALLDIR of an MSI and set the App-V 5 PVAD to this location
  • Select the ‘AV4.6’ or ‘AV5’ check box to convert the installer to the relevant format
  • Configure Feature Block 1 with FullLoad
  • Specify the Virtual Machine to use, and select the appropriate snapshots for sequencing App-V 4.6 and App-V 5 packages.  You must also specify the login credentials for the admin user.

Step 3 – Click Virtualise!


Two folders will be created in your source dump folder called ‘Virtualisr-AV5’ and ‘Virtualisr-AV46’.  Your packages will be output to these locations, and named according to the package name you specified in the DataGrid.

A log file will also be created in the source dump folder called virtualisr.log.  This is appended to each time you run Virtualisr, and is opened at the end of each session.  It should contain installer exit codes (in case the installer fails), sequencer exit codes (in case the sequencer fails) and VBoxManage.exe exit codes (in case the VirtualBox element fails).

Virtualisr log

A HTML report will be generated called Virtualisr_Report.html.  This will contain information from the sequencing session such as success rate, time elapsed, configurations, and package success etc.  Remember that ‘green’ means it has been successfully virtualised, but this does not imply that it will work in the App-V technology.  You will need to use a compatibility toolset to find this out before passing the application through Virtualisr.

Virtualisr report

Under the hood:

When the conversion process starts, a shared folder is created on the guest virtual machine and is mapped to the source dump folder on the host (in our example this is C:\ToConvert).  This is the only location on the host that the VirtualBox session has access to.  Hence any installation source, App-V templates, log files, reports and App-V output is located here.  See the screenshot below for an example of the files required for input, and the files which are output:

Virtualisr Input Output


I’m aware that once you kick off the conversion process that the GUI becomes unresponsive.  However the status in the bottom left will update, the log file will update and also the datagrid cells will be highlighted red (failure)/green (success) after each application has been processed.

Multi-threading in Powershell is not trivial, and one day I may look into using the Start-Job cmdlet and timers, as referenced to here: http://www.sapien.com/blog/2012/05/16/powershell-studio-creating-responsive-forms/.  But until that day, click ‘Virtualise’ and go and make yourself a brew (or a few brews, depending upon how many apps you choose to virtualise).

Virtualisr – Purchase Licenses


Please purchase Virtualisr licenses using the form below. Please note that we will notify you of your license key and conversion count upon receipt of Paypal payment.

[purchase_link id=”968″ text=”Purchase” style=”button” color=”blue”]


GUIDMakr – Generate a random GUID and copy to clipboard

This is some VBScript code which generates a random GUID and copies it to the clipboard.  It doesn’t copy the brackets intentionally, so that pasting the GUID can be used for multiple scenario’s such as MSI (ProductCodes and PackageCode etc, by adding the brackets manually) and App-V XML attribute values (For Package GUIDs and Version GUIDs etc, where no brackets are required).

Function CreateGUID
  	Dim TypeLib : Set TypeLib = CreateObject("Scriptlet.TypeLib")
  	CreateGUID = Mid(TypeLib.Guid, 2, 36)
	Set TypeLib = Nothing
End Function

Dim WshShell : Set WshShell = WScript.CreateObject("WScript.Shell")
WshShell.Run "cmd.exe /c echo|set /p=" & CreateGUID & "|clip", 0, TRUE
Set WshShell = Nothing

MsgBox CreateGUID & " copied to clipboard!"


ValMakr – Command Line Windows Installer Automated Validation

ValMakr Prerequisites

Visual C++ Redistributables
evalcom2.dll (installed with Orca.msi)


ValMakr is a tool which enables us to validate Windows Installers and Windows Installer transforms from the command line. We can use it to automate part of the Quality Assurance process during the packaging of applications.

As you probably already know, Microsoft already provide a tool called Msival2.exe which can be found here: http://msdn.microsoft.com/en-us/library/windows/desktop/aa370504(v=vs.85).aspx

Although a nice concept, if any organisation started using this tool they would notice some limitations:

  • You cannot specify a transform (MST) file to validate
  • You can only filter out informational messages (and not errors and warnings)
  • Although you can specify ICE messages to include, you cannot specify ICE messages to omit
  • You can not do differential validation – that is, only find validation messages introduced by transforms
  • You can not validate the summary information stream of transforms

The second point above is probably just a nice-to-have, and may not ever be used to suppress showing warnings/errors.  The third point above is a useful one, since there are a few ICE messages which my current client chooses to ignore.  So rather than specifying every single ICE message apart from the ones we omit (as we would have to with Msival2.exe), we can (more efficiently) just specify the ones to omit.

We thought the lack of ability to validate a transform mentioned in the first point – especially in the modern era where most vendor’s supply their own MSI files – was a real showstopper.  ValMakr enables us to do all of this.  And a little bit more….

For example, if we chose to validate an MSI with an MST we could either do a full validation (whereby the MSI with applied MST is validated as a whole) or a differential validation (whereby we only capture the ICE messages which have been introduced by the transform).

In my current organisation we would generally do a differential validation pointing to darice.cub (since we’re generally not interested in vendor ICE messages, but only the ones which we may introduce) and then a full validation to our own custom cub file, which has been created using CubMakr. ValMakr also checks the Summary Information Stream (SIS) of transforms too, to ensure they validate against SIS rules specified in CubMakr. The following SIS entries are checked:


Here is a list of available parameters to pass (square brackets are optional, mandatory parameters are -msi, -cub and -log):

ValMakr.exe -msi <Full Path to MSI> [-mst <Full Path to MST>] [-diff] -cub <Full Path to CUB> [-ice <Colon separated list of ICE routines>] [-noice <Colon separated list of ICE routines to omit>] [-type <e/w/i>] [-log <Full Path to LOG>]


Full validation of MSI

ValMakr.exe -msi "c:\example.msi" -cub "c:\custom.cub" -log "c:\mylog.log"

Full validation of MSI, omit ICE33 and ICE03

ValMakr.exe -msi "c:\example.msi" -cub "c:\custom.cub" -noice "ICE33:ICE03" -log "c:\mylog.log"

Full validation of MSI and MST

ValMakr.exe -msi "c:\example.msi" -mst "c:\example.mst" -cub "c:\custom.cub" -log "c:\mylog.log"

Differential validation of MSI and MST

ValMakr.exe -msi "c:\example.msi" -mst "c:\example.mst" -diff -cub "c:\darice.cub" -log "c:\mylog.log"

CubMakr – Version 2 BETA Release

Last week we released CubMakr V2, with a few new features.  You can find a quick video here.

One of the new features was simply being able to specify custom ICE references as opposed to all the ICE messages appearing as ALKANEICE01.  This was a request from Rory over at Rorymon.com.This can now be specified when you’re creating a new rule, at the very top:

ice_nameIf you’ve already created your rules you can edit each rule and change the name accordingly.  This is useful so that you can refer to specific ICE messages like you can with normal MSI validation (ICE33, ICE03 etc).  It also means you can set up a web page with references to your custom ICE messages and instructions on how they can be resolved.  Unfortunately, the HelpURL part of the ICE message no longer appears to be supported by most products so we can’t include this in the CubMakr toolset (sad face).

The second new feature in Version 2 is Advanced Scripting.  This is really interesting, as it enables advanced users to include their own custom VBScripts into the cub files.  These scripts can be used to perform custom ICE routines (which cannot be handled by CubMakr natively, such as detecting empty components) and even integrate with your own application tracking tools for real-time validation!!  The scripts can be sequenced to run before the CubMakr rules, or after the CubMakr rules.  An example of advanced scripting can be found here.


CubMakr – Advanced Scripting Example

I thought I’d give a quick example of how advanced users can utilise advanced scripting in CubMakr.

Example 1: Integrating with your Application Tracker

The first example I want to show is how we can integrate the CubMakr with your in-house tracking tool.  Some places use Sharepoint to track applications, some places use a SQL Server back-end (ok…so does Sharepoint….but I believe the preferred approach for querying Sharepoint is to use CAML and you can find a VBScript example on my blog here).

I’ll let you read the commented code to see what the script does, but basically:

  • In order to link your package to your tracker record, you’ll need a reference.  In this example I create a property in the MSI called ALKANEREF and set the value to the unique reference for my package.
  • In the script, I read the value of ALKANEREF and then query the SQL Server database to retrieve various records (vendor, application and version).
  • I then create a session property (i.e, not a property which will persist in my database) called ALKANEMANUFACTURER which we can then use as part of a CubMakr rule to validate (for example) the Manufacturer property value.

Because we want to set the ALKANEMANUFACTURER property before the CubMakr rules are run, we would obviously set this script to run before the CubMakr rules.  Here’s the script:

Dim objConnection : Set objConnection = CreateObject("ADODB.Connection") 
Dim objRecordSet : Set objRecordSet = CreateObject("ADODB.Recordset") 

'Connection string for our fictitious application tracker
objConnection.Open _ 
    "Provider=SQLOLEDB;Data Source=.\SQLExpress;" & _ 
        "Trusted_Connection=Yes;Initial Catalog=alkanetracker;" & _ 
             "User ID=alkanetest;Password=24rHj7Zz4OFNhYV;" 

Dim packagevendor : packagevendor = ""
Dim packageapplication : packageapplication = ""
Dim packageversion : packageversion = ""
Const adVarChar = 200
Const adParamInput = 1
Dim sql : sql = ""
Dim oView, oRecord

'First get the package reference.  We use Session.ProductProperty here because the ALKANEREF property is part of our MSI, and 
'not a property which has been created in the current in-memory session.
alkaneRef = Session.ProductProperty("ALKANEREF")

'select all columns for our package with the specified package reference
set cmd = CreateObject("ADODB.Command")
with cmd
    .ActiveConnection = objConnection
    .CommandText = "SELECT * FROM packages WHERE packagereference = ?"
    .Parameters.Append .CreateParameter("packagereference",adVarChar,adParamInput,50,alkaneRef)
end with
set objRecordSet = cmd.execute

Do While NOT objRecordSet.Eof
	packagevendor = objRecordSet("packagevendor") 
	packageapplication = objRecordSet("packageapplication") 
	packageversion = objRecordSet("packageversion") 

	'set ALKANEMANUFACTURER for our CubMakr rule.  Remember that Session.Property is read/write, Session.ProductProperty is read only!
	Session.Property("ALKANEMANUFACTURER") = packagevendor


set cmd = nothing 
Set objConnection = Nothing
Set objRecordSet = Nothing

Example 2: Writing your own ICE routines

The second example I wanted to show you was writing your own custom ICE routines.  CubMakr is used to provide a simplified way of checking for certain entries within the windows installer tables and summary information stream.  It cannot handle more complex scenarios, such as checking for empty component, checking for duplicate registry entries etc.  For these scenarios there is now the facility to add your own custom ICE routines.  Here is an example of some custom ICE routines:

'initialise variables and constants

Const msiMessageTypeError =  &H01000000
Const msiMessageTypeWarning = &H02000000
Const msiMessageTypeUser = &H03000000

Dim messageIceName, messageDescription, messageDescriptionAddition, messageType, messageTable, messageColumn
'Message Types
'0	MessageFail
'1	MessageError
'2	MessageWarning
'3	MessageInfo

'call Custom ICEs in this order
Call checkEmptyComponents()
Call checkNoKeypath()
Call noDuplicateRegistry()

'This function checks for empty components
Function checkEmptyComponents()

	'Here we specify:
	'The name of the ICE message
	messageIceName = "EXAMPLE01"
	'The type of ICE message (see top of script)
	messageType = 2
	'The description we want to show in the ICE message
	messageDescription = "'[1]' is an empty component.  Please delete."	
	'The table that the ICE message relates to
	messageTable = "Component"
	'The column where the issue resides
	messageColumn = "Component"

	Dim componentsView, componentsRec, tableView, tableRec, dataView, dataRec

	Dim emptyComponent : emptyComponent = True
	Dim tempComponent : tempComponent = ""

	If Session.Database.TablePersistent("Component") = 1 Then

		Set componentsView = Session.Database.OpenView("Select `Component` From `Component` ORDER BY `Component`")
		Set componentsRec = componentsView.Fetch
		Do While Not componentsRec is Nothing

			tempComponent = componentsRec.StringData(1)

			'list the tables that have 'Component_' (foreign key) columns
			Set tableView = Session.Database.OpenView("SELECT `Table` FROM `_Columns` WHERE `Name`= 'Component_' AND `Table` <> 'FeatureComponents'") 
			Set tableRec = tableView.Fetch
			Do While Not tableRec is Nothing

				emptyComponent = True
				Set dataView = Session.Database.OpenView("SELECT  `Component_` FROM `" & tableRec.StringData(1) & "`  WHERE `Component_`='" & tempComponent & "'")
				If Not dataView.Fetch is Nothing Then 'this table has a some data belonging to some component
					'component contains data
					emptyComponent = False
					'skip component and move to next
					Exit Do
				End If

				Set tableRec = tableView.Fetch

			If emptyComponent Then				
				componentsRec.StringData(0) = messageIceName & Chr(9) & messageType & Chr(9) & messageDescription & Chr(9) & "" & Chr(9) & messageTable & chr(9) & messageColumn & chr(9) & "[1]"
				Session.Message msiMessageTypeUser,componentsRec
			End If

			Set componentsRec = componentsView.Fetch

		Set tableRec = Nothing
		Set tableView = Nothing
		Set componentsView = Nothing
		Set componentsRec = Nothing
		Set dataView = Nothing

	End If	

	'return success
	checkEmptyComponents = 1
  End Function

  'This function checks for components without a keypath, and if a keypath is available, suggests it.
  Function checkNoKeypath()

	Dim keypathView, keypathRec, blnKeypathSet, tempView, tempRec

    messageIceName = "EXAMPLE02"
	messageType = 2
	messageDescription = "Component '[1]' does not have a keypath set."
	messageTable = "Component"
	messageColumn = "Component"

	If Session.Database.TablePersistent("Component") = 1 Then

		'find all components which do not have Keypaths
		Set keypathView = Session.Database.OpenView("SELECT `Component`,`ComponentId`, `Attributes` FROM `Component` WHERE `KeyPath` IS Null")
		Set keypathRec = keypathView.Fetch
		Do Until keypathRec Is Nothing

			'initiate this to false
			blnKeypathSet = False	
			messageDescriptionAddition = " No suitable keypath entries were found."

			If Session.Database.TablePersistent("File") = 1 Then
				'Check file table			
				Set Tempview = Session.Database.OpenView("SELECT `File`,`Component_` FROM `File` WHERE `Component_`='" & keypathRec.StringData(1) & "'")
				Set tempRec  = Tempview.Fetch
				If Not tempRec Is Nothing Then

					blnKeypathSet = True
					messageDescriptionAddition = " A suitable keypath may be '" & tempRec.StringData(1) & "' in the File table."					

				End If
				Set Tempview = Nothing
				Set tempRec = Nothing
			End If

			If Not blnKeypathSet Then
				If Session.Database.TablePersistent("Registry") = 1 Then 
					Set Tempview = Session.Database.OpenView("SELECT `Registry`, `Component_` FROM `Registry` WHERE `Component_`='" & keypathRec.StringData(1) & "'")
					Set tempRec = Tempview.fetch
					If Not tempRec is Nothing Then	

						blnKeypathSet = True
						messageDescriptionAddition = " A suitable keypath may be '" & tempRec.StringData(1) & "' in the Registry table."

					end If
					Set Tempview = Nothing
					Set tempRec = Nothing
				End If
			End If

			If Not blnKeypathSet Then
				If Session.Database.TablePersistent("ODBCDataSource") = 1 Then
				'check ODBCDataSource table 
					Set Tempview = Session.Database.OpenView("SELECT `DataSource`, `Component_` FROM `ODBCDataSource` WHERE `Component_`='" & keypathRec.StringData(1) & "'")
					Set tempRec = Tempview.fetch
					If Not tempRec is Nothing Then

						blnKeypathSet = True						
						messageDescriptionAddition = " A suitable keypath may be '" & tempRec.StringData(1) & "' in the ODBCDataSource table."

					end If
					Set Tempview = Nothing
					Set tempRec = Nothing
				End If
			End If

			keypathRec.StringData(0) = messageIceName & Chr(9) & messageType & Chr(9) & messageDescription & messageDescriptionAddition & Chr(9) & "" & Chr(9) & messageTable & chr(9) & messageColumn & chr(9) & "[1]"
			Session.Message msiMessageTypeUser,keypathRec

			Set keypathRec = keypathView.Fetch
	End If

	Set keypathRec = Nothing
	Set keypathView = Nothing

	Set TempRec = Nothing
	Set Tempview = Nothing

	checkNoKeypath = 1

  End Function

  'This function checks that we do not have any duplicate registry.  Duplicate registry items usually occur when users start to import
  'registry and they're not being careful!
  Function noDuplicateRegistry()

    messageIceName = "EXAMPLE03"
	messageType = 2
	messageTable = "Registry"
	messageColumn = "Registry"

	Dim registryView, registryRecord, duplicateView, duplicateRecord, tempRecord

	If Session.Database.TablePersistent("Registry") = 1 And Session.Database.TablePersistent("Component") = 1  Then

		Set registryView = Session.Database.OpenView("SELECT `Registry`,`Key`,`Name`,`Value`,`Component_` FROM `Registry`")
		Set registryRecord = registryView.Fetch
		Do Until registryRecord Is Nothing

			Set duplicateView = Session.Database.OpenView("SELECT `Registry` FROM `Registry` WHERE `Key`=? AND `Name`=? AND `Value`=? AND `Registry` <> ?")

			Set tempRecord = Session.Installer.CreateRecord(4)    
			tempRecord.StringData(1) = registryRecord.StringData(2)    
			tempRecord.StringData(2) = registryRecord.StringData(3)
			tempRecord.StringData(3) = registryRecord.StringData(4)  
			tempRecord.StringData(4) = registryRecord.StringData(1) 		
			Set tempRecord = Nothing

			Set duplicateRecord = duplicateView.Fetch
			While not duplicateRecord is Nothing

				if not isMSMData(registryRecord.StringData(1)) and not isMSMData(duplicateRecord.StringData(1)) Then

					messageDescription = "Registry entry '[1]' is a duplicate of registry entry '" & duplicateRecord.StringData(1) & "'.  Please investigate."
					registryRecord.StringData(0) = messageIceName & Chr(9) & messageType & Chr(9) & messageDescription & Chr(9) & "" & Chr(9) & messageTable & chr(9) & messageColumn & chr(9) & "[1]"
					Session.Message msiMessageTypeUser,registryRecord

				end If
				Set duplicateRecord = duplicateView.Fetch

			Set registryRecord = registryView.Fetch

	End If

	Set registryView = Nothing
	Set registryRecord = Nothing
	Set duplicateView = Nothing
	Set duplicateRecord = Nothing

	noDuplicateRegistry = 1

End  Function

'returns true if tempData contains MSM decoration
Function isMSMData(tempData)
	isMSMData = False
	Dim Match
	Dim regEx : Set regEx = New RegExp
	regEx.MultiLine = vbTrue
	regEx.global = vbTrue
	regEx.Pattern = "[A-Za-z0-9]{8}_[A-Za-z0-9]{4}_[A-Za-z0-9]{4}_[A-Za-z0-9]{4}_[A-Za-z0-9]{12}"
	For Each Match in regEx.Execute(tempData)
		isMSMData = True
	Set regEx = Nothing
End Function