<< 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.
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
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()
}