diff options
-rw-r--r-- | Repository.mk | 1 | ||||
-rw-r--r-- | RepositoryExternal.mk | 4 | ||||
-rw-r--r-- | config_host.mk.in | 3 | ||||
-rw-r--r-- | configure.ac | 44 | ||||
-rw-r--r-- | external/msc-externals/Module_msc-externals.mk | 6 | ||||
-rw-r--r-- | external/msc-externals/Package_ucrt.mk (renamed from external/msc-externals/Package_vcredist_exe.mk) | 11 | ||||
-rw-r--r-- | instsetoo_native/inc_openoffice/windows/msi_templates/Property.idt | 2 | ||||
-rw-r--r-- | postprocess/signing/no_signing.txt | 8 | ||||
-rw-r--r-- | scp2/InstallModule_windows.mk | 9 | ||||
-rw-r--r-- | scp2/source/ooo/ucrt.scp | 154 | ||||
-rw-r--r-- | scp2/source/ooo/vc_redist.scp | 23 | ||||
-rw-r--r-- | setup_native/Library_inst_msu_msi.mk | 40 | ||||
-rw-r--r-- | setup_native/Module_setup_native.mk | 1 | ||||
-rw-r--r-- | setup_native/source/win32/customactions/inst_msu/inst_msu.cxx | 515 | ||||
-rw-r--r-- | setup_native/source/win32/customactions/inst_msu/inst_msu_msi.def | 5 | ||||
-rw-r--r-- | solenv/bin/modules/installer/windows/idtglobal.pm | 11 | ||||
-rw-r--r-- | solenv/bin/modules/installer/windows/upgrade.pm | 12 |
17 files changed, 768 insertions, 81 deletions
diff --git a/Repository.mk b/Repository.mk index 69659692d042..01c51c7a9db0 100644 --- a/Repository.mk +++ b/Repository.mk @@ -672,6 +672,7 @@ endif $(eval $(call gb_Helper_register_libraries_for_install,PLAINLIBS_OOO,ooobinarytable, \ $(if $(WINDOWS_SDK_HOME),\ instooofiltmsi \ + inst_msu_msi \ qslnkmsi \ reg4allmsdoc \ sdqsmsi \ diff --git a/RepositoryExternal.mk b/RepositoryExternal.mk index aecbd71b8b43..514dc485a8f6 100644 --- a/RepositoryExternal.mk +++ b/RepositoryExternal.mk @@ -4165,8 +4165,8 @@ $(eval $(call gb_Helper_register_libraries_for_install,OOOLIBS,ooo,\ )) endif -$(eval $(call gb_Helper_register_packages_for_install,vcredist_exe_binarytable,\ - $(if $(VCREDIST_DIR),vcredist_exe) \ +$(eval $(call gb_Helper_register_packages_for_install,ucrt_binarytable,\ + $(if $(UCRT_REDISTDIR),ucrt) \ )) # vim: set noet sw=4 ts=4: diff --git a/config_host.mk.in b/config_host.mk.in index a031f962a3da..70a428378050 100644 --- a/config_host.mk.in +++ b/config_host.mk.in @@ -591,8 +591,7 @@ export TMPDIR=@TEMP_DIRECTORY@ export TOUCH=@TOUCH@ export UCRTSDKDIR=@UCRTSDKDIR@ export UCRTVERSION=@UCRTVERSION@ -export VCREDIST_DIR=@VCREDIST_DIR@ -export VCREDIST_EXE=@VCREDIST_EXE@ +export UCRT_REDISTDIR=@UCRT_REDISTDIR@ export UNOWINREG_DLL=@UNOWINREG_DLL@ export USE_LIBRARY_BIN_TAR=@USE_LIBRARY_BIN_TAR@ export USE_XINERAMA=@USE_XINERAMA@ diff --git a/configure.ac b/configure.ac index d0240806303f..7ccc9f1997c6 100644 --- a/configure.ac +++ b/configure.ac @@ -6640,13 +6640,21 @@ fi AC_SUBST([JITC_PROCESSOR_TYPE]) # Misc Windows Stuff -AC_ARG_WITH(vcredist-dir, - AS_HELP_STRING([--with-vcredist-dir], - [path to the directory with the arch-specific executables (vc_redist.x64.exe, vc_redist.x86.exe) - for packaging into the installsets (without those the target system needs to install - the Visual C++ Runtimes manually)]), +AC_ARG_WITH(ucrt-dir, + AS_HELP_STRING([--with-ucrt-dir], + [path to the directory with the arch-specific MSU packages of the Windows Universal CRT redistributables + (MS KB 2999226) for packaging into the installsets (without those the target system needs to install + the UCRT or Visual C++ Runtimes manually). The directory must contain the following 6 files: + Windows6.1-KB2999226-x64.msu + Windows6.1-KB2999226-x86.msu + Windows8.1-KB2999226-x64.msu + Windows8.1-KB2999226-x86.msu + Windows8-RT-KB2999226-x64.msu + Windows8-RT-KB2999226-x86.msu + A zip archive including those files is available from Microsoft site: + https://www.microsoft.com/en-us/download/details.aspx?id=48234]), ,) -VCREDIST_DIR="$with_vcredist_dir" +UCRT_REDISTDIR="$with_ucrt_dir" if test $_os = "WINNT"; then find_msvc_x64_dlls find_msms @@ -6659,20 +6667,23 @@ if test $_os = "WINNT"; then else SCPDEFS="$SCPDEFS -DWITH_VC${VCVER}_REDIST" fi - if test "$VCREDIST_DIR" = "no"; then + if test "$UCRT_REDISTDIR" = "no"; then dnl explicitly disabled - VCREDIST_DIR="" - else - if test -f "$VCREDIST_DIR/vc_redist.$WINDOWS_SDK_ARCH.exe"; then - VCREDIST_EXE="vc_redist.$WINDOWS_SDK_ARCH.exe" - else - VCREDIST_DIR="" + UCRT_REDISTDIR="" + else + if ! test -f "$UCRT_REDISTDIR/Windows6.1-KB2999226-x64.msu" -a \ + -f "$UCRT_REDISTDIR/Windows6.1-KB2999226-x86.msu" -a \ + -f "$UCRT_REDISTDIR/Windows8.1-KB2999226-x64.msu" -a \ + -f "$UCRT_REDISTDIR/Windows8.1-KB2999226-x86.msu" -a \ + -f "$UCRT_REDISTDIR/Windows8-RT-KB2999226-x64.msu" -a \ + -f "$UCRT_REDISTDIR/Windows8-RT-KB2999226-x86.msu"; then + UCRT_REDISTDIR="" if test -n "$PKGFORMAT"; then for i in $PKGFORMAT; do case "$i" in msi) - AC_MSG_WARN([--without-vcredist-dir not specified or exe not found - installer will have runtime dependency]) - add_warning "--without-vcredist-dir not specified or exe not found - installer will have runtime dependency" + AC_MSG_WARN([--without-ucrt-dir not specified or MSUs not found - installer will have runtime dependency]) + add_warning "--without-ucrt-dir not specified or MSUs not found - installer will have runtime dependency" ;; esac done @@ -6681,8 +6692,7 @@ if test $_os = "WINNT"; then fi fi -AC_SUBST(VCREDIST_DIR) -AC_SUBST(VCREDIST_EXE) +AC_SUBST(UCRT_REDISTDIR) AC_SUBST(MSVC_DLL_PATH) AC_SUBST(MSVC_DLLS) AC_SUBST(MSM_PATH) diff --git a/external/msc-externals/Module_msc-externals.mk b/external/msc-externals/Module_msc-externals.mk index b7f7f47034f9..07ea3878a106 100644 --- a/external/msc-externals/Module_msc-externals.mk +++ b/external/msc-externals/Module_msc-externals.mk @@ -17,11 +17,11 @@ $(eval $(call gb_Module_add_targets,msc-externals,\ endif -# Install the universal crts and VC runtimes (tdf#108580) -ifneq ($(VCREDIST_DIR),) +# Install the universal crts (tdf#108580) +ifneq ($(UCRT_REDISTDIR),) $(eval $(call gb_Module_add_targets,msc-externals,\ - Package_vcredist_exe \ + Package_ucrt \ )) endif diff --git a/external/msc-externals/Package_vcredist_exe.mk b/external/msc-externals/Package_ucrt.mk index bf9ef632285c..52e6f0cbae97 100644 --- a/external/msc-externals/Package_vcredist_exe.mk +++ b/external/msc-externals/Package_ucrt.mk @@ -7,10 +7,15 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. # -$(eval $(call gb_Package_Package,vcredist_exe,$(VCREDIST_DIR))) +$(eval $(call gb_Package_Package,ucrt,$(UCRT_REDISTDIR))) -$(eval $(call gb_Package_add_files,vcredist_exe,$(LIBO_ETC_FOLDER),\ - $(VCREDIST_EXE) \ +$(eval $(call gb_Package_add_files,ucrt,$(LIBO_ETC_FOLDER),\ + Windows6.1-KB2999226-x64.msu \ + Windows6.1-KB2999226-x86.msu \ + Windows8.1-KB2999226-x64.msu \ + Windows8.1-KB2999226-x86.msu \ + Windows8-RT-KB2999226-x64.msu \ + Windows8-RT-KB2999226-x86.msu \ )) # vim:set shiftwidth=4 tabstop=4 noexpandtab: diff --git a/instsetoo_native/inc_openoffice/windows/msi_templates/Property.idt b/instsetoo_native/inc_openoffice/windows/msi_templates/Property.idt index 12ba10f300c9..32ada67082b4 100644 --- a/instsetoo_native/inc_openoffice/windows/msi_templates/Property.idt +++ b/instsetoo_native/inc_openoffice/windows/msi_templates/Property.idt @@ -43,7 +43,7 @@ ProgressType3 installs Quickstarterlinkname QUICKSTARTERLINKNAMETEMPLATE RebootYesNo Yes ReinstallModeText omus -SecureCustomProperties NEWPRODUCTS;OLDPRODUCTS;VCRUNTIME_DETECTED +SecureCustomProperties NEWPRODUCTS;OLDPRODUCTS SetupType Typical SELECT_WORD 0 SELECT_EXCEL 0 diff --git a/postprocess/signing/no_signing.txt b/postprocess/signing/no_signing.txt index 2fa129a80bab..6de68e2e8cc2 100644 --- a/postprocess/signing/no_signing.txt +++ b/postprocess/signing/no_signing.txt @@ -8,5 +8,9 @@ policy.1.0.cli_oootypes.dll policy.1.0.cli_ure.dll policy.1.0.cli_cppuhelper.dll policy.1.0.cli_basetypes.dll -vc_redist.x64.exe -vc_redist.x86.exe +Windows6.1-KB2999226-x64.msu +Windows6.1-KB2999226-x86.msu +Windows8.1-KB2999226-x64.msu +Windows8.1-KB2999226-x86.msu +Windows8-RT-KB2999226-x64.msu +Windows8-RT-KB2999226-x86.msu diff --git a/scp2/InstallModule_windows.mk b/scp2/InstallModule_windows.mk index ecba37c42bca..bd6e478e7785 100644 --- a/scp2/InstallModule_windows.mk +++ b/scp2/InstallModule_windows.mk @@ -17,17 +17,12 @@ $(eval $(call gb_InstallModule_add_defs,scp2/windows,\ $(if $(WINDOWS_SDK_HOME),\ -DHAVE_WINDOWS_SDK \ ) \ - $(if $(MSM_PATH),\ - -DMSM_PATH \ - ) \ - $(if $(VCREDIST_DIR),\ - -DVCREDIST_EXE_NAME="$(VCREDIST_EXE)" \ - ) \ )) $(eval $(call gb_InstallModule_add_scpfiles,scp2/windows,\ scp2/source/ooo/folder_ooo \ - scp2/source/ooo/vc_redist \ + $(if $(MSM_PATH),scp2/source/ooo/vc_redist) \ + $(if $(UCRT_REDISTDIR),scp2/source/ooo/ucrt) \ scp2/source/ooo/windowscustomaction_ooo \ )) diff --git a/scp2/source/ooo/ucrt.scp b/scp2/source/ooo/ucrt.scp new file mode 100644 index 000000000000..4a13309f6364 --- /dev/null +++ b/scp2/source/ooo/ucrt.scp @@ -0,0 +1,154 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "macros.inc" + +File gid_File_Windows6_1_KB2999226_x64_msu + Name = "Windows6.1-KB2999226-x64.msu"; + Dir = gid_Brand_Dir_Program; + Styles = (PACKED, BINARYTABLE, BINARYTABLE_ONLY); +End + +File gid_File_Windows8_RT_KB2999226_x64_msu + Name = "Windows8-RT-KB2999226-x64.msu"; + Dir = gid_Brand_Dir_Program; + Styles = (PACKED, BINARYTABLE, BINARYTABLE_ONLY); +End + +File gid_File_Windows8_1_KB2999226_x64_msu + Name = "Windows8.1-KB2999226-x64.msu"; + Dir = gid_Brand_Dir_Program; + Styles = (PACKED, BINARYTABLE, BINARYTABLE_ONLY); +End + +#ifndef WINDOWS_X64 + +File gid_File_Windows6_1_KB2999226_x86_msu + Name = "Windows6.1-KB2999226-x86.msu"; + Dir = gid_Brand_Dir_Program; + Styles = (PACKED, BINARYTABLE, BINARYTABLE_ONLY); +End + +File gid_File_Windows8_RT_KB2999226_x86_msu + Name = "Windows8-RT-KB2999226-x86.msu"; + Dir = gid_Brand_Dir_Program; + Styles = (PACKED, BINARYTABLE, BINARYTABLE_ONLY); +End + +File gid_File_Windows8_1_KB2999226_x86_msu + Name = "Windows8.1-KB2999226-x86.msu"; + Dir = gid_Brand_Dir_Program; + Styles = (PACKED, BINARYTABLE, BINARYTABLE_ONLY); +End + +#endif /* WINDOWS_X64 */ + +/* A deferred not-impersonated action that will call wusa.exe to actually install + msu. Since deferred actions don't have access to current DB, the action depends + on immediate-executed action inst_ucrt (see below) that precedes it, unpacks + the binary to a temp file, and sets this action's CustomActionData property. +*/ +WindowsCustomAction gid_Customaction_inst_msu + Name = "inst_msu"; + Typ = "3073"; + Source = "inst_msu_msi.dll"; + Target = "InstallMSU"; + Inbinarytable = 1; + Assignment1 = ("InstallExecuteSequence", "Not Installed And inst_msu", "InstallFiles"); +End + +/* An immediately-executed action that will unpack a binary, which name in binary table is set + in "InstMSUBinary" property, to a temporary file, and sets "inst_msu" and "cleanup_msu" props. +*/ +WindowsCustomAction gid_Customaction_unpack_msu + Name = "unpack_msu"; + Typ = "1"; + Source = "inst_msu_msi.dll"; + Target = "UnpackMSUForInstall"; + Inbinarytable = 1; + Assignment1 = ("InstallExecuteSequence", "Not Installed And InstMSUBinary", "cleanup_msu"); +End + +/* A rollback action that removes temp file. It must precede inst_msu. +*/ +WindowsCustomAction gid_Customaction_cleanup_msu + Name = "cleanup_msu"; + Typ = "1345"; + Source = "inst_msu_msi.dll"; + Target = "CleanupMSU"; + Inbinarytable = 1; + Assignment1 = ("InstallExecuteSequence", "Not Installed And cleanup_msu", "inst_msu"); +End + +WindowsCustomAction gid_Customaction_check_win7x64_ucrt + Name = "check_win7x64_ucrt"; + Typ = "51"; + Source = "InstMSUBinary"; + Target = "Windows61-KB2999226-x64msu"; + Inbinarytable = 0; + Assignment1 = ("InstallExecuteSequence", "Not Installed And VersionNT = 601 And VersionNT64 And Not UCRT_DETECTED", "FileCost"); + Styles = "NO_FILE"; +End + +WindowsCustomAction gid_Customaction_check_win8x64_ucrt + Name = "check_win8x64_ucrt"; + Typ = "51"; + Source = "InstMSUBinary"; + Target = "Windows8-RT-KB2999226-x64msu"; + Inbinarytable = 0; + Assignment1 = ("InstallExecuteSequence", "Not Installed And VersionNT = 602 And VersionNT64 And Not UCRT_DETECTED", "check_win7x64_ucrt"); + Styles = "NO_FILE"; +End + +WindowsCustomAction gid_Customaction_check_win81x64_ucrt + Name = "check_win81x64_ucrt"; + Typ = "51"; + Source = "InstMSUBinary"; + Target = "Windows81-KB2999226-x64msu"; + Inbinarytable = 0; + Assignment1 = ("InstallExecuteSequence", "Not Installed And VersionNT = 603 And VersionNT64 And Not UCRT_DETECTED", "check_win8x64_ucrt"); + Styles = "NO_FILE"; +End + +#ifndef WINDOWS_X64 + +/* 32-bit installer must be prepared to run on both 32- and 64-bit Windows. So, it might need to + install either 32-bit or 64-bit UCRT package, depending on OS bitness. +*/ + +WindowsCustomAction gid_Customaction_check_win7x32_ucrt + Name = "check_win7x32_ucrt"; + Typ = "51"; + Source = "InstMSUBinary"; + Target = "Windows61-KB2999226-x86msu"; + Inbinarytable = 0; + Assignment1 = ("InstallExecuteSequence", "Not Installed And VersionNT = 601 And Not VersionNT64 And Not UCRT_DETECTED", "check_win81x64_ucrt"); + Styles = "NO_FILE"; +End + +WindowsCustomAction gid_Customaction_check_win8x32_ucrt + Name = "check_win8x32_ucrt"; + Typ = "51"; + Source = "InstMSUBinary"; + Target = "Windows8-RT-KB2999226-x86msu"; + Inbinarytable = 0; + Assignment1 = ("InstallExecuteSequence", "Not Installed And VersionNT = 602 And Not VersionNT64 And Not UCRT_DETECTED", "check_win7x32_ucrt"); + Styles = "NO_FILE"; +End + +WindowsCustomAction gid_Customaction_check_win81x32_ucrt + Name = "check_win81x32_ucrt"; + Typ = "51"; + Source = "InstMSUBinary"; + Target = "Windows81-KB2999226-x86msu"; + Inbinarytable = 0; + Assignment1 = ("InstallExecuteSequence", "Not Installed And VersionNT = 603 And Not VersionNT64 And Not UCRT_DETECTED", "check_win8x32_ucrt"); + Styles = "NO_FILE"; +End + +#endif /* WINDOWS_X64 */ diff --git a/scp2/source/ooo/vc_redist.scp b/scp2/source/ooo/vc_redist.scp index 921a9433e820..727c13b27615 100644 --- a/scp2/source/ooo/vc_redist.scp +++ b/scp2/source/ooo/vc_redist.scp @@ -18,8 +18,6 @@ #include "macros.inc" -#if defined(MSM_PATH) - #if defined(WITH_VC140_REDIST) #if defined WINDOWS_X64 @@ -76,24 +74,3 @@ MergeModule gid_MergeModule_Microsoft_VC141_CRT_x86 End #endif - -#endif // MSM_PATH - -#if defined(VCREDIST_EXE_NAME) - -File gid_File_Vcredist_Exe - Name = VCREDIST_EXE_NAME; - Dir = gid_Brand_Dir_Program; - Styles = (PACKED, BINARYTABLE, BINARYTABLE_ONLY); -End - -WindowsCustomAction gid_Customaction_InstallVCRedist - Name = "InstallVCRedist"; - Typ = "66"; /* 0x02 = exe in a binary table stream; 0x40 = ignore exit code and continue */ - Source = VCREDIST_EXE_NAME; - Target = "/repair /norestart /passive"; - Inbinarytable = 1; - Assignment1 = ("InstallUISequence", "Not Installed And Not ( VCRUNTIME_DETECTED And UCRT_DETECTED ) And VC_REDIST = 1", "behind_ExecuteAction"); -End - -#endif diff --git a/setup_native/Library_inst_msu_msi.mk b/setup_native/Library_inst_msu_msi.mk new file mode 100644 index 000000000000..d423b5168697 --- /dev/null +++ b/setup_native/Library_inst_msu_msi.mk @@ -0,0 +1,40 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +$(eval $(call gb_Library_Library,inst_msu_msi)) + +$(eval $(call gb_Library_add_defs,inst_msu_msi,\ + -U_DLL \ +)) + +$(eval $(call gb_Library_add_cxxflags,inst_msu_msi,\ + $(if $(MSVC_USE_DEBUG_RUNTIME),/MTd,/MT) \ +)) + +$(eval $(call gb_Library_add_ldflags,inst_msu_msi,\ + /DEF:$(SRCDIR)/setup_native/source/win32/customactions/inst_msu/inst_msu_msi.def \ + /NODEFAULTLIB \ +)) + +$(eval $(call gb_Library_add_exception_objects,inst_msu_msi,\ + setup_native/source/win32/customactions/inst_msu/inst_msu \ +)) + +$(eval $(call gb_Library_use_system_win32_libs,inst_msu_msi,\ + libcmt \ + libcpmt \ + libucrt \ + libvcruntime \ + kernel32 \ + Ole32 \ + Shell32 \ + Msi \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/setup_native/Module_setup_native.mk b/setup_native/Module_setup_native.mk index 8e11f4d61425..1009c53dcb2d 100644 --- a/setup_native/Module_setup_native.mk +++ b/setup_native/Module_setup_native.mk @@ -23,6 +23,7 @@ $(eval $(call gb_Module_add_targets,setup_native,\ ifeq ($(OS),WNT) $(eval $(call gb_Module_add_targets,setup_native,\ Library_instooofiltmsi \ + Library_inst_msu_msi \ Library_qslnkmsi \ Library_reg4allmsdoc \ Library_regactivex \ diff --git a/setup_native/source/win32/customactions/inst_msu/inst_msu.cxx b/setup_native/source/win32/customactions/inst_msu/inst_msu.cxx new file mode 100644 index 000000000000..b03d3cf3791c --- /dev/null +++ b/setup_native/source/win32/customactions/inst_msu/inst_msu.cxx @@ -0,0 +1,515 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <memory> +#include <string> +#include <sstream> +#include <iomanip> + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <Shlobj.h> +#include <Wuerror.h> +#include <msiquery.h> + +namespace +{ +template <typename IntType> std::string Num2Hex(IntType n) +{ + std::stringstream sMsg; + sMsg << "0x" << std::uppercase << std::setfill('0') << std::setw(sizeof(n) * 2) << std::hex + << n; + return sMsg.str(); +} + +template <typename IntType> std::string Num2Dec(IntType n) +{ + std::stringstream sMsg; + sMsg << n; + return sMsg.str(); +} + +void ThrowHResult(const char* sFunc, HRESULT hr) +{ + std::stringstream sMsg; + sMsg << sFunc << " failed (HRESULT = " << Num2Hex(hr) << ")!"; + + throw std::exception(sMsg.str().c_str()); +} + +void CheckHResult(const char* sFunc, HRESULT hr) +{ + if (FAILED(hr)) + ThrowHResult(sFunc, hr); +} + +void ThrowWin32Error(const char* sFunc, DWORD nWin32Error) +{ + std::stringstream sMsg; + sMsg << sFunc << " failed with Win32 error code " << Num2Hex(nWin32Error) << "!"; + + throw std::exception(sMsg.str().c_str()); +} + +void ThrowLastError(const char* sFunc) { ThrowWin32Error(sFunc, GetLastError()); } + +void CheckWin32Error(const char* sFunc, DWORD nWin32Error) +{ + if (nWin32Error != ERROR_SUCCESS) + ThrowWin32Error(sFunc, nWin32Error); +} + +std::wstring GetKnownFolder(const KNOWNFOLDERID& rfid) +{ + PWSTR sPath = nullptr; + HRESULT hr = SHGetKnownFolderPath(rfid, KF_FLAG_DEFAULT, nullptr, &sPath); + CheckHResult("SHGetKnownFolderPath", hr); + std::wstring sResult(sPath); + CoTaskMemFree(sPath); + return sResult; +} + +void WriteLogElem(MSIHANDLE hInst, MSIHANDLE hRecord, std::ostringstream& sTmpl, UINT) +{ + MsiRecordSetStringA(hRecord, 0, sTmpl.str().c_str()); + MsiProcessMessage(hInst, INSTALLMESSAGE_INFO, hRecord); +} + +void RecSetString(MSIHANDLE hRec, UINT nField, LPCSTR sVal) +{ + MsiRecordSetStringA(hRec, nField, sVal); +} + +void RecSetString(MSIHANDLE hRec, UINT nField, LPCWSTR sVal) +{ + MsiRecordSetStringW(hRec, nField, sVal); +} + +template <class Ch, class... SOther> +void WriteLogElem(MSIHANDLE hInst, MSIHANDLE hRec, std::ostringstream& sTmpl, UINT nField, + const Ch* elem, const SOther&... others) +{ + sTmpl << " [" << nField << "]"; + RecSetString(hRec, nField, elem); + WriteLogElem(hInst, hRec, sTmpl, nField + 1, others...); +} + +template <class S1, class... SOther> +void WriteLogElem(MSIHANDLE hInst, MSIHANDLE hRec, std::ostringstream& sTmpl, UINT nField, + const S1& elem, const SOther&... others) +{ + WriteLogElem(hInst, hRec, sTmpl, nField, elem.c_str(), others...); +} + +static std::string sLogPrefix; + +template <class... StrType> void WriteLog(MSIHANDLE hInst, const StrType&... elements) +{ + PMSIHANDLE hRec = MsiCreateRecord(sizeof...(elements)); + if (!hRec) + return; + + std::ostringstream sTemplate; + sTemplate << sLogPrefix; + WriteLogElem(hInst, hRec, sTemplate, 1, elements...); +} + +typedef std::unique_ptr<void, decltype(&CloseHandle)> CloseHandleGuard; +CloseHandleGuard Guard(HANDLE h) { return CloseHandleGuard(h, CloseHandle); } + +typedef std::unique_ptr<const wchar_t, decltype(&DeleteFileW)> DeleteFileGuard; +DeleteFileGuard Guard(const wchar_t* sFileName) { return DeleteFileGuard(sFileName, DeleteFileW); } + +typedef std::unique_ptr<SC_HANDLE__, decltype(&CloseServiceHandle)> CloseServiceHandleGuard; +CloseServiceHandleGuard Guard(SC_HANDLE h) +{ + return CloseServiceHandleGuard(h, CloseServiceHandle); +} + +std::wstring GetTempFile() +{ + wchar_t sPath[MAX_PATH + 1]; + DWORD nResult = GetTempPathW(sizeof(sPath) / sizeof(*sPath), sPath); + if (!nResult) + ThrowLastError("GetTempPathW"); + + wchar_t sFile[MAX_PATH + 1]; + nResult = GetTempFileNameW(sPath, L"TMP", 0, sFile); + if (!nResult) + ThrowLastError("GetTempFileNameW"); + return sFile; +} + +bool IsWow64Process() +{ +#if !defined _WIN64 + BOOL bResult = FALSE; + typedef BOOL(WINAPI * LPFN_ISWOW64PROCESS)(HANDLE, PBOOL); + LPFN_ISWOW64PROCESS fnIsWow64Process = reinterpret_cast<LPFN_ISWOW64PROCESS>( + GetProcAddress(GetModuleHandleW(L"kernel32"), "IsWow64Process")); + + if (fnIsWow64Process) + { + if (!fnIsWow64Process(GetCurrentProcess(), &bResult)) + ThrowLastError("IsWow64Process"); + } + + return bResult; +#else + return false; +#endif +} + +// Checks if Windows Update service is disabled, and if it is, enables it temporarily. +class WUServiceEnabler +{ +public: + WUServiceEnabler(MSIHANDLE hInstall) + : mhInstall(hInstall) + , mhService(EnableWUService(hInstall)) + { + } + + ~WUServiceEnabler() + { + try + { + if (mhService) + { + EnsureServiceEnabled(mhInstall, mhService.get(), false); + StopService(mhInstall, mhService.get()); + } + } + catch (std::exception& e) + { + WriteLog(mhInstall, e.what()); + } + } + +private: + static CloseServiceHandleGuard EnableWUService(MSIHANDLE hInstall) + { + auto hSCM = Guard(OpenSCManagerW(nullptr, nullptr, SC_MANAGER_ALL_ACCESS)); + if (!hSCM) + ThrowLastError("OpenSCManagerW"); + WriteLog(hInstall, "Opened service control manager"); + + auto hService = Guard(OpenServiceW(hSCM.get(), L"wuauserv", + SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG + | SERVICE_QUERY_STATUS | SERVICE_STOP)); + if (!hService) + ThrowLastError("OpenServiceW"); + WriteLog(hInstall, "Obtained WU service handle"); + + if (ServiceStatus(hInstall, hService.get()) == SERVICE_RUNNING + || !EnsureServiceEnabled(hInstall, hService.get(), true)) + { + // No need to restore anything back, since we didn't change config + hService.reset(); + WriteLog(hInstall, "Service configuration is unchanged"); + } + + return hService; + } + + // Returns if the service configuration was actually changed + static bool EnsureServiceEnabled(MSIHANDLE hInstall, SC_HANDLE hService, bool bEnabled) + { + bool bConfigChanged = false; + + DWORD nCbRequired = 0; + if (!QueryServiceConfigW(hService, nullptr, 0, &nCbRequired)) + { + DWORD nError = GetLastError(); + if (nError != ERROR_INSUFFICIENT_BUFFER) + ThrowLastError("QueryServiceConfigW"); + } + std::unique_ptr<char[]> pBuf(new char[nCbRequired]); + LPQUERY_SERVICE_CONFIGW pConfig = reinterpret_cast<LPQUERY_SERVICE_CONFIGW>(pBuf.get()); + if (!QueryServiceConfigW(hService, pConfig, nCbRequired, &nCbRequired)) + ThrowLastError("QueryServiceConfigW"); + WriteLog(hInstall, "Obtained service config"); + + DWORD eNewStartType = 0; + if (bEnabled && pConfig->dwStartType == SERVICE_DISABLED) + { + bConfigChanged = true; + eNewStartType = SERVICE_DEMAND_START; + WriteLog(hInstall, "Service is disabled, and requested to enable"); + } + else if (!bEnabled && pConfig->dwStartType != SERVICE_DISABLED) + { + bConfigChanged = true; + eNewStartType = SERVICE_DISABLED; + WriteLog(hInstall, "Service is enabled, and requested to disable"); + } + + if (bConfigChanged) + { + if (!ChangeServiceConfigW(hService, SERVICE_NO_CHANGE, eNewStartType, SERVICE_NO_CHANGE, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr)) + ThrowLastError("ChangeServiceConfigW"); + WriteLog(hInstall, "WU service config successfully changed"); + } + else + WriteLog(hInstall, "No need to modify service config"); + + return bConfigChanged; + } + + static DWORD ServiceStatus(MSIHANDLE hInstall, SC_HANDLE hService) + { + SERVICE_STATUS aServiceStatus{}; + if (!QueryServiceStatus(hService, &aServiceStatus)) + ThrowLastError("QueryServiceStatus"); + + std::string sStatus; + switch (aServiceStatus.dwCurrentState) + { + case SERVICE_STOPPED: + sStatus = "SERVICE_STOPPED"; + break; + case SERVICE_START_PENDING: + sStatus = "SERVICE_START_PENDING"; + break; + case SERVICE_STOP_PENDING: + sStatus = "SERVICE_STOP_PENDING"; + break; + case SERVICE_RUNNING: + sStatus = "SERVICE_RUNNING"; + break; + case SERVICE_CONTINUE_PENDING: + sStatus = "SERVICE_CONTINUE_PENDING"; + break; + case SERVICE_PAUSE_PENDING: + sStatus = "SERVICE_PAUSE_PENDING"; + break; + case SERVICE_PAUSED: + sStatus = "SERVICE_PAUSED"; + break; + default: + sStatus = Num2Hex(aServiceStatus.dwCurrentState); + } + WriteLog(hInstall, "Service status is", sStatus); + + return aServiceStatus.dwCurrentState; + } + + static void StopService(MSIHANDLE hInstall, SC_HANDLE hService) + { + if (ServiceStatus(hInstall, hService) != SERVICE_STOPPED) + { + SERVICE_STATUS aServiceStatus{}; + if (!ControlService(hService, SERVICE_CONTROL_STOP, &aServiceStatus)) + ThrowLastError("ControlService"); + WriteLog(hInstall, + "Successfully sent SERVICE_CONTROL_STOP code to Windows Update service"); + // No need to wait for the service stopped + } + else + WriteLog(hInstall, "Windows Update service is not running"); + } + + MSIHANDLE mhInstall; + CloseServiceHandleGuard mhService; +}; +} + +// Immediate action "unpack_msu" that has access to installation database and properties; checks +// "InstMSUBinary" property and unpacks the binary with that name to a temporary file; sets +// "cleanup_msu" and "inst_msu" properties to the full name of the extracted temporary file. These +// properties will become "CustomActionData" property inside relevant deferred actions. +extern "C" UINT __stdcall UnpackMSUForInstall(MSIHANDLE hInstall) +{ + try + { + sLogPrefix = "UnpackMSUForInstall:"; + WriteLog(hInstall, "started"); + + WriteLog(hInstall, "Checking value of InstMSUBinary"); + wchar_t sBinaryName[MAX_PATH + 1]; + DWORD nCCh = sizeof(sBinaryName) / sizeof(*sBinaryName); + CheckWin32Error("MsiGetPropertyW", + MsiGetPropertyW(hInstall, L"InstMSUBinary", sBinaryName, &nCCh)); + WriteLog(hInstall, "Got InstMSUBinary value:", sBinaryName); + + PMSIHANDLE hDatabase = MsiGetActiveDatabase(hInstall); + if (!hDatabase) + ThrowLastError("MsiGetActiveDatabase"); + WriteLog(hInstall, "MsiGetActiveDatabase succeeded"); + + std::wstringstream sQuery; + sQuery << "SELECT `Data` FROM `Binary` WHERE `Name`='" << sBinaryName << "'"; + + PMSIHANDLE hBinaryView; + CheckWin32Error("MsiDatabaseOpenViewW", + MsiDatabaseOpenViewW(hDatabase, sQuery.str().c_str(), &hBinaryView)); + WriteLog(hInstall, "MsiDatabaseOpenViewW succeeded"); + + CheckWin32Error("MsiViewExecute", MsiViewExecute(hBinaryView, 0)); + WriteLog(hInstall, "MsiViewExecute succeeded"); + + PMSIHANDLE hBinaryRecord; + CheckWin32Error("MsiViewFetch", MsiViewFetch(hBinaryView, &hBinaryRecord)); + WriteLog(hInstall, "MsiViewFetch succeeded"); + + const std::wstring sBinary = GetTempFile(); + auto aDeleteFileGuard(Guard(sBinary.c_str())); + WriteLog(hInstall, "Temp file path:", sBinary.c_str()); + + CheckWin32Error("MsiSetPropertyW", + MsiSetPropertyW(hInstall, L"cleanup_msu", sBinary.c_str())); + + { + HANDLE hFile = CreateFileW(sBinary.c_str(), GENERIC_WRITE, 0, 0, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, 0); + if (hFile == INVALID_HANDLE_VALUE) + ThrowLastError("CreateFileW"); + auto aFileHandleGuard(Guard(hFile)); + + const DWORD nBufSize = 1024 * 1024; + std::unique_ptr<char[]> buf(new char[nBufSize]); + DWORD nTotal = 0; + DWORD nRead; + do + { + nRead = nBufSize; + CheckWin32Error("MsiRecordReadStream", + MsiRecordReadStream(hBinaryRecord, 1, buf.get(), &nRead)); + + if (nRead > 0) + { + DWORD nWritten; + if (!WriteFile(hFile, buf.get(), nRead, &nWritten, nullptr)) + ThrowLastError("WriteFile"); + nTotal += nWritten; + } + } while (nRead == nBufSize); + + WriteLog(hInstall, "Successfully wrote", Num2Dec(nTotal), "bytes"); + } + + CheckWin32Error("MsiSetPropertyW", MsiSetPropertyW(hInstall, L"inst_msu", sBinary.c_str())); + + // Don't delete the file: it will be done by following actions (inst_msu or cleanup_msu) + aDeleteFileGuard.release(); + return ERROR_SUCCESS; + } + catch (std::exception& e) + { + WriteLog(hInstall, e.what()); + } + return ERROR_INSTALL_FAILURE; +} + +// Deferred action "inst_msu" that must be run from system account. Receives the tempfile name from +// "CustomActionData" property, and runs wusa.exe to install it. Waits for it and checks exit code. +extern "C" UINT __stdcall InstallMSU(MSIHANDLE hInstall) +{ + try + { + sLogPrefix = "InstallMSU:"; + WriteLog(hInstall, "started"); + + WriteLog(hInstall, "Checking value of CustomActionData"); + wchar_t sBinaryName[MAX_PATH + 1]; + DWORD nCCh = sizeof(sBinaryName) / sizeof(*sBinaryName); + CheckWin32Error("MsiGetPropertyW", + MsiGetPropertyW(hInstall, L"CustomActionData", sBinaryName, &nCCh)); + WriteLog(hInstall, "Got CustomActionData value:", sBinaryName); + auto aDeleteFileGuard(Guard(sBinaryName)); + + // In case the Windows Update service is disabled, we temporarily enable it here + WUServiceEnabler aWUServiceEnabler(hInstall); + + const bool bWow64Process = IsWow64Process(); + WriteLog(hInstall, "Is Wow64 Process:", bWow64Process ? "YES" : "NO"); + std::wstring sWUSAPath = bWow64Process ? GetKnownFolder(FOLDERID_Windows) + L"\\SysNative" + : GetKnownFolder(FOLDERID_System); + sWUSAPath += L"\\wusa.exe"; + WriteLog(hInstall, "Prepared wusa path:", sWUSAPath); + + std::wstring sWUSACmd + = L"\"" + sWUSAPath + L"\" \"" + sBinaryName + L"\" /quiet /norestart"; + WriteLog(hInstall, "Prepared wusa command:", sWUSACmd); + + STARTUPINFOW si{}; + si.cb = sizeof(si); + PROCESS_INFORMATION pi{}; + if (!CreateProcessW(sWUSAPath.c_str(), const_cast<LPWSTR>(sWUSACmd.c_str()), nullptr, + nullptr, FALSE, CREATE_NO_WINDOW, nullptr, nullptr, &si, &pi)) + ThrowLastError("CreateProcessW"); + auto aCloseProcHandleGuard(Guard(pi.hProcess)); + WriteLog(hInstall, "CreateProcessW succeeded"); + + DWORD nWaitResult = WaitForSingleObject(pi.hProcess, INFINITE); + if (nWaitResult != WAIT_OBJECT_0) + ThrowWin32Error("WaitForSingleObject", nWaitResult); + + DWORD nExitCode = 0; + if (!GetExitCodeProcess(pi.hProcess, &nExitCode)) + ThrowLastError("GetExitCodeProcess"); + + HRESULT hr = static_cast<HRESULT>(nExitCode); + if (hr == HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED)) + hr = WU_S_REBOOT_REQUIRED; + + switch (hr) + { + case S_OK: + case S_FALSE: + case WU_S_ALREADY_INSTALLED: + case WU_E_NOT_APPLICABLE: // Windows could lie us about its version, etc. + case ERROR_SUCCESS_REBOOT_REQUIRED: + case WU_S_REBOOT_REQUIRED: + WriteLog(hInstall, "wusa.exe succeeded with exit code", Num2Hex(nExitCode)); + return ERROR_SUCCESS; + + default: + ThrowWin32Error("Execution of wusa.exe", nExitCode); + } + } + catch (std::exception& e) + { + WriteLog(hInstall, e.what()); + } + return ERROR_INSTALL_FAILURE; +} + +// Rollback deferred action "cleanup_msu" that is executed on error or cancel. +// It removes the temporary file created by UnpackMSUForInstall action. +// MUST be placed IMMEDIATELY AFTER "unpack_msu" in execute sequence. +extern "C" UINT __stdcall CleanupMSU(MSIHANDLE hInstall) +{ + try + { + sLogPrefix = "CleanupMSU:"; + WriteLog(hInstall, "started"); + + WriteLog(hInstall, "Checking value of CustomActionData"); + wchar_t sBinaryName[MAX_PATH + 1]; + DWORD nCCh = sizeof(sBinaryName) / sizeof(*sBinaryName); + CheckWin32Error("MsiGetPropertyW", + MsiGetPropertyW(hInstall, L"CustomActionData", sBinaryName, &nCCh)); + WriteLog(hInstall, "Got CustomActionData value:", sBinaryName); + + if (!DeleteFileW(sBinaryName)) + ThrowLastError("DeleteFileW"); + WriteLog(hInstall, "File successfully removed"); + } + catch (std::exception& e) + { + WriteLog(hInstall, e.what()); + } + // Always return success - we don't want rollback to fail. + return ERROR_SUCCESS; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/setup_native/source/win32/customactions/inst_msu/inst_msu_msi.def b/setup_native/source/win32/customactions/inst_msu/inst_msu_msi.def new file mode 100644 index 000000000000..49ade9c0169e --- /dev/null +++ b/setup_native/source/win32/customactions/inst_msu/inst_msu_msi.def @@ -0,0 +1,5 @@ +LIBRARY "inst_msu_msi.dll" +EXPORTS + UnpackMSUForInstall + InstallMSU + CleanupMSU
\ No newline at end of file diff --git a/solenv/bin/modules/installer/windows/idtglobal.pm b/solenv/bin/modules/installer/windows/idtglobal.pm index 3cf086c7a206..db92aac5bd55 100644 --- a/solenv/bin/modules/installer/windows/idtglobal.pm +++ b/solenv/bin/modules/installer/windows/idtglobal.pm @@ -1084,9 +1084,8 @@ sub add_custom_action_to_install_table my $actionposition = 0; - if ( $position eq "end" ) { $actionposition = get_last_position_in_sequencetable($installtable) + 25; } - elsif ( $position =~ /^\s*behind_/ ) { $actionposition = get_position_in_sequencetable($position, $installtable) + 2; } - else { $actionposition = get_position_in_sequencetable($position, $installtable) - 2; } + if ( $position =~ /^\s*\d+\s*$/ ) { $actionposition = $position; } # setting the position directly, number defined in scp2 + else { $actionposition = "POSITIONTEMPLATE_" . $position; } my $line = $actionname . "\t" . $actioncondition . "\t" . $actionposition . "\n"; push(@{$installtable}, $line); @@ -1129,12 +1128,6 @@ sub add_custom_action_to_install_table $actioncondition =~ s/FEATURETEMPLATE/$feature/g; # only execute Custom Action, if feature of the file is installed -# my $actionposition = 0; -# if ( $position eq "end" ) { $actionposition = get_last_position_in_sequencetable($installtable) + 25; } -# elsif ( $position =~ /^\s*behind_/ ) { $actionposition = get_position_in_sequencetable($position, $installtable) + 2; } -# else { $actionposition = get_position_in_sequencetable($position, $installtable) - 2; } -# my $line = $actionname . "\t" . $actioncondition . "\t" . $actionposition . "\n"; - my $positiontemplate = ""; if ( $position =~ /^\s*\d+\s*$/ ) { $positiontemplate = $position; } # setting the position directly, number defined in scp2 else { $positiontemplate = "POSITIONTEMPLATE_" . $position; } diff --git a/solenv/bin/modules/installer/windows/upgrade.pm b/solenv/bin/modules/installer/windows/upgrade.pm index 6c02f99fe0f4..54838212404c 100644 --- a/solenv/bin/modules/installer/windows/upgrade.pm +++ b/solenv/bin/modules/installer/windows/upgrade.pm @@ -45,18 +45,6 @@ sub create_upgrade_table $newline = $installer::globals::upgradecode . "\t" . $installer::globals::msiproductversion . "\t" . "\t" . "\t" . "2" . "\t" . "\t" . "NEWPRODUCTS" . "\n"; push(@upgradetable, $newline); - # Detecting if VC Runtime is installed on system - $newline = "VCRUNTIME_UPGRADE_CODE" . "\t" . "14.0.24215" . "\t" . "15.0.0" . "\t" . "" . "\t" . "258" . "\t" . "" . "\t" . "VCRUNTIME_DETECTED" . "\n"; - if ( $installer::globals::cpuname eq 'X86_64' ) - { - $newline =~ s/VCRUNTIME_UPGRADE_CODE/{36F68A90-239C-34DF-B58C-64B30153CE35}/; - } - else - { - $newline =~ s/VCRUNTIME_UPGRADE_CODE/{65E5BD06-6392-3027-8C26-853107D3CF1A}/; - } - push(@upgradetable, $newline); - # Saving the file my $upgradetablename = $basedir . $installer::globals::separator . "Upgrade.idt"; |