Refact/msi more install options (#8949)

* refact: msi, more install options

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact: msi, reg values on upgrade/modify

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: msi, silent repair/upgrade, RemoveInstallFolder()

Signed-off-by: fufesou <linlong1266@gmail.com>

* Options support both 1/0 and Y/N

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact: msi, preprocess, open file with explicit encoding

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: msi, read previous options

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: mis, install folder, read previous option

Signed-off-by: fufesou <linlong1266@gmail.com>

* Comment on Control -> Checkbox

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: UI, checkbox options, read previous values

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: shortcuts options, init state

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: shortcuts, init state

Signed-off-by: fufesou <linlong1266@gmail.com>

* Better shortcuts property conditions

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
This commit is contained in:
fufesou 2024-08-05 17:49:48 +08:00 committed by GitHub
parent 2266fde26f
commit b3e1c8a907
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 209 additions and 46 deletions

View File

@ -31,6 +31,11 @@ LExit:
return WcaFinalize(er); return WcaFinalize(er);
} }
// CAUTION: We can't simply remove the install folder here, because silent repair/upgrade will fail.
// `RemoveInstallFolder()` is a deferred custom action, it will be executed after the files are copied.
// `msiexec /i package.msi /qn`
//
// So we need to delete the files separately in install folder.
UINT __stdcall RemoveInstallFolder( UINT __stdcall RemoveInstallFolder(
__in MSIHANDLE hInstall) __in MSIHANDLE hInstall)
{ {
@ -41,6 +46,7 @@ UINT __stdcall RemoveInstallFolder(
LPWSTR installFolder = NULL; LPWSTR installFolder = NULL;
LPWSTR pwz = NULL; LPWSTR pwz = NULL;
LPWSTR pwzData = NULL; LPWSTR pwzData = NULL;
WCHAR runtimeBroker[1024] = { 0, };
hr = WcaInitialize(hInstall, "RemoveInstallFolder"); hr = WcaInitialize(hInstall, "RemoveInstallFolder");
ExitOnFailure(hr, "Failed to initialize"); ExitOnFailure(hr, "Failed to initialize");
@ -52,21 +58,22 @@ UINT __stdcall RemoveInstallFolder(
hr = WcaReadStringFromCaData(&pwz, &installFolder); hr = WcaReadStringFromCaData(&pwz, &installFolder);
ExitOnFailure(hr, "failed to read database key from custom action data: %ls", pwz); ExitOnFailure(hr, "failed to read database key from custom action data: %ls", pwz);
StringCchPrintfW(runtimeBroker, sizeof(runtimeBroker) / sizeof(runtimeBroker[0]), L"%ls\\RuntimeBroker_rustdesk.exe", installFolder);
SHFILEOPSTRUCTW fileOp; SHFILEOPSTRUCTW fileOp;
ZeroMemory(&fileOp, sizeof(SHFILEOPSTRUCT)); ZeroMemory(&fileOp, sizeof(SHFILEOPSTRUCT));
fileOp.wFunc = FO_DELETE; fileOp.wFunc = FO_DELETE;
fileOp.pFrom = installFolder; fileOp.pFrom = runtimeBroker;
fileOp.fFlags = FOF_NOCONFIRMATION | FOF_SILENT; fileOp.fFlags = FOF_NOCONFIRMATION | FOF_SILENT;
nResult = SHFileOperationW(&fileOp); nResult = SHFileOperationW(&fileOp);
if (nResult == 0) if (nResult == 0)
{ {
WcaLog(LOGMSG_STANDARD, "The directory \"%ls\" has been deleted.", installFolder); WcaLog(LOGMSG_STANDARD, "The external file \"%ls\" has been deleted.", runtimeBroker);
} }
else else
{ {
WcaLog(LOGMSG_STANDARD, "The directory \"%ls\" has not been deleted, error code: 0x%02X. Please refer to https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shfileoperationa for the error codes.", installFolder, nResult); WcaLog(LOGMSG_STANDARD, "The external file \"%ls\" has not been deleted, error code: 0x%02X. Please refer to https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shfileoperationa for the error codes.", runtimeBroker, nResult);
} }
LExit: LExit:

View File

@ -3,8 +3,26 @@
<?include ../Includes.wxi?> <?include ../Includes.wxi?>
<Fragment> <Fragment>
<!-- For compatibility with command line values from previous versions -->
<Property Id="INSTALLFOLDER" Secure="yes">
<RegistrySearch Id="InstallFolderSearch" Root="HKCR" Key="$(var.RegKeyRoot)" Name="INSTALLFOLDER" Type="raw" />
</Property>
<!-- If a property value has been passed via the command line (which includes when set from the bundle), the registry search will
overwrite the command line value, these actions temporarily store the command line value before the registry search
is performed so they can be restored after the registry search is complete -->
<SetProperty Id="SavedInstallFolderCmdLineValue" Value="[INSTALLFOLDER]" Before="AppSearch" Sequence="first" Condition="INSTALLFOLDER" />
<!-- If a command line value was stored, restore it after the registry search has been performed -->
<SetProperty Action="RestoreSavedInstallFolderValue" Id="INSTALLFOLDER" Value="[SavedInstallFolderCmdLineValue]" After="AppSearch" Sequence="first" Condition="SavedInstallFolderCmdLineValue" />
<!-- If a command line value or registry value was set, update the main properties with the value -->
<SetProperty Id="INSTALLFOLDER_INNER" Value="[INSTALLFOLDER]" After="RestoreSavedInstallFolderValue" Sequence="first" Condition="INSTALLFOLDER" />
<!-- INSTALLFOLDER_INNER is defined for compatibility with previous versions of the installer. -->
<!-- Because we need to use INSTALLFOLDER as the command line argument. -->
<StandardDirectory Id="ProgramFiles6432Folder"> <StandardDirectory Id="ProgramFiles6432Folder">
<Directory Id="INSTALLFOLDER" Name="$(var.Product)" /> <Directory Id="INSTALLFOLDER_INNER" Name="$(var.Product)" />
</StandardDirectory> </StandardDirectory>
<StandardDirectory Id="CommonAppDataFolder"> <StandardDirectory Id="CommonAppDataFolder">

View File

@ -5,16 +5,16 @@
<Fragment> <Fragment>
<!-- Regs for shortcuts are defined in "Fragments/ShortcutProperties.wxs" --> <!-- Regs for shortcuts are defined in "Fragments/ShortcutProperties.wxs" -->
<!-- Component that persists the property values to the registry so they are available during an upgrade/modify --> <!-- Component that persists the property values to the registry so they are available during an upgrade/modify -->
<DirectoryRef Id="INSTALLFOLDER"> <DirectoryRef Id="INSTALLFOLDER_INNER">
<Component Id="Product.Registry.InstallDir" Guid="3196EDA7-9AEF-4705-A0C8-E3F3ECCCB153"> <Component Id="Product.Registry.InstallFolder" Guid="3196EDA7-9AEF-4705-A0C8-E3F3ECCCB153">
<RegistryKey Root="HKCR" Key="$(var.RegKeyRoot)"> <RegistryKey Root="HKCR" Key="$(var.RegKeyRoot)">
<RegistryValue Type="string" Name="INSTALLFOLDER" Value="[INSTALLFOLDER]" /> <RegistryValue Type="string" Name="INSTALLFOLDER" Value="[INSTALLFOLDER_INNER]" />
</RegistryKey> </RegistryKey>
</Component> </Component>
<Component Id="Product.Registry.DefaultIcon" Guid="6DBF2690-0955-4C6A-940F-634DDA503F49"> <Component Id="Product.Registry.DefaultIcon" Guid="6DBF2690-0955-4C6A-940F-634DDA503F49">
<RegistryKey Root="HKCR" Key="$(var.RegKeyRoot)\DefaultIcon"> <RegistryKey Root="HKCR" Key="$(var.RegKeyRoot)\DefaultIcon">
<RegistryValue Type="string" Value='"[INSTALLFOLDER]$(var.Product).exe",0' /> <RegistryValue Type="string" Value='"[INSTALLFOLDER_INNER]$(var.Product).exe",0' />
</RegistryKey> </RegistryKey>
</Component> </Component>
@ -22,7 +22,7 @@
<RegistryKey Root="HKCR" Key="$(var.RegKeyRoot)\shell" /> <RegistryKey Root="HKCR" Key="$(var.RegKeyRoot)\shell" />
<RegistryKey Root="HKCR" Key="$(var.RegKeyRoot)\shell\open" /> <RegistryKey Root="HKCR" Key="$(var.RegKeyRoot)\shell\open" />
<RegistryKey Root="HKCR" Key="$(var.RegKeyRoot)\shell\open\command"> <RegistryKey Root="HKCR" Key="$(var.RegKeyRoot)\shell\open\command">
<RegistryValue Type="string" Value='"[INSTALLFOLDER]$(var.Product).exe" --play "%1"' /> <RegistryValue Type="string" Value='"[INSTALLFOLDER_INNER]$(var.Product).exe" --play "%1"' />
</RegistryKey> </RegistryKey>
</Component> </Component>
@ -36,7 +36,7 @@
<RegistryKey Root="HKCR" Key="$(var.ProductLower)\shell" /> <RegistryKey Root="HKCR" Key="$(var.ProductLower)\shell" />
<RegistryKey Root="HKCR" Key="$(var.ProductLower)\shell\open" /> <RegistryKey Root="HKCR" Key="$(var.ProductLower)\shell\open" />
<RegistryKey Root="HKCR" Key="$(var.ProductLower)\shell\open\command"> <RegistryKey Root="HKCR" Key="$(var.ProductLower)\shell\open\command">
<RegistryValue Type="string" Value='"[INSTALLFOLDER]$(var.Product).exe" "%1"' /> <RegistryValue Type="string" Value='"[INSTALLFOLDER_INNER]$(var.Product).exe" "%1"' />
</RegistryKey> </RegistryKey>
</Component> </Component>

View File

@ -4,7 +4,7 @@
<?include ../Includes.wxi?> <?include ../Includes.wxi?>
<DirectoryRef Id="INSTALLFOLDER" FileSource="$(var.BuildDir)"> <DirectoryRef Id="INSTALLFOLDER_INNER" FileSource="$(var.BuildDir)">
<Component Id="App.exe" Guid="620F0F69-4C17-4320-A619-495E329712A4"> <Component Id="App.exe" Guid="620F0F69-4C17-4320-A619-495E329712A4">
<File Id="App.exe" Name="$(var.Product).exe" KeyPath="yes" Checksum="yes"> <File Id="App.exe" Name="$(var.Product).exe" KeyPath="yes" Checksum="yes">
<!--<fire:FirewallException Id="AppEx" Name="$(var.Product) Service" Scope="any" IgnoreFailure="yes" />--> <!--<fire:FirewallException Id="AppEx" Name="$(var.Product) Service" Scope="any" IgnoreFailure="yes" />-->
@ -12,10 +12,10 @@
</Component> </Component>
</DirectoryRef> </DirectoryRef>
<CustomAction Id="RemoveInstallFolder.SetParam" Return="check" Property="RemoveInstallFolder" Value="[INSTALLFOLDER]" /> <CustomAction Id="RemoveInstallFolder.SetParam" Return="check" Property="RemoveInstallFolder" Value="[INSTALLFOLDER_INNER]" />
<CustomAction Id="AddFirewallRules.SetParam" Return="check" Property="AddFirewallRules" Value="1[INSTALLFOLDER]$(var.Product).exe" /> <CustomAction Id="AddFirewallRules.SetParam" Return="check" Property="AddFirewallRules" Value="1[INSTALLFOLDER_INNER]$(var.Product).exe" />
<CustomAction Id="RemoveFirewallRules.SetParam" Return="check" Property="RemoveFirewallRules" Value="0[INSTALLFOLDER]$(var.Product).exe" /> <CustomAction Id="RemoveFirewallRules.SetParam" Return="check" Property="RemoveFirewallRules" Value="0[INSTALLFOLDER_INNER]$(var.Product).exe" />
<CustomAction Id="CreateStartService.SetParam" Return="check" Property="CreateStartService" Value="$(var.Product);&quot;[INSTALLFOLDER]$(var.Product).exe&quot; --service" /> <CustomAction Id="CreateStartService.SetParam" Return="check" Property="CreateStartService" Value="$(var.Product);&quot;[INSTALLFOLDER_INNER]$(var.Product).exe&quot; --service" />
<CustomAction Id="TryStopDeleteService.SetParam" Return="check" Property="TryStopDeleteService" Value="$(var.Product)" /> <CustomAction Id="TryStopDeleteService.SetParam" Return="check" Property="TryStopDeleteService" Value="$(var.Product)" />
<CustomAction Id="LaunchApp" ExeCommand="" Return="asyncNoWait" FileRef="App.exe" /> <CustomAction Id="LaunchApp" ExeCommand="" Return="asyncNoWait" FileRef="App.exe" />
@ -29,7 +29,7 @@
<CustomAction Id="SetPropertyServiceStop.SetParam.ConfigKey" Return="check" Property="ConfigKey" Value="stop-service" /> <CustomAction Id="SetPropertyServiceStop.SetParam.ConfigKey" Return="check" Property="ConfigKey" Value="stop-service" />
<CustomAction Id="SetPropertyServiceStop.SetParam.PropertyName" Return="check" Property="PropertyName" Value="STOP_SERVICE" /> <CustomAction Id="SetPropertyServiceStop.SetParam.PropertyName" Return="check" Property="PropertyName" Value="STOP_SERVICE" />
<CustomAction Id="TryDeleteStartupShortcut.SetParam" Return="check" Property="ShortcutName" Value="$(var.Product) Tray" /> <CustomAction Id="TryDeleteStartupShortcut.SetParam" Return="check" Property="ShortcutName" Value="$(var.Product) Tray" />
<CustomAction Id="RemoveAmyuniIdd.SetParam" Return="check" Property="RemoveAmyuniIdd" Value="[INSTALLFOLDER]" /> <CustomAction Id="RemoveAmyuniIdd.SetParam" Return="check" Property="RemoveAmyuniIdd" Value="[INSTALLFOLDER_INNER]" />
<InstallExecuteSequence> <InstallExecuteSequence>
<Custom Action="SetPropertyIsServiceRunning" After="InstallInitialize" Condition="Installed" /> <Custom Action="SetPropertyIsServiceRunning" After="InstallInitialize" Condition="Installed" />
@ -88,8 +88,8 @@
</DirectoryRef> </DirectoryRef>
<DirectoryRef Id="App.StartMenu"> <DirectoryRef Id="App.StartMenu">
<Component Id="App.StartMenu.Shortcut" Guid="43ABCAC7-E47D-42D8-A408-25EC70DBB993" Condition="STARTMENUSHORTCUTS = 1"> <Component Id="App.StartMenu.Shortcut" Guid="43ABCAC7-E47D-42D8-A408-25EC70DBB993" Condition="STARTMENUSHORTCUTS = 1 OR STARTMENUSHORTCUTS = &quot;Y&quot; OR STARTMENUSHORTCUTS = &quot;y&quot;">
<Shortcut Id="App.StartMenu.Shortcut" Name="!(loc.SC_Client)" Description="!(loc.SC_Client_Desc)" Target="[!App.exe]" Icon="AppIcon" WorkingDirectory="INSTALLFOLDER" /> <Shortcut Id="App.StartMenu.Shortcut" Name="!(loc.SC_Client)" Description="!(loc.SC_Client_Desc)" Target="[!App.exe]" Icon="AppIcon" WorkingDirectory="INSTALLFOLDER_INNER" />
<!-- <!--
Fix ICE 38 by adding a dummy registry key that is the key for this shortcut. Fix ICE 38 by adding a dummy registry key that is the key for this shortcut.
https://learn.microsoft.com/en-us/windows/win32/msi/ice38 https://learn.microsoft.com/en-us/windows/win32/msi/ice38
@ -97,31 +97,31 @@
<RegistryValue Root="HKCU" Key="Software\$(var.Product)" Name="App.StartMenu.Shortcut" Type="string" Value="1" KeyPath="yes" /> <RegistryValue Root="HKCU" Key="Software\$(var.Product)" Name="App.StartMenu.Shortcut" Type="string" Value="1" KeyPath="yes" />
</Component> </Component>
<Component Id="App.StartMenu.ShortcutUninstall" Guid="E100D7F8-D607-4513-28DA-2C95E5EA698E" Condition="STARTMENUSHORTCUTS = 1"> <Component Id="App.StartMenu.ShortcutUninstall" Guid="E100D7F8-D607-4513-28DA-2C95E5EA698E" Condition="STARTMENUSHORTCUTS = 1 OR STARTMENUSHORTCUTS = &quot;Y&quot; OR STARTMENUSHORTCUTS = &quot;y&quot;">
<Shortcut Id="App.StartMenu.ShortcutUninstall" Name="!(loc.SC_Uninstall)" Description="!(loc.SC_Uninstall_Desc)" Target="[System6432Folder]msiexec.exe" Arguments="/x [ProductCode]" Icon="AppIcon" /> <Shortcut Id="App.StartMenu.ShortcutUninstall" Name="!(loc.SC_Uninstall)" Description="!(loc.SC_Uninstall_Desc)" Target="[System6432Folder]msiexec.exe" Arguments="/x [ProductCode]" Icon="AppIcon" />
<RegistryValue Root="HKCU" Key="Software\$(var.Product)" Name="App.StartMenu.ShortcutUninstall" Type="string" Value="1" KeyPath="yes" /> <RegistryValue Root="HKCU" Key="Software\$(var.Product)" Name="App.StartMenu.ShortcutUninstall" Type="string" Value="1" KeyPath="yes" />
</Component> </Component>
</DirectoryRef> </DirectoryRef>
<StandardDirectory Id="DesktopFolder"> <StandardDirectory Id="DesktopFolder">
<Component Id="App.Desktop.Shortcut" Guid="CA8FB7AA-17F7-4E36-A58A-5A016A303709" Condition="DESKTOPSHORTCUTS = 1"> <Component Id="App.Desktop.Shortcut" Guid="CA8FB7AA-17F7-4E36-A58A-5A016A303709" Condition="DESKTOPSHORTCUTS = 1 OR DESKTOPSHORTCUTS = &quot;Y&quot; OR DESKTOPSHORTCUTS = &quot;y&quot;">
<Shortcut Id="App.Desktop.Shortcut" Name="!(loc.SC_Client)" Description="!(loc.SC_Client_Desc)" Target="[!App.exe]" Icon="AppIcon" WorkingDirectory="INSTALLFOLDER" /> <Shortcut Id="App.Desktop.Shortcut" Name="!(loc.SC_Client)" Description="!(loc.SC_Client_Desc)" Target="[!App.exe]" Icon="AppIcon" WorkingDirectory="INSTALLFOLDER_INNER" />
<RegistryValue Root="HKCU" Key="Software\$(var.Product)" Name="App.Desktop.Shortcut" Type="string" Value="1" KeyPath="yes" /> <RegistryValue Root="HKCU" Key="Software\$(var.Product)" Name="App.Desktop.Shortcut" Type="string" Value="1" KeyPath="yes" />
</Component> </Component>
</StandardDirectory> </StandardDirectory>
<StandardDirectory Id="StartupFolder"> <StandardDirectory Id="StartupFolder">
<Component Id="App.StartupFolder.ShortcutTray" Guid="B1D1E2BB-E53E-E159-DB7C-744D5C726A8C" Condition="STARTUPSHORTCUTS = 1 AND (NOT CC_CONNECTION_TYPE=&quot;outgoing&quot;)"> <Component Id="App.StartupFolder.ShortcutTray" Guid="B1D1E2BB-E53E-E159-DB7C-744D5C726A8C" Condition="STARTUPSHORTCUTS = 1 AND (NOT CC_CONNECTION_TYPE=&quot;outgoing&quot;)">
<Shortcut Id="App.StartupFolder.ShortcutTray" Name="!(loc.SC_Client_Tray)" Description="!(loc.SC_Client_Tray_Desc)" Target="[!App.exe]" Arguments="--tray" Icon="AppIcon" WorkingDirectory="INSTALLFOLDER" /> <Shortcut Id="App.StartupFolder.ShortcutTray" Name="!(loc.SC_Client_Tray)" Description="!(loc.SC_Client_Tray_Desc)" Target="[!App.exe]" Arguments="--tray" Icon="AppIcon" WorkingDirectory="INSTALLFOLDER_INNER" />
<RegistryValue Root="HKCU" Key="Software\$(var.Product)" Name="App.StartupFolder.ShortcutTray" Type="string" Value="1" KeyPath="yes" /> <RegistryValue Root="HKCU" Key="Software\$(var.Product)" Name="App.StartupFolder.ShortcutTray" Type="string" Value="1" KeyPath="yes" />
</Component> </Component>
</StandardDirectory> </StandardDirectory>
<!--<DirectoryRef Id="INSTALLFOLDER"> <!--<DirectoryRef Id="INSTALLFOLDER_INNER">
<Component Id="App.UninstallShortcut" Guid="FB0F2AC7-2AE5-4C54-B860-5E472620B6B1"> <Component Id="App.UninstallShortcut" Guid="FB0F2AC7-2AE5-4C54-B860-5E472620B6B1">
<Shortcut Id="App.UninstallShortcut" Name="!(loc.SC_Uninstall)" Description="!(loc.SC_Uninstall_Desc)" Target="[System6432Folder]msiexec.exe" Arguments="/x [ProductCode]" Icon="AppIcon" /> <Shortcut Id="App.UninstallShortcut" Name="!(loc.SC_Uninstall)" Description="!(loc.SC_Uninstall_Desc)" Target="[System6432Folder]msiexec.exe" Arguments="/x [ProductCode]" Icon="AppIcon" />
</Component> </Component>
</DirectoryRef>--> </DirectoryRef>-->
<ComponentGroup Id="Components" Directory="INSTALLFOLDER"> <ComponentGroup Id="Components" Directory="INSTALLFOLDER_INNER">
<ComponentRef Id="App.exe" /> <ComponentRef Id="App.exe" />
<ComponentRef Id="App.Desktop.Shortcut" /> <ComponentRef Id="App.Desktop.Shortcut" />
<!--<ComponentRef Id="App.UninstallShortcut" />--> <!--<ComponentRef Id="App.UninstallShortcut" />-->

View File

@ -25,11 +25,25 @@
</Property> </Property>
<!-- Component that persists the property values to the registry so they are available during an upgrade/modify --> <!-- Component that persists the property values to the registry so they are available during an upgrade/modify -->
<DirectoryRef Id="INSTALLFOLDER"> <DirectoryRef Id="INSTALLFOLDER_INNER">
<Component Id="Product.Registry.PersistedShortcutProperties" Guid="1BBAD054-6EC2-4362-BF1B-E8BDE988B597"> <Component Id="Product.Registry.PersistedStartMenuShortcutProperties1" Guid="62F79BCF-3367-4ACF-950F-F8BCABACDDC0" Condition="STARTMENUSHORTCUTS = 1 OR STARTMENUSHORTCUTS = &quot;Y&quot; OR STARTMENUSHORTCUTS = &quot;y&quot;">
<RegistryKey Root="HKCR" Key="$(var.RegKeyRoot)"> <RegistryKey Root="HKCR" Key="$(var.RegKeyRoot)">
<RegistryValue Type="string" Name="STARTMENUSHORTCUTS" Value="[STARTMENUSHORTCUTS]" KeyPath="yes" /> <RegistryValue Type="string" Name="STARTMENUSHORTCUTS" Value="1" KeyPath="yes" />
<RegistryValue Type="string" Name="DESKTOPSHORTCUTS" Value="[DESKTOPSHORTCUTS]" /> </RegistryKey>
</Component>
<Component Id="Product.Registry.PersistedStartMenuShortcutProperties0" Guid="8EA2D5A8-6E5D-4BDD-9019-2099297FF519" Condition="NOT (STARTMENUSHORTCUTS = 1 OR STARTMENUSHORTCUTS = &quot;Y&quot; OR STARTMENUSHORTCUTS = &quot;y&quot;)">
<RegistryKey Root="HKCR" Key="$(var.RegKeyRoot)">
<RegistryValue Type="string" Name="STARTMENUSHORTCUTS" Value="0" KeyPath="yes" />
</RegistryKey>
</Component>
<Component Id="Product.Registry.PersistedDesktopShortcutProperties1" Guid="1BBAD054-6EC2-4362-BF1B-E8BDE988B597" Condition="DESKTOPSHORTCUTS = 1 OR DESKTOPSHORTCUTS = &quot;Y&quot; OR DESKTOPSHORTCUTS = &quot;y&quot;">
<RegistryKey Root="HKCR" Key="$(var.RegKeyRoot)">
<RegistryValue Type="string" Name="DESKTOPSHORTCUTS" Value="1" />
</RegistryKey>
</Component>
<Component Id="Product.Registry.PersistedDesktopShortcutProperties0" Guid="FA992614-D2E1-4795-9696-D45A5EF1B9C8" Condition="NOT (DESKTOPSHORTCUTS = 1 OR DESKTOPSHORTCUTS = &quot;Y&quot; OR DESKTOPSHORTCUTS = &quot;y&quot;)">
<RegistryKey Root="HKCR" Key="$(var.RegKeyRoot)">
<RegistryValue Type="string" Name="DESKTOPSHORTCUTS" Value="0" />
</RegistryKey> </RegistryKey>
</Component> </Component>
</DirectoryRef> </DirectoryRef>
@ -45,8 +59,8 @@
<SetProperty Action="RestoreSavedDesktopShortcutsValue" Id="CREATEDESKTOPSHORTCUTS" Value="[SavedDesktopShortcutsCmdLineValue]" After="AppSearch" Sequence="first" Condition="SavedDesktopShortcutsCmdLineValue" /> <SetProperty Action="RestoreSavedDesktopShortcutsValue" Id="CREATEDESKTOPSHORTCUTS" Value="[SavedDesktopShortcutsCmdLineValue]" After="AppSearch" Sequence="first" Condition="SavedDesktopShortcutsCmdLineValue" />
<!-- If a command line value or registry value was set, update the main properties with the value --> <!-- If a command line value or registry value was set, update the main properties with the value -->
<SetProperty Id="STARTMENUSHORTCUTS" Value="[CREATESTARTMENUSHORTCUTS]" After="RestoreSavedStartMenuShortcutsValue" Sequence="first" Condition="CREATESTARTMENUSHORTCUTS" /> <SetProperty Id="STARTMENUSHORTCUTS" Value="" After="RestoreSavedStartMenuShortcutsValue" Sequence="first" Condition="CREATESTARTMENUSHORTCUTS AND NOT (CREATESTARTMENUSHORTCUTS = 1 OR CREATESTARTMENUSHORTCUTS = &quot;Y&quot; OR CREATESTARTMENUSHORTCUTS = &quot;y&quot;)" />
<SetProperty Id="DESKTOPSHORTCUTS" Value="[CREATEDESKTOPSHORTCUTS]" After="RestoreSavedDesktopShortcutsValue" Sequence="first" Condition="CREATEDESKTOPSHORTCUTS" /> <SetProperty Id="DESKTOPSHORTCUTS" Value="" After="RestoreSavedDesktopShortcutsValue" Sequence="first" Condition="CREATEDESKTOPSHORTCUTS AND NOT (CREATEDESKTOPSHORTCUTS = 1 OR CREATEDESKTOPSHORTCUTS = &quot;Y&quot; OR CREATEDESKTOPSHORTCUTS = &quot;y&quot;)" />
</Fragment> </Fragment>
</Wix> </Wix>

View File

@ -49,4 +49,7 @@ This file contains the declaration of all the localizable strings.
<String Id="AnotherAppDialogTitle" Value="Cancel installation."/> <String Id="AnotherAppDialogTitle" Value="Cancel installation."/>
<String Id="AnotherAppDialogDescription" Value="The application is installed by self-installation method, please uninstall it first."/> <String Id="AnotherAppDialogDescription" Value="The application is installed by self-installation method, please uninstall it first."/>
<String Id="MyInstallDirDlgDesktopShortcuts" Value="Create desktop icon" />
<String Id="MyInstallDirDlgStartMenuShortcuts" Value="Create start menu shortcuts" />
</WixLocalization> </WixLocalization>

View File

@ -18,11 +18,11 @@
<!-- User Interface --> <!-- User Interface -->
<WixVariable Id="WixUILicenseRtf" Value="License.rtf" /> <WixVariable Id="WixUILicenseRtf" Value="License.rtf" />
<ui:WixUI Id="WixUI_InstallDir" InstallDirectory="INSTALLFOLDER" /> <ui:WixUI Id="UI_MyInstallDialog" InstallDirectory="INSTALLFOLDER_INNER" />
<UIRef Id="WixUI_ErrorProgressText" /> <UIRef Id="WixUI_ErrorProgressText" />
<InstallUISequence> <InstallUISequence>
<Show Dialog="AnotherAppDialog" Before="WelcomeDlg" Condition="Not installed AND APP_WINDOWS_INSTALLER=&quot;#0&quot;"/> <Show Dialog="UI_AnotherAppDialog" Before="WelcomeDlg" Condition="Not installed AND APP_WINDOWS_INSTALLER=&quot;#0&quot;"/>
</InstallUISequence> </InstallUISequence>
<InstallExecuteSequence> <InstallExecuteSequence>
@ -40,14 +40,17 @@
<Feature Id="App" Level="1" AllowAdvertise="no" Display="expand" Title="!(loc.F_App)" Description="!(loc.F_App_Desc)" AllowAbsent="no"> <Feature Id="App" Level="1" AllowAdvertise="no" Display="expand" Title="!(loc.F_App)" Description="!(loc.F_App_Desc)" AllowAbsent="no">
<ComponentGroupRef Id="Components" /> <ComponentGroupRef Id="Components" />
<ComponentRef Id="Product.Registry.InstallDir" /> <ComponentRef Id="Product.Registry.InstallFolder" />
<ComponentRef Id="Product.Registry.DefaultIcon" /> <ComponentRef Id="Product.Registry.DefaultIcon" />
<ComponentRef Id="Product.Registry.CommandPlay" /> <ComponentRef Id="Product.Registry.CommandPlay" />
<ComponentRef Id="Product.Registry.URLProtocol" /> <ComponentRef Id="Product.Registry.URLProtocol" />
<ComponentRef Id="Product.Registry.Command" /> <ComponentRef Id="Product.Registry.Command" />
<ComponentRef Id="Product.Registry.UninstallApp" /> <ComponentRef Id="Product.Registry.UninstallApp" />
<ComponentRef Id="App.StartMenu" /> <ComponentRef Id="App.StartMenu" />
<ComponentRef Id="Product.Registry.PersistedShortcutProperties" /> <ComponentRef Id="Product.Registry.PersistedStartMenuShortcutProperties1" />
<ComponentRef Id="Product.Registry.PersistedStartMenuShortcutProperties0" />
<ComponentRef Id="Product.Registry.PersistedDesktopShortcutProperties1" />
<ComponentRef Id="Product.Registry.PersistedDesktopShortcutProperties0" />
</Feature> </Feature>
<!--https://wixtoolset.org/docs/tools/wixext/wixui/#customizing-a-dialog-set--> <!--https://wixtoolset.org/docs/tools/wixext/wixui/#customizing-a-dialog-set-->

View File

@ -1,7 +1,7 @@
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> <Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
<Fragment> <Fragment>
<UI> <UI>
<Dialog Id="AnotherAppDialog" Width="370" Height="270" Title="!(loc.ExitDialog_Title)"> <Dialog Id="UI_AnotherAppDialog" Width="370" Height="270" Title="!(loc.ExitDialog_Title)">
<Control Id="Cancel" Type="PushButton" X="304" Y="243" Width="56" Height="17" Default="yes" Cancel="yes" Text="!(loc.WixUICancel)"> <Control Id="Cancel" Type="PushButton" X="304" Y="243" Width="56" Height="17" Default="yes" Cancel="yes" Text="!(loc.WixUICancel)">
<Publish Event="EndDialog" Value="ErrorAbort" /> <Publish Event="EndDialog" Value="ErrorAbort" />
</Control> </Control>

View File

@ -0,0 +1,31 @@
<!-- https://github.com/wixtoolset/wix/blob/ce73352b1fa1d4f9cded10a0ee410f2e786bd326/src/ext/UI/wixlib/InstallDirDlg.wxs -->
<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
<Fragment>
<UI>
<Dialog Id="MyInstallDirDlg" Width="370" Height="270" Title="!(loc.InstallDirDlg_Title)">
<Control Id="Next" Type="PushButton" X="236" Y="243" Width="56" Height="17" Default="yes" Text="!(loc.WixUINext)" />
<Control Id="Back" Type="PushButton" X="180" Y="243" Width="56" Height="17" Text="!(loc.WixUIBack)" />
<Control Id="Cancel" Type="PushButton" X="304" Y="243" Width="56" Height="17" Cancel="yes" Text="!(loc.WixUICancel)">
<Publish Event="SpawnDialog" Value="CancelDlg" />
</Control>
<Control Id="Description" Type="Text" X="25" Y="23" Width="280" Height="15" Transparent="yes" NoPrefix="yes" Text="!(loc.InstallDirDlgDescription)" />
<Control Id="Title" Type="Text" X="15" Y="6" Width="200" Height="15" Transparent="yes" NoPrefix="yes" Text="!(loc.InstallDirDlgTitle)" />
<Control Id="BannerBitmap" Type="Bitmap" X="0" Y="0" Width="370" Height="44" TabSkip="no" Text="!(loc.InstallDirDlgBannerBitmap)" />
<Control Id="BannerLine" Type="Line" X="0" Y="44" Width="370" Height="0" />
<Control Id="BottomLine" Type="Line" X="0" Y="234" Width="370" Height="0" />
<Control Id="FolderLabel" Type="Text" X="20" Y="60" Width="290" Height="18" NoPrefix="yes" Text="!(loc.InstallDirDlgFolderLabel)" />
<Control Id="Folder" Type="PathEdit" X="20" Y="80" Width="320" Height="18" Property="WIXUI_INSTALLDIR" Indirect="yes" />
<Control Id="ChangeFolder" Type="PushButton" X="20" Y="100" Width="56" Height="17" Text="!(loc.InstallDirDlgChange)" />
<Control Id="ChkBoxDesktopShortcuts" Type="CheckBox" X="20" Y="140" Width="290" Height="17" Property="DESKTOPSHORTCUTS" CheckBoxValue="1" Text="!(loc.MyInstallDirDlgDesktopShortcuts)" />
<Control Id="ChkBoxStartMenuShortcuts" Type="CheckBox" X="20" Y="160" Width="290" Height="17" Property="STARTMENUSHORTCUTS" CheckBoxValue="1" Text="!(loc.MyInstallDirDlgStartMenuShortcuts)" />
</Dialog>
</UI>
</Fragment>
</Wix>

View File

@ -0,0 +1,87 @@
<!-- From https://github.com/wixtoolset/wix/blob/ce73352b1fa1d4f9cded10a0ee410f2e786bd326/src/ext/UI/wixlib/WixUI_InstallDir.wxs -->
<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
<!--
First-time install dialog sequence:
- WixUI_WelcomeDlg
- WixUI_LicenseAgreementDlg
- WixUI_InstallDirDlg
- WixUI_VerifyReadyDlg
- WixUI_DiskCostDlg
Maintenance dialog sequence:
- WixUI_MaintenanceWelcomeDlg
- WixUI_MaintenanceTypeDlg
- WixUI_InstallDirDlg
- WixUI_VerifyReadyDlg
Patch dialog sequence:
- WixUI_WelcomeDlg
- WixUI_VerifyReadyDlg
-->
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:ui="http://wixtoolset.org/schemas/v4/wxs/ui">
<?foreach WIXUIARCH in X86;X64;A64 ?>
<Fragment>
<UI Id="UI_MyInstallDialog_$(WIXUIARCH)">
<Publish Dialog="LicenseAgreementDlg" Control="Print" Event="DoAction" Value="WixUIPrintEula_$(WIXUIARCH)" />
<Publish Dialog="BrowseDlg" Control="OK" Event="DoAction" Value="WixUIValidatePath_$(WIXUIARCH)" Order="3" Condition="NOT WIXUI_DONTVALIDATEPATH" />
<Publish Dialog="MyInstallDirDlg" Control="Next" Event="DoAction" Value="WixUIValidatePath_$(WIXUIARCH)" Order="2" Condition="NOT WIXUI_DONTVALIDATEPATH" />
</UI>
<UIRef Id="UI_MyInstallDialog" />
</Fragment>
<?endforeach?>
<Fragment>
<UI Id="file UI_MyInstallDialog">
<TextStyle Id="WixUI_Font_Normal" FaceName="Tahoma" Size="8" />
<TextStyle Id="WixUI_Font_Bigger" FaceName="Tahoma" Size="12" />
<TextStyle Id="WixUI_Font_Title" FaceName="Tahoma" Size="9" Bold="yes" />
<Property Id="DefaultUIFont" Value="WixUI_Font_Normal" />
<DialogRef Id="BrowseDlg" />
<DialogRef Id="DiskCostDlg" />
<DialogRef Id="ErrorDlg" />
<DialogRef Id="FatalError" />
<DialogRef Id="FilesInUse" />
<DialogRef Id="MsiRMFilesInUse" />
<DialogRef Id="PrepareDlg" />
<DialogRef Id="ProgressDlg" />
<DialogRef Id="ResumeDlg" />
<DialogRef Id="UserExit" />
<Publish Dialog="BrowseDlg" Control="OK" Event="SpawnDialog" Value="InvalidDirDlg" Order="4" Condition="NOT WIXUI_DONTVALIDATEPATH AND WIXUI_INSTALLDIR_VALID&lt;&gt;&quot;1&quot;" />
<Publish Dialog="ExitDialog" Control="Finish" Event="EndDialog" Value="Return" Order="999" />
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="LicenseAgreementDlg" Condition="NOT Installed" />
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg" Condition="Installed AND PATCH" />
<Publish Dialog="LicenseAgreementDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg" />
<Publish Dialog="LicenseAgreementDlg" Control="Next" Event="NewDialog" Value="MyInstallDirDlg" Condition="LicenseAccepted = &quot;1&quot;" />
<Publish Dialog="MyInstallDirDlg" Control="Back" Event="NewDialog" Value="LicenseAgreementDlg" />
<Publish Dialog="MyInstallDirDlg" Control="Next" Event="SetTargetPath" Value="[WIXUI_INSTALLDIR]" Order="1" />
<Publish Dialog="MyInstallDirDlg" Control="Next" Event="SpawnDialog" Value="InvalidDirDlg" Order="3" Condition="NOT WIXUI_DONTVALIDATEPATH AND WIXUI_INSTALLDIR_VALID&lt;&gt;&quot;1&quot;" />
<Publish Dialog="MyInstallDirDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg" Order="4" Condition="WIXUI_DONTVALIDATEPATH OR WIXUI_INSTALLDIR_VALID=&quot;1&quot;" />
<Publish Dialog="MyInstallDirDlg" Control="ChangeFolder" Property="_BrowseProperty" Value="[WIXUI_INSTALLDIR]" Order="1" />
<Publish Dialog="MyInstallDirDlg" Control="ChangeFolder" Event="SpawnDialog" Value="BrowseDlg" Order="2" />
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="MyInstallDirDlg" Order="1" Condition="NOT Installed" />
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="MaintenanceTypeDlg" Order="2" Condition="Installed AND NOT PATCH" />
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg" Order="2" Condition="Installed AND PATCH" />
<Publish Dialog="MaintenanceWelcomeDlg" Control="Next" Event="NewDialog" Value="MaintenanceTypeDlg" />
<Publish Dialog="MaintenanceTypeDlg" Control="RepairButton" Event="NewDialog" Value="VerifyReadyDlg" />
<Publish Dialog="MaintenanceTypeDlg" Control="RemoveButton" Event="NewDialog" Value="VerifyReadyDlg" />
<Publish Dialog="MaintenanceTypeDlg" Control="Back" Event="NewDialog" Value="MaintenanceWelcomeDlg" />
<Property Id="ARPNOMODIFY" Value="1" />
</UI>
<UIRef Id="WixUI_Common" />
</Fragment>
</Wix>

View File

@ -90,7 +90,7 @@ def make_parser():
def read_lines_and_start_index(file_path, tag_start, tag_end): def read_lines_and_start_index(file_path, tag_start, tag_end):
with open(file_path, "r") as f: with open(file_path, "r", encoding="utf-8") as f:
lines = f.readlines() lines = f.readlines()
index_start = -1 index_start = -1
index_end = -1 index_end = -1
@ -180,11 +180,11 @@ def gen_pre_vars(args, dist_dir):
def replace_app_name_in_langs(app_name): def replace_app_name_in_langs(app_name):
langs_dir = Path(sys.argv[0]).parent.joinpath("Package/Language") langs_dir = Path(sys.argv[0]).parent.joinpath("Package/Language")
for file_path in langs_dir.glob("*.wxl"): for file_path in langs_dir.glob("*.wxl"):
with open(file_path, "r") as f: with open(file_path, "r", encoding="utf-8") as f:
lines = f.readlines() lines = f.readlines()
for i, line in enumerate(lines): for i, line in enumerate(lines):
lines[i] = line.replace("RustDesk", app_name) lines[i] = line.replace("RustDesk", app_name)
with open(file_path, "w") as f: with open(file_path, "w", encoding="utf-8") as f:
f.writelines(lines) f.writelines(lines)
@ -301,7 +301,7 @@ def gen_custom_ARPSYSTEMCOMPONENT_True(args, dist_dir):
f'{indent}<RegistryValue Type="string" Name="DisplayName" Value="{args.app_name}" />\n' f'{indent}<RegistryValue Type="string" Name="DisplayName" Value="{args.app_name}" />\n'
) )
lines_new.append( lines_new.append(
f'{indent}<RegistryValue Type="string" Name="DisplayIcon" Value="[INSTALLFOLDER]{args.app_name}.exe" />\n' f'{indent}<RegistryValue Type="string" Name="DisplayIcon" Value="[INSTALLFOLDER_INNER]{args.app_name}.exe" />\n'
) )
lines_new.append( lines_new.append(
f'{indent}<RegistryValue Type="string" Name="DisplayVersion" Value="{g_version}" />\n' f'{indent}<RegistryValue Type="string" Name="DisplayVersion" Value="{g_version}" />\n'
@ -314,7 +314,7 @@ def gen_custom_ARPSYSTEMCOMPONENT_True(args, dist_dir):
f'{indent}<RegistryValue Type="string" Name="InstallDate" Value="{installDate}" />\n' f'{indent}<RegistryValue Type="string" Name="InstallDate" Value="{installDate}" />\n'
) )
lines_new.append( lines_new.append(
f'{indent}<RegistryValue Type="string" Name="InstallLocation" Value="[INSTALLFOLDER]" />\n' f'{indent}<RegistryValue Type="string" Name="InstallLocation" Value="[INSTALLFOLDER_INNER]" />\n'
) )
lines_new.append( lines_new.append(
f'{indent}<RegistryValue Type="string" Name="InstallSource" Value="[InstallSource]" />\n' f'{indent}<RegistryValue Type="string" Name="InstallSource" Value="[InstallSource]" />\n'
@ -420,7 +420,7 @@ def gen_content_between_tags(filename, tag_start, tag_end, func):
func(lines, index_start) func(lines, index_start)
with open(target_file, "w") as f: with open(target_file, "w", encoding="utf-8") as f:
f.writelines(lines) f.writelines(lines)
return True return True
@ -480,19 +480,19 @@ def update_license_file(app_name):
if app_name == "RustDesk": if app_name == "RustDesk":
return return
license_file = Path(sys.argv[0]).parent.joinpath("Package/License.rtf") license_file = Path(sys.argv[0]).parent.joinpath("Package/License.rtf")
with open(license_file, "r") as f: with open(license_file, "r", encoding="utf-8") as f:
license_content = f.read() license_content = f.read()
license_content = license_content.replace("website rustdesk.com and other ", "") license_content = license_content.replace("website rustdesk.com and other ", "")
license_content = license_content.replace("RustDesk", app_name) license_content = license_content.replace("RustDesk", app_name)
license_content = re.sub("Purslane Ltd", app_name, license_content, flags=re.IGNORECASE) license_content = re.sub("Purslane Ltd", app_name, license_content, flags=re.IGNORECASE)
with open(license_file, "w") as f: with open(license_file, "w", encoding="utf-8") as f:
f.write(license_content) f.write(license_content)
def replace_component_guids_in_wxs(): def replace_component_guids_in_wxs():
langs_dir = Path(sys.argv[0]).parent.joinpath("Package") langs_dir = Path(sys.argv[0]).parent.joinpath("Package")
for file_path in langs_dir.glob("**/*.wxs"): for file_path in langs_dir.glob("**/*.wxs"):
with open(file_path, "r") as f: with open(file_path, "r", encoding="utf-8") as f:
lines = f.readlines() lines = f.readlines()
# <Component Id="Product.Registry.DefaultIcon" Guid="6DBF2690-0955-4C6A-940F-634DDA503F49"> # <Component Id="Product.Registry.DefaultIcon" Guid="6DBF2690-0955-4C6A-940F-634DDA503F49">
@ -501,7 +501,7 @@ def replace_component_guids_in_wxs():
if match: if match:
lines[i] = re.sub(r'Guid="[^"]+"', f'Guid="{uuid.uuid4()}"', line) lines[i] = re.sub(r'Guid="[^"]+"', f'Guid="{uuid.uuid4()}"', line)
with open(file_path, "w") as f: with open(file_path, "w", encoding="utf-8") as f:
f.writelines(lines) f.writelines(lines)