diff --git a/res/msi/.gitignore b/res/msi/.gitignore
index a2df938b4..44c377c55 100644
--- a/res/msi/.gitignore
+++ b/res/msi/.gitignore
@@ -2,3 +2,10 @@
**/bin
**/obj
+
+x64
+packages
+
+CustomActions/x64
+CustomActions/*.user
+CustomActions/*.filters
diff --git a/res/msi/CustomActions/CustomAction.config b/res/msi/CustomActions/CustomAction.config
deleted file mode 100644
index cfae001eb..000000000
--- a/res/msi/CustomActions/CustomAction.config
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
-
-
-
diff --git a/res/msi/CustomActions/CustomAction.cs b/res/msi/CustomActions/CustomAction.cs
deleted file mode 100644
index cd048f95b..000000000
--- a/res/msi/CustomActions/CustomAction.cs
+++ /dev/null
@@ -1,56 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Diagnostics;
-using System.Runtime.InteropServices;
-using WixToolset.Dtf.WindowsInstaller;
-
-namespace CustomActions
-{
- public class CustomActions
- {
- [CustomAction]
- public static ActionResult CustomActionHello(Session session)
- {
- try
- {
- session.Log("================= Example CustomAction Hello");
- return ActionResult.Success;
- }
- catch (Exception e)
- {
- session.Log("An error occurred: " + e.Message);
- return ActionResult.Failure;
- }
- }
-
- [CustomAction]
- public static ActionResult RunCommandAsSystem(Session session)
- {
- try
- {
- ProcessStartInfo psi = new ProcessStartInfo
- {
-
- FileName = "cmd.exe",
- Arguments = "/c " + session["CMD"],
- UseShellExecute = false,
- WindowStyle = ProcessWindowStyle.Hidden,
- Verb = "runas"
- };
-
- using (Process process = Process.Start(psi))
- {
- process.WaitForExit();
- }
-
- return ActionResult.Success;
- }
- catch (Exception e)
- {
- session.Log("An error occurred: " + e.Message);
- return ActionResult.Failure;
- }
- }
- }
-}
diff --git a/res/msi/CustomActions/CustomActions.cpp b/res/msi/CustomActions/CustomActions.cpp
new file mode 100644
index 000000000..a5c49410f
--- /dev/null
+++ b/res/msi/CustomActions/CustomActions.cpp
@@ -0,0 +1,63 @@
+// CustomAction.cpp : Defines the entry point for the custom action.
+#include "pch.h"
+#include
+
+UINT __stdcall CustomActionHello(
+ __in MSIHANDLE hInstall
+)
+{
+ HRESULT hr = S_OK;
+ DWORD er = ERROR_SUCCESS;
+
+ hr = WcaInitialize(hInstall, "CustomActionHello");
+ ExitOnFailure(hr, "Failed to initialize");
+
+ WcaLog(LOGMSG_STANDARD, "Initialized.");
+
+ // TODO: Add your custom action code here.
+ WcaLog(LOGMSG_STANDARD, "================= Example CustomAction Hello");
+
+LExit:
+ er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
+ return WcaFinalize(er);
+}
+
+UINT __stdcall RemoveInstallFolder(
+ __in MSIHANDLE hInstall
+)
+{
+ HRESULT hr = S_OK;
+ DWORD er = ERROR_SUCCESS;
+
+ int nResult = 0;
+ wchar_t szCustomActionData[256] = { 0 };
+ DWORD cchCustomActionData = sizeof(szCustomActionData) / sizeof(szCustomActionData[0]);
+
+ hr = WcaInitialize(hInstall, "RemoveInstallFolder");
+ ExitOnFailure(hr, "Failed to initialize");
+
+ MsiGetPropertyW(hInstall, L"InstallFolder", szCustomActionData, &cchCustomActionData);
+
+ WcaLog(LOGMSG_STANDARD, "================= Remove Install Folder: %ls", szCustomActionData);
+
+ SHFILEOPSTRUCTW fileOp;
+ ZeroMemory(&fileOp, sizeof(SHFILEOPSTRUCT));
+
+ fileOp.wFunc = FO_DELETE;
+ fileOp.pFrom = szCustomActionData;
+ fileOp.fFlags = FOF_NOCONFIRMATION | FOF_SILENT;
+
+ nResult = SHFileOperationW(&fileOp);
+ if (nResult == 0)
+ {
+ WcaLog(LOGMSG_STANDARD, "The directory \"%ls\" has been deleted.", szCustomActionData);
+ }
+ else
+ {
+ WcaLog(LOGMSG_STANDARD, "The directory \"%ls\" has not been deleted, error code: 0X%02X.", szCustomActionData, nResult);
+ }
+
+LExit:
+ er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
+ return WcaFinalize(er);
+}
diff --git a/res/msi/CustomActions/CustomActions.csproj b/res/msi/CustomActions/CustomActions.csproj
deleted file mode 100644
index e21734032..000000000
--- a/res/msi/CustomActions/CustomActions.csproj
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
- net472
- Release
-
-
-
-
-
-
-
-
-
-
-
diff --git a/res/msi/CustomActions/CustomActions.def b/res/msi/CustomActions/CustomActions.def
new file mode 100644
index 000000000..8eb50ce0d
--- /dev/null
+++ b/res/msi/CustomActions/CustomActions.def
@@ -0,0 +1,5 @@
+LIBRARY "CustomActions"
+
+EXPORTS
+ CustomActionHello
+ RemoveInstallFolder
diff --git a/res/msi/CustomActions/CustomActions.vcxproj b/res/msi/CustomActions/CustomActions.vcxproj
new file mode 100644
index 000000000..1d1521158
--- /dev/null
+++ b/res/msi/CustomActions/CustomActions.vcxproj
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+ Release
+ x64
+
+
+
+ Win32Proj
+ {87e7c13b-ae0e-4048-95cf-4523d510a3cd}
+ CustomActions
+ v143
+ 10.0
+
+
+
+ DynamicLibrary
+ false
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+ Level3
+ true
+ true
+ true
+ NDEBUG;EXAMPLECADLL_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
+ true
+ Use
+ pch.h
+ MultiThreaded
+
+
+ msi.lib;version.lib;%(AdditionalDependencies)
+ Windows
+ true
+ true
+ true
+ false
+ CustomActions.def
+
+
+
+
+
+
+
+
+
+
+ Create
+
+
+
+
+
+
+
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+
+
+
+
+
\ No newline at end of file
diff --git a/res/msi/CustomActions/dllmain.cpp b/res/msi/CustomActions/dllmain.cpp
new file mode 100644
index 000000000..7288d7c64
--- /dev/null
+++ b/res/msi/CustomActions/dllmain.cpp
@@ -0,0 +1,26 @@
+// dllmain.cpp : Defines the entry point for the DLL application.
+#include "pch.h"
+
+BOOL APIENTRY DllMain(
+ __in HMODULE hModule,
+ __in DWORD ulReasonForCall,
+ __in LPVOID
+)
+{
+ switch (ulReasonForCall)
+ {
+ case DLL_PROCESS_ATTACH:
+ WcaGlobalInitialize(hModule);
+ break;
+
+ case DLL_PROCESS_DETACH:
+ WcaGlobalFinalize();
+ break;
+
+ case DLL_THREAD_ATTACH:
+ case DLL_THREAD_DETACH:
+ break;
+ }
+
+ return TRUE;
+}
diff --git a/res/msi/CustomActions/framework.h b/res/msi/CustomActions/framework.h
new file mode 100644
index 000000000..4cd0bc474
--- /dev/null
+++ b/res/msi/CustomActions/framework.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
+// Windows Header Files
+#include
+#include
+#include
+
+// WiX Header Files:
+#include
diff --git a/res/msi/CustomActions/packages.config b/res/msi/CustomActions/packages.config
new file mode 100644
index 000000000..e25f8328a
--- /dev/null
+++ b/res/msi/CustomActions/packages.config
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/res/msi/CustomActions/pch.cpp b/res/msi/CustomActions/pch.cpp
new file mode 100644
index 000000000..64b7eef6d
--- /dev/null
+++ b/res/msi/CustomActions/pch.cpp
@@ -0,0 +1,5 @@
+// pch.cpp: source file corresponding to the pre-compiled header
+
+#include "pch.h"
+
+// When you are using pre-compiled headers, this source file is necessary for compilation to succeed.
diff --git a/res/msi/CustomActions/pch.h b/res/msi/CustomActions/pch.h
new file mode 100644
index 000000000..885d5d62e
--- /dev/null
+++ b/res/msi/CustomActions/pch.h
@@ -0,0 +1,13 @@
+// pch.h: This is a precompiled header file.
+// Files listed below are compiled only once, improving build performance for future builds.
+// This also affects IntelliSense performance, including code completion and many code browsing features.
+// However, files listed here are ALL re-compiled if any one of them is updated between builds.
+// Do not add files here that you will be updating frequently as this negates the performance advantage.
+
+#ifndef PCH_H
+#define PCH_H
+
+// add headers that you want to pre-compile here
+#include "framework.h"
+
+#endif //PCH_H
diff --git a/res/msi/Package/Components/RustDesk.wxs b/res/msi/Package/Components/RustDesk.wxs
index 4abf20bdf..abb43358d 100644
--- a/res/msi/Package/Components/RustDesk.wxs
+++ b/res/msi/Package/Components/RustDesk.wxs
@@ -30,8 +30,8 @@
-
-
+
+
diff --git a/res/msi/Package/Fragments/AddRemoveProperties.wxs b/res/msi/Package/Fragments/AddRemoveProperties.wxs
index 3c0745144..39d0775da 100644
--- a/res/msi/Package/Fragments/AddRemoveProperties.wxs
+++ b/res/msi/Package/Fragments/AddRemoveProperties.wxs
@@ -1,15 +1,15 @@
-
+
+ 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
+ -->
diff --git a/res/msi/Package/Fragments/CustomActions.wxs b/res/msi/Package/Fragments/CustomActions.wxs
index e7759a1a6..79b49b49d 100644
--- a/res/msi/Package/Fragments/CustomActions.wxs
+++ b/res/msi/Package/Fragments/CustomActions.wxs
@@ -1,22 +1,22 @@
-
-
-
+
-
-
+
-
-
+
+
+
+
+
+ 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.-->
-
+
diff --git a/res/msi/Package/Fragments/ShortcutProperties.wxs b/res/msi/Package/Fragments/ShortcutProperties.wxs
index 93af85423..1c4711127 100644
--- a/res/msi/Package/Fragments/ShortcutProperties.wxs
+++ b/res/msi/Package/Fragments/ShortcutProperties.wxs
@@ -4,18 +4,18 @@
+ Properties and related actions for specifying whether to install start menu/desktop shortcuts.
+ -->
+ install start menu shortcuts, they are initialized with a default value to install shortcuts.
+ They should not be set directly from the command line or registry, instead the CREATE* properties
+ below should be set, then they will update these properties with their values only if set. -->
+ if set they update the properties above with their value. -->
@@ -34,8 +34,8 @@
+ 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 -->
diff --git a/res/msi/Package/Fragments/Upgrades.wxs b/res/msi/Package/Fragments/Upgrades.wxs
index 95034f262..ab1118513 100644
--- a/res/msi/Package/Fragments/Upgrades.wxs
+++ b/res/msi/Package/Fragments/Upgrades.wxs
@@ -3,9 +3,8 @@
-
-
-
+
+
diff --git a/res/msi/Package/Includes.wxi b/res/msi/Package/Includes.wxi
index a0e537812..4b43f74c5 100644
--- a/res/msi/Package/Includes.wxi
+++ b/res/msi/Package/Includes.wxi
@@ -4,7 +4,4 @@
-
-
-
diff --git a/res/msi/Package/Package.wixproj b/res/msi/Package/Package.wixproj
index 7500619b1..17dbc4f13 100644
--- a/res/msi/Package/Package.wixproj
+++ b/res/msi/Package/Package.wixproj
@@ -17,6 +17,6 @@
-
+
\ No newline at end of file
diff --git a/res/msi/README.md b/res/msi/README.md
index 79ddbd8ad..80a2490bf 100644
--- a/res/msi/README.md
+++ b/res/msi/README.md
@@ -4,6 +4,13 @@ Use Visual Studio 2022 to compile this project.
This project is mainly derived from .
+## Steps
+
+1. `python preprocess.py`
+2. Build the .sln solution.
+
+Run `msiexec /i package.msi /l*v install.log` to record the log.
+
## TODOs
1. tray, uninstall shortcut
diff --git a/res/msi/msi.sln b/res/msi/msi.sln
index 5a8303f4d..8fb06eb51 100644
--- a/res/msi/msi.sln
+++ b/res/msi/msi.sln
@@ -4,34 +4,39 @@ Microsoft Visual Studio Solution File, Format Version 12.00
VisualStudioVersion = 17.7.34003.232
MinimumVisualStudioVersion = 10.0.40219.1
Project("{B7DD6F7E-DEF8-4E67-B5B7-07EF123DB6F0}") = "Package", "Package\Package.wixproj", "{F403A403-CEFF-4399-B51C-CC646C8E98CF}"
- ProjectSection(ProjectDependencies) = postProject
- {95BE171E-6438-4F45-9876-0B667D9F7830} = {95BE171E-6438-4F45-9876-0B667D9F7830}
- EndProjectSection
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CustomActions", "CustomActions\CustomActions.csproj", "{95BE171E-6438-4F45-9876-0B667D9F7830}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CustomActions", "CustomActions\CustomActions.vcxproj", "{87E7C13B-AE0E-4048-95CF-4523D510A3CD}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
+ Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{F403A403-CEFF-4399-B51C-CC646C8E98CF}.Debug|Any CPU.ActiveCfg = Release|x64
{F403A403-CEFF-4399-B51C-CC646C8E98CF}.Debug|Any CPU.Build.0 = Release|x64
{F403A403-CEFF-4399-B51C-CC646C8E98CF}.Debug|x64.ActiveCfg = Release|x64
{F403A403-CEFF-4399-B51C-CC646C8E98CF}.Debug|x64.Build.0 = Release|x64
+ {F403A403-CEFF-4399-B51C-CC646C8E98CF}.Debug|x86.ActiveCfg = Release|x64
+ {F403A403-CEFF-4399-B51C-CC646C8E98CF}.Debug|x86.Build.0 = Release|x64
{F403A403-CEFF-4399-B51C-CC646C8E98CF}.Release|Any CPU.ActiveCfg = Release|x64
{F403A403-CEFF-4399-B51C-CC646C8E98CF}.Release|Any CPU.Build.0 = Release|x64
{F403A403-CEFF-4399-B51C-CC646C8E98CF}.Release|x64.ActiveCfg = Release|x64
{F403A403-CEFF-4399-B51C-CC646C8E98CF}.Release|x64.Build.0 = Release|x64
- {95BE171E-6438-4F45-9876-0B667D9F7830}.Debug|Any CPU.ActiveCfg = Release|Any CPU
- {95BE171E-6438-4F45-9876-0B667D9F7830}.Debug|x64.ActiveCfg = Release|Any CPU
- {95BE171E-6438-4F45-9876-0B667D9F7830}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {95BE171E-6438-4F45-9876-0B667D9F7830}.Release|Any CPU.Build.0 = Release|Any CPU
- {95BE171E-6438-4F45-9876-0B667D9F7830}.Release|x64.ActiveCfg = Release|Any CPU
- {95BE171E-6438-4F45-9876-0B667D9F7830}.Release|x64.Build.0 = Release|Any CPU
+ {F403A403-CEFF-4399-B51C-CC646C8E98CF}.Release|x86.ActiveCfg = Release|x64
+ {F403A403-CEFF-4399-B51C-CC646C8E98CF}.Release|x86.Build.0 = Release|x64
+ {87E7C13B-AE0E-4048-95CF-4523D510A3CD}.Debug|Any CPU.ActiveCfg = Release|x64
+ {87E7C13B-AE0E-4048-95CF-4523D510A3CD}.Debug|x64.ActiveCfg = Release|x64
+ {87E7C13B-AE0E-4048-95CF-4523D510A3CD}.Debug|x86.ActiveCfg = Release|x64
+ {87E7C13B-AE0E-4048-95CF-4523D510A3CD}.Release|Any CPU.ActiveCfg = Release|x64
+ {87E7C13B-AE0E-4048-95CF-4523D510A3CD}.Release|Any CPU.Build.0 = Release|x64
+ {87E7C13B-AE0E-4048-95CF-4523D510A3CD}.Release|x64.ActiveCfg = Release|x64
+ {87E7C13B-AE0E-4048-95CF-4523D510A3CD}.Release|x64.Build.0 = Release|x64
+ {87E7C13B-AE0E-4048-95CF-4523D510A3CD}.Release|x86.ActiveCfg = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/res/msi/preprocess.py b/res/msi/preprocess.py
index cc1031b88..fe9050b98 100644
--- a/res/msi/preprocess.py
+++ b/res/msi/preprocess.py
@@ -33,24 +33,27 @@ def make_parser():
return parser
-def read_lines_and_start_index(file_path, start_tag, end_tag):
+def read_lines_and_start_index(file_path, tag_start, tag_end):
with open(file_path, "r") as f:
lines = f.readlines()
- start_index = -1
- end_index = -1
+ index_start = -1
+ index_end = -1
for i, line in enumerate(lines):
- if start_tag in line:
- start_index = i
- if end_tag in line:
- end_index = i
+ if tag_start in line:
+ index_start = i
+ if tag_end in line:
+ index_end = i
- if start_index == -1 or end_index == -1:
- print("Error: start or end tag not found")
+ if index_start == -1:
+ print(f'Error: start tag "{tag_start}" not found')
return None, None
- return lines, start_index
+ if index_end == -1:
+ print(f'Error: end tag "{tag_end}" not found')
+ return None, None
+ return lines, index_start
-def insert_components_between_tags(lines, start_index, app_name, build_dir):
+def insert_components_between_tags(lines, index_start, app_name, build_dir):
indent = g_indent_unit * 3
path = Path(build_dir)
idx = 1
@@ -77,58 +80,48 @@ def insert_components_between_tags(lines, start_index, app_name, build_dir):
{indent}{g_indent_unit}
{indent}
"""
- lines.insert(start_index + 1, to_insert_lines[1:])
- start_index += 1
+ lines.insert(index_start + 1, to_insert_lines[1:])
+ index_start += 1
idx += 1
return True
def gen_auto_component(app_name, build_dir):
- target_file = Path(sys.argv[0]).parent.joinpath("Package/Components/RustDesk.wxs")
- start_tag = ""
- end_tag = ""
-
- lines, start_index = read_lines_and_start_index(target_file, start_tag, end_tag)
- if lines is None:
- return False
-
- if not insert_components_between_tags(lines, start_index, app_name, build_dir):
- return False
-
- with open(target_file, "w") as f:
- f.writelines(lines)
-
- return True
+ return gen_content_between_tags(
+ "Package/Components/RustDesk.wxs",
+ "",
+ "",
+ lambda lines, index_start: insert_components_between_tags(
+ lines, index_start, app_name, build_dir
+ ),
+ )
def gen_pre_vars(args, build_dir):
- target_file = Path(sys.argv[0]).parent.joinpath("Package/Includes.wxi")
- start_tag = ""
- end_tag = ""
+ def func(lines, index_start):
+ upgrade_code = uuid.uuid5(uuid.NAMESPACE_OID, app_name + ".exe")
- lines, start_index = read_lines_and_start_index(target_file, start_tag, end_tag)
- if lines is None:
- return False
+ indent = g_indent_unit * 1
+ to_insert_lines = [
+ f'{indent}\n',
+ f'{indent}\n',
+ f'{indent}\n',
+ f'{indent}\n',
+ f'{indent}\n',
+ f'{indent}\n',
+ f'{indent}\n',
+ f'{indent}\n',
+ "\n",
+ f"{indent}\n"
+ f'{indent}\n',
+ ]
- indent = g_indent_unit * 1
- to_insert_lines = [
- f'{indent}\n',
- f'{indent}\n',
- f'{indent}\n',
- f'{indent}\n',
- f'{indent}\n',
- f'{indent}\n',
- f'{indent}\n',
- f'{indent}\n',
- ]
+ for i, line in enumerate(to_insert_lines):
+ lines.insert(index_start + i + 1, line)
- for i, line in enumerate(to_insert_lines):
- lines.insert(start_index + i + 1, line)
-
- with open(target_file, "w") as f:
- f.writelines(lines)
-
- return True
+ return gen_content_between_tags(
+ "Package/Includes.wxi", "", "", func
+ )
def replace_app_name_in_lans(app_name):
@@ -142,6 +135,44 @@ def replace_app_name_in_lans(app_name):
f.writelines(lines)
+def gen_upgrade_info(version):
+ def func(lines, index_start):
+ indent = g_indent_unit * 3
+
+ major, _, _ = version.split(".")
+ upgrade_id = uuid.uuid4()
+ to_insert_lines = [
+ f'{indent}\n',
+ f'{indent}" ?>\n',
+ f"{indent}\n",
+ ]
+
+ for i, line in enumerate(to_insert_lines):
+ lines.insert(index_start + i + 1, line)
+ return lines
+
+ return gen_content_between_tags(
+ "Package/Fragments/Upgrades.wxs",
+ "",
+ "",
+ func,
+ )
+
+
+def gen_content_between_tags(filename, tag_start, tag_end, func):
+ target_file = Path(sys.argv[0]).parent.joinpath(filename)
+ lines, index_start = read_lines_and_start_index(target_file, tag_start, tag_end)
+ if lines is None:
+ return False
+
+ func(lines, index_start)
+
+ with open(target_file, "w") as f:
+ f.writelines(lines)
+
+ return True
+
+
if __name__ == "__main__":
parser = make_parser()
args = parser.parse_args()
@@ -158,6 +189,9 @@ if __name__ == "__main__":
if not gen_pre_vars(args, build_dir):
sys.exit(-1)
+ if not gen_upgrade_info(args.version):
+ sys.exit(-1)
+
if not gen_auto_component(app_name, build_dir):
sys.exit(-1)