summaryrefslogtreecommitdiff
path: root/setup_native
diff options
context:
space:
mode:
Diffstat (limited to 'setup_native')
-rw-r--r--setup_native/Library_reg_dlls.mk40
-rw-r--r--setup_native/Module_setup_native.mk1
-rw-r--r--setup_native/source/win32/customactions/reg_dlls/reg_dlls.cxx309
-rw-r--r--setup_native/source/win32/customactions/reg_dlls/reg_dlls.def5
4 files changed, 355 insertions, 0 deletions
diff --git a/setup_native/Library_reg_dlls.mk b/setup_native/Library_reg_dlls.mk
new file mode 100644
index 000000000000..f0ac9a875c60
--- /dev/null
+++ b/setup_native/Library_reg_dlls.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,reg_dlls))
+
+$(eval $(call gb_Library_add_defs,reg_dlls,\
+ -U_DLL \
+))
+
+$(eval $(call gb_Library_add_cxxflags,reg_dlls,\
+ $(if $(MSVC_USE_DEBUG_RUNTIME),/MTd,/MT) \
+))
+
+$(eval $(call gb_Library_add_ldflags,reg_dlls,\
+ /DEF:$(SRCDIR)/setup_native/source/win32/customactions/reg_dlls/reg_dlls.def \
+ /NODEFAULTLIB \
+))
+
+$(eval $(call gb_Library_add_exception_objects,reg_dlls,\
+ setup_native/source/win32/customactions/reg_dlls/reg_dlls \
+))
+
+$(eval $(call gb_Library_use_system_win32_libs,reg_dlls,\
+ 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 1009c53dcb2d..42fa1421786d 100644
--- a/setup_native/Module_setup_native.mk
+++ b/setup_native/Module_setup_native.mk
@@ -25,6 +25,7 @@ $(eval $(call gb_Module_add_targets,setup_native,\
Library_instooofiltmsi \
Library_inst_msu_msi \
Library_qslnkmsi \
+ Library_reg_dlls \
Library_reg4allmsdoc \
Library_regactivex \
Library_sdqsmsi \
diff --git a/setup_native/source/win32/customactions/reg_dlls/reg_dlls.cxx b/setup_native/source/win32/customactions/reg_dlls/reg_dlls.cxx
new file mode 100644
index 000000000000..250a81bd0fa0
--- /dev/null
+++ b/setup_native/source/win32/customactions/reg_dlls/reg_dlls.cxx
@@ -0,0 +1,309 @@
+/* -*- 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 <iomanip>
+#include <memory>
+#include <string>
+#include <sstream>
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <Shlobj.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();
+}
+
+std::string Win32ErrorMessage(const char* sFunc, DWORD nWin32Error)
+{
+ std::stringstream sMsg;
+ sMsg << sFunc << " failed with Win32 error code " << Num2Hex(nWin32Error) << "!";
+
+ 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)
+{
+ throw std::exception(Win32ErrorMessage(sFunc, nWin32Error).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...);
+}
+
+std::wstring MsiGetPropertyW(MSIHANDLE hInst, LPCWSTR szName)
+{
+ std::wstring sResult;
+ DWORD nSz = 0;
+ UINT nRet = ::MsiGetPropertyW(hInst, szName, L"", &nSz);
+ if (nRet == ERROR_MORE_DATA)
+ {
+ ++nSz;
+ auto buf = std::make_unique<wchar_t[]>(nSz);
+ CheckWin32Error("MsiGetPropertyW", ::MsiGetPropertyW(hInst, szName, buf.get(), &nSz));
+ sResult = buf.get();
+ WriteLog(hInst, "Property", szName, "=", sResult);
+ }
+ else
+ CheckWin32Error("MsiGetPropertyW", nRet);
+
+ return sResult;
+}
+
+typedef std::unique_ptr<void, decltype(&CloseHandle)> CloseHandleGuard;
+CloseHandleGuard Guard(HANDLE h) { return CloseHandleGuard(h, CloseHandle); }
+
+void RegDLL(MSIHANDLE hInst, const std::wstring& sArgs, bool bUnreg)
+{
+ static std::wstring sRegSvr32 = GetKnownFolder(FOLDERID_System) + L"\\regsvr32.exe";
+
+ try
+ {
+ std::wstring sCmd = L"\"" + sRegSvr32 + L"\" /s ";
+ if (bUnreg)
+ sCmd += L"/u ";
+ sCmd += sArgs;
+ WriteLog(hInst, "Prepared regsvr32 command:", sCmd);
+
+ STARTUPINFOW si{};
+ si.cb = sizeof(si);
+ PROCESS_INFORMATION pi{};
+ if (!CreateProcessW(sRegSvr32.c_str(), const_cast<LPWSTR>(sCmd.c_str()), nullptr, nullptr,
+ FALSE, CREATE_NO_WINDOW, nullptr, nullptr, &si, &pi))
+ ThrowLastError("CreateProcessW");
+ auto aCloseProcHandleGuard(Guard(pi.hProcess));
+ WriteLog(hInst, "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");
+
+ WriteLog(hInst, "regsvr32 returned:", Num2Dec(nExitCode));
+ }
+ catch (std::exception& e)
+ {
+ WriteLog(hInst, e.what());
+ }
+}
+
+void ProcessCustomActionData(MSIHANDLE hInst, bool bUnreg)
+{
+ WriteLog(hInst, "Checking value of CustomActionData");
+ std::wstring sCustomActionData = MsiGetPropertyW(hInst, L"CustomActionData");
+ WriteLog(hInst, "Got CustomActionData value:", sCustomActionData);
+ std::wstringstream ss(sCustomActionData);
+ std::wstring sToken;
+ while (std::getline(ss, sToken, L'|'))
+ {
+ if (!sToken.empty())
+ {
+ RegDLL(hInst, sToken, bUnreg);
+ }
+ }
+}
+} // namespace
+
+// Deferred action "reg_dlls" that must be run from system account. Receives a list of regsvr32
+// arguments: DLLs which need registering, and possibly /i argument with its parameter.
+extern "C" __declspec(dllexport) UINT __stdcall RegDLLs(MSIHANDLE hInstall)
+{
+ sLogPrefix = "RegDLLs:";
+ WriteLog(hInstall, "started");
+
+ ProcessCustomActionData(hInstall, false);
+ return ERROR_SUCCESS;
+}
+
+// Deferred action "unreg_dlls" that must be run from system account. Receives a list of regsvr32
+// arguments: DLLs which need registering, and possibly /i argument with its parameter.
+extern "C" __declspec(dllexport) UINT __stdcall UnregDLLs(MSIHANDLE hInstall)
+{
+ sLogPrefix = "UnregDLLs:";
+ WriteLog(hInstall, "started");
+
+ ProcessCustomActionData(hInstall, true);
+ return ERROR_SUCCESS;
+}
+
+// Immediate action "prep_reg_unreg_dlls". Checks states of the features to prepare custom action data
+// for reg_dlls and unreg_dlls deferred actions.
+extern "C" __declspec(dllexport) UINT __stdcall PrepRegUnregDLLs(MSIHANDLE hInstall)
+{
+ sLogPrefix = "PrepRegUnregDLLs:";
+ WriteLog(hInstall, "started");
+
+ try
+ {
+ INSTALLSTATE current_state_SubstMSO;
+ INSTALLSTATE future_state_SubstMSO;
+ CheckWin32Error("MsiGetFeatureStateW",
+ MsiGetFeatureStateW(hInstall, L"gm_SharePointSupport_SubstMSO",
+ &current_state_SubstMSO, &future_state_SubstMSO));
+ INSTALLSTATE current_state_Main;
+ INSTALLSTATE future_state_Main;
+ CheckWin32Error("MsiGetFeatureStateW",
+ MsiGetFeatureStateW(hInstall, L"gm_o_SharePointSupport",
+ &current_state_Main, &future_state_Main));
+
+ const bool bUnregSubstMSO = current_state_SubstMSO == INSTALLSTATE_LOCAL
+ && future_state_SubstMSO == INSTALLSTATE_ABSENT;
+ const bool bUnregMain
+ = current_state_Main == INSTALLSTATE_LOCAL && future_state_Main == INSTALLSTATE_ABSENT;
+ const bool bRegSubstMSO = current_state_SubstMSO == INSTALLSTATE_ABSENT
+ && future_state_SubstMSO == INSTALLSTATE_LOCAL;
+ // basic registration is needed when either:
+ // 1. gm_o_SharePointSupport is installed;
+ // 2. gm_SharePointSupport_SubstMSO is uninstalled (and unregisters everything), but
+ // gm_o_SharePointSupport is not, so needs to be re-registered
+ const bool bRegMain
+ = (current_state_Main == INSTALLSTATE_ABSENT && future_state_Main == INSTALLSTATE_LOCAL)
+ || (bUnregSubstMSO && !bUnregMain);
+
+ std::wstring sUnregStr;
+ if (bUnregSubstMSO)
+ {
+ sUnregStr = L"/i:Substitute_OWSSUPP \"[#spsupp_x86.dll]\"|"
+ L"/i:Substitute_OWSSUPP \"[#spsupp_x64.dll]\"";
+ }
+ else if (bUnregMain)
+ {
+ sUnregStr = L"\"[#spsupp_x86.dll]\"|\"[#spsupp_x64.dll]\"";
+ }
+
+ std::wstring sRegStr;
+ if (bRegSubstMSO)
+ {
+ sRegStr = L"/i:Substitute_OWSSUPP \"[#spsupp_x86.dll]\"|"
+ L"/i:Substitute_OWSSUPP \"[#spsupp_x64.dll]\"";
+ }
+ else if (bRegMain)
+ {
+ sRegStr = L"\"[#spsupp_x86.dll]\"|\"[#spsupp_x64.dll]\"";
+ }
+
+ auto SetFormattedPropW = [&](LPCWSTR sProp, const std::wstring& sVal) {
+ PMSIHANDLE hRec = MsiCreateRecord(0);
+ if (!hRec)
+ throw std::exception("MsiCreateRecord failed!");
+ MsiRecordSetStringW(hRec, 0, sVal.c_str());
+ DWORD nSz = 0;
+ if (MsiFormatRecordW(hInstall, hRec, L"", &nSz) == ERROR_MORE_DATA)
+ {
+ ++nSz;
+ auto buf = std::make_unique<wchar_t[]>(nSz);
+ CheckWin32Error("MsiFormatRecordW",
+ MsiFormatRecordW(hInstall, hRec, buf.get(), &nSz));
+ CheckWin32Error("MsiSetPropertyW", MsiSetPropertyW(hInstall, sProp, buf.get()));
+ }
+ };
+ if (!sRegStr.empty())
+ SetFormattedPropW(L"reg_dlls", sRegStr);
+ if (!sUnregStr.empty())
+ SetFormattedPropW(L"unreg_dlls", sUnregStr);
+ }
+ catch (std::exception& e)
+ {
+ WriteLog(hInstall, e.what());
+ }
+
+ return ERROR_SUCCESS;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/setup_native/source/win32/customactions/reg_dlls/reg_dlls.def b/setup_native/source/win32/customactions/reg_dlls/reg_dlls.def
new file mode 100644
index 000000000000..d0f31c491b1c
--- /dev/null
+++ b/setup_native/source/win32/customactions/reg_dlls/reg_dlls.def
@@ -0,0 +1,5 @@
+LIBRARY "reg_dlls.dll"
+EXPORTS
+ RegDLLs
+ UnregDLLs
+ PrepRegUnregDLLs