summaryrefslogtreecommitdiffstats
path: root/setup_native/source/win32/customactions
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
commit940b4d1848e8c70ab7642901a68594e8016caffc (patch)
treeeb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /setup_native/source/win32/customactions
parentInitial commit. (diff)
downloadlibreoffice-upstream.tar.xz
libreoffice-upstream.zip
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'setup_native/source/win32/customactions')
-rw-r--r--setup_native/source/win32/customactions/indexingfilter/instooofiltmsi.def3
-rw-r--r--setup_native/source/win32/customactions/indexingfilter/restartindexingservice.cxx157
-rw-r--r--setup_native/source/win32/customactions/inst_msu/inst_msu.cxx674
-rw-r--r--setup_native/source/win32/customactions/inst_msu/inst_msu_msi.def5
-rw-r--r--setup_native/source/win32/customactions/quickstarter/qslnkmsi.def3
-rw-r--r--setup_native/source/win32/customactions/quickstarter/quickstarter.cxx131
-rw-r--r--setup_native/source/win32/customactions/quickstarter/quickstarter.hxx36
-rw-r--r--setup_native/source/win32/customactions/quickstarter/remove_quickstart_link.cxx42
-rw-r--r--setup_native/source/win32/customactions/quickstarter/sdqsmsi.def3
-rw-r--r--setup_native/source/win32/customactions/quickstarter/shutdown_quickstart.cxx73
-rw-r--r--setup_native/source/win32/customactions/reg4allmsdoc/reg4allmsdoc.def6
-rw-r--r--setup_native/source/win32/customactions/reg4allmsdoc/reg4allmsi.cxx519
-rw-r--r--setup_native/source/win32/customactions/reg_dlls/reg_dlls.cxx319
-rw-r--r--setup_native/source/win32/customactions/reg_dlls/reg_dlls.def5
-rw-r--r--setup_native/source/win32/customactions/regactivex/regactivex.cxx344
-rw-r--r--setup_native/source/win32/customactions/regactivex/regactivex.def4
-rw-r--r--setup_native/source/win32/customactions/sellang/sellang.cxx387
-rw-r--r--setup_native/source/win32/customactions/sellang/sellang.def4
-rw-r--r--setup_native/source/win32/customactions/sellang/sorttree.cxx55
-rw-r--r--setup_native/source/win32/customactions/shellextensions/checkdirectory.cxx66
-rw-r--r--setup_native/source/win32/customactions/shellextensions/checkpatches.cxx59
-rw-r--r--setup_native/source/win32/customactions/shellextensions/completeinstallpath.cxx116
-rw-r--r--setup_native/source/win32/customactions/shellextensions/migrateinstallpath.cxx96
-rw-r--r--setup_native/source/win32/customactions/shellextensions/shlxtmsi.def11
-rw-r--r--setup_native/source/win32/customactions/shellextensions/shlxtmsi.hxx59
-rw-r--r--setup_native/source/win32/customactions/shellextensions/startmenuicon.cxx63
-rw-r--r--setup_native/source/win32/customactions/shellextensions/upgrade.cxx158
-rw-r--r--setup_native/source/win32/customactions/shellextensions/vistaspecial.cxx126
-rw-r--r--setup_native/source/win32/customactions/tools/checkversion.cxx98
-rw-r--r--setup_native/source/win32/customactions/tools/seterror.cxx84
-rw-r--r--setup_native/source/win32/customactions/tools/seterror.hxx36
-rw-r--r--setup_native/source/win32/customactions/tools/sn_tools.def3
32 files changed, 3745 insertions, 0 deletions
diff --git a/setup_native/source/win32/customactions/indexingfilter/instooofiltmsi.def b/setup_native/source/win32/customactions/indexingfilter/instooofiltmsi.def
new file mode 100644
index 000000000..0126556a4
--- /dev/null
+++ b/setup_native/source/win32/customactions/indexingfilter/instooofiltmsi.def
@@ -0,0 +1,3 @@
+LIBRARY "instooofiltmsi.dll"
+EXPORTS
+ RestartIndexingService \ No newline at end of file
diff --git a/setup_native/source/win32/customactions/indexingfilter/restartindexingservice.cxx b/setup_native/source/win32/customactions/indexingfilter/restartindexingservice.cxx
new file mode 100644
index 000000000..e1c26501b
--- /dev/null
+++ b/setup_native/source/win32/customactions/indexingfilter/restartindexingservice.cxx
@@ -0,0 +1,157 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+/*
+ After installation of the OOo filter for the indexing service
+ it is necessary to restart the indexing service in order to
+ activate the filter. This is the most reliable way to get the
+ indexing service working. We only restart the service if it is
+ already running. If we have insufficient privileges to restart
+ the service we do nothing.
+*/
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <msiquery.h>
+
+const wchar_t * const INDEXING_SERVICE_NAME = L"cisvc";
+
+static bool StopIndexingService(SC_HANDLE hService)
+{
+ SERVICE_STATUS status;
+
+ if (ControlService(hService, SERVICE_CONTROL_STOP, &status))
+ {
+ // Check the status until the service is no longer stop pending.
+ if (QueryServiceStatus(hService, &status))
+ {
+ DWORD startTime = GetTickCount();
+ DWORD oldCheckPoint = status.dwCheckPoint;
+
+ while (status.dwCurrentState == SERVICE_STOP_PENDING)
+ {
+ // Do not wait longer than the wait hint. A good interval is
+ // one tenth the wait hint, but no less than 1 second and no
+ // more than 10 seconds.
+ DWORD waitTime = status.dwWaitHint / 10;
+
+ if (waitTime < 1000)
+ waitTime = 1000;
+ else if (waitTime > 10000)
+ waitTime = 10000;
+
+ Sleep(waitTime);
+
+ // Check the status again.
+ if (!QueryServiceStatus(hService, &status) ||
+ (status.dwCurrentState == SERVICE_STOPPED))
+ break;
+
+ if (status.dwCheckPoint > oldCheckPoint)
+ {
+ startTime = GetTickCount();
+ oldCheckPoint = status.dwCheckPoint;
+ }
+ else if ((GetTickCount() - startTime) > status.dwWaitHint)
+ {
+ break; // service doesn't react anymore
+ }
+ }
+ }
+ }
+ return (status.dwCurrentState == SERVICE_STOPPED);
+}
+
+static void StartIndexingService(SC_HANDLE hService)
+{
+ if (StartServiceW(hService, 0, nullptr))
+ {
+ SERVICE_STATUS status;
+
+ // Check the status until the service is no longer stop pending.
+ if (QueryServiceStatus(hService, &status))
+ {
+ DWORD startTime = GetTickCount();
+ DWORD oldCheckPoint = status.dwCheckPoint;
+
+ while (status.dwCurrentState == SERVICE_START_PENDING)
+ {
+ // Do not wait longer than the wait hint. A good interval is
+ // one tenth the wait hint, but no less than 1 second and no
+ // more than 10 seconds.
+ DWORD waitTime = status.dwWaitHint / 10;
+
+ if (waitTime < 1000)
+ waitTime = 1000;
+ else if (waitTime > 10000)
+ waitTime = 10000;
+
+ Sleep(waitTime);
+
+ // Check the status again.
+ if (!QueryServiceStatus(hService, &status) ||
+ (status.dwCurrentState == SERVICE_STOPPED))
+ break;
+
+ if (status.dwCheckPoint > oldCheckPoint)
+ {
+ startTime = GetTickCount();
+ oldCheckPoint = status.dwCheckPoint;
+ }
+ else if ((GetTickCount() - startTime) > status.dwWaitHint)
+ {
+ // service doesn't react anymore
+ break;
+ }
+ }
+ }
+ }
+}
+
+extern "C" __declspec(dllexport) UINT __stdcall RestartIndexingService(MSIHANDLE)
+{
+ SC_HANDLE hSCManager = OpenSCManagerW(
+ nullptr, // local machine
+ nullptr, // ServicesActive database
+ SC_MANAGER_ALL_ACCESS);
+
+ if (hSCManager != nullptr)
+ {
+ SC_HANDLE hIndexingService = OpenServiceW(
+ hSCManager, INDEXING_SERVICE_NAME, SERVICE_QUERY_STATUS | SERVICE_START | SERVICE_STOP);
+
+ if (hIndexingService)
+ {
+ SERVICE_STATUS status;
+ ZeroMemory(&status, sizeof(status));
+
+ if (QueryServiceStatus(hIndexingService, &status) &&
+ (status.dwCurrentState == SERVICE_RUNNING))
+ {
+ if (StopIndexingService(hIndexingService))
+ StartIndexingService(hIndexingService);
+ }
+ CloseServiceHandle(hIndexingService);
+ }
+ CloseServiceHandle(hSCManager);
+ }
+ return ERROR_SUCCESS;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
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 000000000..26f7668ff
--- /dev/null
+++ b/setup_native/source/win32/customactions/inst_msu/inst_msu.cxx
@@ -0,0 +1,674 @@
+/* -*- 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();
+}
+
+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...);
+}
+
+// Show a warning message box. This will be automatically suppressed in unattended installation.
+void ShowWarning(MSIHANDLE hInst, const std::wstring& sKBNo, const char* sMessage)
+{
+ // Error table's message #25000: "Installing a pre-requisite [2] failed.
+ // You might need to manually install it from Microsoft site to be able to run the product.[3]"
+ PMSIHANDLE hRec = MsiCreateRecord(3);
+ // To show a message from Error table, record's Field 0 must be null
+ MsiRecordSetInteger(hRec, 1, 25000);
+ MsiRecordSetStringW(hRec, 2, sKBNo.c_str());
+ std::string s("\n");
+ s += sMessage;
+ MsiRecordSetStringA(hRec, 3, s.c_str());
+ MsiProcessMessage(hInst, INSTALLMESSAGE_WARNING, hRec);
+}
+
+// Set custom action description visible in progress dialog
+void SetStatusText(MSIHANDLE hInst, const std::wstring& actName, const std::wstring& actDesc)
+{
+ PMSIHANDLE hRec = MsiCreateRecord(3);
+ // For INSTALLMESSAGE_ACTIONSTART, record's Field 0 must be null
+ // Field 1: Action name - must be non-null
+ MsiRecordSetStringW(hRec, 1, actName.c_str());
+ // Field 2: Action description - displayed in dialog
+ MsiRecordSetStringW(hRec, 2, actDesc.c_str());
+ // Let Field 3 stay null - no action template
+ MsiProcessMessage(hInst, INSTALLMESSAGE_ACTIONSTART, hRec);
+}
+
+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
+}
+
+// This class uses MsiProcessMessage to check for user input: it returns IDCANCEL when user cancels
+// installation. It throws a special exception, to be intercepted in main action function to return
+// corresponding exit code.
+class UserInputChecker
+{
+public:
+ class eUserCancelled
+ {
+ };
+
+ UserInputChecker(MSIHANDLE hInstall)
+ : m_hInstall(hInstall)
+ , m_hProgressRec(MsiCreateRecord(3))
+ {
+ // Use explicit progress messages
+ MsiRecordSetInteger(m_hProgressRec, 1, 1);
+ MsiRecordSetInteger(m_hProgressRec, 2, 1);
+ MsiRecordSetInteger(m_hProgressRec, 3, 0);
+ int nResult = MsiProcessMessage(m_hInstall, INSTALLMESSAGE_PROGRESS, m_hProgressRec);
+ if (nResult == IDCANCEL)
+ throw eUserCancelled();
+ // Prepare the record to following progress update calls
+ MsiRecordSetInteger(m_hProgressRec, 1, 2);
+ MsiRecordSetInteger(m_hProgressRec, 2, 0); // step by 0 - don't move progress
+ MsiRecordSetInteger(m_hProgressRec, 3, 0);
+ }
+
+ void ThrowIfUserCancelled()
+ {
+ // Check if user has cancelled
+ int nResult = MsiProcessMessage(m_hInstall, INSTALLMESSAGE_PROGRESS, m_hProgressRec);
+ if (nResult == IDCANCEL)
+ throw eUserCancelled();
+ }
+
+private:
+ MSIHANDLE m_hInstall;
+ PMSIHANDLE m_hProgressRec;
+};
+
+// Checks if Windows Update service is disabled, and if it is, enables it temporarily.
+// Also stops the service if it's currently running, because it seems that wusa.exe
+// does not freeze when it starts the service itself.
+class WUServiceEnabler
+{
+public:
+ WUServiceEnabler(MSIHANDLE hInstall)
+ : mhInstall(hInstall)
+ , mhService(EnableWUService(hInstall))
+ {
+ }
+
+ ~WUServiceEnabler()
+ {
+ try
+ {
+ if (mhService)
+ {
+ EnsureServiceEnabled(mhInstall, mhService.get(), false);
+ StopService(mhInstall, mhService.get(), false);
+ }
+ }
+ 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");
+
+ const DWORD nCurrentStatus = ServiceStatus(hInstall, hService.get());
+ // Stop currently running service to prevent wusa.exe from hanging trying to detect if the
+ // update is applicable (sometimes this freezes it ~indefinitely; it seems that it doesn't
+ // happen if wusa.exe starts the service itself: https://superuser.com/questions/1044528/).
+ // tdf#124794: Wait for service to stop.
+ if (nCurrentStatus == SERVICE_RUNNING)
+ StopService(hInstall, hService.get(), true);
+
+ if (nCurrentStatus == 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))
+ {
+ if (DWORD nError = GetLastError(); nError != ERROR_INSUFFICIENT_BUFFER)
+ ThrowWin32Error("QueryServiceConfigW", nError);
+ }
+ 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, bool bWait)
+ {
+ try
+ {
+ 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");
+ if (aServiceStatus.dwCurrentState != SERVICE_STOPPED && bWait)
+ {
+ // Let user cancel too long wait
+ UserInputChecker aInputChecker(hInstall);
+ // aServiceStatus.dwWaitHint is unreasonably high for Windows Update (30000),
+ // so don't use it, but simply poll service status each second
+ for (int nWait = 0; nWait < 30; ++nWait) // arbitrary limit of 30 s
+ {
+ for (int i = 0; i < 2; ++i) // check user input twice a second
+ {
+ Sleep(500);
+ aInputChecker.ThrowIfUserCancelled();
+ }
+
+ if (!QueryServiceStatus(hService, &aServiceStatus))
+ ThrowLastError("QueryServiceStatus");
+
+ if (aServiceStatus.dwCurrentState == SERVICE_STOPPED)
+ break;
+ }
+ }
+ if (aServiceStatus.dwCurrentState == SERVICE_STOPPED)
+ WriteLog(hInstall, "Successfully stopped Windows Update service");
+ else if (bWait)
+ WriteLog(hInstall, "Wait for Windows Update stop timed out - proceeding");
+ }
+ else
+ WriteLog(hInstall, "Windows Update service is not running");
+ }
+ catch (std::exception& e)
+ {
+ WriteLog(hInstall, e.what());
+ }
+ }
+
+ 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" __declspec(dllexport) UINT __stdcall UnpackMSUForInstall(MSIHANDLE hInstall)
+{
+ try
+ {
+ sLogPrefix = "UnpackMSUForInstall:";
+ WriteLog(hInstall, "started");
+
+ WriteLog(hInstall, "Checking value of InstMSUBinary");
+ wchar_t sInstMSUBinary[MAX_PATH + 10];
+ DWORD nCCh = sizeof(sInstMSUBinary) / sizeof(*sInstMSUBinary);
+ CheckWin32Error("MsiGetPropertyW",
+ MsiGetPropertyW(hInstall, L"InstMSUBinary", sInstMSUBinary, &nCCh));
+ WriteLog(hInstall, "Got InstMSUBinary value:",
+ sInstMSUBinary); // KB2999226|Windows61-KB2999226-x64msu
+ const wchar_t* sBinaryName = wcschr(sInstMSUBinary, L'|');
+ if (!sBinaryName)
+ throw std::exception("No KB number in InstMSUBinary!");
+ // "KB2999226"
+ const std::wstring sKBNo(sInstMSUBinary, sBinaryName - sInstMSUBinary);
+ ++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, nullptr, CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL, nullptr);
+ 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");
+ }
+ const std::wstring s_inst_msu = sKBNo + L"|" + sBinary;
+ CheckWin32Error("MsiSetPropertyW",
+ MsiSetPropertyW(hInstall, L"inst_msu", s_inst_msu.c_str()));
+
+ // Don't delete the file: it will be done by following actions (inst_msu or cleanup_msu)
+ (void)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" __declspec(dllexport) UINT __stdcall InstallMSU(MSIHANDLE hInstall)
+{
+ std::wstring sKBNo; // "KB2999226"
+ try
+ {
+ sLogPrefix = "InstallMSU:";
+ WriteLog(hInstall, "started");
+
+ WriteLog(hInstall, "Checking value of CustomActionData");
+ wchar_t sCustomActionData[MAX_PATH + 10]; // "KB2999226|C:\Temp\binary.tmp"
+ DWORD nCCh = sizeof(sCustomActionData) / sizeof(*sCustomActionData);
+ CheckWin32Error("MsiGetPropertyW",
+ MsiGetPropertyW(hInstall, L"CustomActionData", sCustomActionData, &nCCh));
+ WriteLog(hInstall, "Got CustomActionData value:", sCustomActionData);
+ const wchar_t* sBinaryName = wcschr(sCustomActionData, L'|');
+ if (!sBinaryName)
+ throw std::exception("No KB number in CustomActionData!");
+ sKBNo = std::wstring(sCustomActionData, sBinaryName - sCustomActionData);
+ ++sBinaryName;
+ auto aDeleteFileGuard(Guard(sBinaryName));
+
+ SetStatusText(hInstall, L"WU service state check",
+ L"Checking Windows Update service state");
+
+ // In case the Windows Update service is disabled, we temporarily enable it here. We also
+ // stop running WU service, to avoid wusa.exe freeze (see comment in EnableWUService).
+ WUServiceEnabler aWUServiceEnabler(hInstall);
+
+ SetStatusText(hInstall, sKBNo + L" installation", L"Installing " + sKBNo);
+
+ 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");
+ CloseHandle(pi.hThread);
+ auto aCloseProcHandleGuard(Guard(pi.hProcess));
+ WriteLog(hInstall, "CreateProcessW succeeded");
+
+ {
+ // This block waits when the started wusa.exe process finishes. Since it's possible
+ // for wusa.exe in some circumstances to wait really long (indefinitely?), we check
+ // for user input here.
+ UserInputChecker aInputChecker(hInstall);
+ for (;;)
+ {
+ DWORD nWaitResult = WaitForSingleObject(pi.hProcess, 500);
+ if (nWaitResult == WAIT_OBJECT_0)
+ break; // wusa.exe finished
+ else if (nWaitResult == WAIT_TIMEOUT)
+ aInputChecker.ThrowIfUserCancelled();
+ else
+ ThrowWin32Error("WaitForSingleObject", nWaitResult);
+ }
+ }
+
+ DWORD nExitCode = 0;
+ if (!GetExitCodeProcess(pi.hProcess, &nExitCode))
+ ThrowLastError("GetExitCodeProcess");
+
+ HRESULT hr = static_cast<HRESULT>(nExitCode);
+
+ // HRESULT_FROM_WIN32 is defined as an inline function in SDK 8.1 without the constexpr
+ // And it won't work to place it inside the switch statement.
+ if (HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED) == hr)
+ {
+ hr = ERROR_SUCCESS_REBOOT_REQUIRED;
+ }
+
+ switch (hr)
+ {
+ case S_OK:
+ 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:
+ ThrowHResult("Execution of wusa.exe", hr);
+ }
+ }
+ catch (const UserInputChecker::eUserCancelled&)
+ {
+ return ERROR_INSTALL_USEREXIT;
+ }
+ catch (std::exception& e)
+ {
+ WriteLog(hInstall, e.what());
+ ShowWarning(hInstall, sKBNo, e.what());
+ }
+ return ERROR_SUCCESS; // Do not break on MSU installation errors
+}
+
+// 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" __declspec(dllexport) 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))
+ {
+ if (DWORD nError = GetLastError(); nError != ERROR_FILE_NOT_FOUND)
+ ThrowWin32Error("DeleteFileW", nError);
+ }
+ 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 000000000..49ade9c01
--- /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/setup_native/source/win32/customactions/quickstarter/qslnkmsi.def b/setup_native/source/win32/customactions/quickstarter/qslnkmsi.def
new file mode 100644
index 000000000..f50a3a396
--- /dev/null
+++ b/setup_native/source/win32/customactions/quickstarter/qslnkmsi.def
@@ -0,0 +1,3 @@
+LIBRARY "qslnkmsi.dll"
+EXPORTS
+ RemoveQuickstarterLink \ No newline at end of file
diff --git a/setup_native/source/win32/customactions/quickstarter/quickstarter.cxx b/setup_native/source/win32/customactions/quickstarter/quickstarter.cxx
new file mode 100644
index 000000000..960274f39
--- /dev/null
+++ b/setup_native/source/win32/customactions/quickstarter/quickstarter.cxx
@@ -0,0 +1,131 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "quickstarter.hxx"
+
+#include <psapi.h>
+
+#include <malloc.h>
+
+std::wstring GetOfficeInstallationPathW(MSIHANDLE handle)
+{
+ std::wstring progpath;
+ DWORD sz = 0;
+ PWSTR dummy = const_cast<PWSTR>(L"");
+
+ if (MsiGetPropertyW(handle, L"INSTALLLOCATION", dummy, &sz) == ERROR_MORE_DATA)
+ {
+ sz++; // space for the final '\0'
+ DWORD nbytes = sz * sizeof(WCHAR);
+ PWSTR buff = static_cast<PWSTR>(_alloca(nbytes));
+ ZeroMemory(buff, nbytes);
+ MsiGetPropertyW(handle, L"INSTALLLOCATION", buff, &sz);
+ progpath = buff;
+ }
+ return progpath;
+}
+
+std::wstring GetOfficeProductNameW(MSIHANDLE handle)
+{
+ std::wstring productname;
+ DWORD sz = 0;
+ PWSTR dummy = const_cast<PWSTR>(L"");
+
+ if (MsiGetPropertyW(handle, L"ProductName", dummy, &sz) == ERROR_MORE_DATA)
+ {
+ sz++; // space for the final '\0'
+ DWORD nbytes = sz * sizeof(WCHAR);
+ PWSTR buff = static_cast<PWSTR>(_alloca(nbytes));
+ ZeroMemory(buff, nbytes);
+ MsiGetPropertyW(handle, L"ProductName", buff, &sz);
+ productname = buff;
+ }
+ return productname;
+}
+
+std::wstring GetQuickstarterLinkNameW(MSIHANDLE handle)
+{
+ std::wstring quickstarterlinkname;
+ DWORD sz = 0;
+ PWSTR dummy = const_cast<PWSTR>(L"");
+
+ if (MsiGetPropertyW(handle, L"Quickstarterlinkname", dummy, &sz) == ERROR_MORE_DATA)
+ {
+ sz++; // space for the final '\0'
+ DWORD nbytes = sz * sizeof(WCHAR);
+ PWSTR buff = static_cast<PWSTR>(_alloca(nbytes));
+ ZeroMemory(buff, nbytes);
+ MsiGetPropertyW(handle, L"Quickstarterlinkname", buff, &sz);
+ quickstarterlinkname = buff;
+ }
+ else if (MsiGetPropertyW(handle, L"ProductName", dummy, &sz) == ERROR_MORE_DATA)
+ {
+ sz++; // space for the final '\0'
+ DWORD nbytes = sz * sizeof(WCHAR);
+ PWSTR buff = static_cast<PWSTR>(_alloca(nbytes));
+ ZeroMemory(buff, nbytes);
+ MsiGetPropertyW(handle, L"ProductName", buff, &sz);
+ quickstarterlinkname = buff;
+ }
+ return quickstarterlinkname;
+}
+
+static bool IsValidHandle( HANDLE handle )
+{
+ return nullptr != handle && INVALID_HANDLE_VALUE != handle;
+}
+
+static DWORD WINAPI GetModuleFileNameExW_( HANDLE hProcess, HMODULE hModule, PWSTR lpFileName, DWORD nSize )
+{
+ static auto lpProc = []() {
+ HMODULE hLibrary = LoadLibraryW(L"PSAPI.DLL");
+ decltype(GetModuleFileNameExW)* pRet = nullptr;
+ if (hLibrary)
+ pRet = reinterpret_cast<decltype(GetModuleFileNameExW)*>(
+ GetProcAddress(hLibrary, "GetModuleFileNameExW"));
+ return pRet;
+ }();
+
+ if ( lpProc )
+ return lpProc( hProcess, hModule, lpFileName, nSize );
+
+ return 0;
+
+}
+
+std::wstring GetProcessImagePathW( DWORD dwProcessId )
+{
+ std::wstring sImagePath;
+
+ HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwProcessId );
+
+ if ( IsValidHandle( hProcess ) )
+ {
+ WCHAR szPathBuffer[MAX_PATH] = L"";
+
+ if ( GetModuleFileNameExW_( hProcess, nullptr, szPathBuffer, sizeof(szPathBuffer)/sizeof(szPathBuffer[0]) ) )
+ sImagePath = szPathBuffer;
+
+ CloseHandle( hProcess );
+ }
+
+ return sImagePath;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/setup_native/source/win32/customactions/quickstarter/quickstarter.hxx b/setup_native/source/win32/customactions/quickstarter/quickstarter.hxx
new file mode 100644
index 000000000..2370f4691
--- /dev/null
+++ b/setup_native/source/win32/customactions/quickstarter/quickstarter.hxx
@@ -0,0 +1,36 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SETUP_NATIVE_SOURCE_WIN32_CUSTOMACTIONS_QUICKSTARTER_QUICKSTARTER_HXX
+#define INCLUDED_SETUP_NATIVE_SOURCE_WIN32_CUSTOMACTIONS_QUICKSTARTER_QUICKSTARTER_HXX
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <msiquery.h>
+
+#include <string>
+
+std::wstring GetOfficeInstallationPathW(MSIHANDLE handle);
+std::wstring GetOfficeProductNameW(MSIHANDLE handle);
+std::wstring GetQuickstarterLinkNameW(MSIHANDLE handle);
+std::wstring GetProcessImagePathW(DWORD dwProcessId);
+
+#endif // INCLUDED_SETUP_NATIVE_SOURCE_WIN32_CUSTOMACTIONS_QUICKSTARTER_QUICKSTARTER_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/setup_native/source/win32/customactions/quickstarter/remove_quickstart_link.cxx b/setup_native/source/win32/customactions/quickstarter/remove_quickstart_link.cxx
new file mode 100644
index 000000000..68faed45a
--- /dev/null
+++ b/setup_native/source/win32/customactions/quickstarter/remove_quickstart_link.cxx
@@ -0,0 +1,42 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "quickstarter.hxx"
+
+#include <shlobj.h>
+
+extern "C" __declspec(dllexport) UINT __stdcall RemoveQuickstarterLink( MSIHANDLE hMSI )
+{
+ WCHAR szStartupPath[MAX_PATH];
+
+ if ( SHGetSpecialFolderPathW( nullptr, szStartupPath, CSIDL_STARTUP, FALSE ) )
+ {
+ std::wstring sQuickstartLinkPath = szStartupPath;
+
+ sQuickstartLinkPath += L"\\";
+ sQuickstartLinkPath += GetQuickstarterLinkNameW( hMSI );
+ sQuickstartLinkPath += L".lnk";
+
+ DeleteFileW( sQuickstartLinkPath.c_str() );
+ }
+
+ return ERROR_SUCCESS;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/setup_native/source/win32/customactions/quickstarter/sdqsmsi.def b/setup_native/source/win32/customactions/quickstarter/sdqsmsi.def
new file mode 100644
index 000000000..c8df896e5
--- /dev/null
+++ b/setup_native/source/win32/customactions/quickstarter/sdqsmsi.def
@@ -0,0 +1,3 @@
+LIBRARY "sdqsmsi.dll"
+EXPORTS
+ ShutDownQuickstarter \ No newline at end of file
diff --git a/setup_native/source/win32/customactions/quickstarter/shutdown_quickstart.cxx b/setup_native/source/win32/customactions/quickstarter/shutdown_quickstart.cxx
new file mode 100644
index 000000000..c93b000e4
--- /dev/null
+++ b/setup_native/source/win32/customactions/quickstarter/shutdown_quickstart.cxx
@@ -0,0 +1,73 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "quickstarter.hxx"
+
+#include <systools/win32/qswin32.h>
+
+static BOOL CALLBACK EnumWindowsProc( HWND hWnd, LPARAM lParam )
+{
+ MSIHANDLE hMSI = static_cast< MSIHANDLE >( lParam );
+ WCHAR szClassName[sizeof(QUICKSTART_CLASSNAME)/sizeof(WCHAR) + 1];
+
+ int nCharsCopied = GetClassNameW( hWnd, szClassName, sizeof(szClassName)/sizeof(szClassName[0]) );
+
+ if ( nCharsCopied && !_wcsicmp( QUICKSTART_CLASSNAME, szClassName ) )
+ {
+ DWORD dwProcessId;
+
+ if ( GetWindowThreadProcessId( hWnd, &dwProcessId ) )
+ {
+ std::wstring sImagePath = GetProcessImagePathW( dwProcessId );
+ std::wstring sOfficeImageDir = GetOfficeInstallationPathW( hMSI ) + L"program\\";
+
+ if ( !_wcsnicmp( sImagePath.c_str(), sOfficeImageDir.c_str(), sOfficeImageDir.length() ) )
+ {
+ UINT uMsgShutdownQuickstart = RegisterWindowMessageW( SHUTDOWN_QUICKSTART_MESSAGE );
+
+ if ( uMsgShutdownQuickstart )
+ SendMessageW( hWnd, uMsgShutdownQuickstart, 0, 0 );
+
+
+ HANDLE hProcess = OpenProcess( SYNCHRONIZE, FALSE, dwProcessId );
+
+ if ( hProcess )
+ {
+ WaitForSingleObject( hProcess, 30000 ); // Wait at most 30 seconds for process to terminate
+ CloseHandle( hProcess );
+ }
+
+ return FALSE;
+ }
+
+ }
+ }
+
+ return TRUE;
+}
+
+
+extern "C" __declspec(dllexport) UINT __stdcall ShutDownQuickstarter( MSIHANDLE hMSI )
+{
+ EnumWindows( EnumWindowsProc, hMSI );
+
+ return ERROR_SUCCESS;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/setup_native/source/win32/customactions/reg4allmsdoc/reg4allmsdoc.def b/setup_native/source/win32/customactions/reg4allmsdoc/reg4allmsdoc.def
new file mode 100644
index 000000000..cea8b4699
--- /dev/null
+++ b/setup_native/source/win32/customactions/reg4allmsdoc/reg4allmsdoc.def
@@ -0,0 +1,6 @@
+LIBRARY "reg4allmsdoc.dll"
+EXPORTS
+ FindRegisteredExtensions
+ LookForRegisteredExtensions
+ RegisterSomeExtensions
+ RestoreRegAllMSDoc \ No newline at end of file
diff --git a/setup_native/source/win32/customactions/reg4allmsdoc/reg4allmsi.cxx b/setup_native/source/win32/customactions/reg4allmsdoc/reg4allmsi.cxx
new file mode 100644
index 000000000..1a2e82eb7
--- /dev/null
+++ b/setup_native/source/win32/customactions/reg4allmsdoc/reg4allmsi.cxx
@@ -0,0 +1,519 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <msiquery.h>
+
+#include <malloc.h>
+#include <string>
+#include <strsafe.h>
+
+static const WCHAR* g_Extensions[] =
+{
+ L".doc", // Microsoft Word Text [0]
+ L".dot", // Microsoft Word Template
+ L".wps", // Kingsoft Writer Document
+ L".wpt", // Kingsoft Writer Template
+ L".rtf", // rtf text
+ L".docx", // Office Word 2007 XML document
+ L".docm", // Office Word 2007 XML macro-enabled document
+ L".dotx", // Office Word 2007 XML template
+ L".dotm", // Office Word 2007 XML macro-enabled template
+ L".xlw", // Microsoft Excel
+ L".xls", // Microsoft Excel
+ L".xlt", // Microsoft Excel Template
+ L".xlsx", // Office Excel 2007 XML workbook
+ L".xlsm", // Office Excel 2007 XML macro-enabled workbook
+ L".xltx", // Office Excel 2007 XML template
+ L".xltm", // Office Excel 2007 XML macro-enabled template
+ L".xlsb", // Office Excel 2007 binary workbook (BIFF12)
+ L".iqy", // Microsoft Excel Web Query File
+ L".et", // Kingsoft Spreadsheet
+ L".ett", // Kingsoft SpreadSheet Template
+ L".ppt", // Microsoft Powerpoint
+ L".pps", // Microsoft Powerpoint
+ L".pot", // Microsoft Powerpoint Template
+ L".pptx", // Office PowerPoint 2007 XML presentation
+ L".pptm", // Office PowerPoint 2007 macro-enabled XML presentation
+ L".potx", // Office PowerPoint 2007 XML template
+ L".potm", // Office PowerPoint 2007 macro-enabled XML template
+ L".ppsx", // Office PowerPoint 2007 XML show
+ L".dps", // Kingsoft Presentation
+ L".dpt", // Kingsoft Presentation Template
+ L".vsd", // Visio 2000/XP/2003 document
+ L".vst", // Visio 2000/XP/2003 template
+ nullptr
+};
+
+static const int WORD_START = 0;
+static const int EXCEL_START = 9;
+static const int POWERPOINT_START = 20;
+static const int VISIO_START = 30;
+static const int VISIO_END = 32;
+
+// ".xlam", // Office Excel 2007 XML macro-enabled add-in
+// ".ppam", // Office PowerPoint 2007 macro-enabled XML add-in
+// ".ppsm", // Office PowerPoint 2007 macro-enabled XML show
+
+#ifdef DEBUG
+inline void OutputDebugStringFormatW( LPCWSTR pFormat, ... )
+{
+ WCHAR buffer[1024];
+ va_list args;
+
+ va_start( args, pFormat );
+ StringCchVPrintfW( buffer, sizeof(buffer)/sizeof(*buffer), pFormat, args );
+ OutputDebugStringW( buffer );
+ va_end(args);
+}
+#else
+static void OutputDebugStringFormatW( LPCWSTR, ... )
+{
+}
+#endif
+
+static bool CheckExtensionInRegistry( LPCWSTR lpSubKey )
+{
+ bool bRet = false;
+ HKEY hKey = nullptr;
+ LONG lResult = RegOpenKeyExW( HKEY_CLASSES_ROOT, lpSubKey, 0, KEY_QUERY_VALUE, &hKey );
+
+ if ( ERROR_SUCCESS == lResult )
+ {
+ WCHAR szBuffer[1024];
+ DWORD nSize = sizeof( szBuffer );
+
+ lResult = RegQueryValueExW( hKey, L"", nullptr, nullptr, reinterpret_cast<LPBYTE>(szBuffer), &nSize );
+ if ( ERROR_SUCCESS == lResult && nSize > 0 )
+ {
+ szBuffer[nSize/sizeof(*szBuffer)] = L'\0';
+ OutputDebugStringFormatW( L"Found value [%s] for key [%s].\n", szBuffer, lpSubKey );
+
+ if ( wcsncmp( szBuffer, L"WordPad.Document.1", 18 ) == 0 )
+ { // We will replace registration for WordPad (alas, on XP only) FIXME
+ bRet = true;
+ }
+ else if ( wcsncmp( szBuffer, L"LibreOffice.", 12 ) == 0 )
+ { // We will replace registration for our own types, too
+ bRet = true;
+ }
+ else if ( wcsncmp( szBuffer, L"lostub.", 7 ) == 0 )
+ { // We will replace registration for lostub, too
+ bRet = true;
+ }
+ else // we have a default value -> do not register, see fdo#39791
+ bRet = false;
+ }
+ else // no default value found -> return TRUE to register for that key
+ bRet = true;
+
+ RegCloseKey( hKey );
+ }
+ else // no key found -> return TRUE to register for that key
+ bRet = true;
+
+ return bRet;
+}
+
+static bool GetMsiPropW( MSIHANDLE handle, LPCWSTR name, /*out*/std::wstring& value )
+{
+ DWORD sz = 0;
+ LPWSTR dummy = const_cast<LPWSTR>(L"");
+ if (MsiGetPropertyW(handle, name, dummy, &sz) == ERROR_MORE_DATA)
+ {
+ sz++;
+ DWORD nbytes = sz * sizeof(WCHAR);
+ LPWSTR buff = static_cast<LPWSTR>(_alloca(nbytes));
+ ZeroMemory(buff, nbytes);
+ MsiGetPropertyW(handle, name, buff, &sz);
+ value = buff;
+ return true;
+ }
+ return false;
+}
+
+static bool IsSetMsiPropW( MSIHANDLE handle, LPCWSTR name )
+{
+ std::wstring val;
+ GetMsiPropW( handle, name, val );
+ return (val == L"1");
+}
+
+static void registerForExtension( MSIHANDLE handle, const int nIndex, bool bRegister )
+{
+ WCHAR sPropName[256];
+ StringCchCopyW( sPropName, 256, L"REGISTER_" );
+ StringCchCatW( sPropName, 256, (g_Extensions[nIndex])+1 );
+ CharUpperBuffW( sPropName+9, 4 );
+
+ if ( bRegister ) {
+ MsiSetPropertyW( handle, sPropName, L"1" );
+ OutputDebugStringFormatW( L"Set MSI property %s.\n", sPropName );
+ } else {
+ MsiSetPropertyW( handle, sPropName, L"0" );
+ OutputDebugStringFormatW( L"Unset MSI property %s.\n", sPropName );
+ }
+}
+
+static void saveOldRegistration( LPCWSTR lpSubKey )
+{
+ HKEY hKey = nullptr;
+ LONG lResult = RegOpenKeyExW( HKEY_CLASSES_ROOT, lpSubKey, 0,
+ KEY_QUERY_VALUE|KEY_SET_VALUE, &hKey );
+
+ if ( ERROR_SUCCESS == lResult )
+ {
+ WCHAR szBuffer[1024];
+ DWORD nSize = sizeof( szBuffer );
+
+ lResult = RegQueryValueExW( hKey, L"", nullptr, nullptr, reinterpret_cast<LPBYTE>(szBuffer), &nSize );
+ if ( ERROR_SUCCESS == lResult )
+ {
+ szBuffer[nSize/sizeof(*szBuffer)] = L'\0';
+
+ // No need to save associations for our own types
+ if ( wcsncmp( szBuffer, L"LibreOffice.", 12 ) != 0 )
+ {
+ // Save the old association
+ RegSetValueExW( hKey, L"LOBackupAssociation", 0,
+ REG_SZ, reinterpret_cast<LPBYTE>(szBuffer), nSize );
+ // Also save what the old association means, just so we can try to verify
+ // if/when restoring it that the old application still exists
+ HKEY hKey2 = nullptr;
+ lResult = RegOpenKeyExW( HKEY_CLASSES_ROOT, szBuffer, 0,
+ KEY_QUERY_VALUE, &hKey2 );
+ if ( ERROR_SUCCESS == lResult )
+ {
+ nSize = sizeof( szBuffer );
+ lResult = RegQueryValueExW( hKey2, L"", nullptr, nullptr, reinterpret_cast<LPBYTE>(szBuffer), &nSize );
+ if ( ERROR_SUCCESS == lResult )
+ {
+ RegSetValueExW( hKey, L"LOBackupAssociationDeref", 0,
+ REG_SZ, reinterpret_cast<LPBYTE>(szBuffer), nSize );
+ }
+ RegCloseKey( hKey2 );
+ }
+ }
+ }
+ RegCloseKey( hKey );
+ }
+}
+
+static void registerForExtensions( MSIHANDLE handle, bool bRegisterAll )
+{ // Check all file extensions
+ int nIndex = 0;
+ while ( g_Extensions[nIndex] != nullptr )
+ {
+ saveOldRegistration( g_Extensions[nIndex] );
+
+ bool bRegister = bRegisterAll || CheckExtensionInRegistry( g_Extensions[nIndex] );
+ if ( bRegister )
+ registerForExtension( handle, nIndex, true );
+ ++nIndex;
+ }
+}
+
+static bool checkSomeExtensionInRegistry( const int nStart, const int nEnd )
+{ // Check all file extensions
+ int nIndex = nStart;
+ bool bFound = false;
+
+ while ( !bFound && (nIndex < nEnd) && (g_Extensions[nIndex] != nullptr) )
+ {
+ bFound = ! CheckExtensionInRegistry( g_Extensions[nIndex] );
+
+ if ( bFound )
+ OutputDebugStringFormatW( L"Found registration for [%s].\n", g_Extensions[nIndex] );
+
+ ++nIndex;
+ }
+ return bFound;
+}
+
+static void registerSomeExtensions( MSIHANDLE handle, const int nStart, const int nEnd, bool bRegister )
+{ // Check all file extensions
+ int nIndex = nStart;
+
+ while ( (nIndex < nEnd) && (g_Extensions[nIndex] != nullptr) )
+ {
+ registerForExtension( handle, nIndex++, bRegister );
+ }
+}
+
+extern "C" __declspec(dllexport) UINT __stdcall LookForRegisteredExtensions( MSIHANDLE handle )
+{
+ OutputDebugStringFormatW( L"LookForRegisteredExtensions: " );
+
+ INSTALLSTATE current_state;
+ INSTALLSTATE future_state;
+
+ bool bWriterEnabled = false;
+ bool bCalcEnabled = false;
+ bool bImpressEnabled = false;
+ bool bDrawEnabled = false;
+ bool bRegisterNone = IsSetMsiPropW( handle, L"REGISTER_NO_MSO_TYPES" );
+
+ if ( ( ERROR_SUCCESS == MsiGetFeatureStateW( handle, L"gm_p_Wrt", &current_state, &future_state ) ) &&
+ ( (future_state == INSTALLSTATE_LOCAL) || ((current_state == INSTALLSTATE_LOCAL) && (future_state == INSTALLSTATE_UNKNOWN) ) ) )
+ bWriterEnabled = true;
+
+ OutputDebugStringFormatW( L"LookForRegisteredExtensions: Install state Writer is [%d], will be [%d]", current_state, future_state );
+ if ( bWriterEnabled )
+ OutputDebugStringFormatW( L"LookForRegisteredExtensions: Writer is enabled" );
+ else
+ OutputDebugStringFormatW( L"LookForRegisteredExtensions: Writer is NOT enabled" );
+
+ if ( ( ERROR_SUCCESS == MsiGetFeatureStateW( handle, L"gm_p_Calc", &current_state, &future_state ) ) &&
+ ( (future_state == INSTALLSTATE_LOCAL) || ((current_state == INSTALLSTATE_LOCAL) && (future_state == INSTALLSTATE_UNKNOWN) ) ) )
+ bCalcEnabled = true;
+
+ OutputDebugStringFormatW( L"LookForRegisteredExtensions: Install state Calc is [%d], will be [%d]", current_state, future_state );
+ if ( bCalcEnabled )
+ OutputDebugStringFormatW( L"LookForRegisteredExtensions: Calc is enabled" );
+ else
+ OutputDebugStringFormatW( L"LookForRegisteredExtensions: Calc is NOT enabled" );
+
+ if ( ( ERROR_SUCCESS == MsiGetFeatureStateW( handle, L"gm_p_Impress", &current_state, &future_state ) ) &&
+ ( (future_state == INSTALLSTATE_LOCAL) || ((current_state == INSTALLSTATE_LOCAL) && (future_state == INSTALLSTATE_UNKNOWN) ) ) )
+ bImpressEnabled = true;
+
+ OutputDebugStringFormatW( L"LookForRegisteredExtensions: Install state Impress is [%d], will be [%d]", current_state, future_state );
+ if ( bImpressEnabled )
+ OutputDebugStringFormatW( L"LookForRegisteredExtensions: Impress is enabled" );
+ else
+ OutputDebugStringFormatW( L"LookForRegisteredExtensions: Impress is NOT enabled" );
+
+ if ( ( ERROR_SUCCESS == MsiGetFeatureStateW( handle, L"gm_p_Draw", &current_state, &future_state ) ) &&
+ ( (future_state == INSTALLSTATE_LOCAL) || ((current_state == INSTALLSTATE_LOCAL) && (future_state == INSTALLSTATE_UNKNOWN) ) ) )
+ bDrawEnabled = true;
+
+ OutputDebugStringFormatW( L"LookForRegisteredExtensions: Install state Draw is [%d], will be [%d]", current_state, future_state );
+ if ( bImpressEnabled )
+ OutputDebugStringFormatW( L"LookForRegisteredExtensions: Draw is enabled" );
+ else
+ OutputDebugStringFormatW( L"LookForRegisteredExtensions: Draw is NOT enabled" );
+
+ MsiSetPropertyW( handle, L"SELECT_WORD", L"" );
+ MsiSetPropertyW( handle, L"SELECT_EXCEL", L"" );
+ MsiSetPropertyW( handle, L"SELECT_POWERPOINT", L"" );
+ MsiSetPropertyW( handle, L"SELECT_VISIO", L"" );
+
+ if ( ! bRegisterNone )
+ {
+ if ( IsSetMsiPropW( handle, L"REGISTER_ALL_MSO_TYPES" ) )
+ {
+ if ( bWriterEnabled )
+ MsiSetPropertyW( handle, L"SELECT_WORD", L"1" );
+ if ( bCalcEnabled )
+ MsiSetPropertyW( handle, L"SELECT_EXCEL", L"1" );
+ if ( bImpressEnabled )
+ MsiSetPropertyW( handle, L"SELECT_POWERPOINT", L"1" );
+ if ( bDrawEnabled )
+ MsiSetPropertyW( handle, L"SELECT_VISIO", L"1" );
+ }
+ else
+ {
+ if ( bWriterEnabled && ! checkSomeExtensionInRegistry( WORD_START, EXCEL_START ) )
+ {
+ MsiSetPropertyW( handle, L"SELECT_WORD", L"1" );
+ OutputDebugStringFormatW( L"LookForRegisteredExtensions: Register for Microsoft Word" );
+ }
+ if ( bCalcEnabled && ! checkSomeExtensionInRegistry( EXCEL_START, POWERPOINT_START ) )
+ {
+ MsiSetPropertyW( handle, L"SELECT_EXCEL", L"1" );
+ OutputDebugStringFormatW( L"LookForRegisteredExtensions: Register for Microsoft Excel" );
+ }
+ if ( bImpressEnabled && ! checkSomeExtensionInRegistry( POWERPOINT_START, VISIO_START ) )
+ {
+ MsiSetPropertyW( handle, L"SELECT_POWERPOINT", L"1" );
+ OutputDebugStringFormatW( L"LookForRegisteredExtensions: Register for Microsoft PowerPoint" );
+ }
+ if ( bImpressEnabled && ! checkSomeExtensionInRegistry( VISIO_START, VISIO_END ) )
+ {
+ MsiSetPropertyW( handle, L"SELECT_VISIO", L"1" );
+ OutputDebugStringFormatW( L"LookForRegisteredExtensions: Register for Microsoft Visio" );
+ }
+ }
+ }
+
+ MsiSetPropertyW( handle, L"FILETYPEDIALOGUSED", L"1" );
+
+ return ERROR_SUCCESS;
+}
+
+extern "C" __declspec(dllexport) UINT __stdcall RegisterSomeExtensions( MSIHANDLE handle )
+{
+ OutputDebugStringFormatW( L"RegisterSomeExtensions: " );
+
+ if ( IsSetMsiPropW( handle, L"SELECT_WORD" ) )
+ {
+ registerSomeExtensions( handle, WORD_START, EXCEL_START, true );
+ MsiSetFeatureStateW( handle, L"gm_p_Wrt_MSO_Reg", INSTALLSTATE_LOCAL );
+ OutputDebugStringFormatW( L"RegisterSomeExtensions: Register for Microsoft Word" );
+ }
+ else
+ {
+ registerSomeExtensions( handle, WORD_START, EXCEL_START, false );
+ MsiSetFeatureStateW( handle, L"gm_p_Wrt_MSO_Reg", INSTALLSTATE_ABSENT );
+ }
+
+ if ( IsSetMsiPropW( handle, L"SELECT_EXCEL" ) )
+ {
+ registerSomeExtensions( handle, EXCEL_START, POWERPOINT_START, true );
+ MsiSetFeatureStateW( handle, L"gm_p_Calc_MSO_Reg", INSTALLSTATE_LOCAL );
+ OutputDebugStringFormatW( L"RegisterSomeExtensions: Register for Microsoft Excel" );
+ }
+ else
+ {
+ registerSomeExtensions( handle, EXCEL_START, POWERPOINT_START, false );
+ MsiSetFeatureStateW( handle, L"gm_p_Calc_MSO_Reg", INSTALLSTATE_ABSENT );
+ }
+
+ if ( IsSetMsiPropW( handle, L"SELECT_POWERPOINT" ) )
+ {
+ registerSomeExtensions( handle, POWERPOINT_START, VISIO_START, true );
+ MsiSetFeatureStateW( handle, L"gm_p_Impress_MSO_Reg", INSTALLSTATE_LOCAL );
+ OutputDebugStringFormatW( L"RegisterSomeExtensions: Register for Microsoft PowerPoint" );
+ }
+ else
+ {
+ registerSomeExtensions( handle, POWERPOINT_START, VISIO_START, false );
+ MsiSetFeatureStateW( handle, L"gm_p_Impress_MSO_Reg", INSTALLSTATE_ABSENT );
+ }
+
+ if ( IsSetMsiPropW( handle, L"SELECT_VISIO" ) )
+ {
+ registerSomeExtensions( handle, VISIO_START, VISIO_END, true );
+ MsiSetFeatureStateW( handle, L"gm_p_Draw_MSO_Reg", INSTALLSTATE_LOCAL );
+ OutputDebugStringFormatW( L"RegisterSomeExtensions: Register for Microsoft Visio" );
+ }
+ else
+ {
+ registerSomeExtensions( handle, VISIO_START, VISIO_END, false );
+ MsiSetFeatureStateW( handle, L"gm_p_Draw_MSO_Reg", INSTALLSTATE_ABSENT );
+ }
+ return ERROR_SUCCESS;
+}
+
+extern "C" __declspec(dllexport) UINT __stdcall FindRegisteredExtensions( MSIHANDLE handle )
+{
+ if ( IsSetMsiPropW( handle, L"FILETYPEDIALOGUSED" ) )
+ {
+ OutputDebugStringFormatW( L"FindRegisteredExtensions: FILETYPEDIALOGUSED!" );
+ return ERROR_SUCCESS;
+ }
+
+ OutputDebugStringFormatW( L"FindRegisteredExtensions:" );
+
+ bool bRegisterAll = IsSetMsiPropW( handle, L"REGISTER_ALL_MSO_TYPES" );
+
+ if ( IsSetMsiPropW( handle, L"REGISTER_NO_MSO_TYPES" ) )
+ {
+ OutputDebugStringFormatW( L"FindRegisteredExtensions: Register none!" );
+ return ERROR_SUCCESS;
+ }
+ else if ( bRegisterAll )
+ OutputDebugStringFormatW( L"FindRegisteredExtensions: Force all on" );
+ else
+ OutputDebugStringFormatW( L"FindRegisteredExtensions: " );
+
+ // setting the msi properties SELECT_* will force registering for all corresponding
+ // file types
+ if ( IsSetMsiPropW( handle, L"SELECT_WORD" ) )
+ registerSomeExtensions( handle, WORD_START, EXCEL_START, true );
+ if ( IsSetMsiPropW( handle, L"SELECT_EXCEL" ) )
+ registerSomeExtensions( handle, EXCEL_START, POWERPOINT_START, true );
+ if ( IsSetMsiPropW( handle, L"SELECT_POWERPOINT" ) )
+ registerSomeExtensions( handle, POWERPOINT_START, VISIO_START, true );
+ if ( IsSetMsiPropW( handle, L"SELECT_VISIO" ) )
+ registerSomeExtensions( handle, VISIO_START, VISIO_END, true );
+
+ registerForExtensions( handle, bRegisterAll );
+
+ return ERROR_SUCCESS;
+}
+
+static void restoreOldRegistration( LPCWSTR lpSubKey )
+{
+ HKEY hKey = nullptr;
+ LONG lResult = RegOpenKeyExW( HKEY_CLASSES_ROOT, lpSubKey, 0,
+ KEY_QUERY_VALUE|KEY_SET_VALUE, &hKey );
+
+ if ( ERROR_SUCCESS == lResult )
+ {
+ WCHAR szBuffer[1024];
+ DWORD nSize = sizeof( szBuffer );
+
+ lResult = RegQueryValueExW( hKey, L"LOBackupAssociation", nullptr, nullptr,
+ reinterpret_cast<LPBYTE>(szBuffer), &nSize );
+ if ( ERROR_SUCCESS == lResult )
+ {
+ szBuffer[nSize/sizeof(*szBuffer)] = L'\0';
+ HKEY hKey2 = nullptr;
+ lResult = RegOpenKeyExW( HKEY_CLASSES_ROOT, szBuffer, 0,
+ KEY_QUERY_VALUE, &hKey2 );
+ if ( ERROR_SUCCESS == lResult )
+ {
+ WCHAR szBuffer2[1024];
+ DWORD nSize2 = sizeof( szBuffer2 );
+
+ lResult = RegQueryValueExW( hKey2, L"", nullptr, nullptr, reinterpret_cast<LPBYTE>(szBuffer2), &nSize2 );
+ if ( ERROR_SUCCESS == lResult )
+ {
+ WCHAR szBuffer3[1024];
+ DWORD nSize3 = sizeof( szBuffer3 );
+
+ // Try to verify that the old association is OK to restore
+ lResult = RegQueryValueExW( hKey, L"LOBackupAssociationDeref", nullptr, nullptr,
+ reinterpret_cast<LPBYTE>(szBuffer3), &nSize3 );
+ if ( ERROR_SUCCESS == lResult )
+ {
+ if ( nSize2 == nSize3 && wcsncmp (szBuffer2, szBuffer3, nSize2/sizeof(*szBuffer2)) == 0)
+ {
+ // Yep. So restore it
+ RegSetValueExW( hKey, L"", 0, REG_SZ, reinterpret_cast<LPBYTE>(szBuffer), nSize );
+ }
+ }
+ }
+ RegCloseKey( hKey2 );
+ }
+ RegDeleteValueW( hKey, L"LOBackupAssociation" );
+ }
+ RegDeleteValueW( hKey, L"LOBackupAssociationDeref" );
+ RegCloseKey( hKey );
+ }
+}
+
+extern "C" __declspec(dllexport) UINT __stdcall RestoreRegAllMSDoc( MSIHANDLE /*handle*/ )
+{
+ OutputDebugStringFormatW( L"RestoreRegAllMSDoc\n" );
+
+ int nIndex = 0;
+ while ( g_Extensions[nIndex] != nullptr )
+ {
+ restoreOldRegistration( g_Extensions[nIndex] );
+ ++nIndex;
+ }
+
+
+ return ERROR_SUCCESS;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
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 000000000..c7b1cc742
--- /dev/null
+++ b/setup_native/source/win32/customactions/reg_dlls/reg_dlls.cxx
@@ -0,0 +1,319 @@
+/* -*- 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, const_cast<wchar_t*>(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));
+
+ WriteLog(hInstall, "gm_SharePointSupport_SubstMSO state:", //
+ "current", std::to_string(current_state_SubstMSO), //
+ "future", std::to_string(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));
+
+ WriteLog(hInstall, "gm_o_SharePointSupport state:", //
+ "current", std::to_string(current_state_Main), //
+ "future", std::to_string(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, const_cast<wchar_t*>(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 000000000..d0f31c491
--- /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
diff --git a/setup_native/source/win32/customactions/regactivex/regactivex.cxx b/setup_native/source/win32/customactions/regactivex/regactivex.cxx
new file mode 100644
index 000000000..48b11bd4f
--- /dev/null
+++ b/setup_native/source/win32/customactions/regactivex/regactivex.cxx
@@ -0,0 +1,344 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#if !defined WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#include <msiquery.h>
+
+#include <cassert>
+#include <string.h>
+#include <malloc.h>
+
+#define CHART_COMPONENT 1
+#define DRAW_COMPONENT 2
+#define IMPRESS_COMPONENT 4
+#define CALC_COMPONENT 8
+#define WRITER_COMPONENT 16
+#define MATH_COMPONENT 32
+
+typedef int ( __stdcall * DllNativeRegProc ) ( int, BOOL, BOOL, const wchar_t* );
+typedef int ( __stdcall * DllNativeUnregProc ) ( int, BOOL, BOOL );
+
+static bool UnicodeEquals( wchar_t const * pStr1, wchar_t const * pStr2 )
+{
+ if ( pStr1 == nullptr && pStr2 == nullptr )
+ return true;
+ else if ( pStr1 == nullptr || pStr2 == nullptr )
+ return false;
+
+ while( *pStr1 == *pStr2 && *pStr1 && *pStr2 )
+ {
+ pStr1++;
+ pStr2++;
+ }
+
+ return ( *pStr1 == 0 && *pStr2 == 0 );
+}
+
+
+static void RegisterActiveXNative( const wchar_t* pActiveXPath, int nMode, bool InstallForAllUser, bool InstallFor64Bit )
+{
+ HINSTANCE hModule = LoadLibraryExW( pActiveXPath, nullptr, LOAD_WITH_ALTERED_SEARCH_PATH );
+ if( hModule )
+ {
+ DllNativeRegProc pNativeProc = reinterpret_cast<DllNativeRegProc>(GetProcAddress( hModule, "DllRegisterServerNative" ));
+ if( pNativeProc!=nullptr )
+ {
+ int nLen = wcslen( pActiveXPath );
+ int nRemoveLen = strlen( "\\so_activex.dll" );
+ if ( nLen > nRemoveLen )
+ {
+ wchar_t* pProgramPath = static_cast<wchar_t*>( malloc( (nLen - nRemoveLen + 1) * sizeof(wchar_t) ) );
+ assert(pProgramPath); // Don't handle OOM conditions
+ wcsncpy( pProgramPath, pActiveXPath, nLen - nRemoveLen );
+ pProgramPath[ nLen - nRemoveLen ] = 0;
+
+ ( *pNativeProc )( nMode, InstallForAllUser, InstallFor64Bit, pProgramPath );
+
+ free( pProgramPath );
+ }
+ }
+
+ FreeLibrary( hModule );
+ }
+}
+
+
+static void UnregisterActiveXNative( const wchar_t* pActiveXPath, int nMode, bool InstallForAllUser, bool InstallFor64Bit )
+{
+ HINSTANCE hModule = LoadLibraryExW( pActiveXPath, nullptr, LOAD_WITH_ALTERED_SEARCH_PATH );
+ if( hModule )
+ {
+ DllNativeUnregProc pNativeProc = reinterpret_cast<DllNativeUnregProc>(GetProcAddress( hModule, "DllUnregisterServerNative" ));
+ if( pNativeProc!=nullptr )
+ ( *pNativeProc )( nMode, InstallForAllUser, InstallFor64Bit );
+
+ FreeLibrary( hModule );
+ }
+}
+
+
+static bool GetMsiPropW( MSIHANDLE hMSI, const wchar_t* pPropName, wchar_t** ppValue )
+{
+ DWORD sz = 0;
+ if ( MsiGetPropertyW( hMSI, pPropName, const_cast<wchar_t *>(L""), &sz ) == ERROR_MORE_DATA )
+ {
+ sz++;
+ DWORD nbytes = sz * sizeof( wchar_t );
+ wchar_t* buff = static_cast<wchar_t*>( malloc( nbytes ) );
+ assert(buff); // Don't handle OOM conditions
+ ZeroMemory( buff, nbytes );
+ MsiGetPropertyW( hMSI, pPropName, buff, &sz );
+ *ppValue = buff;
+
+ return true;
+ }
+
+ return false;
+}
+
+
+static bool GetActiveXControlPath( MSIHANDLE hMSI, wchar_t** ppActiveXPath )
+{
+ wchar_t* pProgPath = nullptr;
+ if ( GetMsiPropW( hMSI, L"INSTALLLOCATION", &pProgPath ) && pProgPath )
+ {
+ int nLen = wcslen( pProgPath );
+ *ppActiveXPath = static_cast<wchar_t*>( malloc( (nLen + 23) * sizeof(wchar_t) ) );
+ wcsncpy( *ppActiveXPath, pProgPath, nLen );
+ wcsncpy( (*ppActiveXPath) + nLen, L"program\\so_activex.dll", 22 );
+ (*ppActiveXPath)[nLen+22] = 0;
+
+ free(pProgPath);
+
+ return true;
+ }
+
+ return false;
+}
+
+
+static bool GetDelta( MSIHANDLE hMSI, int& nOldInstallMode, int& nInstallMode, int& nDeinstallMode )
+{
+ // for now the chart is always installed
+ nOldInstallMode = CHART_COMPONENT;
+ nInstallMode = CHART_COMPONENT;
+ nDeinstallMode = 0;
+
+ INSTALLSTATE current_state;
+ INSTALLSTATE future_state;
+
+ if ( ERROR_SUCCESS == MsiGetFeatureStateW( hMSI, L"gm_p_Wrt_Bin", &current_state, &future_state ) )
+ {
+ // analyze writer installation mode
+ if ( current_state == INSTALLSTATE_LOCAL )
+ nOldInstallMode |= WRITER_COMPONENT;
+
+ if ( future_state == INSTALLSTATE_LOCAL
+ || ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_UNKNOWN ) )
+ nInstallMode |= WRITER_COMPONENT;
+ else if ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_ABSENT )
+ nDeinstallMode |= WRITER_COMPONENT;
+ }
+ else
+ {
+ // assert( FALSE );
+ }
+
+ if ( ERROR_SUCCESS == MsiGetFeatureStateW( hMSI, L"gm_p_Calc_Bin", &current_state, &future_state ) )
+ {
+ // analyze calc installation mode
+ if ( current_state == INSTALLSTATE_LOCAL )
+ nOldInstallMode |= CALC_COMPONENT;
+
+ if ( future_state == INSTALLSTATE_LOCAL
+ || ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_UNKNOWN ) )
+ nInstallMode |= CALC_COMPONENT;
+ else if ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_ABSENT )
+ nDeinstallMode |= CALC_COMPONENT;
+ }
+ else
+ {
+ // assert( FALSE );
+ }
+
+ if ( ERROR_SUCCESS == MsiGetFeatureStateW( hMSI, L"gm_p_Draw_Bin", &current_state, &future_state ) )
+ {
+ // analyze draw installation mode
+ if ( current_state == INSTALLSTATE_LOCAL )
+ nOldInstallMode |= DRAW_COMPONENT;
+
+ if ( future_state == INSTALLSTATE_LOCAL
+ || ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_UNKNOWN ) )
+ nInstallMode |= DRAW_COMPONENT;
+ else if ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_ABSENT )
+ nDeinstallMode |= DRAW_COMPONENT;
+ }
+ else
+ {
+ // assert( FALSE );
+ }
+
+ if ( ERROR_SUCCESS == MsiGetFeatureStateW( hMSI, L"gm_p_Impress_Bin", &current_state, &future_state ) )
+ {
+ // analyze impress installation mode
+ if ( current_state == INSTALLSTATE_LOCAL )
+ nOldInstallMode |= IMPRESS_COMPONENT;
+
+ if ( future_state == INSTALLSTATE_LOCAL
+ || ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_UNKNOWN ) )
+ nInstallMode |= IMPRESS_COMPONENT;
+ else if ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_ABSENT )
+ nDeinstallMode |= IMPRESS_COMPONENT;
+ }
+ else
+ {
+ // assert( FALSE );
+ }
+
+ if ( ERROR_SUCCESS == MsiGetFeatureStateW( hMSI, L"gm_p_Math_Bin", &current_state, &future_state ) )
+ {
+ // analyze math installation mode
+ if ( current_state == INSTALLSTATE_LOCAL )
+ nOldInstallMode |= MATH_COMPONENT;
+
+ if ( future_state == INSTALLSTATE_LOCAL
+ || ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_UNKNOWN ) )
+ nInstallMode |= MATH_COMPONENT;
+ else if ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_ABSENT )
+ nDeinstallMode |= MATH_COMPONENT;
+ }
+ else
+ {
+ // assert( FALSE );
+ }
+
+ return true;
+}
+
+
+static bool MakeInstallForAllUsers( MSIHANDLE hMSI )
+{
+ bool bResult = false;
+ wchar_t* pVal = nullptr;
+ if ( GetMsiPropW( hMSI, L"ALLUSERS", &pVal ) && pVal )
+ {
+ bResult = UnicodeEquals( pVal , L"1" );
+ free( pVal );
+ }
+
+ return bResult;
+}
+
+
+static bool MakeInstallFor64Bit( MSIHANDLE hMSI )
+{
+ bool bResult = false;
+ wchar_t* pVal = nullptr;
+ if ( GetMsiPropW( hMSI, L"VersionNT64", &pVal ) && pVal )
+ {
+ bResult = true;
+ free( pVal );
+ }
+
+ return bResult;
+}
+
+extern "C" __declspec(dllexport) UINT __stdcall InstallActiveXControl( MSIHANDLE hMSI )
+{
+ INSTALLSTATE current_state;
+ INSTALLSTATE future_state;
+
+ if ( ERROR_SUCCESS == MsiGetFeatureStateW( hMSI, L"gm_o_Activexcontrol", &current_state, &future_state ) )
+ {
+ int nOldInstallMode = 0;
+ int nInstallMode = 0;
+ int nDeinstallMode = 0;
+ bool bInstallForAllUser = MakeInstallForAllUsers( hMSI );
+ bool bInstallFor64Bit = MakeInstallFor64Bit( hMSI );
+
+ wchar_t* pActiveXPath = nullptr;
+ if ( GetActiveXControlPath( hMSI, &pActiveXPath ) && pActiveXPath
+ && GetDelta( hMSI, nOldInstallMode, nInstallMode, nDeinstallMode ) )
+ {
+ if ( future_state == INSTALLSTATE_LOCAL
+ || ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_UNKNOWN ) )
+ {
+ // the control is installed in the new selected configuration
+
+ if ( current_state == INSTALLSTATE_LOCAL && nDeinstallMode )
+ UnregisterActiveXNative( pActiveXPath, nDeinstallMode, bInstallForAllUser, bInstallFor64Bit );
+
+ if ( nInstallMode )
+ RegisterActiveXNative( pActiveXPath, nInstallMode, bInstallForAllUser, bInstallFor64Bit );
+ }
+ else if ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_ABSENT )
+ {
+ if ( nOldInstallMode )
+ UnregisterActiveXNative( pActiveXPath, nOldInstallMode, bInstallForAllUser, bInstallFor64Bit );
+ }
+ }
+
+ if ( pActiveXPath )
+ free( pActiveXPath );
+ }
+ else
+ {
+ // assert( FALSE );
+ }
+
+ return ERROR_SUCCESS;
+}
+
+
+extern "C" __declspec(dllexport) UINT __stdcall DeinstallActiveXControl( MSIHANDLE hMSI )
+{
+ INSTALLSTATE current_state;
+ INSTALLSTATE future_state;
+
+ if ( ERROR_SUCCESS == MsiGetFeatureStateW( hMSI, L"gm_o_Activexcontrol", &current_state, &future_state ) )
+ {
+ wchar_t* pActiveXPath = nullptr;
+ if ( current_state == INSTALLSTATE_LOCAL && GetActiveXControlPath( hMSI, &pActiveXPath ) && pActiveXPath )
+ {
+ bool bInstallForAllUser = MakeInstallForAllUsers( hMSI );
+ bool bInstallFor64Bit = MakeInstallFor64Bit( hMSI );
+
+ {
+ UnregisterActiveXNative( pActiveXPath,
+ CHART_COMPONENT
+ | DRAW_COMPONENT
+ | IMPRESS_COMPONENT
+ | CALC_COMPONENT
+ | WRITER_COMPONENT
+ | MATH_COMPONENT,
+ bInstallForAllUser,
+ bInstallFor64Bit );
+ }
+
+ free( pActiveXPath );
+ }
+ }
+
+ return ERROR_SUCCESS;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/setup_native/source/win32/customactions/regactivex/regactivex.def b/setup_native/source/win32/customactions/regactivex/regactivex.def
new file mode 100644
index 000000000..8a4c744d7
--- /dev/null
+++ b/setup_native/source/win32/customactions/regactivex/regactivex.def
@@ -0,0 +1,4 @@
+LIBRARY "regactivex.dll"
+EXPORTS
+ InstallActiveXControl
+ DeinstallActiveXControl \ No newline at end of file
diff --git a/setup_native/source/win32/customactions/sellang/sellang.cxx b/setup_native/source/win32/customactions/sellang/sellang.cxx
new file mode 100644
index 000000000..3154e1190
--- /dev/null
+++ b/setup_native/source/win32/customactions/sellang/sellang.cxx
@@ -0,0 +1,387 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+/* Currently the "all" installer has a bit over 100 UI languages, and
+ * I doubt it will grow a lot over that.
+ */
+#define MAX_LANGUAGES 200
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <msiquery.h>
+#include <malloc.h>
+
+#include <cassert>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sal/macros.h>
+#include <systools/win32/uwinapi.h>
+#include <algorithm>
+
+#include <spellchecker_selection.hxx>
+
+static bool GetMsiPropA( MSIHANDLE hMSI, const char* pPropName, char** ppValue )
+{
+ DWORD sz = 0;
+ if ( MsiGetPropertyA( hMSI, pPropName, const_cast<char *>(""), &sz ) == ERROR_MORE_DATA ) {
+ sz++;
+ DWORD nbytes = sz * sizeof( char );
+ char* buff = static_cast<char*>( malloc( nbytes ) );
+ assert(buff); // Don't handle OOM conditions
+ ZeroMemory( buff, nbytes );
+ MsiGetPropertyA( hMSI, pPropName, buff, &sz );
+ *ppValue = buff;
+ return ( strlen(buff) > 0 );
+ }
+ return false;
+}
+
+static const char *
+langid_to_string( LANGID langid )
+{
+ /* Map from LANGID to string. The languages below are now in
+ * alphabetical order of codes as in
+ * l10ntools/source/ulfconv/msi-encodinglist.txt. Only the
+ * language part is returned in the string.
+ */
+ switch (PRIMARYLANGID (langid)) {
+ case LANG_AFRIKAANS: return "af";
+ case LANG_AMHARIC: return "am";
+ case LANG_ARABIC: return "ar";
+ case LANG_ASSAMESE: return "as";
+ case LANG_BELARUSIAN: return "be";
+ case LANG_BULGARIAN: return "bg";
+ case LANG_BENGALI: return "bn";
+ case LANG_BRETON: return "br";
+ case LANG_CATALAN: return "ca";
+ case LANG_CZECH: return "cs";
+ case LANG_WELSH: return "cy";
+ case LANG_DANISH: return "da";
+ case LANG_GERMAN: return "de";
+ case LANG_GREEK: return "el";
+ case LANG_ENGLISH: return "en";
+ case LANG_SPANISH: return "es";
+ case LANG_ESTONIAN: return "et";
+ case LANG_BASQUE: return "eu";
+ case LANG_FARSI: return "fa";
+ case LANG_FINNISH: return "fi";
+ case LANG_FAEROESE: return "fo";
+ case LANG_FRENCH: return "fr";
+ case LANG_IRISH: return "ga";
+ case LANG_GALICIAN: return "gl";
+ case LANG_GUJARATI: return "gu";
+ case LANG_HEBREW: return "he";
+ case LANG_HINDI: return "hi";
+ case LANG_HUNGARIAN: return "hu";
+ case LANG_ARMENIAN: return "hy";
+ case LANG_INDONESIAN: return "id";
+ case LANG_ICELANDIC: return "is";
+ case LANG_ITALIAN: return "it";
+ case LANG_JAPANESE: return "ja";
+ case LANG_GEORGIAN: return "ka";
+ case LANG_KAZAK: return "kk";
+ case LANG_KHMER: return "km";
+ case LANG_KANNADA: return "kn";
+ case LANG_KOREAN: return "ko";
+ case LANG_KASHMIRI: return "ks";
+ case LANG_LAO: return "lo";
+ case LANG_LITHUANIAN: return "lt";
+ case LANG_LATVIAN: return "lv";
+ case LANG_MACEDONIAN: return "mk";
+ case LANG_MALAYALAM: return "ml";
+ case LANG_MONGOLIAN: return "mn";
+ case LANG_MARATHI: return "mr";
+ case LANG_MALAY: return "ms";
+ case LANG_MALTESE: return "mt";
+ case LANG_NEPALI: return "ne";
+ case LANG_DUTCH: return "nl";
+ case LANG_SOTHO: return "ns";
+ case LANG_ORIYA: return "or";
+ case LANG_PUNJABI: return "pa";
+ case LANG_POLISH: return "pl";
+ case LANG_PORTUGUESE: return "pt";
+ case LANG_ROMANSH: return "rm";
+ case LANG_ROMANIAN: return "ro";
+ case LANG_RUSSIAN: return "ru";
+ case LANG_KINYARWANDA: return "rw";
+ case LANG_SANSKRIT: return "sa";
+ case LANG_UPPER_SORBIAN: return "sb";
+ case LANG_SINDHI: return "sd";
+ case LANG_SLOVAK: return "sk";
+ case LANG_SLOVENIAN: return "sl";
+ case LANG_ALBANIAN: return "sq";
+ case LANG_SWEDISH: return "sv";
+ case LANG_SWAHILI: return "sw";
+ case LANG_TAMIL: return "ta";
+ case LANG_TELUGU: return "te";
+ case LANG_TAJIK: return "tg";
+ case LANG_THAI: return "th";
+ case LANG_TIGRIGNA: return "ti";
+ case LANG_TSWANA: return "tn";
+ case LANG_TURKISH: return "tr";
+ case LANG_TATAR: return "tt";
+ case LANG_UKRAINIAN: return "uk";
+ case LANG_URDU: return "ur";
+ case LANG_UZBEK: return "uz";
+ case LANG_VIETNAMESE: return "vi";
+ case LANG_XHOSA: return "xh";
+ case LANG_CHINESE: return "zh";
+ case LANG_ZULU: return "zu";
+ /* Special cases */
+ default:
+ switch (langid) {
+ case MAKELANGID(LANG_SERBIAN, 0x05): return "bs";
+ case MAKELANGID(LANG_SERBIAN, SUBLANG_DEFAULT): return "hr";
+ case MAKELANGID(LANG_NORWEGIAN, SUBLANG_NORWEGIAN_BOKMAL): return "nb";
+ case MAKELANGID(LANG_NORWEGIAN, SUBLANG_NORWEGIAN_NYNORSK): return "nn";
+ case MAKELANGID(LANG_SERBIAN, SUBLANG_SERBIAN_LATIN): return "sh";
+ case MAKELANGID(LANG_SERBIAN, SUBLANG_SERBIAN_CYRILLIC): return "sr";
+ default: return nullptr;
+ }
+ }
+}
+
+/* Here we collect the UI languages present on the system;
+ * MAX_LANGUAGES is certainly enough for that
+ */
+static const char *ui_langs[MAX_LANGUAGES];
+static int num_ui_langs = 0;
+
+static void add_ui_lang(char const * lang)
+{
+ if (lang != nullptr && num_ui_langs != SAL_N_ELEMENTS(ui_langs)) {
+ ui_langs[num_ui_langs++] = lang;
+ }
+}
+
+static BOOL CALLBACK
+enum_ui_lang_proc (LPTSTR language, LONG_PTR /* unused_lParam */)
+{
+ long langid = strtol(language, nullptr, 16);
+ if (langid > 0xFFFF)
+ return TRUE;
+ add_ui_lang(langid_to_string(static_cast<LANGID>(langid)));
+ if (num_ui_langs == SAL_N_ELEMENTS(ui_langs) )
+ return FALSE;
+ return TRUE;
+}
+
+static bool
+present_in_ui_langs(const char *lang)
+{
+ for (int i = 0; i < num_ui_langs; i++)
+ {
+ if (strchr (lang, '_') != nullptr)
+ if (memcmp (ui_langs[i], lang, std::min(strlen(ui_langs[i]), strlen(lang))) == 0)
+ return true;
+ if (strcmp (ui_langs[i], lang) == 0)
+ return true;
+ }
+ return false;
+}
+
+namespace {
+
+/* TODO-BCP47: unlimit this, and if possible change from '_' to '-' separator
+ * to ease things. */
+// XXX NOTE: the sizeof needs to follow what is accepted in
+// setup_native/source/packinfo/spellchecker_selection.pl
+struct InstallLocalized {
+ char lang[sizeof("lll_Ssss_CC_vvvvvvvv")];
+ bool install;
+};
+
+void addMatchingDictionaries(
+ char const * lang, InstallLocalized * dicts, int ndicts)
+{
+ for (int i = 0; i != SAL_N_ELEMENTS(setup_native::languageDictionaries);
+ ++i)
+ {
+ if (strcmp(lang, setup_native::languageDictionaries[i].language) == 0) {
+ for (char const * const * p = setup_native::languageDictionaries[i].
+ dictionaries;
+ *p != nullptr; ++p)
+ {
+ for (int j = 0; j != ndicts; ++j) {
+ if (_stricmp(*p, dicts[j].lang) == 0) {
+ dicts[j].install = true;
+ break;
+ }
+ }
+ }
+ break;
+ }
+ }
+}
+
+}
+
+extern "C" __declspec(dllexport) UINT __stdcall SelectLanguage( MSIHANDLE handle )
+{
+ char feature[100];
+ MSIHANDLE database, view, record;
+ DWORD length;
+ int nlangs = 0;
+ InstallLocalized langs[MAX_LANGUAGES];
+ int ndicts = 0;
+ InstallLocalized dicts[MAX_LANGUAGES];
+
+ database = MsiGetActiveDatabase(handle);
+
+ if (MsiDatabaseOpenViewA(database, "SELECT Feature from Feature WHERE Feature_Parent = 'gm_Langpack_Languageroot'", &view) != ERROR_SUCCESS) {
+ MsiCloseHandle(database);
+ return ERROR_SUCCESS;
+ }
+
+ if (MsiViewExecute(view, NULL) != ERROR_SUCCESS) {
+ MsiCloseHandle(view);
+ MsiCloseHandle(database);
+ return ERROR_SUCCESS;
+ }
+
+ while (nlangs < MAX_LANGUAGES &&
+ MsiViewFetch(view, &record) == ERROR_SUCCESS) {
+ length = sizeof(feature);
+ if (MsiRecordGetStringA(record, 1, feature, &length) != ERROR_SUCCESS) {
+ MsiCloseHandle(record);
+ MsiCloseHandle(view);
+ MsiCloseHandle(database);
+ return ERROR_SUCCESS;
+ }
+
+ /* Keep track of what langpacks are included in this installer.
+ */
+ strcpy(langs[nlangs].lang, feature + strlen("gm_Langpack_r_"));
+ langs[nlangs].install = false;
+ ++nlangs;
+
+ MsiCloseHandle(record);
+ }
+
+ MsiCloseHandle(view);
+
+ /* Keep track of what dictionaries are included in this installer:
+ */
+ if (MsiDatabaseOpenViewA(
+ database,
+ ("SELECT Feature from Feature WHERE"
+ " Feature_Parent = 'gm_Dictionaries'"),
+ &view)
+ == ERROR_SUCCESS)
+ {
+ if (MsiViewExecute(view, NULL) == ERROR_SUCCESS) {
+ while (ndicts < MAX_LANGUAGES &&
+ MsiViewFetch(view, &record) == ERROR_SUCCESS)
+ {
+ length = sizeof(feature);
+ if (MsiRecordGetStringA(record, 1, feature, &length)
+ == ERROR_SUCCESS)
+ {
+ if (strncmp(
+ feature, "gm_r_ex_Dictionary_",
+ strlen("gm_r_ex_Dictionary_"))
+ == 0)
+ {
+ strcpy(
+ dicts[ndicts].lang,
+ feature + strlen("gm_r_ex_Dictionary_"));
+ dicts[ndicts].install = false;
+ ++ndicts;
+ }
+ }
+ MsiCloseHandle(record);
+ }
+ }
+ MsiCloseHandle(view);
+ }
+
+ /* Keep track of what UI languages are relevant, either the ones explicitly
+ * requested with the UI_LANGS property, or all available on the system:
+ */
+ char* pVal = nullptr;
+ if ( (GetMsiPropA( handle, "UI_LANGS", &pVal )) && pVal ) {
+ char *str_ptr;
+ str_ptr = strtok(pVal, ",");
+ for(; str_ptr != nullptr ;) {
+ add_ui_lang(str_ptr);
+ str_ptr = strtok(nullptr, ",");
+ }
+ } else {
+ add_ui_lang(langid_to_string(GetSystemDefaultUILanguage()));
+ add_ui_lang(langid_to_string(LANGIDFROMLCID(GetThreadLocale())));
+ //TODO: are the above two explicit additions necessary, or will
+ // those values always be included in the below EnumUILanguages
+ // anyway?
+ EnumUILanguagesA(enum_ui_lang_proc, 0, 0);
+ }
+
+ // If the set of langpacks that match any of the relevant UI languages is
+ // non-empty, select just those matching langpacks; otherwise, if an en_US
+ // langpack is included, select just that langpack (this happens if, e.g.,
+ // a multi-language en-US,de,es,fr,it,pt-BR installation set is installed on
+ // a Finnish Windows); otherwise, select all langpacks (this happens if,
+ // e.g., a single-language de installation set is installed on a Finnish
+ // Windows):
+ bool matches = false;
+ for (int i = 0; i < nlangs; i++) {
+ if (present_in_ui_langs(langs[i].lang)) {
+ langs[i].install = true;
+ matches = true;
+ }
+ }
+ if (!matches) {
+ for (int i = 0; i < nlangs; i++) {
+ if (strcmp(langs[i].lang, "en_US") == 0) {
+ langs[i].install = true;
+ matches = true;
+ break;
+ }
+ }
+ if (!matches) {
+ for (int i = 0; i < nlangs; i++) {
+ langs[i].install = true;
+ }
+ }
+ }
+
+ for (int i = 0; i < nlangs; i++) {
+ if (langs[i].install) {
+ addMatchingDictionaries(langs[i].lang, dicts, ndicts);
+ } else {
+ sprintf(feature, "gm_Langpack_r_%s", langs[i].lang);
+ MsiSetFeatureStateA(handle, feature, INSTALLSTATE_ABSENT);
+ }
+ }
+
+ // Select just those dictionaries that match any of the selected langpacks:
+ for (int i = 0; i != ndicts; ++i) {
+ if (!dicts[i].install) {
+ sprintf(feature, "gm_r_ex_Dictionary_%s", dicts[i].lang);
+ MsiSetFeatureStateA(handle, feature, INSTALLSTATE_ABSENT);
+ }
+ }
+
+ MsiCloseHandle(database);
+
+ return ERROR_SUCCESS;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/setup_native/source/win32/customactions/sellang/sellang.def b/setup_native/source/win32/customactions/sellang/sellang.def
new file mode 100644
index 000000000..8d3c4d44c
--- /dev/null
+++ b/setup_native/source/win32/customactions/sellang/sellang.def
@@ -0,0 +1,4 @@
+LIBRARY "sellangmsi.dll"
+EXPORTS
+ SelectLanguage
+ SortTree \ No newline at end of file
diff --git a/setup_native/source/win32/customactions/sellang/sorttree.cxx b/setup_native/source/win32/customactions/sellang/sorttree.cxx
new file mode 100644
index 000000000..cb44675b9
--- /dev/null
+++ b/setup_native/source/win32/customactions/sellang/sorttree.cxx
@@ -0,0 +1,55 @@
+/* -*- 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/.
+ */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <msi.h>
+#include <commctrl.h>
+
+extern "C" __declspec(dllexport) UINT __stdcall SortTree(MSIHANDLE)
+{
+ // Sort items (languages) in SelectionTree control, fdo#46355
+
+ HWND hwndMSI = FindWindowW(L"MsiDialogCloseClass", nullptr);
+ if (hwndMSI == nullptr)
+ {
+ OutputDebugStringW(L"SortTree: MsiDialogCloseClass not found\n");
+ return ERROR_SUCCESS;
+ }
+ HWND hwndTV = FindWindowExW(hwndMSI, nullptr, L"SysTreeView32", nullptr);
+ if (hwndTV == nullptr)
+ {
+ OutputDebugStringW(L"SortTree: SysTreeView32 not found\n");
+ return ERROR_SUCCESS;
+ }
+ HTREEITEM optional = TreeView_GetRoot(hwndTV);
+ if (optional == nullptr)
+ {
+ OutputDebugStringW(L"SortTree: Optional Components branch not found\n");
+ return ERROR_SUCCESS;
+ }
+ HTREEITEM dicts = TreeView_GetChild(hwndTV, optional);
+ if (dicts == nullptr)
+ {
+ OutputDebugStringW(L"SortTree: Dictionaries branch not found\n");
+ return ERROR_SUCCESS;
+ }
+ TreeView_SortChildren(hwndTV, dicts, TRUE);
+ HTREEITEM langs = TreeView_GetNextSibling(hwndTV, optional);
+ if (langs == nullptr)
+ {
+ OutputDebugStringW(L"SortTree: Additional UI Languages branch not found\n");
+ return ERROR_SUCCESS;
+ }
+ TreeView_SortChildren(hwndTV, langs, TRUE);
+
+ return ERROR_SUCCESS;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/setup_native/source/win32/customactions/shellextensions/checkdirectory.cxx b/setup_native/source/win32/customactions/shellextensions/checkdirectory.cxx
new file mode 100644
index 000000000..5c6865995
--- /dev/null
+++ b/setup_native/source/win32/customactions/shellextensions/checkdirectory.cxx
@@ -0,0 +1,66 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "shlxtmsi.hxx"
+
+#include <malloc.h>
+#include <assert.h>
+
+#include <queue>
+#include <stdio.h>
+
+#include <systools/win32/uwinapi.h>
+#include "../tools/seterror.hxx"
+
+extern "C" __declspec(dllexport) UINT __stdcall CheckInstallDirectory(MSIHANDLE handle)
+{
+ std::wstring sInstallPath = GetMsiPropertyW(handle, L"INSTALLLOCATION");
+ std::wstring sOfficeHostnamePath = GetMsiPropertyW(handle, L"OFFICEDIRHOSTNAME");
+
+ // MessageBoxW(NULL, sInstallPath.c_str(), L"DEBUG", MB_OK);
+
+ // unsetting all properties
+
+ UnsetMsiPropertyW( handle, L"DIRECTORY_NOT_EMPTY" );
+
+ // 1. Searching for file setup.ini
+
+ std::wstring sSetupIniPath = sInstallPath + sOfficeHostnamePath + L"\\program\\setup.ini";
+
+ WIN32_FIND_DATAW data;
+ HANDLE hdl = FindFirstFileW(sSetupIniPath.c_str(), &data);
+
+ // std::wstring mystr = L"Searching for " + sSetupIniPath;
+ // MessageBoxW(NULL, mystr.c_str(), L"DEBUG", MB_OK);
+
+ if ( IsValidHandle(hdl) )
+ {
+ // setup.ini found -> directory cannot be used for installation.
+ SetMsiPropertyW( handle, L"DIRECTORY_NOT_EMPTY", L"1" );
+ SetMsiErrorCode( MSI_ERROR_DIRECTORY_NOT_EMPTY );
+ // std::wstring notEmptyStr = L"Directory is not empty. Please choose another installation directory.";
+ // std::wstring notEmptyTitle = L"Directory not empty";
+ // MessageBoxW(NULL, notEmptyStr.c_str(), notEmptyTitle.c_str(), MB_OK);
+ FindClose(hdl);
+ }
+
+ return ERROR_SUCCESS;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/setup_native/source/win32/customactions/shellextensions/checkpatches.cxx b/setup_native/source/win32/customactions/shellextensions/checkpatches.cxx
new file mode 100644
index 000000000..0a879dc1b
--- /dev/null
+++ b/setup_native/source/win32/customactions/shellextensions/checkpatches.cxx
@@ -0,0 +1,59 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "shlxtmsi.hxx"
+#include <strsafe.h>
+#include <systools/win32/uwinapi.h>
+
+#ifdef DEBUG
+inline void OutputDebugStringFormatW( PCWSTR pFormat, ... )
+{
+ WCHAR buffer[1024];
+ va_list args;
+
+ va_start( args, pFormat );
+ StringCchVPrintfW( buffer, SAL_N_ELEMENTS(buffer), pFormat, args );
+ OutputDebugStringW( buffer );
+ va_end(args);
+}
+#else
+static void OutputDebugStringFormatW( PCWSTR, ... )
+{
+}
+#endif
+
+extern "C" __declspec(dllexport) UINT __stdcall CheckPatchList( MSIHANDLE handle )
+{
+ std::wstring sPatchList = GetMsiPropertyW( handle, L"PATCH" );
+ std::wstring sRequiredPatch = GetMsiPropertyW( handle, L"PREREQUIREDPATCH" );
+
+ OutputDebugStringFormatW( L"CheckPatchList called with PATCH=%s and PRQ=%s\n", sPatchList.c_str(), sRequiredPatch.c_str() );
+
+ if ( ( sPatchList.length() != 0 ) && ( sRequiredPatch.length() != 0 ) )
+ {
+ if ( wcsstr( sPatchList.c_str(), sRequiredPatch.c_str() ) )
+ {
+ SetMsiPropertyW( handle, L"IGNOREPREREQUIREDPATCH", L"1" );
+ OutputDebugStringFormatW( L"Set Property IgnorePrerequiredPatch!\n" );
+ }
+ }
+ return ERROR_SUCCESS;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/setup_native/source/win32/customactions/shellextensions/completeinstallpath.cxx b/setup_native/source/win32/customactions/shellextensions/completeinstallpath.cxx
new file mode 100644
index 000000000..787041ed8
--- /dev/null
+++ b/setup_native/source/win32/customactions/shellextensions/completeinstallpath.cxx
@@ -0,0 +1,116 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "shlxtmsi.hxx"
+
+#include <malloc.h>
+
+extern "C" __declspec(dllexport) UINT __stdcall CompleteInstallPath( MSIHANDLE handle )
+{
+ // This CustomAction is necessary for updates from OOo 3.0, OOo 3.1 and OOo 3.2 to versions
+ // OOo 3.3 or later. This is caused by a change of INSTALLLOCATION, that starting with OOo 3.3
+ // contains the name of the product again (instead of only "c:\program files"). Unfortunately
+ // this causes in an update installation, that INSTALLLOCATION is set to "c:\program files",
+ // so that in an OOo 3.3 or later, the directory "program" or "share" are directly created
+ // below "c:\program files".
+
+ HKEY hKey;
+
+ // Reading property OFFICEDIRHOSTNAME_, that contains the part of the path behind
+ // the program files folder.
+
+ std::wstring sInstallLocation = GetMsiPropertyW( handle, L"INSTALLLOCATION" );
+ std::wstring sOfficeDirHostname = GetMsiPropertyW( handle, L"OFFICEDIRHOSTNAME_" );
+
+ // If sInstallLocation ends with (contains) the string sOfficeDirHostname,
+ // INSTALLLOCATION is good and nothing has to be done here.
+
+ bool pathCompletionRequired = true;
+
+ if ( wcsstr( sInstallLocation.c_str(), sOfficeDirHostname.c_str() ) )
+ {
+ pathCompletionRequired = false; // nothing to do
+ }
+
+ // If the path INSTALLLOCATION does not end with this string, INSTALLLOCATION is maybe
+ // transferred from an OOo 3.0, OOo 3.1 and OOo 3.2 and need to be changed therefore.
+
+ if ( pathCompletionRequired )
+ {
+ std::wstring sManufacturer = GetMsiPropertyW( handle, L"Manufacturer" );
+ std::wstring sDefinedName = GetMsiPropertyW( handle, L"DEFINEDPRODUCT" );
+ std::wstring sUpgradeCode = GetMsiPropertyW( handle, L"UpgradeCode" );
+
+ // sUpdateVersion can be "3.0", "3.1" or "3.2"
+
+ std::wstring sProductKey30 = L"Software\\" + sManufacturer + L"\\" + sDefinedName +
+ L"\\" L"3.0" L"\\" + sUpgradeCode;
+
+ std::wstring sProductKey31 = L"Software\\" + sManufacturer + L"\\" + sDefinedName +
+ L"\\" L"3.1" L"\\" + sUpgradeCode;
+
+ std::wstring sProductKey32 = L"Software\\" + sManufacturer + L"\\" + sDefinedName +
+ L"\\" L"3.2" L"\\" + sUpgradeCode;
+
+ bool oldVersionExists = false;
+
+ if ( ERROR_SUCCESS == RegOpenKeyW( HKEY_CURRENT_USER, sProductKey30.c_str(), &hKey ) )
+ {
+ oldVersionExists = true;
+ RegCloseKey( hKey );
+ }
+ else if ( ERROR_SUCCESS == RegOpenKeyW( HKEY_CURRENT_USER, sProductKey31.c_str(), &hKey ) )
+ {
+ oldVersionExists = true;
+ RegCloseKey( hKey );
+ }
+ else if ( ERROR_SUCCESS == RegOpenKeyW( HKEY_CURRENT_USER, sProductKey32.c_str(), &hKey ) )
+ {
+ oldVersionExists = true;
+ RegCloseKey( hKey );
+ }
+ else if ( ERROR_SUCCESS == RegOpenKeyW( HKEY_LOCAL_MACHINE, sProductKey30.c_str(), &hKey ) )
+ {
+ oldVersionExists = true;
+ RegCloseKey( hKey );
+ }
+ else if ( ERROR_SUCCESS == RegOpenKeyW( HKEY_LOCAL_MACHINE, sProductKey31.c_str(), &hKey ) )
+ {
+ oldVersionExists = true;
+ RegCloseKey( hKey );
+ }
+ else if ( ERROR_SUCCESS == RegOpenKeyW( HKEY_LOCAL_MACHINE, sProductKey32.c_str(), &hKey ) )
+ {
+ oldVersionExists = true;
+ RegCloseKey( hKey );
+ }
+
+ if ( oldVersionExists )
+ {
+ // Adding the new path content sOfficeDirHostname
+ sInstallLocation += sOfficeDirHostname;
+ // Setting the new property value
+ MsiSetPropertyW(handle, L"INSTALLLOCATION", sInstallLocation.c_str());
+ }
+ }
+
+ return ERROR_SUCCESS;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/setup_native/source/win32/customactions/shellextensions/migrateinstallpath.cxx b/setup_native/source/win32/customactions/shellextensions/migrateinstallpath.cxx
new file mode 100644
index 000000000..10c817169
--- /dev/null
+++ b/setup_native/source/win32/customactions/shellextensions/migrateinstallpath.cxx
@@ -0,0 +1,96 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "shlxtmsi.hxx"
+#include <algorithm>
+#include <sstream>
+#include <systools/win32/uwinapi.h>
+
+extern "C" __declspec(dllexport) UINT __stdcall MigrateInstallPath(MSIHANDLE handle)
+{
+ std::wstring sInstDir = GetMsiPropertyW(handle, L"INSTALLLOCATION");
+ if (!sInstDir.empty())
+ return ERROR_SUCCESS; // Don't overwrite explicitly set value
+
+ auto RegValue = [](HKEY hRoot, const WCHAR* sKey, const WCHAR* sVal) {
+ std::wstring sResult;
+ WCHAR buf[32767]; // max longpath
+ DWORD bufsize = sizeof(buf); // yes, it is the number of bytes
+ if (RegGetValueW(hRoot, sKey, sVal, RRF_RT_REG_SZ, nullptr, buf, &bufsize) == ERROR_SUCCESS)
+ sResult = buf; // RegGetValueW null-terminates strings
+
+ return sResult;
+ };
+
+ const std::wstring sManufacturer = GetMsiPropertyW( handle, L"Manufacturer" );
+ const std::wstring sDefinedName = GetMsiPropertyW( handle, L"DEFINEDPRODUCT" );
+ const std::wstring sUpdateVersion = GetMsiPropertyW( handle, L"DEFINEDVERSION" );
+ const std::wstring sUpgradeCode = GetMsiPropertyW( handle, L"UpgradeCode" );
+ const std::wstring sBrandPackageVersion = GetMsiPropertyW(handle, L"BRANDPACKAGEVERSION");
+
+ std::wstring sKey = L"Software\\" + sManufacturer + L"\\" + sDefinedName +
+ L"\\" + sUpdateVersion + L"\\" + sUpgradeCode;
+
+ sInstDir = RegValue(HKEY_CURRENT_USER, sKey.c_str(), L"INSTALLLOCATION");
+ if (sInstDir.empty())
+ sInstDir = RegValue(HKEY_LOCAL_MACHINE, sKey.c_str(), L"INSTALLLOCATION");
+ // See #i93032# for layers description
+ if (sInstDir.empty())
+ {
+ sKey = L"Software\\LibreOffice\\Layers\\" + sDefinedName + L"\\" + sBrandPackageVersion;
+ sInstDir = RegValue(HKEY_CURRENT_USER, sKey.c_str(), L"INSTALLLOCATION");
+ }
+ if (sInstDir.empty())
+ {
+ sKey = L"Software\\LibreOffice\\Layers_\\" + sDefinedName + L"\\" + sBrandPackageVersion;
+ sInstDir = RegValue(HKEY_CURRENT_USER, sKey.c_str(), L"INSTALLLOCATION");
+ }
+ if (sInstDir.empty())
+ {
+ sKey = L"Software\\LibreOffice\\Layers\\" + sDefinedName + L"\\" + sBrandPackageVersion;
+ sInstDir = RegValue(HKEY_LOCAL_MACHINE, sKey.c_str(), L"INSTALLLOCATION");
+ }
+ if (sInstDir.empty())
+ {
+ sKey = L"Software\\LibreOffice\\Layers_\\" + sDefinedName + L"\\" + sBrandPackageVersion;
+ sInstDir = RegValue(HKEY_LOCAL_MACHINE, sKey.c_str(), L"INSTALLLOCATION");
+ }
+ if (sInstDir.empty())
+ {
+ std::wistringstream sOlds{ GetMsiPropertyW(handle, L"OLDPRODUCTS") };
+ std::wstring sOld;
+ while (std::getline(sOlds, sOld, L';'))
+ {
+ if (sOld.empty())
+ continue;
+ sKey = L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\" + sOld;
+ sInstDir = RegValue(HKEY_LOCAL_MACHINE, sKey.c_str(), L"InstallLocation");
+ if (!sInstDir.empty())
+ break;
+ }
+ }
+
+ if (!sInstDir.empty())
+ MsiSetPropertyW(handle, L"INSTALLLOCATION", sInstDir.c_str());
+
+ return ERROR_SUCCESS;
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/setup_native/source/win32/customactions/shellextensions/shlxtmsi.def b/setup_native/source/win32/customactions/shellextensions/shlxtmsi.def
new file mode 100644
index 000000000..e0e667953
--- /dev/null
+++ b/setup_native/source/win32/customactions/shellextensions/shlxtmsi.def
@@ -0,0 +1,11 @@
+LIBRARY "shlxtmsi.dll"
+EXPORTS
+ CheckInstallDirectory
+ CheckPatchList
+ CompleteInstallPath
+ MigrateInstallPath
+ InstallStartmenuFolderIcon
+ DeinstallStartmenuFolderIcon
+ SetProductInstallMode
+ RenamePrgFolder
+ RemovePrgFolder
diff --git a/setup_native/source/win32/customactions/shellextensions/shlxtmsi.hxx b/setup_native/source/win32/customactions/shellextensions/shlxtmsi.hxx
new file mode 100644
index 000000000..3a08060be
--- /dev/null
+++ b/setup_native/source/win32/customactions/shellextensions/shlxtmsi.hxx
@@ -0,0 +1,59 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SETUP_NATIVE_SOURCE_WIN32_CUSTOMACTIONS_SHELLEXTENSIONS_SHLXTMSI_HXX
+#define INCLUDED_SETUP_NATIVE_SOURCE_WIN32_CUSTOMACTIONS_SHELLEXTENSIONS_SHLXTMSI_HXX
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <msiquery.h>
+
+#include <string>
+
+static inline std::wstring GetMsiPropertyW( MSIHANDLE handle, const std::wstring& sProperty )
+{
+ std::wstring result;
+ WCHAR szDummy[1] = L"";
+ DWORD nChars = 0;
+
+ if ( MsiGetPropertyW( handle, sProperty.c_str(), szDummy, &nChars ) == ERROR_MORE_DATA )
+ {
+ DWORD nBytes = ++nChars * sizeof(WCHAR);
+ PWSTR buffer = static_cast<PWSTR>(_alloca(nBytes));
+ ZeroMemory( buffer, nBytes );
+ MsiGetPropertyW( handle, sProperty.c_str(), buffer, &nChars );
+ result = buffer;
+ }
+
+ return result;
+}
+
+static inline void SetMsiPropertyW( MSIHANDLE handle, const std::wstring& sProperty, const std::wstring& sValue )
+{
+ MsiSetPropertyW( handle, sProperty.c_str(), sValue.c_str() );
+}
+
+static inline void UnsetMsiPropertyW( MSIHANDLE handle, const std::wstring& sProperty )
+{
+ MsiSetPropertyW( handle, sProperty.c_str(), nullptr );
+}
+
+#endif // INCLUDED_SETUP_NATIVE_SOURCE_WIN32_CUSTOMACTIONS_SHELLEXTENSIONS_SHLXTMSI_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/setup_native/source/win32/customactions/shellextensions/startmenuicon.cxx b/setup_native/source/win32/customactions/shellextensions/startmenuicon.cxx
new file mode 100644
index 000000000..6e95c6e1e
--- /dev/null
+++ b/setup_native/source/win32/customactions/shellextensions/startmenuicon.cxx
@@ -0,0 +1,63 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "shlxtmsi.hxx"
+
+#include <malloc.h>
+
+/*
+ Called during installation to customize the start menu folder icon.
+ See: http://msdn.microsoft.com/library/en-us/shellcc/platform/shell/programmersguide/shell_basics/shell_basics_extending/custom.asp
+*/
+extern "C" __declspec(dllexport) UINT __stdcall InstallStartmenuFolderIcon( MSIHANDLE handle )
+{
+ std::wstring sOfficeMenuFolder = GetMsiPropertyW( handle, L"OfficeMenuFolder" );
+ std::wstring sDesktopFile = sOfficeMenuFolder + L"Desktop.ini";
+
+ // at the moment there exists no Vista Icon, so we use the default folder icon.
+ // add the icon into desktop/util/verinfo.rc
+
+ // The value '0' is to avoid a message like "You Are Deleting a System Folder" warning when deleting or moving the folder.
+ WritePrivateProfileStringW(
+ L".ShellClassInfo",
+ L"ConfirmFileOp",
+ L"0",
+ sDesktopFile.c_str() );
+
+ SetFileAttributesW( sDesktopFile.c_str(), FILE_ATTRIBUTE_HIDDEN );
+ SetFileAttributesW( sOfficeMenuFolder.c_str(), FILE_ATTRIBUTE_SYSTEM );
+
+
+ return ERROR_SUCCESS;
+}
+
+extern "C" __declspec(dllexport) UINT __stdcall DeinstallStartmenuFolderIcon(MSIHANDLE handle)
+{
+ std::wstring sOfficeMenuFolder = GetMsiPropertyW( handle, L"OfficeMenuFolder" );
+ std::wstring sDesktopFile = sOfficeMenuFolder + L"Desktop.ini";
+
+ SetFileAttributesW( sDesktopFile.c_str(), FILE_ATTRIBUTE_NORMAL );
+ DeleteFileW( sDesktopFile.c_str() );
+
+ SetFileAttributesW( sOfficeMenuFolder.c_str(), FILE_ATTRIBUTE_NORMAL );
+
+ return ERROR_SUCCESS;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/setup_native/source/win32/customactions/shellextensions/upgrade.cxx b/setup_native/source/win32/customactions/shellextensions/upgrade.cxx
new file mode 100644
index 000000000..494cfaeda
--- /dev/null
+++ b/setup_native/source/win32/customactions/shellextensions/upgrade.cxx
@@ -0,0 +1,158 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "shlxtmsi.hxx"
+
+#include <malloc.h>
+#include <assert.h>
+
+namespace
+{
+ // The provided GUID must be without surrounding '{}'
+ std::wstring GetGuidPart(const std::wstring& guid, int index)
+ {
+ assert((guid.length() == 36) && "No GUID or wrong format!");
+ assert(((index > -1) && (index < 5)) && "Out of range!");
+
+ if (index == 0) return std::wstring(guid.c_str(), 8);
+ if (index == 1) return std::wstring(guid.c_str() + 9, 4);
+ if (index == 2) return std::wstring(guid.c_str() + 14, 4);
+ if (index == 3) return std::wstring(guid.c_str() + 19, 4);
+ if (index == 4) return std::wstring(guid.c_str() + 24, 12);
+
+ return std::wstring();
+ }
+
+ void Swap(wchar_t* p1, wchar_t* p2)
+ {
+ wchar_t tmp = *p1;
+ *p1 = *p2;
+ *p2 = tmp;
+ }
+
+ std::wstring Invert(const std::wstring& str)
+ {
+ wchar_t* buff = static_cast<wchar_t*>(_alloca(str.length()*sizeof(wchar_t)));
+ wcsncpy(buff, str.c_str(), str.length());
+
+ wchar_t* front = buff;
+ wchar_t* back = buff + str.length() - 1;
+
+ while (front < back)
+ Swap(front++, back--);
+
+ return std::wstring(buff, str.length());
+ }
+
+ // Convert the upgrade code (which is a GUID) according
+ // to the way the windows installer does when writing it
+ // to the registry
+ // The first 8 bytes will be inverted, from the last
+ // 8 bytes always the nibbles will be inverted for further
+ // details look in the MSDN under compressed registry keys
+ std::wstring ConvertGuid(const std::wstring& guid)
+ {
+ std::wstring convertedGuid;
+
+ std::wstring part = GetGuidPart(guid, 0);
+ convertedGuid = Invert(part);
+
+ part = GetGuidPart(guid, 1);
+ convertedGuid += Invert(part);
+
+ part = GetGuidPart(guid, 2);
+ convertedGuid += Invert(part);
+
+ part = GetGuidPart(guid, 3);
+ convertedGuid += Invert(std::wstring(part.c_str(), 2));
+ convertedGuid += Invert(std::wstring(part.c_str() + 2, 2));
+
+ part = GetGuidPart(guid, 4);
+ int pos = 0;
+ for (int i = 0; i < 6; i++)
+ {
+ convertedGuid += Invert(std::wstring(part.c_str() + pos, 2));
+ pos += 2;
+ }
+ return convertedGuid;
+ }
+
+ bool IsSetMsiPropertyW(MSIHANDLE handle, const std::wstring& sProperty)
+ {
+ return (GetMsiPropertyW(handle, sProperty).length() > 0);
+ }
+
+ void SetMsiPropertyW(MSIHANDLE handle, const std::wstring& sProperty)
+ {
+ MsiSetPropertyW(handle, sProperty.c_str(), L"1");
+ }
+
+ bool RegistryKeyHasUpgradeSubKey(
+ HKEY hRootKey, const std::wstring& regKey, const std::wstring& upgradeKey)
+ {
+ HKEY hKey;
+ if (RegOpenKeyW(hRootKey, regKey.c_str(), &hKey) == ERROR_SUCCESS)
+ {
+ DWORD nSubKeys;
+ DWORD lLongestSubKey;
+
+ if (RegQueryInfoKeyW(
+ hKey, nullptr, nullptr, nullptr, &nSubKeys, &lLongestSubKey, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr) == ERROR_SUCCESS)
+ {
+ LPWSTR buffer = static_cast<LPWSTR>(_alloca((lLongestSubKey + 1)*sizeof(WCHAR)));
+
+ for (DWORD i = 0; i < nSubKeys; i++)
+ {
+ LONG ret = RegEnumKeyW(hKey, i, buffer, lLongestSubKey + 1);
+ if ((ret == ERROR_SUCCESS) && (buffer == upgradeKey))
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+} // namespace
+
+extern "C" __declspec(dllexport) UINT __stdcall SetProductInstallMode(MSIHANDLE handle)
+{
+ std::wstring upgradeCode = GetMsiPropertyW(handle, L"UpgradeCode");
+ upgradeCode = ConvertGuid(std::wstring(upgradeCode.c_str() + 1, upgradeCode.length() - 2));
+
+ // MessageBoxW(NULL, upgradeCode.c_str(), "Debug", MB_OK);
+
+ if (RegistryKeyHasUpgradeSubKey(
+ HKEY_CURRENT_USER,
+ L"Software\\Microsoft\\Installer\\UpgradeCodes",
+ upgradeCode) && IsSetMsiPropertyW(handle, L"ALLUSERS"))
+ {
+ UnsetMsiPropertyW(handle, L"ALLUSERS");
+ // MessageBoxW(NULL, L"ALLUSERS removed", L"DEBUG", MB_OK);
+ }
+ else if (RegistryKeyHasUpgradeSubKey(
+ HKEY_LOCAL_MACHINE,
+ L"Software\\Classes\\Installer\\UpgradeCodes",
+ upgradeCode) && !IsSetMsiPropertyW(handle, L"ALLUSERS"))
+ {
+ SetMsiPropertyW(handle, L"ALLUSERS");
+ // MessageBoxW(NULL, L"ALLUSERS set", L"DEBUG", MB_OK);
+ }
+ return ERROR_SUCCESS;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/setup_native/source/win32/customactions/shellextensions/vistaspecial.cxx b/setup_native/source/win32/customactions/shellextensions/vistaspecial.cxx
new file mode 100644
index 000000000..e61f4a43f
--- /dev/null
+++ b/setup_native/source/win32/customactions/shellextensions/vistaspecial.cxx
@@ -0,0 +1,126 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "shlxtmsi.hxx"
+
+#include <strsafe.h>
+
+#include <systools/win32/uwinapi.h>
+#include "../tools/seterror.hxx"
+
+static bool RemoveCompleteDirectoryW(const std::wstring& rPath)
+{
+ bool bDirectoryRemoved = true;
+
+ std::wstring sPattern = rPath + L"\\" + L"*.*";
+ WIN32_FIND_DATAW aFindData;
+
+ // Finding all content in rPath
+
+ HANDLE hFindContent = FindFirstFileW( sPattern.c_str(), &aFindData );
+
+ if ( hFindContent != INVALID_HANDLE_VALUE )
+ {
+ bool fNextFile = false;
+ std::wstring sCurrentDir = L".";
+ std::wstring sParentDir = L"..";
+
+ do
+ {
+ std::wstring sFileName = aFindData.cFileName;
+
+ if (( wcscmp(sFileName.c_str(),sCurrentDir.c_str()) != 0 ) &&
+ ( wcscmp(sFileName.c_str(),sParentDir.c_str()) != 0 ))
+ {
+ std::wstring sCompleteFileName = rPath + L"\\" + sFileName;
+
+ if ( aFindData.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY )
+ {
+ RemoveCompleteDirectoryW(sCompleteFileName);
+ }
+ else
+ {
+ DeleteFileW( sCompleteFileName.c_str() );
+ }
+ }
+
+ fNextFile = FindNextFileW( hFindContent, &aFindData );
+
+ } while ( fNextFile );
+
+ FindClose( hFindContent );
+
+ // empty directory can be removed now
+ // RemoveDirectory is only successful, if the last handle to the directory is closed
+ // -> first removing content -> closing handle -> remove empty directory
+
+
+ if( !( RemoveDirectoryW(rPath.c_str()) ) )
+ {
+ bDirectoryRemoved = false;
+ }
+ }
+
+ return bDirectoryRemoved;
+}
+
+extern "C" __declspec(dllexport) UINT __stdcall RenamePrgFolder( MSIHANDLE handle )
+{
+ std::wstring sOfficeInstallPath = GetMsiPropertyW(handle, L"INSTALLLOCATION");
+
+ std::wstring sRenameSrc = sOfficeInstallPath + L"program";
+ std::wstring sRenameDst = sOfficeInstallPath + L"program_old";
+
+ bool bSuccess = MoveFileW( sRenameSrc.c_str(), sRenameDst.c_str() );
+ if ( !bSuccess )
+ {
+ WCHAR sAppend[2] = L"0";
+ for ( int i = 0; i < 10; i++ )
+ {
+ sRenameDst = sOfficeInstallPath + L"program_old" + sAppend;
+ bSuccess = MoveFileW( sRenameSrc.c_str(), sRenameDst.c_str() );
+ if ( bSuccess )
+ break;
+ sAppend[0] += 1;
+ }
+ }
+
+ // ? This succeeds unconditionally, even if bSuccess is false!
+ return ERROR_SUCCESS;
+}
+
+extern "C" __declspec(dllexport) UINT __stdcall RemovePrgFolder( MSIHANDLE handle )
+{
+ std::wstring sOfficeInstallPath = GetMsiPropertyW(handle, L"INSTALLLOCATION");
+ std::wstring sRemoveDir = sOfficeInstallPath + L"program_old";
+
+ RemoveCompleteDirectoryW( sRemoveDir );
+
+ WCHAR sAppend[2] = L"0";
+ for ( int i = 0; i < 10; i++ )
+ {
+ sRemoveDir = sOfficeInstallPath + L"program_old" + sAppend;
+ RemoveCompleteDirectoryW( sRemoveDir );
+ sAppend[0] += 1;
+ }
+
+ return ERROR_SUCCESS;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/setup_native/source/win32/customactions/tools/checkversion.cxx b/setup_native/source/win32/customactions/tools/checkversion.cxx
new file mode 100644
index 000000000..3f0b30f3c
--- /dev/null
+++ b/setup_native/source/win32/customactions/tools/checkversion.cxx
@@ -0,0 +1,98 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <msiquery.h>
+
+#include <cassert>
+#include <string.h>
+#include <malloc.h>
+#include <stdio.h>
+#include <strsafe.h>
+
+#include "seterror.hxx"
+
+
+static bool GetMsiPropW( MSIHANDLE hMSI, const wchar_t* pPropName, wchar_t** ppValue )
+{
+ DWORD sz = 0;
+ if ( MsiGetPropertyW( hMSI, pPropName, const_cast<wchar_t *>(L""), &sz ) == ERROR_MORE_DATA )
+ {
+ sz++;
+ DWORD nbytes = sz * sizeof( wchar_t );
+ wchar_t* buff = static_cast<wchar_t*>( malloc( nbytes ) );
+ assert(buff); // Don't handle OOM conditions
+ ZeroMemory( buff, nbytes );
+ MsiGetPropertyW( hMSI, pPropName, buff, &sz );
+ *ppValue = buff;
+
+ return true;
+ }
+
+ return false;
+}
+
+
+#ifdef DEBUG
+inline void OutputDebugStringFormatW( PCWSTR pFormat, ... )
+{
+ WCHAR buffer[1024];
+ va_list args;
+
+ va_start( args, pFormat );
+ StringCchVPrintfW( buffer, sizeof(buffer)/sizeof(buffer[0]), pFormat, args );
+ OutputDebugStringW( buffer );
+ va_end(args);
+}
+#else
+static void OutputDebugStringFormatW( PCWSTR, ... )
+{
+}
+#endif
+
+
+extern "C" __declspec(dllexport) UINT __stdcall CheckVersions( MSIHANDLE hMSI )
+{
+ // MessageBoxW(NULL, L"CheckVersions", L"Information", MB_OK | MB_ICONINFORMATION);
+
+ wchar_t* pVal = nullptr;
+
+ if ( GetMsiPropW( hMSI, L"NEWPRODUCTS", &pVal ) && pVal )
+ {
+ OutputDebugStringFormatW( L"DEBUG: NEWPRODUCTS found [%s]", pVal );
+ if ( *pVal != 0 )
+ SetMsiErrorCode( MSI_ERROR_NEW_VERSION_FOUND );
+ free( pVal );
+ }
+ pVal = nullptr;
+ if ( GetMsiPropW( hMSI, L"OLDPRODUCTS", &pVal ) && pVal )
+ {
+ OutputDebugStringFormatW( L"DEBUG: OLDPRODUCTS found [%s]", pVal );
+ if ( *pVal != 0 )
+ SetMsiErrorCode( MSI_ERROR_OLD_VERSION_FOUND );
+ free( pVal );
+ }
+ pVal = nullptr;
+
+ return ERROR_SUCCESS;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/setup_native/source/win32/customactions/tools/seterror.cxx b/setup_native/source/win32/customactions/tools/seterror.cxx
new file mode 100644
index 000000000..f1800ffb7
--- /dev/null
+++ b/setup_native/source/win32/customactions/tools/seterror.cxx
@@ -0,0 +1,84 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include <string.h>
+#include <malloc.h>
+#include <stdio.h>
+#include <strsafe.h>
+
+#include "seterror.hxx"
+
+
+#ifdef DEBUG
+inline void OutputDebugStringFormatW( PCWSTR pFormat, ... )
+{
+ WCHAR buffer[1024];
+ va_list args;
+
+ va_start( args, pFormat );
+ StringCchVPrintfW( buffer, sizeof(buffer)/sizeof(buffer[0]), pFormat, args );
+ OutputDebugStringW( buffer );
+ va_end(args);
+}
+#else
+static void OutputDebugStringFormatW( PCWSTR, ... )
+{
+}
+#endif
+
+
+void SetMsiErrorCode( int nErrorCode )
+{
+ const WCHAR sMemMapName[] = L"Global\\MsiErrorObject";
+
+ HANDLE hMapFile;
+ int *pBuf;
+
+ hMapFile = OpenFileMappingW(
+ FILE_MAP_ALL_ACCESS, // read/write access
+ FALSE, // do not inherit the name
+ sMemMapName ); // name of mapping object
+
+ if ( hMapFile == nullptr ) // can not set error code
+ {
+ OutputDebugStringFormatW( L"Could not open map file (%d).\n", GetLastError() );
+ return;
+ }
+
+ pBuf = static_cast<int*>(MapViewOfFile( hMapFile, // handle to map object
+ FILE_MAP_ALL_ACCESS, // read/write permission
+ 0,
+ 0,
+ sizeof( int ) ));
+ if ( pBuf )
+ {
+ *pBuf = nErrorCode;
+ UnmapViewOfFile( pBuf );
+ }
+ else
+ OutputDebugStringFormatW( L"Could not map view of file (%d).\n", GetLastError() );
+
+ CloseHandle( hMapFile );
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/setup_native/source/win32/customactions/tools/seterror.hxx b/setup_native/source/win32/customactions/tools/seterror.hxx
new file mode 100644
index 000000000..010af4a8c
--- /dev/null
+++ b/setup_native/source/win32/customactions/tools/seterror.hxx
@@ -0,0 +1,36 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SETUP_NATIVE_SOURCE_WIN32_CUSTOMACTIONS_TOOLS_SETERROR_HXX
+#define INCLUDED_SETUP_NATIVE_SOURCE_WIN32_CUSTOMACTIONS_TOOLS_SETERROR_HXX
+
+
+// list of own error codes
+
+#define MSI_ERROR_NEW_VERSION_FOUND 9010
+#define MSI_ERROR_OLD_VERSION_FOUND 9012
+
+#define MSI_ERROR_DIRECTORY_NOT_EMPTY 9030
+
+
+void SetMsiErrorCode( int nErrorCode );
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/setup_native/source/win32/customactions/tools/sn_tools.def b/setup_native/source/win32/customactions/tools/sn_tools.def
new file mode 100644
index 000000000..3c881861b
--- /dev/null
+++ b/setup_native/source/win32/customactions/tools/sn_tools.def
@@ -0,0 +1,3 @@
+LIBRARY "sn_tools.dll"
+EXPORTS
+ CheckVersions \ No newline at end of file