LibreOffice Module setup_native (master) 1
reg_dlls.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
9
10#include <iomanip>
11#include <memory>
12#include <string>
13#include <sstream>
14
15#define WIN32_LEAN_AND_MEAN
16#include <windows.h>
17#include <Shlobj.h>
18#include <msiquery.h>
19
20namespace
21{
22template <typename IntType> std::string Num2Hex(IntType n)
23{
24 std::stringstream sMsg;
25 sMsg << "0x" << std::uppercase << std::setfill('0') << std::setw(sizeof(n) * 2) << std::hex
26 << n;
27 return sMsg.str();
28}
29
30template <typename IntType> std::string Num2Dec(IntType n)
31{
32 std::stringstream sMsg;
33 sMsg << n;
34 return sMsg.str();
35}
36
37std::string Win32ErrorMessage(const char* sFunc, DWORD nWin32Error)
38{
39 std::stringstream sMsg;
40 sMsg << sFunc << " failed with Win32 error code " << Num2Hex(nWin32Error) << "!";
41
42 return sMsg.str();
43}
44
45void ThrowHResult(const char* sFunc, HRESULT hr)
46{
47 std::stringstream sMsg;
48 sMsg << sFunc << " failed (HRESULT = " << Num2Hex(hr) << ")!";
49
50 throw std::exception(sMsg.str().c_str());
51}
52
53void CheckHResult(const char* sFunc, HRESULT hr)
54{
55 if (FAILED(hr))
56 ThrowHResult(sFunc, hr);
57}
58
59void ThrowWin32Error(const char* sFunc, DWORD nWin32Error)
60{
61 throw std::exception(Win32ErrorMessage(sFunc, nWin32Error).c_str());
62}
63
64void ThrowLastError(const char* sFunc) { ThrowWin32Error(sFunc, GetLastError()); }
65
66void CheckWin32Error(const char* sFunc, DWORD nWin32Error)
67{
68 if (nWin32Error != ERROR_SUCCESS)
69 ThrowWin32Error(sFunc, nWin32Error);
70}
71
72std::wstring GetKnownFolder(const KNOWNFOLDERID& rfid)
73{
74 PWSTR sPath = nullptr;
75 HRESULT hr = SHGetKnownFolderPath(rfid, KF_FLAG_DEFAULT, nullptr, &sPath);
76 CheckHResult("SHGetKnownFolderPath", hr);
77 std::wstring sResult(sPath);
78 CoTaskMemFree(sPath);
79 return sResult;
80}
81
82void WriteLogElem(MSIHANDLE hInst, MSIHANDLE hRecord, std::ostringstream& sTmpl, UINT)
83{
84 MsiRecordSetStringA(hRecord, 0, sTmpl.str().c_str());
85 MsiProcessMessage(hInst, INSTALLMESSAGE_INFO, hRecord);
86}
87
88void RecSetString(MSIHANDLE hRec, UINT nField, LPCSTR sVal)
89{
90 MsiRecordSetStringA(hRec, nField, sVal);
91}
92
93void RecSetString(MSIHANDLE hRec, UINT nField, LPCWSTR sVal)
94{
95 MsiRecordSetStringW(hRec, nField, sVal);
96}
97
98template <class S1, class... SOther>
99void WriteLogElem(MSIHANDLE hInst, MSIHANDLE hRec, std::ostringstream& sTmpl, UINT nField,
100 const S1& elem, const SOther&... others);
101
102template <class Ch, class... SOther>
103void WriteLogElem(MSIHANDLE hInst, MSIHANDLE hRec, std::ostringstream& sTmpl, UINT nField,
104 const Ch* elem, const SOther&... others)
105{
106 sTmpl << " [" << nField << "]";
107 RecSetString(hRec, nField, elem);
108 WriteLogElem(hInst, hRec, sTmpl, nField + 1, others...);
109}
110
111template <class S1, class... SOther>
112void WriteLogElem(MSIHANDLE hInst, MSIHANDLE hRec, std::ostringstream& sTmpl, UINT nField,
113 const S1& elem, const SOther&... others)
114{
115 WriteLogElem(hInst, hRec, sTmpl, nField, elem.c_str(), others...);
116}
117
118std::string sLogPrefix;
119
120template <class... StrType> void WriteLog(MSIHANDLE hInst, const StrType&... elements)
121{
122 PMSIHANDLE hRec = MsiCreateRecord(sizeof...(elements));
123 if (!hRec)
124 return;
125
126 std::ostringstream sTemplate;
127 sTemplate << sLogPrefix;
128 WriteLogElem(hInst, hRec, sTemplate, 1, elements...);
129}
130
131std::wstring MsiGetPropertyW(MSIHANDLE hInst, LPCWSTR szName)
132{
133 std::wstring sResult;
134 DWORD nSz = 0;
135 UINT nRet = ::MsiGetPropertyW(hInst, szName, const_cast<wchar_t*>(L""), &nSz);
136 if (nRet == ERROR_MORE_DATA)
137 {
138 ++nSz;
139 auto buf = std::make_unique<wchar_t[]>(nSz);
140 CheckWin32Error("MsiGetPropertyW", ::MsiGetPropertyW(hInst, szName, buf.get(), &nSz));
141 sResult = buf.get();
142 WriteLog(hInst, "Property", szName, "=", sResult);
143 }
144 else
145 CheckWin32Error("MsiGetPropertyW", nRet);
146
147 return sResult;
148}
149
150typedef std::unique_ptr<void, decltype(&CloseHandle)> CloseHandleGuard;
151CloseHandleGuard Guard(HANDLE h) { return CloseHandleGuard(h, CloseHandle); }
152
153void RegDLL(MSIHANDLE hInst, const std::wstring& sArgs, bool bUnreg)
154{
155 static std::wstring sRegSvr32 = GetKnownFolder(FOLDERID_System) + L"\\regsvr32.exe";
156
157 try
158 {
159 std::wstring sCmd = L"\"" + sRegSvr32 + L"\" /s ";
160 if (bUnreg)
161 sCmd += L"/u ";
162 sCmd += sArgs;
163 WriteLog(hInst, "Prepared regsvr32 command:", sCmd);
164
165 STARTUPINFOW si{};
166 si.cb = sizeof(si);
167 PROCESS_INFORMATION pi{};
168 if (!CreateProcessW(sRegSvr32.c_str(), const_cast<LPWSTR>(sCmd.c_str()), nullptr, nullptr,
169 FALSE, CREATE_NO_WINDOW, nullptr, nullptr, &si, &pi))
170 ThrowLastError("CreateProcessW");
171 auto aCloseProcHandleGuard(Guard(pi.hProcess));
172 WriteLog(hInst, "CreateProcessW succeeded");
173
174 DWORD nWaitResult = WaitForSingleObject(pi.hProcess, INFINITE);
175 if (nWaitResult != WAIT_OBJECT_0)
176 ThrowWin32Error("WaitForSingleObject", nWaitResult);
177
178 DWORD nExitCode = 0;
179 if (!GetExitCodeProcess(pi.hProcess, &nExitCode))
180 ThrowLastError("GetExitCodeProcess");
181
182 WriteLog(hInst, "regsvr32 returned:", Num2Dec(nExitCode));
183 }
184 catch (std::exception& e)
185 {
186 WriteLog(hInst, e.what());
187 }
188}
189
190void ProcessCustomActionData(MSIHANDLE hInst, bool bUnreg)
191{
192 WriteLog(hInst, "Checking value of CustomActionData");
193 std::wstring sCustomActionData = MsiGetPropertyW(hInst, L"CustomActionData");
194 WriteLog(hInst, "Got CustomActionData value:", sCustomActionData);
195 std::wstringstream ss(sCustomActionData);
196 std::wstring sToken;
197 while (std::getline(ss, sToken, L'|'))
198 {
199 if (!sToken.empty())
200 {
201 RegDLL(hInst, sToken, bUnreg);
202 }
203 }
204}
205} // namespace
206
207// Deferred action "reg_dlls" that must be run from system account. Receives a list of regsvr32
208// arguments: DLLs which need registering, and possibly /i argument with its parameter.
209extern "C" __declspec(dllexport) UINT __stdcall RegDLLs(MSIHANDLE hInstall)
210{
211 sLogPrefix = "RegDLLs:";
212 WriteLog(hInstall, "started");
213
214 ProcessCustomActionData(hInstall, false);
215 return ERROR_SUCCESS;
216}
217
218// Deferred action "unreg_dlls" that must be run from system account. Receives a list of regsvr32
219// arguments: DLLs which need registering, and possibly /i argument with its parameter.
220extern "C" __declspec(dllexport) UINT __stdcall UnregDLLs(MSIHANDLE hInstall)
221{
222 sLogPrefix = "UnregDLLs:";
223 WriteLog(hInstall, "started");
224
225 ProcessCustomActionData(hInstall, true);
226 return ERROR_SUCCESS;
227}
228
229// Immediate action "prep_reg_unreg_dlls". Checks states of the features to prepare custom action data
230// for reg_dlls and unreg_dlls deferred actions.
231extern "C" __declspec(dllexport) UINT __stdcall PrepRegUnregDLLs(MSIHANDLE hInstall)
232{
233 sLogPrefix = "PrepRegUnregDLLs:";
234 WriteLog(hInstall, "started");
235
236 try
237 {
238 INSTALLSTATE current_state_SubstMSO;
239 INSTALLSTATE future_state_SubstMSO;
240 CheckWin32Error("MsiGetFeatureStateW",
241 MsiGetFeatureStateW(hInstall, L"gm_SharePointSupport_SubstMSO",
242 &current_state_SubstMSO, &future_state_SubstMSO));
243
244 WriteLog(hInstall, "gm_SharePointSupport_SubstMSO state:", //
245 "current", std::to_string(current_state_SubstMSO), //
246 "future", std::to_string(future_state_SubstMSO)); //
247
248 INSTALLSTATE current_state_Main;
249 INSTALLSTATE future_state_Main;
250 CheckWin32Error("MsiGetFeatureStateW",
251 MsiGetFeatureStateW(hInstall, L"gm_o_SharePointSupport",
252 &current_state_Main, &future_state_Main));
253
254 WriteLog(hInstall, "gm_o_SharePointSupport state:", //
255 "current", std::to_string(current_state_Main), //
256 "future", std::to_string(future_state_Main)); //
257
258 const bool bUnregSubstMSO = current_state_SubstMSO == INSTALLSTATE_LOCAL
259 && future_state_SubstMSO == INSTALLSTATE_ABSENT;
260 const bool bUnregMain
261 = current_state_Main == INSTALLSTATE_LOCAL && future_state_Main == INSTALLSTATE_ABSENT;
262 const bool bRegSubstMSO = current_state_SubstMSO == INSTALLSTATE_ABSENT
263 && future_state_SubstMSO == INSTALLSTATE_LOCAL;
264 // basic registration is needed when either:
265 // 1. gm_o_SharePointSupport is installed;
266 // 2. gm_SharePointSupport_SubstMSO is uninstalled (and unregisters everything), but
267 // gm_o_SharePointSupport is not, so needs to be re-registered
268 const bool bRegMain
269 = (current_state_Main == INSTALLSTATE_ABSENT && future_state_Main == INSTALLSTATE_LOCAL)
270 || (bUnregSubstMSO && !bUnregMain);
271
272 std::wstring sUnregStr;
273 if (bUnregSubstMSO)
274 {
275 sUnregStr = L"/i:Substitute_OWSSUPP \"[#spsupp_x86.dll]\"|"
276 L"/i:Substitute_OWSSUPP \"[#spsupp_x64.dll]\"";
277 }
278 else if (bUnregMain)
279 {
280 sUnregStr = L"\"[#spsupp_x86.dll]\"|\"[#spsupp_x64.dll]\"";
281 }
282
283 std::wstring sRegStr;
284 if (bRegSubstMSO)
285 {
286 sRegStr = L"/i:Substitute_OWSSUPP \"[#spsupp_x86.dll]\"|"
287 L"/i:Substitute_OWSSUPP \"[#spsupp_x64.dll]\"";
288 }
289 else if (bRegMain)
290 {
291 sRegStr = L"\"[#spsupp_x86.dll]\"|\"[#spsupp_x64.dll]\"";
292 }
293
294 auto SetFormattedPropW = [&](LPCWSTR sProp, const std::wstring& sVal) {
295 PMSIHANDLE hRec = MsiCreateRecord(0);
296 if (!hRec)
297 throw std::exception("MsiCreateRecord failed!");
298 MsiRecordSetStringW(hRec, 0, sVal.c_str());
299 DWORD nSz = 0;
300 if (MsiFormatRecordW(hInstall, hRec, const_cast<wchar_t*>(L""), &nSz)
301 == ERROR_MORE_DATA)
302 {
303 ++nSz;
304 auto buf = std::make_unique<wchar_t[]>(nSz);
305 CheckWin32Error("MsiFormatRecordW",
306 MsiFormatRecordW(hInstall, hRec, buf.get(), &nSz));
307 CheckWin32Error("MsiSetPropertyW", MsiSetPropertyW(hInstall, sProp, buf.get()));
308 }
309 };
310 if (!sRegStr.empty())
311 SetFormattedPropW(L"reg_dlls", sRegStr);
312 if (!sUnregStr.empty())
313 SetFormattedPropW(L"unreg_dlls", sUnregStr);
314 }
315 catch (std::exception& e)
316 {
317 WriteLog(hInstall, e.what());
318 }
319
320 return ERROR_SUCCESS;
321}
322
323/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
#define FALSE
sal_Int64 n
sal_Int32 h
__declspec(dllexport) UINT __stdcall RegDLLs(MSIHANDLE hInstall)
Definition: reg_dlls.cxx:209
return hr
std::string Num2Hex(IntType n)
void ThrowLastError(const char *sFunc)
void ThrowWin32Error(const char *sFunc, DWORD nWin32Error)