Creating PackDesigner Scripts

<< Click to Display Table of Contents >>

RayPack > 7.3 u6 > User Guide > Advanced Topics 

Creating PackDesigner Scripts

PackDesigner provides an easy way to implement scripting-based logic to all MSI / RPP / MST projects. The scripts (macros) are shown in the aggregated Plugins view. Scripts are simple VBS or PowerShell files that should be present in the following location.

 

<PackPoint>\Scripts

 

Here <PackPoint> is the location where the PackPoint resources are installed. Default installation of RayPack already contains two sample scripts for GUID manipulations (both equal from a functional point of view; one written in VBS, the other one in PowerShell). Scripts are shown in the tree, and file names are used as node names.

 

Accessing the Temporary Database

Regardless of the technology chosen, the script is started with a single argument - a full path to a temporary database that the script should update. Below is an example of a script that simply starts an Orca tool and opens the current package in Orca.

 

const DontWaitUntilFinished = false, ShowWindow = 1, DontShowWindow = 0, WaitUntilFinished = true

 

'Standard VBS object, used to operate with windows shell (explorer)

Dim objShell

Set objShell = WScript.CreateObject( "WScript.Shell" )

 

'Finding correct path to the ProgramFiles directory

programFilesPath = objShell.ExpandEnvironmentStrings("%ProgramFiles(x86)%")

arguments = ""

 

'Checking, if we have at least one command line argument. It's expected to be MSI file path.

if WScript.Arguments.Count > 0 Then

arguments = """" & WScript.Arguments(0) & """"

end if

 

'Creating final executable path with arguments

exePath = """" & programFilesPath & "\Orca\Orca.exe"" " & arguments

'Running the application and waiting for it to finish

objShell.Run exePath, ShowWindow, WaitUntilFinished

 

Set objShell = Nothing

 

Updating the Temporary Database

Below is a default script (also VBS) that resets all package GUIDs and saves the temporary database. After the scripts is finished, RayPack collects the changes and applies them in the current project.

 

In order to open, edit and save an MSI database, MSI COM interface is used. The documentation of its methods can be found in MSDN website: https://msdn.microsoft.com/en-us/library/windows/desktop/aa367810(v=vs.85).aspx.

 

Option Explicit

 

Function CreateGUID

Dim TypeLib

Set TypeLib = CreateObject("Scriptlet.TypeLib")

dim guid

guid = TypeLib.Guid

 

CreateGUID = Left(UCase(guid), 38)

End Function

 

Dim installer, database, view, record, viewUpdate

 

Set installer = CreateObject("WindowsInstaller.Installer")

Set database = installer.OpenDatabase (WScript.Arguments(0), 1)

 

Set view = database.OpenView ("SELECT Component FROM Component")

view.execute

 

set record = view.Fetch

 

Do While Not record Is Nothing

 

dim componentName, componentGuid

componentName = record.StringData(1)

componentGuid = CreateGUID

 

set viewUpdate = database.OpenView ("UPDATE Component SET ComponentId='" & componentGuid & "' WHERE Component='" & componentName & "'")

viewUpdate.Execute

 

set record = view.Fetch

loop

 

set viewUpdate = database.OpenView ("UPDATE Property SET Value='" & CreateGUID & "' WHERE Property='ProductCode'")

viewUpdate.Execute

 

set viewUpdate = database.OpenView ("UPDATE Property SET Value='" & CreateGUID & "' WHERE Property='UpgradeCode'")

viewUpdate.Execute

 

database.Commit

Set database = Nothing

Set installer = Nothing

Set view = Nothing

 

A similar approach can be executed with PowerShell. Please note that the parameter $Path is defined, and the value of that parameter will be set externally by RayPack before calling the PowerShell script engine. In this example MSI COM interface is used to update the MSI, and its methods are called using Reflection.

 

param(

[parameter(Mandatory=$true)]

[ValidateNotNullOrEmpty()]

[String]$Path

)

Process {

try {

# Read property from MSI database

$OpenReadOnly = 0

$OpenTransact = 1

 

$WindowsInstaller = New-Object -ComObject WindowsInstaller.Installer

 

$MSIDatabase = $WindowsInstaller.GetType().InvokeMember("OpenDatabase", "InvokeMethod", $null, $WindowsInstaller, @($Path, $OpenTransact))

 

$Query = "SELECT Component FROM Component"

$View = $MSIDatabase.GetType().InvokeMember("OpenView", "InvokeMethod", $null, $MSIDatabase, ($Query))

$View.GetType().InvokeMember("Execute", "InvokeMethod", $null, $View, $null)

$Record = $View.GetType().InvokeMember("Fetch", "InvokeMethod", $null, $View, $null)

 

 

while ($Record -ne $null)

{

$componentName = $Record.GetType().InvokeMember("StringData", "GetProperty", $null, $Record, 1)

$newGuid = [guid]::NewGuid().ToString("B").ToUpper()

 

$updateQuery = "UPDATE Component SET ComponentId='" + $newGuid + "' WHERE Component='" + $componentName + "'"

$viewUpdate = $MSIDatabase.GetType().InvokeMember("OpenView", "InvokeMethod", $null, $MSIDatabase, ($updateQuery))

$viewUpdate.GetType().InvokeMember("Execute", "InvokeMethod", $null, $viewUpdate, $null)

 

$Record = $View.GetType().InvokeMember("Fetch", "InvokeMethod", $null, $View, $null)

$viewUpdate.GetType().InvokeMember("Close", "InvokeMethod", $null, $viewUpdate, $null)  

}

 

$viewUpdate = $MSIDatabase.GetType().InvokeMember("OpenView", "InvokeMethod", $null, $MSIDatabase, ("UPDATE Property SET Value='" + [guid]::NewGuid().ToString("B").ToUpper() + "' WHERE Property='ProductCode'"))

$viewUpdate.GetType().InvokeMember("Execute", "InvokeMethod", $null, $viewUpdate, $null)

 

$viewUpdate = $MSIDatabase.GetType().InvokeMember("OpenView", "InvokeMethod", $null, $MSIDatabase, ("UPDATE Property SET Value='" + [guid]::NewGuid().ToString("B").ToUpper() + "' WHERE Property='UpgradeCode'"))

$viewUpdate.GetType().InvokeMember("Execute", "InvokeMethod", $null, $viewUpdate, $null)

 

 

# Commit database and close view

$MSIDatabase.GetType().InvokeMember("Commit", "InvokeMethod", $null, $MSIDatabase, $null)

$View.GetType().InvokeMember("Close", "InvokeMethod", $null, $View, $null)

$viewUpdate.GetType().InvokeMember("Close", "InvokeMethod", $null, $viewUpdate, $null)    

$MSIDatabase = $null

$View = $null

$viewUpdate = $null

}

catch {

Write-Warning -Message $_.Exception.Message ; break

}

}

End {

# Run garbage collection and release ComObject

[System.Runtime.Interopservices.Marshal]::ReleaseComObject($WindowsInstaller)

[System.GC]::Collect()

}