From 8231d077064e6b02a64bcb7e85a302981a10d9d6 Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 12 Apr 2024 17:42:26 +0800 Subject: [PATCH] Fix. Msi. Terminate brokers. (#7693) * Fix. Msi. Terminate brokers. Signed-off-by: fufesou * Fix. Msi, remove tray shortcut in startmenu Signed-off-by: fufesou * Msi. format Signed-off-by: fufesou * Feat. Msi, set property Signed-off-by: fufesou * Fix. Mis, only do InstallValidate if is Install Signed-off-by: fufesou --------- Signed-off-by: fufesou --- res/msi/CustomActions/Common.h | 15 ++ res/msi/CustomActions/CustomActions.cpp | 220 +++++++++++++++++- res/msi/CustomActions/CustomActions.def | 5 + res/msi/CustomActions/CustomActions.vcxproj | 3 + res/msi/CustomActions/ReadConfig.cpp | 36 +++ res/msi/CustomActions/ServiceUtils.cpp | 173 ++++++++++++++ res/msi/Package/Components/RustDesk.wxs | 75 ++++-- .../Package/Fragments/AddRemoveProperties.wxs | 5 +- res/msi/Package/Fragments/CustomActions.wxs | 25 +- .../Package/Fragments/ShortcutProperties.wxs | 65 +++--- res/msi/Package/Fragments/Upgrades.wxs | 10 +- res/msi/Package/Package.wxs | 20 +- res/msi/Package/UI/AnotherApp.wxs | 26 +-- res/msi/README.md | 1 + 14 files changed, 581 insertions(+), 98 deletions(-) create mode 100644 res/msi/CustomActions/Common.h create mode 100644 res/msi/CustomActions/ReadConfig.cpp create mode 100644 res/msi/CustomActions/ServiceUtils.cpp diff --git a/res/msi/CustomActions/Common.h b/res/msi/CustomActions/Common.h new file mode 100644 index 000000000..631fc5d38 --- /dev/null +++ b/res/msi/CustomActions/Common.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +bool AddFirewallRule(bool add, LPWSTR exeName, LPWSTR exeFile); + +bool IsServiceRunningW(LPCWSTR serviceName); +bool MyCreateServiceW(LPCWSTR serviceName, LPCWSTR displayName, LPCWSTR binaryPath); +bool MyDeleteServiceW(LPCWSTR serviceName); +bool MyStartServiceW(LPCWSTR serviceName); +bool MyStopServiceW(LPCWSTR serviceName); + +std::wstring ReadConfig(const std::wstring& filename, const std::wstring& key); + diff --git a/res/msi/CustomActions/CustomActions.cpp b/res/msi/CustomActions/CustomActions.cpp index 66ea6e473..7f7f24eee 100644 --- a/res/msi/CustomActions/CustomActions.cpp +++ b/res/msi/CustomActions/CustomActions.cpp @@ -8,6 +8,8 @@ #include #include +#include "./Common.h" + #pragma comment(lib, "Shlwapi.lib") UINT __stdcall CustomActionHello( @@ -271,7 +273,6 @@ void RemoveFirewallRuleCmdline(LPWSTR exeName) } } -bool AddFirewallRule(bool add, LPWSTR exeName, LPWSTR exeFile); UINT __stdcall AddFirewallRules( __in MSIHANDLE hInstall) { @@ -323,3 +324,220 @@ LExit: er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; return WcaFinalize(er); } + +UINT __stdcall SetPropertyIsServiceRunning(__in MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + wchar_t szAppName[500] = { 0 }; + DWORD cchAppName = sizeof(szAppName) / sizeof(szAppName[0]); + wchar_t szPropertyName[500] = { 0 }; + DWORD cchPropertyName = sizeof(szPropertyName) / sizeof(szPropertyName[0]); + bool isRunning = false; + + hr = WcaInitialize(hInstall, "SetPropertyIsServiceRunning"); + ExitOnFailure(hr, "Failed to initialize"); + + MsiGetPropertyW(hInstall, L"AppName", szAppName, &cchAppName); + WcaLog(LOGMSG_STANDARD, "Try query service of : \"%ls\"", szAppName); + + MsiGetPropertyW(hInstall, L"PropertyName", szPropertyName, &cchPropertyName); + WcaLog(LOGMSG_STANDARD, "Try set is service running, property name : \"%ls\"", szPropertyName); + + isRunning = IsServiceRunningW(szAppName); + MsiSetPropertyW(hInstall, szPropertyName, isRunning ? L"'N'" : L"'Y'"); + +LExit: + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} + +UINT __stdcall CreateStartService(__in MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + LPWSTR svcParams = NULL; + LPWSTR pwz = NULL; + LPWSTR pwzData = NULL; + LPWSTR svcName = NULL; + LPWSTR svcBinary = NULL; + wchar_t szSvcDisplayName[500] = { 0 }; + DWORD cchSvcDisplayName = sizeof(szSvcDisplayName) / sizeof(szSvcDisplayName[0]); + + hr = WcaInitialize(hInstall, "CreateStartService"); + ExitOnFailure(hr, "Failed to initialize"); + + hr = WcaGetProperty(L"CustomActionData", &pwzData); + ExitOnFailure(hr, "failed to get CustomActionData"); + + pwz = pwzData; + hr = WcaReadStringFromCaData(&pwz, &svcParams); + ExitOnFailure(hr, "failed to read database key from custom action data: %ls", pwz); + + WcaLog(LOGMSG_STANDARD, "Try create start service : %ls", svcParams); + + svcName = svcParams; + svcBinary = wcschr(svcParams, L';'); + if (svcBinary == NULL) { + WcaLog(LOGMSG_STANDARD, "Failed to find binary : %ls", svcParams); + goto LExit; + } + svcBinary[0] = L'\0'; + svcBinary += 1; + + hr = StringCchPrintfW(szSvcDisplayName, cchSvcDisplayName, L"%ls Service", svcName); + ExitOnFailure(hr, "Failed to compose a resource identifier string"); + if (MyCreateServiceW(svcName, szSvcDisplayName, svcBinary)) { + WcaLog(LOGMSG_STANDARD, "Service \"%ls\" is created.", svcName); + if (MyStartServiceW(svcName)) { + WcaLog(LOGMSG_STANDARD, "Service \"%ls\" is started.", svcName); + } + else { + WcaLog(LOGMSG_STANDARD, "Failed to start service: \"%ls\"", svcName); + } + } + else { + WcaLog(LOGMSG_STANDARD, "Failed to create service: \"%ls\"", svcName); + } + +LExit: + if (pwzData) { + ReleaseStr(pwzData); + } + + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} + +UINT __stdcall TryStopDeleteService(__in MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + int nResult = 0; + LPWSTR svcName = NULL; + LPWSTR pwz = NULL; + LPWSTR pwzData = NULL; + wchar_t szExeFile[500] = { 0 }; + DWORD cchExeFile = sizeof(szExeFile) / sizeof(szExeFile[0]); + + hr = WcaInitialize(hInstall, "TryStopDeleteService"); + ExitOnFailure(hr, "Failed to initialize"); + + hr = WcaGetProperty(L"CustomActionData", &pwzData); + ExitOnFailure(hr, "failed to get CustomActionData"); + + pwz = pwzData; + hr = WcaReadStringFromCaData(&pwz, &svcName); + ExitOnFailure(hr, "failed to read database key from custom action data: %ls", pwz); + WcaLog(LOGMSG_STANDARD, "Try stop and delete service : %ls", svcName); + + if (MyStopServiceW(svcName)) { + for (int i = 0; i < 10; i++) { + if (IsServiceRunningW(svcName)) { + Sleep(100); + } + else { + break; + } + } + WcaLog(LOGMSG_STANDARD, "Service \"%ls\" is stopped", svcName); + } + else { + WcaLog(LOGMSG_STANDARD, "Failed to stop service: \"%ls\"", svcName); + } + + if (MyDeleteServiceW(svcName)) { + WcaLog(LOGMSG_STANDARD, "Service \"%ls\" is deleted", svcName); + } + else { + WcaLog(LOGMSG_STANDARD, "Failed to delete service: \"%ls\"", svcName); + } + + // It's really strange that we need sleep here. + // But the upgrading may be stucked at "copying new files" because the file is in using. + // Steps to reproduce: Install -> stop service in tray --> start service -> upgrade + // Sleep(300); + + // Or we can terminate the process + hr = StringCchPrintfW(szExeFile, cchExeFile, L"%ls.exe", svcName); + ExitOnFailure(hr, "Failed to compose a resource identifier string"); + TerminateProcessesByNameW(szExeFile, L"--not-in-use"); + +LExit: + if (pwzData) { + ReleaseStr(pwzData); + } + + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} + +UINT __stdcall TryDeleteStartupShortcut(__in MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + wchar_t szShortcut[500] = { 0 }; + DWORD cchShortcut = sizeof(szShortcut) / sizeof(szShortcut[0]); + wchar_t szStartupDir[500] = { 0 }; + DWORD cchStartupDir = sizeof(szStartupDir) / sizeof(szStartupDir[0]); + WCHAR pwszTemp[1024] = L""; + + hr = WcaInitialize(hInstall, "DeleteStartupShortcut"); + ExitOnFailure(hr, "Failed to initialize"); + + MsiGetPropertyW(hInstall, L"StartupFolder", szStartupDir, &cchStartupDir); + + MsiGetPropertyW(hInstall, L"ShortcutName", szShortcut, &cchShortcut); + WcaLog(LOGMSG_STANDARD, "Try delete startup shortcut of : \"%ls\"", szShortcut); + + hr = StringCchPrintfW(pwszTemp, 1024, L"%ls%ls.lnk", szStartupDir, szShortcut); + ExitOnFailure(hr, "Failed to compose a resource identifier string"); + + if (DeleteFile(pwszTemp)) { + WcaLog(LOGMSG_STANDARD, "Failed to delete startup shortcut of : \"%ls\"", pwszTemp); + } + else { + WcaLog(LOGMSG_STANDARD, "Startup shortcut is deleted : \"%ls\"", pwszTemp); + } + +LExit: + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} + +UINT __stdcall SetPropertyFromConfig(__in MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + wchar_t szConfigFile[1024] = { 0 }; + DWORD cchConfigFile = sizeof(szConfigFile) / sizeof(szConfigFile[0]); + wchar_t szConfigKey[500] = { 0 }; + DWORD cchConfigKey = sizeof(szConfigKey) / sizeof(szConfigKey[0]); + wchar_t szPropertyName[500] = { 0 }; + DWORD cchPropertyName = sizeof(szPropertyName) / sizeof(szPropertyName[0]); + std::wstring configValue; + + hr = WcaInitialize(hInstall, "SetPropertyFromConfig"); + ExitOnFailure(hr, "Failed to initialize"); + + MsiGetPropertyW(hInstall, L"ConfigFile", szConfigFile, &cchConfigFile); + WcaLog(LOGMSG_STANDARD, "Try read config file of : \"%ls\"", szConfigFile); + + MsiGetPropertyW(hInstall, L"ConfigKey", szConfigKey, &cchConfigKey); + WcaLog(LOGMSG_STANDARD, "Try read configuration, config key : \"%ls\"", szConfigKey); + + MsiGetPropertyW(hInstall, L"PropertyName", szPropertyName, &cchPropertyName); + WcaLog(LOGMSG_STANDARD, "Try read configuration, property name : \"%ls\"", szPropertyName); + + configValue = ReadConfig(szConfigFile, szConfigKey); + MsiSetPropertyW(hInstall, szPropertyName, configValue.c_str()); + +LExit: + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} diff --git a/res/msi/CustomActions/CustomActions.def b/res/msi/CustomActions/CustomActions.def index 31a10eb63..a089c583e 100644 --- a/res/msi/CustomActions/CustomActions.def +++ b/res/msi/CustomActions/CustomActions.def @@ -5,3 +5,8 @@ EXPORTS RemoveInstallFolder TerminateProcesses AddFirewallRules + SetPropertyIsServiceRunning + TryStopDeleteService + CreateStartService + TryDeleteStartupShortcut + SetPropertyFromConfig diff --git a/res/msi/CustomActions/CustomActions.vcxproj b/res/msi/CustomActions/CustomActions.vcxproj index fcadf9a76..de16f1039 100644 --- a/res/msi/CustomActions/CustomActions.vcxproj +++ b/res/msi/CustomActions/CustomActions.vcxproj @@ -54,6 +54,7 @@ + @@ -64,6 +65,8 @@ Create + + diff --git a/res/msi/CustomActions/ReadConfig.cpp b/res/msi/CustomActions/ReadConfig.cpp new file mode 100644 index 000000000..695ebcada --- /dev/null +++ b/res/msi/CustomActions/ReadConfig.cpp @@ -0,0 +1,36 @@ +#include "pch.h" + +#include +#include +#include +#include + +void trim(std::wstring& str) { + str.erase(str.begin(), std::find_if(str.begin(), str.end(), [](wchar_t ch) { + return !std::iswspace(ch); + })); + str.erase(std::find_if(str.rbegin(), str.rend(), [](wchar_t ch) { + return !std::iswspace(ch); + }).base(), str.end()); +} + +std::wstring ReadConfig(const std::wstring& filename, const std::wstring& key) +{ + std::wstring configValue; + std::wstring line; + std::wifstream file(filename); + while (std::getline(file, line)) { + trim(line); + if (line.find(key) == 0) { + std::size_t position = line.find(L"=", key.size()); + if (position != std::string::npos) { + configValue = line.substr(position + 1); + trim(configValue); + break; + } + } + } + + file.close(); + return configValue; +} diff --git a/res/msi/CustomActions/ServiceUtils.cpp b/res/msi/CustomActions/ServiceUtils.cpp new file mode 100644 index 000000000..5bf8c9fe5 --- /dev/null +++ b/res/msi/CustomActions/ServiceUtils.cpp @@ -0,0 +1,173 @@ +// https://learn.microsoft.com/en-us/windows/win32/services/installing-a-service + +#include "pch.h" + +#include +#include +#include + +bool MyCreateServiceW(LPCWSTR serviceName, LPCWSTR displayName, LPCWSTR binaryPath) +{ + SC_HANDLE schSCManager; + SC_HANDLE schService; + + // Get a handle to the SCM database. + schSCManager = OpenSCManager( + NULL, // local computer + NULL, // ServicesActive database + SC_MANAGER_ALL_ACCESS); // full access rights + + if (NULL == schSCManager) + { + WcaLog(LOGMSG_STANDARD, "OpenSCManager failed (%d)\n", GetLastError()); + return false; + } + + // Create the service + schService = CreateService( + schSCManager, // SCM database + serviceName, // name of service + displayName, // service name to display + SERVICE_ALL_ACCESS, // desired access + SERVICE_WIN32_OWN_PROCESS, // service type + SERVICE_AUTO_START, // start type + SERVICE_ERROR_NORMAL, // error control type + binaryPath, // path to service's binary + NULL, // no load ordering group + NULL, // no tag identifier + NULL, // no dependencies + NULL, // LocalSystem account + NULL); // no password + if (schService == NULL) + { + WcaLog(LOGMSG_STANDARD, "CreateService failed (%d)\n", GetLastError()); + CloseServiceHandle(schSCManager); + return false; + } + else + { + WcaLog(LOGMSG_STANDARD, "Service installed successfully\n"); + } + + CloseServiceHandle(schService); + CloseServiceHandle(schSCManager); + return true; +} + +bool MyDeleteServiceW(LPCWSTR serviceName) +{ + SC_HANDLE hSCManager = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT); + if (hSCManager == NULL) { + WcaLog(LOGMSG_STANDARD, "Failed to open Service Control Manager"); + return false; + } + + SC_HANDLE hService = OpenServiceW(hSCManager, serviceName, SERVICE_STOP | DELETE); + if (hService == NULL) { + WcaLog(LOGMSG_STANDARD, "Failed to open service: %ls", serviceName); + CloseServiceHandle(hSCManager); + return false; + } + + SERVICE_STATUS serviceStatus; + if (ControlService(hService, SERVICE_CONTROL_STOP, &serviceStatus)) { + WcaLog(LOGMSG_STANDARD, "Stopping service: %ls", serviceName); + } + + bool success = DeleteService(hService); + if (!success) { + WcaLog(LOGMSG_STANDARD, "Failed to delete service: %ls", serviceName); + } + + CloseServiceHandle(hService); + CloseServiceHandle(hSCManager); + + return success; +} + +bool MyStartServiceW(LPCWSTR serviceName) +{ + SC_HANDLE hSCManager = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT); + if (hSCManager == NULL) { + WcaLog(LOGMSG_STANDARD, "Failed to open Service Control Manager"); + return false; + } + + SC_HANDLE hService = OpenServiceW(hSCManager, serviceName, SERVICE_START); + if (hService == NULL) { + WcaLog(LOGMSG_STANDARD, "Failed to open service: %ls", serviceName); + CloseServiceHandle(hSCManager); + return false; + } + + bool success = StartService(hService, 0, NULL); + if (!success) { + WcaLog(LOGMSG_STANDARD, "Failed to start service: %ls", serviceName); + } + + CloseServiceHandle(hService); + CloseServiceHandle(hSCManager); + + return success; +} + +bool MyStopServiceW(LPCWSTR serviceName) +{ + SC_HANDLE hSCManager = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT); + if (hSCManager == NULL) { + WcaLog(LOGMSG_STANDARD, "Failed to open Service Control Manager"); + return false; + } + + SC_HANDLE hService = OpenServiceW(hSCManager, serviceName, SERVICE_STOP); + if (hService == NULL) { + WcaLog(LOGMSG_STANDARD, "Failed to open service: %ls", serviceName); + CloseServiceHandle(hSCManager); + return false; + } + + SERVICE_STATUS serviceStatus; + if (!ControlService(hService, SERVICE_CONTROL_STOP, &serviceStatus)) { + WcaLog(LOGMSG_STANDARD, "Failed to stop service: %ls", serviceName); + CloseServiceHandle(hService); + CloseServiceHandle(hSCManager); + return false; + } + + CloseServiceHandle(hService); + CloseServiceHandle(hSCManager); + + return true; +} + +bool IsServiceRunningW(LPCWSTR serviceName) +{ + SC_HANDLE hSCManager = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT); + if (hSCManager == NULL) { + WcaLog(LOGMSG_STANDARD, "Failed to open Service Control Manager"); + return false; + } + + SC_HANDLE hService = OpenServiceW(hSCManager, serviceName, SERVICE_QUERY_STATUS); + if (hService == NULL) { + WcaLog(LOGMSG_STANDARD, "Failed to open service: %ls", serviceName); + CloseServiceHandle(hSCManager); + return false; + } + + SERVICE_STATUS_PROCESS serviceStatus; + DWORD bytesNeeded; + if (!QueryServiceStatusEx(hService, SC_STATUS_PROCESS_INFO, reinterpret_cast(&serviceStatus), sizeof(serviceStatus), &bytesNeeded)) { + WcaLog(LOGMSG_STANDARD, "Failed to query service: %ls", serviceName); + CloseServiceHandle(hService); + CloseServiceHandle(hSCManager); + return false; + } + + bool isRunning = (serviceStatus.dwCurrentState == SERVICE_RUNNING); + + CloseServiceHandle(hService); + CloseServiceHandle(hSCManager); + + return isRunning; +} diff --git a/res/msi/Package/Components/RustDesk.wxs b/res/msi/Package/Components/RustDesk.wxs index eab059804..a44d8bb73 100644 --- a/res/msi/Package/Components/RustDesk.wxs +++ b/res/msi/Package/Components/RustDesk.wxs @@ -9,35 +9,67 @@ - - - - - + + + + + + + + + + + + + + - + + + + + + + + + + + - + + + + + + + + - - + + - - - - + + + + + + + + + + + + @@ -58,11 +90,6 @@ - - - - - @@ -74,6 +101,12 @@ + + + + + + - + diff --git a/res/msi/Package/Fragments/AddRemoveProperties.wxs b/res/msi/Package/Fragments/AddRemoveProperties.wxs index 5711aa5a8..f568b963d 100644 --- a/res/msi/Package/Fragments/AddRemoveProperties.wxs +++ b/res/msi/Package/Fragments/AddRemoveProperties.wxs @@ -6,6 +6,9 @@ + + + - + diff --git a/res/msi/Package/Fragments/CustomActions.wxs b/res/msi/Package/Fragments/CustomActions.wxs index 29ae04492..0454a6731 100644 --- a/res/msi/Package/Fragments/CustomActions.wxs +++ b/res/msi/Package/Fragments/CustomActions.wxs @@ -1,14 +1,19 @@  - - + + - + - - - - - - - + + + + + + + + + + + + diff --git a/res/msi/Package/Fragments/ShortcutProperties.wxs b/res/msi/Package/Fragments/ShortcutProperties.wxs index 1c4711127..e800262fb 100644 --- a/res/msi/Package/Fragments/ShortcutProperties.wxs +++ b/res/msi/Package/Fragments/ShortcutProperties.wxs @@ -1,51 +1,52 @@  - + - + - - - - + + + - - - - - - - + + + + + + - - - - - - - - - + + + + + + + + + - - - + + - - - + + + - - - + + + - + diff --git a/res/msi/Package/Fragments/Upgrades.wxs b/res/msi/Package/Fragments/Upgrades.wxs index ab1118513..8109efd9c 100644 --- a/res/msi/Package/Fragments/Upgrades.wxs +++ b/res/msi/Package/Fragments/Upgrades.wxs @@ -1,10 +1,10 @@  - + - + - - + + - + diff --git a/res/msi/Package/Package.wxs b/res/msi/Package/Package.wxs index 9a37b3092..bc07ca536 100644 --- a/res/msi/Package/Package.wxs +++ b/res/msi/Package/Package.wxs @@ -9,7 +9,7 @@ - + @@ -26,25 +26,15 @@ - - - - - - - - - + + + - - - - - + diff --git a/res/msi/Package/UI/AnotherApp.wxs b/res/msi/Package/UI/AnotherApp.wxs index fdf184e6d..ea46812f7 100644 --- a/res/msi/Package/UI/AnotherApp.wxs +++ b/res/msi/Package/UI/AnotherApp.wxs @@ -1,15 +1,15 @@ - - - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/res/msi/README.md b/res/msi/README.md index 2650ad69b..5ff2a1080 100644 --- a/res/msi/README.md +++ b/res/msi/README.md @@ -39,5 +39,6 @@ Run `msiexec /i package.msi /l*v install.log` to record the log. ## Refs +1. [windows-installer-portal](https://learn.microsoft.com/en-us/windows/win32/Msi/windows-installer-portal) 1. [wxs](https://wixtoolset.org/docs/schema/wxs/) 1. [wxs github](https://github.com/wixtoolset/wix)