[desktop] Add migration and improve msi installer

This commit is contained in:
Vladimir Stoilov 2025-03-04 18:07:56 +02:00
parent b6c3fba691
commit 0200404eac
No known key found for this signature in database
9 changed files with 216 additions and 48 deletions

View file

@ -123,11 +123,12 @@
},
"wix": {
"fragmentPaths": [
"templates/service.wxs",
"templates/files.wxs"
"templates/wix/files.wxs",
"templates/wix/old_service_check.wxs",
"templates/wix/migration.wxs",
],
"componentGroupRefs": ["BinaryAndIntelFiles"],
"template": "templates/main.wxs"
"template": "templates/wix/main.wxs"
}
},
"targets": [

View file

@ -1,37 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<CustomAction Id="InstallPortmasterService"
Directory="INSTALLDIR"
ExeCommand="sc.exe create PortmasterCore binPath= &quot;[INSTALLDIR]portmaster-core.exe --log-dir=%PROGRAMDATA%\Portmaster\logs&quot;"
Execute="commit"
Return="check"
Impersonate="no"
/>
<CustomAction Id="StopPortmasterService"
Directory="INSTALLDIR"
ExeCommand="sc.exe stop PortmasterCore"
Execute="commit"
Return="ignore"
Impersonate="no"
/>
<CustomAction Id="DeletePortmasterService"
Directory="INSTALLDIR"
ExeCommand="sc.exe delete PortmasterCore"
Execute="commit"
Return="ignore"
Impersonate="no"
/>
<InstallExecuteSequence>
<Custom Action="InstallPortmasterService" Before='InstallFinalize'>
<![CDATA[NOT Installed]]>
</Custom>
<Custom Action="StopPortmasterService" Before='InstallFinalize'>
REMOVE
</Custom>
<Custom Action="DeletePortmasterService" Before='InstallFinalize'>
REMOVE
</Custom>
</InstallExecuteSequence>
</Fragment>
</Wix>

View file

@ -0,0 +1,44 @@
Option Explicit
Dim objShell, objExec, strOutput, arrLines, i, arrStatus
' Create an instance of the WScript.Shell object
Set objShell = CreateObject("WScript.Shell")
' Run the sc.exe command to query the service
Set objExec = objShell.Exec("cmd /c sc.exe query PortmasterCore")
' Initialize an empty string to store the output
strOutput = ""
' Read all output from the command line
Do While Not objExec.StdOut.AtEndOfStream
strOutput = strOutput & objExec.StdOut.ReadLine() & vbCrLf
Loop
' Split the output into lines
arrLines = Split(strOutput, vbCrLf)
' Example Output
' SERVICE_NAME: PortmasterCore
' TYPE : 10 WIN32_OWN_PROCESS
' STATE : 1 STOPPED
' WIN32_EXIT_CODE : 1077 (0x435)
' SERVICE_EXIT_CODE : 0 (0x0)
' CHECKPOINT : 0x0
' WAIT_HINT : 0x0
For i = LBound(arrLines) To UBound(arrLines)
' Example line: STATE : 1 STOPPED
If InStr(arrLines(i), "STATE") > 0 Then
' Extract and display the service state
' Example string: "1 STOPPED"
arrStatus = Split(Trim(Mid(arrLines(i), InStr(arrLines(i), ":") + 1)), " ")
' Anything other the STOPPED consider as running
If Not arrStatus(2) = "STOPPED" Then
MsgBox("Portmaster service is running. Stop it and run the installer again.")
' Notify the installer that it should fail.
WScript.Quit 1
End If
End If
Next

View file

@ -0,0 +1,78 @@
Dim FSO
Set FSO = CreateObject("Scripting.FileSystemObject")
Dim customData, args, oldInstallationDir, migrationFlagFile, newDataDir, doMigration
customData = Session.Property("CustomActionData")
' Split the string by commas
args = Split(customData, ",")
' Access individual arguments
Dim commonAppDataFolder, programMenuFolder, startupFolder, appDataFolder
commonAppDataFolder = Trim(args(0))
programMenuFolder = Trim(args(1))
startupFolder = Trim(args(2))
appDataFolder = Trim(args(3))
' Read variables from the session object
oldInstallationDir = commonAppDataFolder & "Safing\Portmaster\"
newDataDir = commonAppDataFolder & "Portmaster"
migrationFlagFile = oldInstallationDir & "migrated.txt"
doMigration = true
' Check for existing installtion
If Not fso.FolderExists(oldInstallationDir) Then
doMigration = false
End If
' Check if migration was already done
If fso.FileExists(migrationFlagFile) Then
doMigration = false
End If
If doMigration Then
' Copy the config file
dim configFile
configFile = "config.json"
If fso.FileExists(oldInstallationDir & configFile) Then
fso.CopyFile oldInstallationDir & configFile, newDataDir & configFile
End If
' Copy the database folder
dim databaseFolder
databaseFolder = "databases"
If fso.FolderExists(oldInstallationDir & databaseFolder) Then
fso.CopyFolder oldInstallationDir & databaseFolder, newDataDir & databaseFolder
End If
' Delete shortcuts
dim shortcutsFolder
notifierShortcut = programMenuFolder & "Portmaster/Portmaster Notifier.lnk"
If fso.FileExists(notifierShortcut) Then
fso.DeleteFile notifierShortcut, True
End If
' Delete startup shortcut
dim srartupFile
srartupFile = startupFolder & "Portmaster Notifier.lnk"
If fso.FileExists(srartupFile) Then
fso.DeleteFile srartupFile, True
End If
' Delete shortuct in user folder
dim userShortcut
userShortcut = appDataFolder & "Microsoft\Windows\Start Menu\Programs\Portmaster.lnk"
If fso.FileExists(userShortcut) Then
fso.DeleteFile userShortcut, True
End If
' Delete the old installer
dim oldUninstaller
oldUninstaller = oldInstallationDir & "portmaster-uninstaller.exe"
If fso.FileExists(oldUninstaller) Then
fso.DeleteFile oldUninstaller, True
End If
' Set the migration flag file
fso.CreateTextFile(migrationFlagFile).Close
End If

View file

@ -13,7 +13,6 @@
<Fragment>
<Component Id="BinaryFiles" Directory="INSTALLDIR" Guid="850cdd31-424d-45f5-b8f0-95df950ebd0d">
<File Id="BinIndexJson" Source="..\..\..\..\binary\index.json" />
<File Id="PortmasterCoreExe" Source="..\..\..\..\binary\portmaster-core.exe" />
<File Id="PortmasterCoreDLL" Source="..\..\..\..\binary\portmaster-core.dll" />
<File Id="PortmasterKextSys" Source="..\..\..\..\binary\portmaster-kext.sys" />
<File Id="WebView2Loader" Source="..\..\..\..\binary\WebView2Loader.dll" />
@ -30,12 +29,37 @@
<File Id="IntermediateDsdl" Source="..\..\..\..\intel\intermediate.dsdl" />
<File Id="UrgentDsdl" Source="..\..\..\..\intel\urgent.dsdl" />
</Component>
<Component Id="PortmasterCoreService" Directory="INSTALLDIR" Guid="76ebd748-d620-484b-9035-5a64bbe2c26d">
<File Id="PortmasterCoreExe" Source="..\..\..\..\binary\portmaster-core.exe" />
<ServiceInstall
Id="PortmasterServiceInstall"
Type="ownProcess"
Name="PortmasterCore"
DisplayName="Portmaster Core"
Description="Portmaster Application Firewall - Core Service"
Start="auto"
ErrorControl="normal"
Vital="yes"
Interactive="no"
Arguments="--log-dir=%PROGRAMDATA%\Portmaster\logs"
Account="LocalSystem" />
<ServiceControl
Id="PortmasterServiceDeleteExisting"
Name="PortmasterCore"
Remove="uninstall"
Stop="both"
Wait="yes"
/>
</Component>
</Fragment>
<Fragment>
<ComponentGroup Id="BinaryAndIntelFiles">
<ComponentRef Id="BinaryFiles" />
<ComponentRef Id="IntelFiles" />
<ComponentRef Id="PortmasterCoreService" />
</ComponentGroup>
</Fragment>
</Wix>

View file

@ -70,9 +70,12 @@
<Property Id="ARPURLUPDATEINFO" Value="{{homepage}}"/>
{{/if}}
<!-- initialize with previous InstallDir -->
<Property Id="INSTALLDIR">
<RegistrySearch Id="PrevInstallDirReg" Root="HKCU" Key="Software\\{{manufacturer}}\\{{product_name}}" Name="InstallDir" Type="raw"/>
<!-- First attempt: Search for "InstallDir" -->
<RegistrySearch Id="PrevInstallDirWithName" Root="HKCU" Key="Software\\{{manufacturer}}\\{{product_name}}" Name="InstallDir" Type="raw" />
<!-- Second attempt: If the first fails, search for the default key value (this is how the nsis installer currently stores the path) -->
<RegistrySearch Id="PrevInstallDirNoName" Root="HKCU" Key="Software\\{{manufacturer}}\\{{product_name}}" Type="raw" />
</Property>
<!-- launch app checkbox -->
@ -161,6 +164,7 @@
</Component>
<Component Id="UpdateTaskInstaller" Guid="011F25ED-9BE3-50A7-9E9B-3519ED2B9932" Win64="$(var.Win64)">
<File Id="UpdateTaskInstaller" Source="install-task.ps1" KeyPath="yes" Checksum="yes"/>
</Component>
<Component Id="UpdateTaskUninstaller" Guid="D4F6CC3F-32DC-5FD0-95E8-782FFD7BBCE1" Win64="$(var.Win64)">
<File Id="UpdateTaskUninstaller" Source="uninstall-task.ps1" KeyPath="yes" Checksum="yes"/>
@ -323,6 +327,7 @@
Execute="commit"
Impersonate="yes"
ExeCommand="powershell.exe -WindowStyle hidden .\install-task.ps1" />
<InstallExecuteSequence>
<Custom Action='CreateUpdateTask' Before='InstallFinalize'>
NOT(REMOVE)
@ -348,9 +353,11 @@
<SetProperty Id="ARPINSTALLLOCATION" Value="[INSTALLDIR]" After="CostFinalize"/>
<!-- Service fragments -->
<CustomActionRef Id='InstallPortmasterService' />
<CustomActionRef Id='StopPortmasterService' />
<CustomActionRef Id='DeletePortmasterService' />
<CustomActionRef Id='MigrationPropertySet' />
<CustomActionRef Id='Migration' />
<!-- Uncommenting the next line will cause the installer to check if the old service is running and fail. Without, it will automatically stop and remove the old service without notifing the user. -->
<!-- <CustomActionRef Id='CheckServiceStatus' /> -->
<!-- End Service fragments -->
</Product>
</Wix>

View file

@ -70,9 +70,12 @@
<Property Id="ARPURLUPDATEINFO" Value="{{homepage}}"/>
{{/if}}
<!-- initialize with previous InstallDir -->
<Property Id="INSTALLDIR">
<RegistrySearch Id="PrevInstallDirReg" Root="HKCU" Key="Software\\{{manufacturer}}\\{{product_name}}" Name="InstallDir" Type="raw"/>
<!-- First attempt: Search for "InstallDir" -->
<RegistrySearch Id="PrevInstallDirWithName" Root="HKCU" Key="Software\\{{manufacturer}}\\{{product_name}}" Name="InstallDir" Type="raw" />
<!-- Second attempt: If the first fails, search for the default key value (this is how the nsis installer currently stores the path) -->
<RegistrySearch Id="PrevInstallDirNoName" Root="HKCU" Key="Software\\{{manufacturer}}\\{{product_name}}" Type="raw" />
</Property>
<!-- launch app checkbox -->

View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<!-- Load the VBscript -->
<Binary Id="Migration.vbs" SourceFile="..\..\..\..\templates\wix\Migration.vbs" />
<!-- VBscript script to copy the files from the old installation -->
<CustomAction
Id="Migration"
VBScriptCall=""
BinaryKey="Migration.vbs"
Return="check"
Impersonate="no"
Execute="deferred"
/>
<!-- This passes the path to CommonAppDataFolder path (usually "C:\ProgramData\") to the vbscirt custom action -->
<CustomAction Id="MigrationPropertySet" Property="Migration" Value="[CommonAppDataFolder], [ProgramMenuFolder], [StartupFolder], [AppDataFolder]" />
<!-- Check if service is running -->
<InstallExecuteSequence>
<Custom Action="MigrationPropertySet" Before="Migration">NOT(REMOVE)</Custom>
<Custom Action="Migration" Before="InstallFinalize">NOT(REMOVE)</Custom>
</InstallExecuteSequence>
</Fragment>
</Wix>

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<!-- Load the VBscript -->
<Binary Id="CheckServiceStatusScript" SourceFile="..\..\..\..\templates\wix\CheckServiceStatus.vbs" />
<!-- VBscript script custom action to check if the service is running -->
<CustomAction
Id="CheckServiceStatus"
VBScriptCall=""
BinaryKey="CheckServiceStatusScript"
Return="check" />
<!-- Check if service is running -->
<InstallExecuteSequence>
<Custom Action="CheckServiceStatus" Before="InstallInitialize">NOT(REMOVE)</Custom>
</InstallExecuteSequence>
</Fragment>
</Wix>