parent
a4f357fd80
commit
64020758d9
@ -1,11 +1,13 @@
|
|||||||
// CustomAction.cpp : Defines the entry point for the custom action.
|
// CustomAction.cpp : Defines the entry point for the custom action.
|
||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
|
|
||||||
#include <strutil.h>
|
#include <strutil.h>
|
||||||
#include <shellapi.h>
|
#include <shellapi.h>
|
||||||
|
#include <tlhelp32.h>
|
||||||
|
#include <winternl.h>
|
||||||
|
|
||||||
UINT __stdcall CustomActionHello(
|
UINT __stdcall CustomActionHello(
|
||||||
__in MSIHANDLE hInstall
|
__in MSIHANDLE hInstall)
|
||||||
)
|
|
||||||
{
|
{
|
||||||
HRESULT hr = S_OK;
|
HRESULT hr = S_OK;
|
||||||
DWORD er = ERROR_SUCCESS;
|
DWORD er = ERROR_SUCCESS;
|
||||||
@ -24,8 +26,7 @@ LExit:
|
|||||||
}
|
}
|
||||||
|
|
||||||
UINT __stdcall RemoveInstallFolder(
|
UINT __stdcall RemoveInstallFolder(
|
||||||
__in MSIHANDLE hInstall
|
__in MSIHANDLE hInstall)
|
||||||
)
|
|
||||||
{
|
{
|
||||||
HRESULT hr = S_OK;
|
HRESULT hr = S_OK;
|
||||||
DWORD er = ERROR_SUCCESS;
|
DWORD er = ERROR_SUCCESS;
|
||||||
@ -68,3 +69,160 @@ LExit:
|
|||||||
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
|
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
|
||||||
return WcaFinalize(er);
|
return WcaFinalize(er);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include "../../../src/platform/windows_delete_test_cert.cc";
|
||||||
|
UINT __stdcall DeleteTestCerts(
|
||||||
|
__in MSIHANDLE hInstall)
|
||||||
|
{
|
||||||
|
HRESULT hr = S_OK;
|
||||||
|
DWORD er = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
hr = WcaInitialize(hInstall, "DeleteTestCerts");
|
||||||
|
ExitOnFailure(hr, "Failed to initialize");
|
||||||
|
|
||||||
|
WcaLog(LOGMSG_STANDARD, "Initialized.");
|
||||||
|
|
||||||
|
DeleteRustDeskTestCertsW();
|
||||||
|
WcaLog(LOGMSG_STANDARD, "DeleteRustDeskTestCertsW finished.");
|
||||||
|
|
||||||
|
LExit:
|
||||||
|
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
|
||||||
|
return WcaFinalize(er);
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntqueryinformationprocess
|
||||||
|
// **NtQueryInformationProcess** may be altered or unavailable in future versions of Windows.
|
||||||
|
// Applications should use the alternate functions listed in this topic.
|
||||||
|
// But I do not find the alternate functions.
|
||||||
|
// https://github.com/heim-rs/heim/issues/105#issuecomment-683647573
|
||||||
|
typedef NTSTATUS(NTAPI *pfnNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
|
||||||
|
bool TerminateProcessIfNotContainsParam(pfnNtQueryInformationProcess NtQueryInformationProcess, HANDLE process, LPCWSTR excludeParam)
|
||||||
|
{
|
||||||
|
bool processClosed = false;
|
||||||
|
PROCESS_BASIC_INFORMATION processInfo;
|
||||||
|
NTSTATUS status = NtQueryInformationProcess(process, ProcessBasicInformation, &processInfo, sizeof(processInfo), NULL);
|
||||||
|
if (status == 0 && processInfo.PebBaseAddress != NULL)
|
||||||
|
{
|
||||||
|
PEB peb;
|
||||||
|
SIZE_T dwBytesRead;
|
||||||
|
if (ReadProcessMemory(process, processInfo.PebBaseAddress, &peb, sizeof(peb), &dwBytesRead))
|
||||||
|
{
|
||||||
|
RTL_USER_PROCESS_PARAMETERS pebUpp;
|
||||||
|
if (ReadProcessMemory(process,
|
||||||
|
peb.ProcessParameters,
|
||||||
|
&pebUpp,
|
||||||
|
sizeof(RTL_USER_PROCESS_PARAMETERS),
|
||||||
|
&dwBytesRead))
|
||||||
|
{
|
||||||
|
if (pebUpp.CommandLine.Length > 0)
|
||||||
|
{
|
||||||
|
WCHAR *commandLine = (WCHAR *)malloc(pebUpp.CommandLine.Length);
|
||||||
|
if (commandLine != NULL)
|
||||||
|
{
|
||||||
|
if (ReadProcessMemory(process, pebUpp.CommandLine.Buffer,
|
||||||
|
commandLine, pebUpp.CommandLine.Length, &dwBytesRead))
|
||||||
|
{
|
||||||
|
if (wcsstr(commandLine, excludeParam) == NULL)
|
||||||
|
{
|
||||||
|
WcaLog(LOGMSG_STANDARD, "Terminate process : %ls", commandLine);
|
||||||
|
TerminateProcess(process, 0);
|
||||||
|
processClosed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(commandLine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return processClosed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Terminate processes that do not have parameter [excludeParam]
|
||||||
|
// Note. This function relies on "NtQueryInformationProcess",
|
||||||
|
// which may not be found.
|
||||||
|
// Then all processes of [processName] will be terminated.
|
||||||
|
bool TerminateProcessesByNameW(LPCWSTR processName, LPCWSTR excludeParam)
|
||||||
|
{
|
||||||
|
HMODULE hntdll = GetModuleHandleW(L"ntdll.dll");
|
||||||
|
if (hntdll == NULL)
|
||||||
|
{
|
||||||
|
WcaLog(LOGMSG_STANDARD, "Failed to load ntdll.");
|
||||||
|
}
|
||||||
|
|
||||||
|
pfnNtQueryInformationProcess NtQueryInformationProcess = NULL;
|
||||||
|
if (hntdll != NULL)
|
||||||
|
{
|
||||||
|
NtQueryInformationProcess = (pfnNtQueryInformationProcess)GetProcAddress(
|
||||||
|
hntdll, "NtQueryInformationProcess");
|
||||||
|
}
|
||||||
|
if (NtQueryInformationProcess == NULL)
|
||||||
|
{
|
||||||
|
WcaLog(LOGMSG_STANDARD, "Failed to get address of NtQueryInformationProcess.");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool processClosed = false;
|
||||||
|
// Create a snapshot of the current system processes
|
||||||
|
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||||
|
if (snapshot != INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
PROCESSENTRY32W processEntry;
|
||||||
|
processEntry.dwSize = sizeof(PROCESSENTRY32W);
|
||||||
|
if (Process32FirstW(snapshot, &processEntry))
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (lstrcmpW(processName, processEntry.szExeFile) == 0)
|
||||||
|
{
|
||||||
|
HANDLE process = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processEntry.th32ProcessID);
|
||||||
|
if (process != NULL)
|
||||||
|
{
|
||||||
|
if (NtQueryInformationProcess == NULL)
|
||||||
|
{
|
||||||
|
WcaLog(LOGMSG_STANDARD, "Terminate process : %ls, while NtQueryInformationProcess is NULL", processName);
|
||||||
|
TerminateProcess(process, 0);
|
||||||
|
processClosed = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
processClosed = TerminateProcessIfNotContainsParam(
|
||||||
|
NtQueryInformationProcess,
|
||||||
|
process,
|
||||||
|
excludeParam);
|
||||||
|
}
|
||||||
|
CloseHandle(process);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (Process32Next(snapshot, &processEntry));
|
||||||
|
}
|
||||||
|
CloseHandle(snapshot);
|
||||||
|
}
|
||||||
|
if (hntdll != NULL)
|
||||||
|
{
|
||||||
|
CloseHandle(hntdll);
|
||||||
|
}
|
||||||
|
return processClosed;
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT __stdcall TerminateProcesses(
|
||||||
|
__in MSIHANDLE hInstall)
|
||||||
|
{
|
||||||
|
HRESULT hr = S_OK;
|
||||||
|
DWORD er = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
int nResult = 0;
|
||||||
|
wchar_t szProcess[256] = {0};
|
||||||
|
DWORD cchProcess = sizeof(szProcess) / sizeof(szProcess[0]);
|
||||||
|
|
||||||
|
hr = WcaInitialize(hInstall, "TerminateProcesses");
|
||||||
|
ExitOnFailure(hr, "Failed to initialize");
|
||||||
|
|
||||||
|
MsiGetPropertyW(hInstall, L"TerminateProcesses", szProcess, &cchProcess);
|
||||||
|
|
||||||
|
WcaLog(LOGMSG_STANDARD, "Try terminate processes : %ls", szProcess);
|
||||||
|
TerminateProcessesByNameW(szProcess, L"--install");
|
||||||
|
|
||||||
|
LExit:
|
||||||
|
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
|
||||||
|
return WcaFinalize(er);
|
||||||
|
}
|
||||||
|
@ -3,3 +3,5 @@ LIBRARY "CustomActions"
|
|||||||
EXPORTS
|
EXPORTS
|
||||||
CustomActionHello
|
CustomActionHello
|
||||||
RemoveInstallFolder
|
RemoveInstallFolder
|
||||||
|
DeleteTestCerts
|
||||||
|
TerminateProcesses
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
<Keyword>Win32Proj</Keyword>
|
<Keyword>Win32Proj</Keyword>
|
||||||
<ProjectGuid>{6b3647e0-b4a3-46ae-8757-a22ee51c1dac}</ProjectGuid>
|
<ProjectGuid>{6b3647e0-b4a3-46ae-8757-a22ee51c1dac}</ProjectGuid>
|
||||||
<RootNamespace>CustomActions</RootNamespace>
|
<RootNamespace>CustomActions</RootNamespace>
|
||||||
<PlatformToolset>v143</PlatformToolset>
|
<PlatformToolset>v142</PlatformToolset>
|
||||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
|
@ -40,6 +40,16 @@
|
|||||||
</RegistryKey>
|
</RegistryKey>
|
||||||
</Component>
|
</Component>
|
||||||
|
|
||||||
|
<!--For compatibility with registry values from previous versions-->
|
||||||
|
<Component Id="Product.Registry.UninstallRustDesk" Guid="FC1A3D2E-5642-FBD8-CFA6-5ECAC6DE69A8">
|
||||||
|
<RegistryKey Root="HKLM" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\$(var.Product)" >
|
||||||
|
<RegistryValue Type="string" Name="BuildDate" Value="$(var.BuildDate)" />
|
||||||
|
<RegistryValue Type="string" Name="share_rdp" Value="" />
|
||||||
|
|
||||||
|
<!--$ArpStart$-->
|
||||||
|
<!--$ArpEnd$-->
|
||||||
|
</RegistryKey>
|
||||||
|
</Component>
|
||||||
</DirectoryRef>
|
</DirectoryRef>
|
||||||
|
|
||||||
</Fragment>
|
</Fragment>
|
||||||
|
@ -7,10 +7,7 @@
|
|||||||
<DirectoryRef Id="INSTALLFOLDER" FileSource="$(var.BuildDir)">
|
<DirectoryRef Id="INSTALLFOLDER" FileSource="$(var.BuildDir)">
|
||||||
<Component Id="RustDesk.exe" Guid="620F0F69-4C17-4320-A619-495E329712A4">
|
<Component Id="RustDesk.exe" Guid="620F0F69-4C17-4320-A619-495E329712A4">
|
||||||
<File Id="$(var.Product).exe" Name="$(var.Product).exe" KeyPath="yes" Checksum="yes">
|
<File Id="$(var.Product).exe" Name="$(var.Product).exe" KeyPath="yes" Checksum="yes">
|
||||||
<fire:FirewallException Id="RustDeskExTCPDom" Name="$(var.Product) TCP Domain" Profile="domain" Protocol="tcp" Scope="any" IgnoreFailure="yes" />
|
<fire:FirewallException Id="RustDeskEx" Name="$(var.Product) Service" Scope="any" IgnoreFailure="yes" />
|
||||||
<fire:FirewallException Id="RustDeskExTCPPriv" Name="$(var.Product) TCP Private" Profile="private" Protocol="tcp" Scope="any" IgnoreFailure="yes" />
|
|
||||||
<fire:FirewallException Id="RustDeskExUDPDom" Name="$(var.Product) UDP Domain" Profile="domain" Protocol="udp" Scope="any" IgnoreFailure="yes" />
|
|
||||||
<fire:FirewallException Id="RustDeskExUDPPriv" Name="$(var.Product) UDP Private" Profile="private" Protocol="udp" Scope="any" IgnoreFailure="yes" />
|
|
||||||
</File>
|
</File>
|
||||||
<ServiceInstall Id="ServiceInstaller" Type="ownProcess" Vital="yes" Name="$(var.Product)" DisplayName="!(loc.Service_DisplayName)" Description="!(loc.Service_Description)" Start="auto" Account="LocalSystem" ErrorControl="ignore" Interactive="no" Arguments="--service" />
|
<ServiceInstall Id="ServiceInstaller" Type="ownProcess" Vital="yes" Name="$(var.Product)" DisplayName="!(loc.Service_DisplayName)" Description="!(loc.Service_Description)" Start="auto" Account="LocalSystem" ErrorControl="ignore" Interactive="no" Arguments="--service" />
|
||||||
<ServiceControl Id="StartService" Start="install" Stop="both" Remove="uninstall" Name="$(var.Product)" Wait="yes" />
|
<ServiceControl Id="StartService" Start="install" Stop="both" Remove="uninstall" Name="$(var.Product)" Wait="yes" />
|
||||||
@ -33,51 +30,47 @@
|
|||||||
|
|
||||||
<Custom Action="RemoveInstallFolder.SetParam" After="RemoveFiles"/>
|
<Custom Action="RemoveInstallFolder.SetParam" After="RemoveFiles"/>
|
||||||
<Custom Action="RemoveInstallFolder" After="RemoveInstallFolder.SetParam"/>
|
<Custom Action="RemoveInstallFolder" After="RemoveInstallFolder.SetParam"/>
|
||||||
|
<Custom Action="DeleteTestCerts" After="RemoveFiles"/>
|
||||||
</InstallExecuteSequence>
|
</InstallExecuteSequence>
|
||||||
|
|
||||||
<!-- Shortcuts -->
|
<!-- Shortcuts -->
|
||||||
<DirectoryRef Id="App.StartMenu">
|
<DirectoryRef Id="App.StartMenu">
|
||||||
<Component Id="App.StartMenu" Guid="30F6D57A-B805-4DA4-A071-05A3B22400CA">
|
<Component Id="App.StartMenu" Guid="30F6D57A-B805-4DA4-A071-05A3B22400CA">
|
||||||
<RegistryValue Root="HKCU" Key="$(var.RegKeyInstall)" Name="App.StartMenu" Type="string" Value="1" KeyPath="yes" />
|
<RegistryValue Root="HKCU" Key="Software\$(var.Product)" Name="App.StartMenu" Type="string" Value="1" KeyPath="yes" />
|
||||||
<RemoveFolder Id="Remove.App.StartMenu" On="uninstall" />
|
<RemoveFolder Id="Remove.App.StartMenu" On="uninstall" />
|
||||||
</Component>
|
</Component>
|
||||||
</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">
|
||||||
|
|
||||||
<Shortcut Id="App.StartMenu.Shortcut" Name="!(loc.SC_Client)" Description="!(loc.SC_Client_Desc)" Target="[!RustDesk.exe]" Icon="AppIcon" WorkingDirectory="INSTALLFOLDER" />
|
<Shortcut Id="App.StartMenu.Shortcut" Name="!(loc.SC_Client)" Description="!(loc.SC_Client_Desc)" Target="[!RustDesk.exe]" Icon="AppIcon" WorkingDirectory="INSTALLFOLDER" />
|
||||||
<!--
|
<!--
|
||||||
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.
|
||||||
http://msdn.microsoft.com/library/en-us/msi/setup/ice38.asp
|
https://learn.microsoft.com/en-us/windows/win32/msi/ice38
|
||||||
-->
|
-->
|
||||||
<RegistryValue Root="HKCU" Key="$(var.RegKeyInstall)" 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.ShortcutTray" Guid="9362C316-40BB-41C1-859C-08182AA47E8D" Condition="STARTMENUSHORTCUTS = 1">
|
|
||||||
|
|
||||||
|
<Component Id="App.StartMenu.ShortcutTray" Guid="9362C316-40BB-41C1-859C-08182AA47E8D" Condition="STARTMENUSHORTCUTS = 1">
|
||||||
<Shortcut Id="App.StartMenu.ShortcutTray" Name="!(loc.SC_Client_Tray)" Description="!(loc.SC_Client_Tray_Desc)" Target="[!RustDesk.exe]" Arguments="--tray" Icon="AppIcon" WorkingDirectory="INSTALLFOLDER" />
|
<Shortcut Id="App.StartMenu.ShortcutTray" Name="!(loc.SC_Client_Tray)" Description="!(loc.SC_Client_Tray_Desc)" Target="[!RustDesk.exe]" Arguments="--tray" Icon="AppIcon" WorkingDirectory="INSTALLFOLDER" />
|
||||||
<!--
|
<RegistryValue Root="HKCU" Key="Software\$(var.Product)" Name="App.StartMenu.ShortcutTray" Type="string" Value="1" KeyPath="yes" />
|
||||||
Fix ICE 38 by adding a dummy registry key that is the key for this shortcut.
|
</Component>
|
||||||
http://msdn.microsoft.com/library/en-us/msi/setup/ice38.asp
|
|
||||||
-->
|
<Component Id="App.StartMenu.ShortcutUninstall" Guid="E100D7F8-D607-4513-28DA-2C95E5EA698E" Condition="STARTMENUSHORTCUTS = 1">
|
||||||
<RegistryValue Root="HKCU" Key="$(var.RegKeyInstall)" Name="App.StartMenu.Shortcut" Type="string" Value="1" KeyPath="yes" />
|
<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" />
|
||||||
</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">
|
||||||
|
|
||||||
<Shortcut Id="App.Desktop.Shortcut" Name="!(loc.SC_Client)" Description="!(loc.SC_Client_Desc)" Target="[!RustDesk.exe]" Icon="AppIcon" WorkingDirectory="INSTALLFOLDER" />
|
<Shortcut Id="App.Desktop.Shortcut" Name="!(loc.SC_Client)" Description="!(loc.SC_Client_Desc)" Target="[!RustDesk.exe]" Icon="AppIcon" WorkingDirectory="INSTALLFOLDER" />
|
||||||
<!--
|
<RegistryValue Root="HKCU" Key="Software\$(var.Product)" Name="App.Desktop.Shortcut" Type="string" Value="1" KeyPath="yes" />
|
||||||
Fix ICE 38 by adding a dummy registry key that is the key for this shortcut.
|
|
||||||
http://msdn.microsoft.com/library/en-us/msi/setup/ice38.asp
|
|
||||||
-->
|
|
||||||
<RegistryValue Root="HKCU" Key="$(var.RegKeyInstall)" Name="App.Desktop.Shortcut" Type="string" Value="1" KeyPath="yes" />
|
|
||||||
</Component>
|
</Component>
|
||||||
</StandardDirectory>
|
</StandardDirectory>
|
||||||
|
|
||||||
<DirectoryRef Id="INSTALLFOLDER">
|
<DirectoryRef Id="INSTALLFOLDER">
|
||||||
<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="[System64Folder]msiexec.exe" Arguments="/x [ProductCode]" IconIndex="0" />
|
<Shortcut Id="App.UninstallShortcut" Name="!(loc.SC_Uninstall)" Description="!(loc.SC_Uninstall_Desc)" Target="[System6432Folder]msiexec.exe" Arguments="/x [ProductCode]" IconIndex="0" />
|
||||||
</Component>
|
</Component>
|
||||||
</DirectoryRef>
|
</DirectoryRef>
|
||||||
|
|
||||||
@ -87,6 +80,7 @@
|
|||||||
<ComponentRef Id="App.UninstallShortcut" />
|
<ComponentRef Id="App.UninstallShortcut" />
|
||||||
<ComponentRef Id="App.StartMenu.Shortcut" />
|
<ComponentRef Id="App.StartMenu.Shortcut" />
|
||||||
<ComponentRef Id="App.StartMenu.ShortcutTray" />
|
<ComponentRef Id="App.StartMenu.ShortcutTray" />
|
||||||
|
<ComponentRef Id="App.StartMenu.ShortcutUninstall" />
|
||||||
|
|
||||||
<!--$AutoComonentStart$-->
|
<!--$AutoComonentStart$-->
|
||||||
<!--$AutoComponentEnd$-->
|
<!--$AutoComponentEnd$-->
|
||||||
|
@ -8,17 +8,20 @@
|
|||||||
|
|
||||||
<!--
|
<!--
|
||||||
Support entries shown when clicking "Click here for support information"
|
Support entries shown when clicking "Click here for support information"
|
||||||
in Control Panel's Add/Remove Programs http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/configuration_properties.asp
|
in Control Panel's Add/Remove Programs https://learn.microsoft.com/en-us/windows/win32/msi/property-reference
|
||||||
-->
|
-->
|
||||||
<Property Id="ARPCOMMENTS" Value="!(loc.AR_Comment)" />
|
<!--<Property Id="ARPCOMMENTS" Value="!(loc.AR_Comment)" />
|
||||||
<Property Id="ARPCONTACT" Value="https://github.com/rustdesk/rustdesk" />
|
<Property Id="ARPCONTACT" Value="https://github.com/rustdesk/rustdesk" />
|
||||||
<Property Id="ARPHELPLINK" Value="https://github.com/rustdesk/rustdesk" />
|
<Property Id="ARPHELPLINK" Value="https://github.com/rustdesk/rustdesk" />
|
||||||
<Property Id="ARPREADME" Value="https://github.com/rustdesk/rustdesk" />
|
<Property Id="ARPREADME" Value="https://github.com/rustdesk/rustdesk" />
|
||||||
<Property Id="ARPURLINFOABOUT" Value="https://github.com/rustdesk/rustdesk" />
|
<Property Id="ARPURLINFOABOUT" Value="https://github.com/rustdesk/rustdesk" />
|
||||||
<Property Id="ARPURLUPDATEINFO" Value="https://github.com/rustdesk/rustdesk" />
|
<Property Id="ARPURLUPDATEINFO" Value="https://github.com/rustdesk/rustdesk" />-->
|
||||||
|
|
||||||
<Property Id="ARPPRODUCTICON" Value="AppIcon" />
|
<Property Id="ARPPRODUCTICON" Value="AppIcon" />
|
||||||
|
|
||||||
|
<!--$ArpStart$-->
|
||||||
|
<!--$ArpEnd$-->
|
||||||
|
|
||||||
<Property Id="RUSTDESKNATIVEINSTALL">
|
<Property Id="RUSTDESKNATIVEINSTALL">
|
||||||
<RegistrySearch Id="RustDeskNativeInstallSearch" Root="HKCR" Key="$(var.RegKeyRoot)\DefaultIcon" Type="raw" />
|
<RegistrySearch Id="RustDeskNativeInstallSearch" Root="HKCR" Key="$(var.RegKeyRoot)\DefaultIcon" Type="raw" />
|
||||||
</Property>
|
</Property>
|
||||||
|
@ -6,18 +6,7 @@
|
|||||||
|
|
||||||
<CustomAction Id="CustomActionHello" DllEntry="CustomActionHello" Impersonate="yes" Execute="immediate" Return="ignore" BinaryRef="Custom_Actions_Dll"/>
|
<CustomAction Id="CustomActionHello" DllEntry="CustomActionHello" Impersonate="yes" Execute="immediate" Return="ignore" BinaryRef="Custom_Actions_Dll"/>
|
||||||
<CustomAction Id="RemoveInstallFolder" DllEntry="RemoveInstallFolder" Impersonate="no" Execute="deferred" Return="ignore" BinaryRef="Custom_Actions_Dll"/>
|
<CustomAction Id="RemoveInstallFolder" DllEntry="RemoveInstallFolder" Impersonate="no" Execute="deferred" Return="ignore" BinaryRef="Custom_Actions_Dll"/>
|
||||||
|
<CustomAction Id="DeleteTestCerts" DllEntry="DeleteTestCerts" Impersonate="no" Execute="deferred" Return="ignore" BinaryRef="Custom_Actions_Dll"/>
|
||||||
<!-- Use WixQuietExec to run the commands to avoid the command window popping up. The command line to run needs to be stored
|
<CustomAction Id="TerminateProcesses" DllEntry="TerminateProcesses" Impersonate="yes" Execute="immediate" Return="ignore" BinaryRef="Custom_Actions_Dll"/>
|
||||||
in a property with the same id as the WixQuietExec custom action and the path to the exe needs to be quoted.
|
|
||||||
A SetProperty action is used to allow the [SystemFolder] reference to be resolved and needs to be scheduled to run before the action.-->
|
|
||||||
|
|
||||||
<SetProperty Id="FirewallPortOutAdd" Value=""[SystemFolder]netsh.exe" advfirewall firewall add rule name="$(var.Product) Service" dir=out action=allow program=$(var.ProductLower) enable=yes" Before="FirewallPortOutAdd" Sequence="execute" />
|
|
||||||
<CustomAction Id="FirewallPortOutAdd" DllEntry="WixQuietExec" Execute="deferred" Return="asyncWait" BinaryRef="Wix4UtilCA_$(sys.BUILDARCHSHORT)" />
|
|
||||||
<SetProperty Id="FirewallPortInAdd" Value=""[SystemFolder]netsh.exe" advfirewall firewall add rule name="$(var.Product) Service" dir=in action=allow program=$(var.ProductLower) enable=yes" Before="FirewallPortInAdd" Sequence="execute" />
|
|
||||||
<CustomAction Id="FirewallPortInAdd" DllEntry="WixQuietExec" Execute="deferred" Return="asyncWait" BinaryRef="Wix4UtilCA_$(sys.BUILDARCHSHORT)" />
|
|
||||||
|
|
||||||
<SetProperty Id="FirewallPortRemove" Value=""[SystemFolder]netsh.exe" advfirewall firewall delete rule name="$(var.Product) Service"" Before="FirewallPortRemove" Sequence="execute" />
|
|
||||||
<CustomAction Id="FirewallPortRemove" DllEntry="WixQuietExec" Execute="deferred" Return="asyncWait" BinaryRef="Wix4UtilCA_$(sys.BUILDARCHSHORT)" />
|
|
||||||
|
|
||||||
</Fragment>
|
</Fragment>
|
||||||
</Wix>
|
</Wix>
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
<?include Includes.wxi?>
|
<?include Includes.wxi?>
|
||||||
|
|
||||||
<Package Name="$(var.Product)" Version="$(var.Version)" Manufacturer="$(var.Manufacturer)" Language="!(loc.ProductLanguage)" Codepage="0" UpgradeCode="$(var.UpgradeCode)" Scope="perMachine">
|
<Package Name="$(var.Product)" Version="$(var.Version)" Manufacturer="$(var.Manufacturer)" Language="!(loc.ProductLanguage)" UpgradeCode="$(var.UpgradeCode)" Scope="perMachine">
|
||||||
|
|
||||||
<SummaryInformation Keywords="Installer" Description="$(var.Description)" Codepage="!(loc.SummaryCodepage)" />
|
<SummaryInformation Keywords="Installer" Description="$(var.Description)" Codepage="!(loc.SummaryCodepage)" />
|
||||||
|
|
||||||
@ -23,38 +23,26 @@
|
|||||||
|
|
||||||
<InstallUISequence>
|
<InstallUISequence>
|
||||||
<!--<Custom Action="ReadCustomPathsFromExistingPathsFile" Before="CostFinalize" Condition="NOT Installed" />-->
|
<!--<Custom Action="ReadCustomPathsFromExistingPathsFile" Before="CostFinalize" Condition="NOT Installed" />-->
|
||||||
<Show Dialog="AnotherAppDialog" Before="WelcomeDlg" Condition="Not installed AND RUSTDESKNATIVEINSTALL AND Not RUSTDESKNATIVEINSTALLFOLDER"/>
|
<!--<Show Dialog="AnotherAppDialog" Before="WelcomeDlg" Condition="Not installed AND RUSTDESKNATIVEINSTALL AND Not RUSTDESKNATIVEINSTALLFOLDER"/>-->
|
||||||
|
|
||||||
</InstallUISequence>
|
</InstallUISequence>
|
||||||
|
|
||||||
<InstallExecuteSequence>
|
<InstallExecuteSequence>
|
||||||
<!-- Stop all MP2 processes -->
|
|
||||||
<!--<Custom Action="StopProcesses" Before="ReadCustomPathsFromExistingPathsFile" />-->
|
|
||||||
|
|
||||||
<!-- Reads custom paths which maybe have been changed by the user in a former installation -->
|
|
||||||
<!--<Custom Action="ReadCustomPathsFromExistingPathsFile" Before="PrepareXmlPathVariables" Condition="(NOT Installed) AND (INSTALLTYPE_CUSTOM = 0)" />-->
|
|
||||||
|
|
||||||
<!--<Custom Action="PrepareXmlPathVariables" Before="FileCost" Condition="NOT Installed" />-->
|
|
||||||
<!--<Custom Action="AttachClientToServer" After="InstallFinalize" Condition="NOT Installed" />-->
|
|
||||||
|
|
||||||
<Custom Action="FirewallPortRemove" Before="InstallFinalize" Condition="REMOVE~="ALL"" />
|
|
||||||
<Custom Action="FirewallPortOutAdd" Before="InstallFinalize" Condition="NOT Installed" />
|
|
||||||
<Custom Action="FirewallPortInAdd" Before="InstallFinalize" Condition="NOT Installed" />
|
|
||||||
|
|
||||||
<!-- Launch ClientLauncher if installing or already installed and not uninstalling -->
|
<!-- Launch ClientLauncher if installing or already installed and not uninstalling -->
|
||||||
<Custom Action="LaunchApp" After="InstallFinalize" />
|
<Custom Action="LaunchApp" After="InstallFinalize" />
|
||||||
<Custom Action="LaunchAppTray" After="InstallFinalize" />
|
<Custom Action="LaunchAppTray" After="InstallFinalize" />
|
||||||
|
|
||||||
<InstallExecute After="RemoveExistingProducts" />
|
<InstallExecute After="RemoveExistingProducts" />
|
||||||
|
|
||||||
<Custom Action="CloseProcesses" Before="RemoveFiles" />
|
<Custom Action="TerminateProcesses" Before="RemoveFiles"/>
|
||||||
|
<Custom Action="TerminateProcesses.SetParam" Before="TerminateProcesses"/>
|
||||||
|
|
||||||
</InstallExecuteSequence>
|
</InstallExecuteSequence>
|
||||||
<CustomAction Id="LaunchApp" ExeCommand="" Return="asyncNoWait" FileRef="RustDesk.exe" />
|
<CustomAction Id="LaunchApp" ExeCommand="" Return="asyncNoWait" FileRef="RustDesk.exe" />
|
||||||
<CustomAction Id="LaunchAppTray" ExeCommand=" --tray" Return="asyncNoWait" FileRef="RustDesk.exe" />
|
<CustomAction Id="LaunchAppTray" ExeCommand=" --tray" Return="asyncNoWait" FileRef="RustDesk.exe" />
|
||||||
<CustomAction Id="CloseProcesses" ExeCommand='taskkill /F /IM "$(var.Product).exe"' Return="asyncNoWait" Directory="INSTALLFOLDER" />
|
<Property Id="TerminateProcesses" Value="RustDeskTest.exe" />
|
||||||
|
<CustomAction Id="TerminateProcesses.SetParam" Return="check" Property="TerminateProcesses" Value="$(var.Product).exe" />
|
||||||
|
|
||||||
<MajorUpgrade DowngradeErrorMessage="!(loc.DowngradeError)" Schedule="afterInstallInitialize" />
|
<MajorUpgrade DowngradeErrorMessage="!(loc.DowngradeError)" Schedule="afterInstallInitialize" AllowSameVersionUpgrades="yes" />
|
||||||
|
|
||||||
<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" />
|
||||||
@ -64,8 +52,13 @@
|
|||||||
<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.UninstallRustDesk" />
|
||||||
<ComponentRef Id="App.StartMenu" />
|
<ComponentRef Id="App.StartMenu" />
|
||||||
<ComponentRef Id="Product.Registry.PersistedShortcutProperties" />
|
<ComponentRef Id="Product.Registry.PersistedShortcutProperties" />
|
||||||
</Feature>
|
</Feature>
|
||||||
|
|
||||||
|
<!--https://wixtoolset.org/docs/tools/wixext/wixui/#customizing-a-dialog-set-->
|
||||||
|
<!--$CustomBitmapsStart$-->
|
||||||
|
<!--$CustomBitmapsEnd$-->
|
||||||
</Package>
|
</Package>
|
||||||
</Wix>
|
</Wix>
|
||||||
|
@ -6,19 +6,38 @@ This project is mainly derived from <https://github.com/MediaPortal/MediaPortal-
|
|||||||
|
|
||||||
## Steps
|
## Steps
|
||||||
|
|
||||||
1. `python preprocess.py`
|
1. `python preprocess.py`, see `python preprocess.py -h` for help.
|
||||||
2. Build the .sln solution.
|
2. Build the .sln solution.
|
||||||
|
|
||||||
Run `msiexec /i package.msi /l*v install.log` to record the log.
|
Run `msiexec /i package.msi /l*v install.log` to record the log.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
1. Put the custom dialog bitmaps in "Resources" directory. The supported bitmaps are `['WixUIBannerBmp', 'WixUIDialogBmp', 'WixUIExclamationIco', 'WixUIInfoIco', 'WixUINewIco', 'WixUIUpIco']`.
|
||||||
|
|
||||||
|
## Knowledge
|
||||||
|
|
||||||
|
### properties
|
||||||
|
|
||||||
|
[wix-toolset-set-custom-action-run-only-on-uninstall](https://www.advancedinstaller.com/versus/wix-toolset/wix-toolset-set-custom-action-run-only-on-uninstall.html)
|
||||||
|
|
||||||
|
| Property Name | Install | Uninstall | Change | Repair | Upgrade |
|
||||||
|
| ------ | ------ | ------ | ------ | ------ | ------ |
|
||||||
|
| Installed | False | True | True | True | True |
|
||||||
|
| REINSTALL | False | False | False | True | False |
|
||||||
|
| UPGRADINGPRODUCTCODE | False | False | False | False | True |
|
||||||
|
| REMOVE | False | True | False | False | True |
|
||||||
|
|
||||||
## TODOs
|
## TODOs
|
||||||
|
|
||||||
1. tray, uninstall shortcut
|
1. Start menu. Uninstall
|
||||||
1. launch client after installation
|
1. custom options
|
||||||
1. github ci
|
|
||||||
1. options
|
|
||||||
1. Custom client.
|
1. Custom client.
|
||||||
1. firewall and tcp allow. Outgoing
|
1. firewall and tcp allow. Outgoing
|
||||||
1. Custom icon. Current `Resources/icon.ico`.
|
|
||||||
1. Show license ?
|
1. Show license ?
|
||||||
1. Do create service. Outgoing.
|
1. Do create service. Outgoing.
|
||||||
|
|
||||||
|
## Refs
|
||||||
|
|
||||||
|
1. [wxs](https://wixtoolset.org/docs/schema/wxs/)
|
||||||
|
1. [wxs github](https://github.com/wixtoolset/wix)
|
||||||
|
@ -1,12 +1,38 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import json
|
||||||
import sys
|
import sys
|
||||||
import uuid
|
import uuid
|
||||||
import argparse
|
import argparse
|
||||||
|
import datetime
|
||||||
|
import re
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
g_indent_unit = "\t"
|
g_indent_unit = "\t"
|
||||||
|
g_version = ""
|
||||||
|
g_build_date = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
|
||||||
|
|
||||||
|
# https://learn.microsoft.com/en-us/windows/win32/msi/property-reference
|
||||||
|
g_arpsystemcomponent = {
|
||||||
|
"Comments": {
|
||||||
|
"msi": "ARPCOMMENTS",
|
||||||
|
"t": "string",
|
||||||
|
"v": "!(loc.AR_Comment)",
|
||||||
|
},
|
||||||
|
"Contact": {
|
||||||
|
"msi": "ARPCONTACT",
|
||||||
|
"v": "https://github.com/rustdesk/rustdesk",
|
||||||
|
},
|
||||||
|
"HelpLink": {
|
||||||
|
"msi": "ARPHELPLINK",
|
||||||
|
"v": "https://github.com/rustdesk/rustdesk/issues/",
|
||||||
|
},
|
||||||
|
"ReadMe": {
|
||||||
|
"msi": "ARPREADME",
|
||||||
|
"v": "https://github.com/fufesou/rustdesk",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def make_parser():
|
def make_parser():
|
||||||
@ -14,6 +40,23 @@ def make_parser():
|
|||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-d", "--debug", action="store_true", help="Is debug", default=False
|
"-d", "--debug", action="store_true", help="Is debug", default=False
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-ci", "--github-ci", action="store_true", help="Is github ci", default=False
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-arp",
|
||||||
|
"--arp",
|
||||||
|
action="store_true",
|
||||||
|
help="Is ARPSYSTEMCOMPONENT",
|
||||||
|
default=False,
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-custom-arp",
|
||||||
|
"--custom-arp",
|
||||||
|
type=str,
|
||||||
|
default="{}",
|
||||||
|
help='Custom arp properties, e.g. \'["Comments": {"msi": "ARPCOMMENTS", "v": "Remote control application."}]\'',
|
||||||
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-c", "--custom", action="store_true", help="Is custom client", default=False
|
"-c", "--custom", action="store_true", help="Is custom client", default=False
|
||||||
)
|
)
|
||||||
@ -21,13 +64,13 @@ def make_parser():
|
|||||||
"-an", "--app-name", type=str, default="RustDesk", help="The app name."
|
"-an", "--app-name", type=str, default="RustDesk", help="The app name."
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-v", "--version", type=str, default="1.2.4", help="The app version."
|
"-v", "--version", type=str, default="", help="The app version."
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-m",
|
"-m",
|
||||||
"--manufacturer",
|
"--manufacturer",
|
||||||
type=str,
|
type=str,
|
||||||
default="Purslane Ltd",
|
default="RustDesk",
|
||||||
help="The app manufacturer.",
|
help="The app manufacturer.",
|
||||||
)
|
)
|
||||||
return parser
|
return parser
|
||||||
@ -103,7 +146,7 @@ def gen_pre_vars(args, build_dir):
|
|||||||
|
|
||||||
indent = g_indent_unit * 1
|
indent = g_indent_unit * 1
|
||||||
to_insert_lines = [
|
to_insert_lines = [
|
||||||
f'{indent}<?define Version="{args.version}" ?>\n',
|
f'{indent}<?define Version="{g_version}" ?>\n',
|
||||||
f'{indent}<?define Manufacturer="{args.manufacturer}" ?>\n',
|
f'{indent}<?define Manufacturer="{args.manufacturer}" ?>\n',
|
||||||
f'{indent}<?define Product="{args.app_name}" ?>\n',
|
f'{indent}<?define Product="{args.app_name}" ?>\n',
|
||||||
f'{indent}<?define Description="{args.app_name} Installer" ?>\n',
|
f'{indent}<?define Description="{args.app_name} Installer" ?>\n',
|
||||||
@ -111,6 +154,7 @@ def gen_pre_vars(args, build_dir):
|
|||||||
f'{indent}<?define RegKeyRoot=".$(var.ProductLower)" ?>\n',
|
f'{indent}<?define RegKeyRoot=".$(var.ProductLower)" ?>\n',
|
||||||
f'{indent}<?define RegKeyInstall="$(var.RegKeyRoot)\Install" ?>\n',
|
f'{indent}<?define RegKeyInstall="$(var.RegKeyRoot)\Install" ?>\n',
|
||||||
f'{indent}<?define BuildDir="{build_dir}" ?>\n',
|
f'{indent}<?define BuildDir="{build_dir}" ?>\n',
|
||||||
|
f'{indent}<?define BuildDate="{g_build_date}" ?>\n',
|
||||||
"\n",
|
"\n",
|
||||||
f"{indent}<!-- The UpgradeCode must be consistent for each product. ! -->\n"
|
f"{indent}<!-- The UpgradeCode must be consistent for each product. ! -->\n"
|
||||||
f'{indent}<?define UpgradeCode = "{upgrade_code}" ?>\n',
|
f'{indent}<?define UpgradeCode = "{upgrade_code}" ?>\n',
|
||||||
@ -118,6 +162,7 @@ def gen_pre_vars(args, build_dir):
|
|||||||
|
|
||||||
for i, line in enumerate(to_insert_lines):
|
for i, line in enumerate(to_insert_lines):
|
||||||
lines.insert(index_start + i + 1, line)
|
lines.insert(index_start + i + 1, line)
|
||||||
|
return lines
|
||||||
|
|
||||||
return gen_content_between_tags(
|
return gen_content_between_tags(
|
||||||
"Package/Includes.wxi", "<!--$PreVarsStart$-->", "<!--$PreVarsEnd$-->", func
|
"Package/Includes.wxi", "<!--$PreVarsStart$-->", "<!--$PreVarsEnd$-->", func
|
||||||
@ -135,15 +180,15 @@ def replace_app_name_in_lans(app_name):
|
|||||||
f.writelines(lines)
|
f.writelines(lines)
|
||||||
|
|
||||||
|
|
||||||
def gen_upgrade_info(version):
|
def gen_upgrade_info():
|
||||||
def func(lines, index_start):
|
def func(lines, index_start):
|
||||||
indent = g_indent_unit * 3
|
indent = g_indent_unit * 3
|
||||||
|
|
||||||
major, _, _ = version.split(".")
|
major, _, _ = g_version.split(".")
|
||||||
upgrade_id = uuid.uuid4()
|
upgrade_id = uuid.uuid4()
|
||||||
to_insert_lines = [
|
to_insert_lines = [
|
||||||
f'{indent}<Upgrade Id="{upgrade_id}">\n',
|
f'{indent}<Upgrade Id="{upgrade_id}">\n',
|
||||||
f'{indent}{g_indent_unit}<UpgradeVersion Property="OLD_VERSION_FOUND" Minimum="{major}.0.0.0" Maximum="{major}.99.99" IncludeMinimum="yes" IncludeMaximum="yes" OnlyDetect="no" IgnoreRemoveFailure="yes" MigrateFeatures="yes" />\n',
|
f'{indent}{g_indent_unit}<UpgradeVersion Property="OLD_VERSION_FOUND" Minimum="{major}.0.0" Maximum="{major}.99.99" IncludeMinimum="yes" IncludeMaximum="yes" OnlyDetect="no" IgnoreRemoveFailure="yes" MigrateFeatures="yes" />\n',
|
||||||
f"{indent}</Upgrade>\n",
|
f"{indent}</Upgrade>\n",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -159,6 +204,175 @@ def gen_upgrade_info(version):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def gen_custom_dialog_bitmaps():
|
||||||
|
def func(lines, index_start):
|
||||||
|
indent = g_indent_unit * 2
|
||||||
|
|
||||||
|
# https://wixtoolset.org/docs/tools/wixext/wixui/#customizing-a-dialog-set
|
||||||
|
vars = [
|
||||||
|
"WixUIBannerBmp",
|
||||||
|
"WixUIDialogBmp",
|
||||||
|
"WixUIExclamationIco",
|
||||||
|
"WixUIInfoIco",
|
||||||
|
"WixUINewIco",
|
||||||
|
"WixUIUpIco",
|
||||||
|
]
|
||||||
|
to_insert_lines = []
|
||||||
|
for var in vars:
|
||||||
|
if Path(f"Package/Resources/{var}.bmp").exists():
|
||||||
|
to_insert_lines.append(
|
||||||
|
f'{indent}<WixVariable Id="{var}" Value="Resources\\{var}.bmp" />\n'
|
||||||
|
)
|
||||||
|
|
||||||
|
for i, line in enumerate(to_insert_lines):
|
||||||
|
lines.insert(index_start + i + 1, line)
|
||||||
|
return lines
|
||||||
|
|
||||||
|
return gen_content_between_tags(
|
||||||
|
"Package/Package.wxs",
|
||||||
|
"<!--$CustomBitmapsStart$-->",
|
||||||
|
"<!--$CustomBitmapsEnd$-->",
|
||||||
|
func,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def gen_custom_ARPSYSTEMCOMPONENT_False(args):
|
||||||
|
def func(lines, index_start):
|
||||||
|
indent = g_indent_unit * 2
|
||||||
|
|
||||||
|
lines_new = []
|
||||||
|
lines_new.append(
|
||||||
|
f"{indent}<!--https://learn.microsoft.com/en-us/windows/win32/msi/arpsystemcomponent?redirectedfrom=MSDN-->\n"
|
||||||
|
)
|
||||||
|
lines_new.append(
|
||||||
|
f'{indent}<!--<Property Id="ARPSYSTEMCOMPONENT" Value="1" />-->\n\n'
|
||||||
|
)
|
||||||
|
|
||||||
|
lines_new.append(
|
||||||
|
f"{indent}<!--https://learn.microsoft.com/en-us/windows/win32/msi/property-reference-->\n"
|
||||||
|
)
|
||||||
|
for _, v in g_arpsystemcomponent.items():
|
||||||
|
if "msi" in v and "v" in v:
|
||||||
|
lines_new.append(f'{indent}<Property Id="{v["msi"]}" Value="{v["v"]}" />\n')
|
||||||
|
|
||||||
|
for i, line in enumerate(lines_new):
|
||||||
|
lines.insert(index_start + i + 1, line)
|
||||||
|
return lines
|
||||||
|
|
||||||
|
return gen_content_between_tags(
|
||||||
|
"Package/Fragments/AddRemoveProperties.wxs",
|
||||||
|
"<!--$ArpStart$-->",
|
||||||
|
"<!--$ArpEnd$-->",
|
||||||
|
func,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_folder_size(folder_path):
|
||||||
|
total_size = 0
|
||||||
|
|
||||||
|
folder = Path(folder_path)
|
||||||
|
for file in folder.glob("**/*"):
|
||||||
|
if file.is_file():
|
||||||
|
total_size += file.stat().st_size
|
||||||
|
|
||||||
|
return total_size
|
||||||
|
|
||||||
|
|
||||||
|
def gen_custom_ARPSYSTEMCOMPONENT_True(args, build_dir):
|
||||||
|
def func(lines, index_start):
|
||||||
|
indent = g_indent_unit * 5
|
||||||
|
|
||||||
|
lines_new = []
|
||||||
|
lines_new.append(
|
||||||
|
f"{indent}<!--https://learn.microsoft.com/en-us/windows/win32/msi/property-reference-->\n"
|
||||||
|
)
|
||||||
|
lines_new.append(
|
||||||
|
f'{indent}<RegistryValue Type="string" Name="DisplayName" Value="{args.app_name}" />\n'
|
||||||
|
)
|
||||||
|
lines_new.append(
|
||||||
|
f'{indent}<RegistryValue Type="string" Name="DisplayIcon" Value="[INSTALLFOLDER]{args.app_name}.exe" />\n'
|
||||||
|
)
|
||||||
|
lines_new.append(
|
||||||
|
f'{indent}<RegistryValue Type="string" Name="DisplayVersion" Value="{g_version}" />\n'
|
||||||
|
)
|
||||||
|
lines_new.append(
|
||||||
|
f'{indent}<RegistryValue Type="string" Name="Publisher" Value="{args.manufacturer}" />\n'
|
||||||
|
)
|
||||||
|
installDate = datetime.datetime.now().strftime("%Y%m%d")
|
||||||
|
lines_new.append(
|
||||||
|
f'{indent}<RegistryValue Type="string" Name="InstallDate" Value="{installDate}" />\n'
|
||||||
|
)
|
||||||
|
lines_new.append(
|
||||||
|
f'{indent}<RegistryValue Type="string" Name="InstallLocation" Value="[INSTALLFOLDER]" />\n'
|
||||||
|
)
|
||||||
|
lines_new.append(
|
||||||
|
f'{indent}<RegistryValue Type="string" Name="InstallSource" Value="[InstallSource]" />\n'
|
||||||
|
)
|
||||||
|
lines_new.append(
|
||||||
|
f'{indent}<RegistryValue Type="integer" Name="Language" Value="[ProductLanguage]" />\n'
|
||||||
|
)
|
||||||
|
|
||||||
|
estimated_size = get_folder_size(build_dir)
|
||||||
|
lines_new.append(
|
||||||
|
f'{indent}<RegistryValue Type="integer" Name="EstimatedSize" Value="{estimated_size}" />\n'
|
||||||
|
)
|
||||||
|
|
||||||
|
lines_new.append(
|
||||||
|
f'{indent}<RegistryValue Type="expandable" Name="ModifyPath" Value="MsiExec.exe /X [ProductCode]" />\n'
|
||||||
|
)
|
||||||
|
lines_new.append(f'{indent}<RegistryValue Type="integer" Id="NoModify" Value="1" />\n')
|
||||||
|
lines_new.append(
|
||||||
|
f'{indent}<RegistryValue Type="expandable" Name="UninstallString" Value="MsiExec.exe /X [ProductCode]" />\n'
|
||||||
|
)
|
||||||
|
|
||||||
|
major, minor, build = g_version.split(".")
|
||||||
|
lines_new.append(
|
||||||
|
f'{indent}<RegistryValue Type="string" Name="Version" Value="{g_version}" />\n'
|
||||||
|
)
|
||||||
|
lines_new.append(
|
||||||
|
f'{indent}<RegistryValue Type="integer" Name="VersionMajor" Value="{major}" />\n'
|
||||||
|
)
|
||||||
|
lines_new.append(
|
||||||
|
f'{indent}<RegistryValue Type="integer" Name="VersionMinor" Value="{minor}" />\n'
|
||||||
|
)
|
||||||
|
lines_new.append(
|
||||||
|
f'{indent}<RegistryValue Type="integer" Name="VersionBuild" Value="{build}" />\n'
|
||||||
|
)
|
||||||
|
|
||||||
|
lines_new.append(
|
||||||
|
f'{indent}<RegistryValue Type="integer" Name="WindowsInstaller" Value="1" />\n'
|
||||||
|
)
|
||||||
|
for k, v in g_arpsystemcomponent.items():
|
||||||
|
if 'v' in v:
|
||||||
|
t = v['t'] if 't' in v is None else 'string'
|
||||||
|
lines_new.append(f'{indent}<RegistryValue Type="{t}" Name="{k}" Value="{v["v"]}" />\n')
|
||||||
|
|
||||||
|
for i, line in enumerate(lines_new):
|
||||||
|
lines.insert(index_start + i + 1, line)
|
||||||
|
return lines
|
||||||
|
|
||||||
|
return gen_content_between_tags(
|
||||||
|
"Package/Components/Regs.wxs",
|
||||||
|
"<!--$ArpStart$-->",
|
||||||
|
"<!--$ArpEnd$-->",
|
||||||
|
func,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def gen_custom_ARPSYSTEMCOMPONENT(args, build_dir):
|
||||||
|
try:
|
||||||
|
custom_arp = json.loads(args.custom_arp)
|
||||||
|
g_arpsystemcomponent.update(custom_arp)
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
print(f"Failed to decode custom arp: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
if args.arp:
|
||||||
|
return gen_custom_ARPSYSTEMCOMPONENT_True(args, build_dir)
|
||||||
|
else:
|
||||||
|
return gen_custom_ARPSYSTEMCOMPONENT_False(args)
|
||||||
|
|
||||||
|
|
||||||
def gen_content_between_tags(filename, tag_start, tag_end, func):
|
def gen_content_between_tags(filename, tag_start, tag_end, func):
|
||||||
target_file = Path(sys.argv[0]).parent.joinpath(filename)
|
target_file = Path(sys.argv[0]).parent.joinpath(filename)
|
||||||
lines, index_start = read_lines_and_start_index(target_file, tag_start, tag_end)
|
lines, index_start = read_lines_and_start_index(target_file, tag_start, tag_end)
|
||||||
@ -173,26 +387,69 @@ def gen_content_between_tags(filename, tag_start, tag_end, func):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def init_global_vars(args):
|
||||||
|
var_file = "../../src/version.rs"
|
||||||
|
if not Path(var_file).exists():
|
||||||
|
print(f"Error: {var_file} not found")
|
||||||
|
return False
|
||||||
|
|
||||||
|
with open(var_file, "r") as f:
|
||||||
|
content = f.readlines()
|
||||||
|
|
||||||
|
global g_version
|
||||||
|
global g_build_date
|
||||||
|
g_version = args.version.replace("-", ".")
|
||||||
|
if g_version == "":
|
||||||
|
# pub const VERSION: &str = "1.2.4";
|
||||||
|
version_pattern = re.compile(r'.*VERSION: &str = "(.*)";.*')
|
||||||
|
for line in content:
|
||||||
|
match = version_pattern.match(line)
|
||||||
|
if match:
|
||||||
|
g_version = match.group(1)
|
||||||
|
break
|
||||||
|
if g_version == "":
|
||||||
|
print(f"Error: version not found in {var_file}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# pub const BUILD_DATE: &str = "2024-04-08 23:11";
|
||||||
|
build_date_pattern = re.compile(r'BUILD_DATE: &str = "(.*)";')
|
||||||
|
for line in content:
|
||||||
|
match = build_date_pattern.match(line)
|
||||||
|
if match:
|
||||||
|
g_build_date = match.group(1)
|
||||||
|
break
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
parser = make_parser()
|
parser = make_parser()
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
app_name = args.app_name
|
app_name = args.app_name
|
||||||
build_dir = (
|
build_dir = (
|
||||||
Path(sys.argv[0])
|
|
||||||
.parent.joinpath(
|
|
||||||
f'../../flutter/build/windows/x64/runner/{"Debug" if args.debug else "Release"}'
|
f'../../flutter/build/windows/x64/runner/{"Debug" if args.debug else "Release"}'
|
||||||
)
|
)
|
||||||
.resolve()
|
if args.github_ci:
|
||||||
)
|
build_dir = "../../rustdesk"
|
||||||
|
build_dir = Path(sys.argv[0]).parent.joinpath(build_dir).resolve()
|
||||||
|
|
||||||
|
if not init_global_vars(args):
|
||||||
|
sys.exit(-1)
|
||||||
|
|
||||||
if not gen_pre_vars(args, build_dir):
|
if not gen_pre_vars(args, build_dir):
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
|
|
||||||
if not gen_upgrade_info(args.version):
|
if not gen_upgrade_info():
|
||||||
|
sys.exit(-1)
|
||||||
|
|
||||||
|
if not gen_custom_ARPSYSTEMCOMPONENT(args, build_dir):
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
|
|
||||||
if not gen_auto_component(app_name, build_dir):
|
if not gen_auto_component(app_name, build_dir):
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
|
|
||||||
|
if not gen_custom_dialog_bitmaps():
|
||||||
|
sys.exit(-1)
|
||||||
|
|
||||||
replace_app_name_in_lans(args.app_name)
|
replace_app_name_in_lans(args.app_name)
|
||||||
|
@ -1279,6 +1279,11 @@ fn get_before_uninstall(kill_self: bool) -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_uninstall(kill_self: bool) -> String {
|
fn get_uninstall(kill_self: bool) -> String {
|
||||||
|
let reg_uninstall_string = get_reg("UninstallString");
|
||||||
|
if reg_uninstall_string.to_lowercase().contains("msiexec.exe") {
|
||||||
|
return reg_uninstall_string;
|
||||||
|
}
|
||||||
|
|
||||||
let mut uninstall_cert_cmd = "".to_string();
|
let mut uninstall_cert_cmd = "".to_string();
|
||||||
if let Ok(exe) = std::env::current_exe() {
|
if let Ok(exe) = std::env::current_exe() {
|
||||||
if let Some(exe_path) = exe.to_str() {
|
if let Some(exe_path) = exe.to_str() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user