diff options
Diffstat (limited to 'src/libs/dutil/WixToolset.DUtil/wiutil.cpp')
-rw-r--r-- | src/libs/dutil/WixToolset.DUtil/wiutil.cpp | 1629 |
1 files changed, 1629 insertions, 0 deletions
diff --git a/src/libs/dutil/WixToolset.DUtil/wiutil.cpp b/src/libs/dutil/WixToolset.DUtil/wiutil.cpp new file mode 100644 index 00000000..7414ac42 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/wiutil.cpp | |||
@@ -0,0 +1,1629 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | #include "precomp.h" | ||
4 | |||
5 | |||
6 | // Exit macros | ||
7 | #define WiuExitTrace(x, s, ...) ExitTraceSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__) | ||
8 | #define WiuExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__) | ||
9 | #define WiuExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__) | ||
10 | #define WiuExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__) | ||
11 | #define WiuExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__) | ||
12 | #define WiuExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__) | ||
13 | #define WiuExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__) | ||
14 | #define WiuExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_WIUTIL, p, x, e, s, __VA_ARGS__) | ||
15 | #define WiuExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_WIUTIL, p, x, s, __VA_ARGS__) | ||
16 | #define WiuExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_WIUTIL, p, x, e, s, __VA_ARGS__) | ||
17 | #define WiuExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_WIUTIL, p, x, s, __VA_ARGS__) | ||
18 | #define WiuExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_WIUTIL, e, x, s, __VA_ARGS__) | ||
19 | #define WiuExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_WIUTIL, g, x, s, __VA_ARGS__) | ||
20 | |||
21 | |||
22 | // constants | ||
23 | |||
24 | const DWORD WIU_MSI_PROGRESS_INVALID = 0xFFFFFFFF; | ||
25 | const DWORD WIU_GOOD_ENOUGH_PROPERTY_LENGTH = 64; | ||
26 | |||
27 | |||
28 | // structs | ||
29 | |||
30 | |||
31 | static PFN_MSIENABLELOGW vpfnMsiEnableLogW = ::MsiEnableLogW; | ||
32 | static PFN_MSIGETPRODUCTINFOW vpfnMsiGetProductInfoW = ::MsiGetProductInfoW; | ||
33 | static PFN_MSIQUERYFEATURESTATEW vpfnMsiQueryFeatureStateW = ::MsiQueryFeatureStateW; | ||
34 | static PFN_MSIGETCOMPONENTPATHW vpfnMsiGetComponentPathW = ::MsiGetComponentPathW; | ||
35 | static PFN_MSILOCATECOMPONENTW vpfnMsiLocateComponentW = ::MsiLocateComponentW; | ||
36 | static PFN_MSIINSTALLPRODUCTW vpfnMsiInstallProductW = ::MsiInstallProductW; | ||
37 | static PFN_MSICONFIGUREPRODUCTEXW vpfnMsiConfigureProductExW = ::MsiConfigureProductExW; | ||
38 | static PFN_MSIREMOVEPATCHESW vpfnMsiRemovePatchesW = ::MsiRemovePatchesW; | ||
39 | static PFN_MSISETINTERNALUI vpfnMsiSetInternalUI = ::MsiSetInternalUI; | ||
40 | static PFN_MSISETEXTERNALUIW vpfnMsiSetExternalUIW = ::MsiSetExternalUIW; | ||
41 | static PFN_MSIENUMPRODUCTSW vpfnMsiEnumProductsW = ::MsiEnumProductsW; | ||
42 | static PFN_MSIENUMRELATEDPRODUCTSW vpfnMsiEnumRelatedProductsW = ::MsiEnumRelatedProductsW; | ||
43 | |||
44 | // MSI 3.0+ | ||
45 | static PFN_MSIDETERMINEPATCHSEQUENCEW vpfnMsiDeterminePatchSequenceW = NULL; | ||
46 | static PFN_MSIDETERMINEAPPLICABLEPATCHESW vpfnMsiDetermineApplicablePatchesW = NULL; | ||
47 | static PFN_MSIENUMPRODUCTSEXW vpfnMsiEnumProductsExW = NULL; | ||
48 | static PFN_MSIGETPATCHINFOEXW vpfnMsiGetPatchInfoExW = NULL; | ||
49 | static PFN_MSIGETPRODUCTINFOEXW vpfnMsiGetProductInfoExW = NULL; | ||
50 | static PFN_MSISETEXTERNALUIRECORD vpfnMsiSetExternalUIRecord = NULL; | ||
51 | static PFN_MSISOURCELISTADDSOURCEEXW vpfnMsiSourceListAddSourceExW = NULL; | ||
52 | |||
53 | static HMODULE vhMsiDll = NULL; | ||
54 | static PFN_MSIDETERMINEPATCHSEQUENCEW vpfnMsiDeterminePatchSequenceWFromLibrary = NULL; | ||
55 | static PFN_MSIDETERMINEAPPLICABLEPATCHESW vpfnMsiDetermineApplicablePatchesWFromLibrary = NULL; | ||
56 | static PFN_MSIENUMPRODUCTSEXW vpfnMsiEnumProductsExWFromLibrary = NULL; | ||
57 | static PFN_MSIGETPATCHINFOEXW vpfnMsiGetPatchInfoExWFromLibrary = NULL; | ||
58 | static PFN_MSIGETPRODUCTINFOEXW vpfnMsiGetProductInfoExWFromLibrary = NULL; | ||
59 | static PFN_MSISETEXTERNALUIRECORD vpfnMsiSetExternalUIRecordFromLibrary = NULL; | ||
60 | static PFN_MSISOURCELISTADDSOURCEEXW vpfnMsiSourceListAddSourceExWFromLibrary = NULL; | ||
61 | |||
62 | // MSI Transactions v4.5+ | ||
63 | static PFN_MSIBEGINTRANSACTIONW vpfnMsiBeginTransaction = NULL; | ||
64 | static PFN_MSIENDTRANSACTION vpfnMsiEndTransaction = NULL; | ||
65 | |||
66 | static BOOL vfWiuInitialized = FALSE; | ||
67 | |||
68 | // globals | ||
69 | static DWORD vdwMsiDllMajorMinor = 0; | ||
70 | static DWORD vdwMsiDllBuildRevision = 0; | ||
71 | |||
72 | |||
73 | // internal function declarations | ||
74 | |||
75 | static DWORD CheckForRestartErrorCode( | ||
76 | __in DWORD dwErrorCode, | ||
77 | __out WIU_RESTART* pRestart | ||
78 | ); | ||
79 | static INT CALLBACK InstallEngineCallback( | ||
80 | __in LPVOID pvContext, | ||
81 | __in UINT uiMessage, | ||
82 | __in_z_opt LPCWSTR wzMessage | ||
83 | ); | ||
84 | static INT CALLBACK InstallEngineRecordCallback( | ||
85 | __in LPVOID pvContext, | ||
86 | __in UINT uiMessage, | ||
87 | __in_opt MSIHANDLE hRecord | ||
88 | ); | ||
89 | static INT HandleInstallMessage( | ||
90 | __in WIU_MSI_EXECUTE_CONTEXT* pContext, | ||
91 | __in INSTALLMESSAGE mt, | ||
92 | __in UINT uiFlags, | ||
93 | __in_z LPCWSTR wzMessage, | ||
94 | __in_opt MSIHANDLE hRecord | ||
95 | ); | ||
96 | static INT HandleInstallProgress( | ||
97 | __in WIU_MSI_EXECUTE_CONTEXT* pContext, | ||
98 | __in_z_opt LPCWSTR wzMessage, | ||
99 | __in_opt MSIHANDLE hRecord | ||
100 | ); | ||
101 | static INT SendMsiMessage( | ||
102 | __in WIU_MSI_EXECUTE_CONTEXT* pContext, | ||
103 | __in INSTALLMESSAGE mt, | ||
104 | __in UINT uiFlags, | ||
105 | __in_z LPCWSTR wzMessage, | ||
106 | __in_opt MSIHANDLE hRecord | ||
107 | ); | ||
108 | static INT SendErrorMessage( | ||
109 | __in WIU_MSI_EXECUTE_CONTEXT* pContext, | ||
110 | __in UINT uiFlags, | ||
111 | __in_z LPCWSTR wzMessage, | ||
112 | __in_opt MSIHANDLE hRecord | ||
113 | ); | ||
114 | static INT SendFilesInUseMessage( | ||
115 | __in WIU_MSI_EXECUTE_CONTEXT* pContext, | ||
116 | __in_opt MSIHANDLE hRecord, | ||
117 | __in BOOL fRestartManagerRequest | ||
118 | ); | ||
119 | static INT SendProgressUpdate( | ||
120 | __in WIU_MSI_EXECUTE_CONTEXT* pContext | ||
121 | ); | ||
122 | static void ResetProgress( | ||
123 | __in WIU_MSI_EXECUTE_CONTEXT* pContext | ||
124 | ); | ||
125 | static DWORD CalculatePhaseProgress( | ||
126 | __in WIU_MSI_EXECUTE_CONTEXT* pContext, | ||
127 | __in DWORD dwProgressIndex, | ||
128 | __in DWORD dwWeightPercentage | ||
129 | ); | ||
130 | void InitializeMessageData( | ||
131 | __in_opt MSIHANDLE hRecord, | ||
132 | __deref_out_ecount(*pcData) LPWSTR** prgsczData, | ||
133 | __out DWORD* pcData | ||
134 | ); | ||
135 | void UninitializeMessageData( | ||
136 | __in LPWSTR* rgsczData, | ||
137 | __in DWORD cData | ||
138 | ); | ||
139 | |||
140 | |||
141 | /******************************************************************** | ||
142 | WiuInitialize - initializes wiutil | ||
143 | |||
144 | *********************************************************************/ | ||
145 | extern "C" HRESULT DAPI WiuInitialize( | ||
146 | ) | ||
147 | { | ||
148 | HRESULT hr = S_OK; | ||
149 | LPWSTR sczMsiDllPath = NULL; | ||
150 | |||
151 | hr = LoadSystemLibraryWithPath(L"Msi.dll", &vhMsiDll, &sczMsiDllPath); | ||
152 | WiuExitOnFailure(hr, "Failed to load Msi.DLL"); | ||
153 | |||
154 | // Ignore failures | ||
155 | FileVersion(sczMsiDllPath, &vdwMsiDllMajorMinor, &vdwMsiDllBuildRevision); | ||
156 | |||
157 | vpfnMsiDeterminePatchSequenceWFromLibrary = reinterpret_cast<PFN_MSIDETERMINEPATCHSEQUENCEW>(::GetProcAddress(vhMsiDll, "MsiDeterminePatchSequenceW")); | ||
158 | if (NULL == vpfnMsiDeterminePatchSequenceW) | ||
159 | { | ||
160 | vpfnMsiDeterminePatchSequenceW = vpfnMsiDeterminePatchSequenceWFromLibrary; | ||
161 | } | ||
162 | |||
163 | vpfnMsiDetermineApplicablePatchesWFromLibrary = reinterpret_cast<PFN_MSIDETERMINEAPPLICABLEPATCHESW>(::GetProcAddress(vhMsiDll, "MsiDetermineApplicablePatchesW")); | ||
164 | if (NULL == vpfnMsiDetermineApplicablePatchesW) | ||
165 | { | ||
166 | vpfnMsiDetermineApplicablePatchesW = vpfnMsiDetermineApplicablePatchesWFromLibrary; | ||
167 | } | ||
168 | |||
169 | vpfnMsiEnumProductsExWFromLibrary = reinterpret_cast<PFN_MSIENUMPRODUCTSEXW>(::GetProcAddress(vhMsiDll, "MsiEnumProductsExW")); | ||
170 | if (NULL == vpfnMsiEnumProductsExW) | ||
171 | { | ||
172 | vpfnMsiEnumProductsExW = vpfnMsiEnumProductsExWFromLibrary; | ||
173 | } | ||
174 | |||
175 | vpfnMsiGetPatchInfoExWFromLibrary = reinterpret_cast<PFN_MSIGETPATCHINFOEXW>(::GetProcAddress(vhMsiDll, "MsiGetPatchInfoExW")); | ||
176 | if (NULL == vpfnMsiGetPatchInfoExW) | ||
177 | { | ||
178 | vpfnMsiGetPatchInfoExW = vpfnMsiGetPatchInfoExWFromLibrary; | ||
179 | } | ||
180 | |||
181 | vpfnMsiGetProductInfoExWFromLibrary = reinterpret_cast<PFN_MSIGETPRODUCTINFOEXW>(::GetProcAddress(vhMsiDll, "MsiGetProductInfoExW")); | ||
182 | if (NULL == vpfnMsiGetProductInfoExW) | ||
183 | { | ||
184 | vpfnMsiGetProductInfoExW = vpfnMsiGetProductInfoExWFromLibrary; | ||
185 | } | ||
186 | |||
187 | vpfnMsiSetExternalUIRecordFromLibrary = reinterpret_cast<PFN_MSISETEXTERNALUIRECORD>(::GetProcAddress(vhMsiDll, "MsiSetExternalUIRecord")); | ||
188 | if (NULL == vpfnMsiSetExternalUIRecord) | ||
189 | { | ||
190 | vpfnMsiSetExternalUIRecord = vpfnMsiSetExternalUIRecordFromLibrary; | ||
191 | } | ||
192 | |||
193 | //static PFN_MSISOURCELISTADDSOURCEEXW vpfnMsiSourceListAddSourceExW = NULL; | ||
194 | vpfnMsiSourceListAddSourceExWFromLibrary = reinterpret_cast<PFN_MSISOURCELISTADDSOURCEEXW>(::GetProcAddress(vhMsiDll, "MsiSourceListAddSourceExW")); | ||
195 | if (NULL == vpfnMsiSourceListAddSourceExW) | ||
196 | { | ||
197 | vpfnMsiSourceListAddSourceExW = vpfnMsiSourceListAddSourceExWFromLibrary; | ||
198 | } | ||
199 | |||
200 | // MSI Transaction functions | ||
201 | if (NULL == vpfnMsiBeginTransaction) | ||
202 | { | ||
203 | vpfnMsiBeginTransaction = reinterpret_cast<PFN_MSIBEGINTRANSACTIONW>(::GetProcAddress(vhMsiDll, "MsiBeginTransactionW")); | ||
204 | } | ||
205 | |||
206 | if (NULL == vpfnMsiEndTransaction) | ||
207 | { | ||
208 | vpfnMsiEndTransaction = reinterpret_cast<PFN_MSIENDTRANSACTION>(::GetProcAddress(vhMsiDll, "MsiEndTransaction")); | ||
209 | } | ||
210 | |||
211 | vfWiuInitialized = TRUE; | ||
212 | |||
213 | LExit: | ||
214 | ReleaseStr(sczMsiDllPath); | ||
215 | return hr; | ||
216 | } | ||
217 | |||
218 | |||
219 | /******************************************************************** | ||
220 | WiuUninitialize - uninitializes wiutil | ||
221 | |||
222 | *********************************************************************/ | ||
223 | extern "C" void DAPI WiuUninitialize( | ||
224 | ) | ||
225 | { | ||
226 | if (vhMsiDll) | ||
227 | { | ||
228 | ::FreeLibrary(vhMsiDll); | ||
229 | vhMsiDll = NULL; | ||
230 | vpfnMsiSetExternalUIRecordFromLibrary = NULL; | ||
231 | vpfnMsiGetProductInfoExWFromLibrary = NULL; | ||
232 | vpfnMsiGetPatchInfoExWFromLibrary = NULL; | ||
233 | vpfnMsiEnumProductsExWFromLibrary = NULL; | ||
234 | vpfnMsiDetermineApplicablePatchesWFromLibrary = NULL; | ||
235 | vpfnMsiDeterminePatchSequenceWFromLibrary = NULL; | ||
236 | vpfnMsiSourceListAddSourceExWFromLibrary = NULL; | ||
237 | vpfnMsiBeginTransaction = NULL; | ||
238 | vpfnMsiEndTransaction = NULL; | ||
239 | } | ||
240 | |||
241 | vfWiuInitialized = FALSE; | ||
242 | } | ||
243 | |||
244 | |||
245 | /******************************************************************** | ||
246 | WiuFunctionOverride - overrides the Windows installer functions. Typically used | ||
247 | for unit testing. | ||
248 | |||
249 | *********************************************************************/ | ||
250 | extern "C" void DAPI WiuFunctionOverride( | ||
251 | __in_opt PFN_MSIENABLELOGW pfnMsiEnableLogW, | ||
252 | __in_opt PFN_MSIGETCOMPONENTPATHW pfnMsiGetComponentPathW, | ||
253 | __in_opt PFN_MSILOCATECOMPONENTW pfnMsiLocateComponentW, | ||
254 | __in_opt PFN_MSIQUERYFEATURESTATEW pfnMsiQueryFeatureStateW, | ||
255 | __in_opt PFN_MSIGETPRODUCTINFOW pfnMsiGetProductInfoW, | ||
256 | __in_opt PFN_MSIGETPRODUCTINFOEXW pfnMsiGetProductInfoExW, | ||
257 | __in_opt PFN_MSIINSTALLPRODUCTW pfnMsiInstallProductW, | ||
258 | __in_opt PFN_MSICONFIGUREPRODUCTEXW pfnMsiConfigureProductExW, | ||
259 | __in_opt PFN_MSISETINTERNALUI pfnMsiSetInternalUI, | ||
260 | __in_opt PFN_MSISETEXTERNALUIW pfnMsiSetExternalUIW, | ||
261 | __in_opt PFN_MSIENUMRELATEDPRODUCTSW pfnMsiEnumRelatedProductsW, | ||
262 | __in_opt PFN_MSISETEXTERNALUIRECORD pfnMsiSetExternalUIRecord, | ||
263 | __in_opt PFN_MSISOURCELISTADDSOURCEEXW pfnMsiSourceListAddSourceExW | ||
264 | ) | ||
265 | { | ||
266 | vpfnMsiEnableLogW = pfnMsiEnableLogW ? pfnMsiEnableLogW : ::MsiEnableLogW; | ||
267 | vpfnMsiGetComponentPathW = pfnMsiGetComponentPathW ? pfnMsiGetComponentPathW : ::MsiGetComponentPathW; | ||
268 | vpfnMsiLocateComponentW = pfnMsiLocateComponentW ? pfnMsiLocateComponentW : ::MsiLocateComponentW; | ||
269 | vpfnMsiQueryFeatureStateW = pfnMsiQueryFeatureStateW ? pfnMsiQueryFeatureStateW : ::MsiQueryFeatureStateW; | ||
270 | vpfnMsiGetProductInfoW = pfnMsiGetProductInfoW ? pfnMsiGetProductInfoW : vpfnMsiGetProductInfoW; | ||
271 | vpfnMsiInstallProductW = pfnMsiInstallProductW ? pfnMsiInstallProductW : ::MsiInstallProductW; | ||
272 | vpfnMsiConfigureProductExW = pfnMsiConfigureProductExW ? pfnMsiConfigureProductExW : ::MsiConfigureProductExW; | ||
273 | vpfnMsiSetInternalUI = pfnMsiSetInternalUI ? pfnMsiSetInternalUI : ::MsiSetInternalUI; | ||
274 | vpfnMsiSetExternalUIW = pfnMsiSetExternalUIW ? pfnMsiSetExternalUIW : ::MsiSetExternalUIW; | ||
275 | vpfnMsiEnumRelatedProductsW = pfnMsiEnumRelatedProductsW ? pfnMsiEnumRelatedProductsW : ::MsiEnumRelatedProductsW; | ||
276 | vpfnMsiGetProductInfoExW = pfnMsiGetProductInfoExW ? pfnMsiGetProductInfoExW : vpfnMsiGetProductInfoExWFromLibrary; | ||
277 | vpfnMsiSetExternalUIRecord = pfnMsiSetExternalUIRecord ? pfnMsiSetExternalUIRecord : vpfnMsiSetExternalUIRecordFromLibrary; | ||
278 | vpfnMsiSourceListAddSourceExW = pfnMsiSourceListAddSourceExW ? pfnMsiSourceListAddSourceExW : vpfnMsiSourceListAddSourceExWFromLibrary; | ||
279 | } | ||
280 | |||
281 | |||
282 | extern "C" HRESULT DAPI WiuGetComponentPath( | ||
283 | __in_z LPCWSTR wzProductCode, | ||
284 | __in_z LPCWSTR wzComponentId, | ||
285 | __out INSTALLSTATE* pInstallState, | ||
286 | __out_z LPWSTR* psczValue | ||
287 | ) | ||
288 | { | ||
289 | HRESULT hr = S_OK; | ||
290 | DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH; | ||
291 | DWORD cchCompare; | ||
292 | |||
293 | hr = StrAlloc(psczValue, cch); | ||
294 | WiuExitOnFailure(hr, "Failed to allocate string for component path."); | ||
295 | |||
296 | cchCompare = cch; | ||
297 | *pInstallState = vpfnMsiGetComponentPathW(wzProductCode, wzComponentId, *psczValue, &cch); | ||
298 | if (INSTALLSTATE_MOREDATA == *pInstallState) | ||
299 | { | ||
300 | ++cch; | ||
301 | hr = StrAlloc(psczValue, cch); | ||
302 | WiuExitOnFailure(hr, "Failed to reallocate string for component path."); | ||
303 | |||
304 | cchCompare = cch; | ||
305 | *pInstallState = vpfnMsiGetComponentPathW(wzProductCode, wzComponentId, *psczValue, &cch); | ||
306 | } | ||
307 | |||
308 | if (INSTALLSTATE_INVALIDARG == *pInstallState) | ||
309 | { | ||
310 | hr = E_INVALIDARG; | ||
311 | WiuExitOnRootFailure(hr, "Invalid argument when getting component path."); | ||
312 | } | ||
313 | else if (INSTALLSTATE_UNKNOWN == *pInstallState) | ||
314 | { | ||
315 | ExitFunction(); | ||
316 | } | ||
317 | |||
318 | // If the actual path length is greater than or equal to the original buffer | ||
319 | // allocate a larger buffer and get the path again, just in case we are | ||
320 | // missing any part of the path. | ||
321 | if (cchCompare <= cch) | ||
322 | { | ||
323 | ++cch; | ||
324 | hr = StrAlloc(psczValue, cch); | ||
325 | WiuExitOnFailure(hr, "Failed to reallocate string for component path."); | ||
326 | |||
327 | *pInstallState = vpfnMsiGetComponentPathW(wzProductCode, wzComponentId, *psczValue, &cch); | ||
328 | } | ||
329 | |||
330 | LExit: | ||
331 | return hr; | ||
332 | } | ||
333 | |||
334 | |||
335 | extern "C" HRESULT DAPI WiuLocateComponent( | ||
336 | __in_z LPCWSTR wzComponentId, | ||
337 | __out INSTALLSTATE* pInstallState, | ||
338 | __out_z LPWSTR* psczValue | ||
339 | ) | ||
340 | { | ||
341 | HRESULT hr = S_OK; | ||
342 | DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH; | ||
343 | DWORD cchCompare; | ||
344 | |||
345 | hr = StrAlloc(psczValue, cch); | ||
346 | WiuExitOnFailure(hr, "Failed to allocate string for component path."); | ||
347 | |||
348 | cchCompare = cch; | ||
349 | *pInstallState = vpfnMsiLocateComponentW(wzComponentId, *psczValue, &cch); | ||
350 | if (INSTALLSTATE_MOREDATA == *pInstallState) | ||
351 | { | ||
352 | ++cch; | ||
353 | hr = StrAlloc(psczValue, cch); | ||
354 | WiuExitOnFailure(hr, "Failed to reallocate string for component path."); | ||
355 | |||
356 | cchCompare = cch; | ||
357 | *pInstallState = vpfnMsiLocateComponentW(wzComponentId, *psczValue, &cch); | ||
358 | } | ||
359 | |||
360 | if (INSTALLSTATE_INVALIDARG == *pInstallState) | ||
361 | { | ||
362 | hr = E_INVALIDARG; | ||
363 | WiuExitOnRootFailure(hr, "Invalid argument when locating component."); | ||
364 | } | ||
365 | else if (INSTALLSTATE_UNKNOWN == *pInstallState) | ||
366 | { | ||
367 | ExitFunction(); | ||
368 | } | ||
369 | |||
370 | // If the actual path length is greater than or equal to the original buffer | ||
371 | // allocate a larger buffer and get the path again, just in case we are | ||
372 | // missing any part of the path. | ||
373 | if (cchCompare <= cch) | ||
374 | { | ||
375 | ++cch; | ||
376 | hr = StrAlloc(psczValue, cch); | ||
377 | WiuExitOnFailure(hr, "Failed to reallocate string for component path."); | ||
378 | |||
379 | *pInstallState = vpfnMsiLocateComponentW(wzComponentId, *psczValue, &cch); | ||
380 | } | ||
381 | |||
382 | LExit: | ||
383 | return hr; | ||
384 | } | ||
385 | |||
386 | |||
387 | extern "C" HRESULT DAPI WiuQueryFeatureState( | ||
388 | __in_z LPCWSTR wzProduct, | ||
389 | __in_z LPCWSTR wzFeature, | ||
390 | __out INSTALLSTATE* pInstallState | ||
391 | ) | ||
392 | { | ||
393 | HRESULT hr = S_OK; | ||
394 | |||
395 | *pInstallState = vpfnMsiQueryFeatureStateW(wzProduct, wzFeature); | ||
396 | if (INSTALLSTATE_INVALIDARG == *pInstallState) | ||
397 | { | ||
398 | hr = E_INVALIDARG; | ||
399 | WiuExitOnRootFailure(hr, "Failed to query state of feature: %ls in product: %ls", wzFeature, wzProduct); | ||
400 | } | ||
401 | |||
402 | LExit: | ||
403 | return hr; | ||
404 | } | ||
405 | |||
406 | |||
407 | extern "C" HRESULT DAPI WiuGetProductInfo( | ||
408 | __in_z LPCWSTR wzProductCode, | ||
409 | __in_z LPCWSTR wzProperty, | ||
410 | __out LPWSTR* psczValue | ||
411 | ) | ||
412 | { | ||
413 | HRESULT hr = S_OK; | ||
414 | UINT er = ERROR_SUCCESS; | ||
415 | DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH; | ||
416 | |||
417 | hr = StrAlloc(psczValue, cch); | ||
418 | WiuExitOnFailure(hr, "Failed to allocate string for product info."); | ||
419 | |||
420 | er = vpfnMsiGetProductInfoW(wzProductCode, wzProperty, *psczValue, &cch); | ||
421 | if (ERROR_MORE_DATA == er) | ||
422 | { | ||
423 | ++cch; | ||
424 | hr = StrAlloc(psczValue, cch); | ||
425 | WiuExitOnFailure(hr, "Failed to reallocate string for product info."); | ||
426 | |||
427 | er = vpfnMsiGetProductInfoW(wzProductCode, wzProperty, *psczValue, &cch); | ||
428 | } | ||
429 | WiuExitOnWin32Error(er, hr, "Failed to get product info."); | ||
430 | |||
431 | LExit: | ||
432 | return hr; | ||
433 | } | ||
434 | |||
435 | |||
436 | extern "C" HRESULT DAPI WiuGetProductInfoEx( | ||
437 | __in_z LPCWSTR wzProductCode, | ||
438 | __in_z_opt LPCWSTR wzUserSid, | ||
439 | __in MSIINSTALLCONTEXT dwContext, | ||
440 | __in_z LPCWSTR wzProperty, | ||
441 | __out LPWSTR* psczValue | ||
442 | ) | ||
443 | { | ||
444 | HRESULT hr = S_OK; | ||
445 | UINT er = ERROR_SUCCESS; | ||
446 | DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH; | ||
447 | |||
448 | if (!vpfnMsiGetProductInfoExW) | ||
449 | { | ||
450 | hr = WiuGetProductInfo(wzProductCode, wzProperty, psczValue); | ||
451 | WiuExitOnFailure(hr, "Failed to get product info when extended info was not available."); | ||
452 | |||
453 | ExitFunction(); | ||
454 | } | ||
455 | |||
456 | hr = StrAlloc(psczValue, cch); | ||
457 | WiuExitOnFailure(hr, "Failed to allocate string for extended product info."); | ||
458 | |||
459 | er = vpfnMsiGetProductInfoExW(wzProductCode, wzUserSid, dwContext, wzProperty, *psczValue, &cch); | ||
460 | if (ERROR_MORE_DATA == er) | ||
461 | { | ||
462 | ++cch; | ||
463 | hr = StrAlloc(psczValue, cch); | ||
464 | WiuExitOnFailure(hr, "Failed to reallocate string for extended product info."); | ||
465 | |||
466 | er = vpfnMsiGetProductInfoExW(wzProductCode, wzUserSid, dwContext, wzProperty, *psczValue, &cch); | ||
467 | } | ||
468 | WiuExitOnWin32Error(er, hr, "Failed to get extended product info."); | ||
469 | |||
470 | LExit: | ||
471 | return hr; | ||
472 | } | ||
473 | |||
474 | |||
475 | extern "C" HRESULT DAPI WiuGetProductProperty( | ||
476 | __in MSIHANDLE hProduct, | ||
477 | __in_z LPCWSTR wzProperty, | ||
478 | __out LPWSTR* psczValue | ||
479 | ) | ||
480 | { | ||
481 | HRESULT hr = S_OK; | ||
482 | UINT er = ERROR_SUCCESS; | ||
483 | DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH; | ||
484 | |||
485 | hr = StrAlloc(psczValue, cch); | ||
486 | WiuExitOnFailure(hr, "Failed to allocate string for product property."); | ||
487 | |||
488 | er = ::MsiGetProductPropertyW(hProduct, wzProperty, *psczValue, &cch); | ||
489 | if (ERROR_MORE_DATA == er) | ||
490 | { | ||
491 | ++cch; | ||
492 | hr = StrAlloc(psczValue, cch); | ||
493 | WiuExitOnFailure(hr, "Failed to reallocate string for product property."); | ||
494 | |||
495 | er = ::MsiGetProductPropertyW(hProduct, wzProperty, *psczValue, &cch); | ||
496 | } | ||
497 | WiuExitOnWin32Error(er, hr, "Failed to get product property."); | ||
498 | |||
499 | LExit: | ||
500 | return hr; | ||
501 | } | ||
502 | |||
503 | |||
504 | extern "C" HRESULT DAPI WiuGetPatchInfoEx( | ||
505 | __in_z LPCWSTR wzPatchCode, | ||
506 | __in_z LPCWSTR wzProductCode, | ||
507 | __in_z_opt LPCWSTR wzUserSid, | ||
508 | __in MSIINSTALLCONTEXT dwContext, | ||
509 | __in_z LPCWSTR wzProperty, | ||
510 | __out LPWSTR* psczValue | ||
511 | ) | ||
512 | { | ||
513 | HRESULT hr = S_OK; | ||
514 | UINT er = ERROR_SUCCESS; | ||
515 | DWORD cch = WIU_GOOD_ENOUGH_PROPERTY_LENGTH; | ||
516 | |||
517 | if (!vpfnMsiGetPatchInfoExW) | ||
518 | { | ||
519 | ExitFunction1(hr = E_NOTIMPL); | ||
520 | } | ||
521 | |||
522 | hr = StrAlloc(psczValue, cch); | ||
523 | WiuExitOnFailure(hr, "Failed to allocate string for extended patch info."); | ||
524 | |||
525 | er = vpfnMsiGetPatchInfoExW(wzPatchCode, wzProductCode, wzUserSid, dwContext, wzProperty, *psczValue, &cch); | ||
526 | if (ERROR_MORE_DATA == er) | ||
527 | { | ||
528 | ++cch; | ||
529 | hr = StrAlloc(psczValue, cch); | ||
530 | WiuExitOnFailure(hr, "Failed to reallocate string for extended patch info."); | ||
531 | |||
532 | er = vpfnMsiGetPatchInfoExW(wzPatchCode, wzProductCode, wzUserSid, dwContext, wzProperty, *psczValue, &cch); | ||
533 | } | ||
534 | WiuExitOnWin32Error(er, hr, "Failed to get extended patch info."); | ||
535 | |||
536 | LExit: | ||
537 | return hr; | ||
538 | } | ||
539 | |||
540 | |||
541 | extern "C" HRESULT DAPI WiuDeterminePatchSequence( | ||
542 | __in_z LPCWSTR wzProductCode, | ||
543 | __in_z_opt LPCWSTR wzUserSid, | ||
544 | __in MSIINSTALLCONTEXT context, | ||
545 | __in PMSIPATCHSEQUENCEINFOW pPatchInfo, | ||
546 | __in DWORD cPatchInfo | ||
547 | ) | ||
548 | { | ||
549 | HRESULT hr = S_OK; | ||
550 | DWORD er = ERROR_SUCCESS; | ||
551 | |||
552 | if (!vpfnMsiDeterminePatchSequenceW) | ||
553 | { | ||
554 | ExitFunction1(hr = E_NOTIMPL); | ||
555 | } | ||
556 | |||
557 | er = vpfnMsiDeterminePatchSequenceW(wzProductCode, wzUserSid, context, cPatchInfo, pPatchInfo); | ||
558 | WiuExitOnWin32Error(er, hr, "Failed to determine patch sequence for product code."); | ||
559 | |||
560 | LExit: | ||
561 | return hr; | ||
562 | } | ||
563 | |||
564 | |||
565 | extern "C" HRESULT DAPI WiuDetermineApplicablePatches( | ||
566 | __in_z LPCWSTR wzProductPackagePath, | ||
567 | __in PMSIPATCHSEQUENCEINFOW pPatchInfo, | ||
568 | __in DWORD cPatchInfo | ||
569 | ) | ||
570 | { | ||
571 | HRESULT hr = S_OK; | ||
572 | DWORD er = ERROR_SUCCESS; | ||
573 | |||
574 | if (!vpfnMsiDetermineApplicablePatchesW) | ||
575 | { | ||
576 | ExitFunction1(hr = E_NOTIMPL); | ||
577 | } | ||
578 | |||
579 | er = vpfnMsiDetermineApplicablePatchesW(wzProductPackagePath, cPatchInfo, pPatchInfo); | ||
580 | WiuExitOnWin32Error(er, hr, "Failed to determine applicable patches for product package."); | ||
581 | |||
582 | LExit: | ||
583 | return hr; | ||
584 | } | ||
585 | |||
586 | |||
587 | extern "C" HRESULT DAPI WiuEnumProducts( | ||
588 | __in DWORD iProductIndex, | ||
589 | __out_ecount(MAX_GUID_CHARS + 1) LPWSTR wzProductCode | ||
590 | ) | ||
591 | { | ||
592 | HRESULT hr = S_OK; | ||
593 | DWORD er = ERROR_SUCCESS; | ||
594 | |||
595 | er = vpfnMsiEnumProductsW(iProductIndex, wzProductCode); | ||
596 | if (ERROR_NO_MORE_ITEMS == er) | ||
597 | { | ||
598 | ExitFunction1(hr = HRESULT_FROM_WIN32(er)); | ||
599 | } | ||
600 | WiuExitOnWin32Error(er, hr, "Failed to enumerate products."); | ||
601 | |||
602 | LExit: | ||
603 | return hr; | ||
604 | } | ||
605 | |||
606 | |||
607 | extern "C" HRESULT DAPI WiuEnumProductsEx( | ||
608 | __in_z_opt LPCWSTR wzProductCode, | ||
609 | __in_z_opt LPCWSTR wzUserSid, | ||
610 | __in DWORD dwContext, | ||
611 | __in DWORD dwIndex, | ||
612 | __out_opt WCHAR wzInstalledProductCode[39], | ||
613 | __out_opt MSIINSTALLCONTEXT *pdwInstalledContext, | ||
614 | __out_opt LPWSTR wzSid, | ||
615 | __inout_opt LPDWORD pcchSid | ||
616 | ) | ||
617 | { | ||
618 | HRESULT hr = S_OK; | ||
619 | DWORD er = ERROR_SUCCESS; | ||
620 | |||
621 | if (!vpfnMsiEnumProductsExW) | ||
622 | { | ||
623 | ExitFunction1(hr = E_NOTIMPL); | ||
624 | } | ||
625 | |||
626 | er = vpfnMsiEnumProductsExW(wzProductCode, wzUserSid, dwContext, dwIndex, wzInstalledProductCode, pdwInstalledContext, wzSid, pcchSid); | ||
627 | if (ERROR_NO_MORE_ITEMS == er) | ||
628 | { | ||
629 | ExitFunction1(hr = HRESULT_FROM_WIN32(er)); | ||
630 | } | ||
631 | WiuExitOnWin32Error(er, hr, "Failed to enumerate products."); | ||
632 | |||
633 | LExit: | ||
634 | return hr; | ||
635 | } | ||
636 | |||
637 | |||
638 | extern "C" HRESULT DAPI WiuEnumRelatedProducts( | ||
639 | __in_z LPCWSTR wzUpgradeCode, | ||
640 | __in DWORD iProductIndex, | ||
641 | __out_ecount(MAX_GUID_CHARS + 1) LPWSTR wzProductCode | ||
642 | ) | ||
643 | { | ||
644 | HRESULT hr = S_OK; | ||
645 | DWORD er = ERROR_SUCCESS; | ||
646 | |||
647 | er = vpfnMsiEnumRelatedProductsW(wzUpgradeCode, 0, iProductIndex, wzProductCode); | ||
648 | if (ERROR_NO_MORE_ITEMS == er) | ||
649 | { | ||
650 | ExitFunction1(hr = HRESULT_FROM_WIN32(er)); | ||
651 | } | ||
652 | WiuExitOnWin32Error(er, hr, "Failed to enumerate related products for updgrade code: %ls", wzUpgradeCode); | ||
653 | |||
654 | LExit: | ||
655 | return hr; | ||
656 | } | ||
657 | |||
658 | /******************************************************************** | ||
659 | WiuEnumRelatedProductCodes - Returns an array of related products for a given upgrade code. | ||
660 | |||
661 | Parameters: | ||
662 | wzUpgradeCode - The upgrade code that will be used to find the related products. | ||
663 | prgsczProductCodes - Pointer to the array that will contain the product codes. | ||
664 | pcRelatedProducts - Returns the count of the number of related products found. | ||
665 | fReturnHighestVersionOnly - When set to "TRUE", will only return the product code of the highest version found. | ||
666 | ********************************************************************/ | ||
667 | extern "C" HRESULT DAPI WiuEnumRelatedProductCodes( | ||
668 | __in_z LPCWSTR wzUpgradeCode, | ||
669 | __deref_out_ecount_opt(*pcRelatedProducts) LPWSTR** prgsczProductCodes, | ||
670 | __out DWORD* pcRelatedProducts, | ||
671 | __in BOOL fReturnHighestVersionOnly | ||
672 | ) | ||
673 | { | ||
674 | HRESULT hr = S_OK; | ||
675 | WCHAR wzCurrentProductCode[MAX_GUID_CHARS + 1] = { }; | ||
676 | LPWSTR sczInstalledVersion = NULL; | ||
677 | VERUTIL_VERSION* pCurrentVersion = NULL; | ||
678 | VERUTIL_VERSION* pHighestVersion = NULL; | ||
679 | int nCompare = 0; | ||
680 | |||
681 | // make sure we start at zero | ||
682 | *pcRelatedProducts = 0; | ||
683 | |||
684 | for (DWORD i = 0; ; ++i) | ||
685 | { | ||
686 | hr = WiuEnumRelatedProducts(wzUpgradeCode, i, wzCurrentProductCode); | ||
687 | |||
688 | if (E_NOMOREITEMS == hr) | ||
689 | { | ||
690 | hr = S_OK; | ||
691 | break; | ||
692 | } | ||
693 | WiuExitOnFailure(hr, "Failed to enumerate related products for upgrade code: %ls", wzUpgradeCode); | ||
694 | |||
695 | if (fReturnHighestVersionOnly) | ||
696 | { | ||
697 | // try to get the version but if the product registration is broken | ||
698 | // (for whatever reason), skip this product | ||
699 | hr = WiuGetProductInfo(wzCurrentProductCode, L"VersionString", &sczInstalledVersion); | ||
700 | if (FAILED(hr)) | ||
701 | { | ||
702 | WiuExitTrace(hr, "Could not get product version for product code: %ls, skipping...", wzCurrentProductCode); | ||
703 | continue; | ||
704 | } | ||
705 | |||
706 | hr = VerParseVersion(sczInstalledVersion, 0, FALSE, &pCurrentVersion); | ||
707 | WiuExitOnFailure(hr, "Failed to parse version: %ls for product code: %ls", sczInstalledVersion, wzCurrentProductCode); | ||
708 | |||
709 | if (pCurrentVersion->fInvalid) | ||
710 | { | ||
711 | WiuExitTrace(E_INVALIDDATA, "Enumerated msi package with invalid version, product code: '%1!ls!', version: '%2!ls!'"); | ||
712 | } | ||
713 | |||
714 | // if this is the first product found then it is the highest version (for now) | ||
715 | if (!pHighestVersion) | ||
716 | { | ||
717 | pHighestVersion = pCurrentVersion; | ||
718 | pCurrentVersion = NULL; | ||
719 | } | ||
720 | else | ||
721 | { | ||
722 | hr = VerCompareParsedVersions(pCurrentVersion, pHighestVersion, &nCompare); | ||
723 | WiuExitOnFailure(hr, "Failed to compare version '%ls' to highest version: '%ls'", pCurrentVersion->sczVersion, pHighestVersion->sczVersion); | ||
724 | |||
725 | // if this is the highest version encountered so far then overwrite | ||
726 | // the first item in the array (there will never be more than one item) | ||
727 | if (nCompare > 0) | ||
728 | { | ||
729 | ReleaseVerutilVersion(pHighestVersion); | ||
730 | pHighestVersion = pCurrentVersion; | ||
731 | pCurrentVersion = NULL; | ||
732 | |||
733 | hr = StrAllocString(prgsczProductCodes[0], wzCurrentProductCode, 0); | ||
734 | WiuExitOnFailure(hr, "Failed to update array with higher versioned product code."); | ||
735 | } | ||
736 | else | ||
737 | { | ||
738 | ReleaseVerutilVersion(pCurrentVersion); | ||
739 | } | ||
740 | |||
741 | // continue here as we don't want anything else added to the list | ||
742 | continue; | ||
743 | } | ||
744 | } | ||
745 | |||
746 | hr = StrArrayAllocString(prgsczProductCodes, (LPUINT)(pcRelatedProducts), wzCurrentProductCode, 0); | ||
747 | WiuExitOnFailure(hr, "Failed to add product code to array."); | ||
748 | } | ||
749 | |||
750 | LExit: | ||
751 | ReleaseVerutilVersion(pCurrentVersion); | ||
752 | ReleaseVerutilVersion(pHighestVersion); | ||
753 | ReleaseStr(sczInstalledVersion); | ||
754 | return hr; | ||
755 | } | ||
756 | |||
757 | |||
758 | extern "C" HRESULT DAPI WiuEnableLog( | ||
759 | __in DWORD dwLogMode, | ||
760 | __in_z LPCWSTR wzLogFile, | ||
761 | __in DWORD dwLogAttributes | ||
762 | ) | ||
763 | { | ||
764 | HRESULT hr = S_OK; | ||
765 | DWORD er = ERROR_SUCCESS; | ||
766 | |||
767 | er = vpfnMsiEnableLogW(dwLogMode, wzLogFile, dwLogAttributes); | ||
768 | WiuExitOnWin32Error(er, hr, "Failed to enable MSI internal logging."); | ||
769 | |||
770 | LExit: | ||
771 | return hr; | ||
772 | } | ||
773 | |||
774 | |||
775 | extern "C" HRESULT DAPI WiuInitializeInternalUI( | ||
776 | __in INSTALLUILEVEL internalUILevel, | ||
777 | __in_opt HWND hwndParent, | ||
778 | __in WIU_MSI_EXECUTE_CONTEXT* pExecuteContext | ||
779 | ) | ||
780 | { | ||
781 | HRESULT hr = S_OK; | ||
782 | |||
783 | memset(pExecuteContext, 0, sizeof(WIU_MSI_EXECUTE_CONTEXT)); | ||
784 | |||
785 | pExecuteContext->previousInstallUILevel = vpfnMsiSetInternalUI(internalUILevel, &hwndParent); | ||
786 | pExecuteContext->hwndPreviousParentWindow = hwndParent; | ||
787 | |||
788 | if (INSTALLUILEVEL_NOCHANGE == pExecuteContext->previousInstallUILevel) | ||
789 | { | ||
790 | hr = E_INVALIDARG; | ||
791 | } | ||
792 | |||
793 | return hr; | ||
794 | } | ||
795 | |||
796 | |||
797 | extern "C" HRESULT DAPI WiuInitializeExternalUI( | ||
798 | __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, | ||
799 | __in INSTALLUILEVEL internalUILevel, | ||
800 | __in_opt HWND hwndParent, | ||
801 | __in LPVOID pvContext, | ||
802 | __in BOOL fRollback, | ||
803 | __in WIU_MSI_EXECUTE_CONTEXT* pExecuteContext | ||
804 | ) | ||
805 | { | ||
806 | HRESULT hr = S_OK; | ||
807 | DWORD er = ERROR_SUCCESS; | ||
808 | |||
809 | DWORD dwMessageFilter = INSTALLLOGMODE_INITIALIZE | INSTALLLOGMODE_TERMINATE | | ||
810 | INSTALLLOGMODE_FATALEXIT | INSTALLLOGMODE_ERROR | INSTALLLOGMODE_WARNING | | ||
811 | INSTALLLOGMODE_RESOLVESOURCE | INSTALLLOGMODE_OUTOFDISKSPACE | | ||
812 | INSTALLLOGMODE_ACTIONSTART | INSTALLLOGMODE_ACTIONDATA | INSTALLLOGMODE_COMMONDATA | | ||
813 | INSTALLLOGMODE_PROGRESS | INSTALLLOGMODE_FILESINUSE; | ||
814 | |||
815 | if (MAKEDWORD(0, 4) <= vdwMsiDllMajorMinor) | ||
816 | { | ||
817 | dwMessageFilter |= INSTALLLOGMODE_RMFILESINUSE; | ||
818 | } | ||
819 | |||
820 | // Wire the internal and external UI handler. | ||
821 | hr = WiuInitializeInternalUI(internalUILevel, hwndParent, pExecuteContext); | ||
822 | WiuExitOnFailure(hr, "Failed to set internal UI level and window."); | ||
823 | |||
824 | pExecuteContext->fRollback = fRollback; | ||
825 | pExecuteContext->pfnMessageHandler = pfnMessageHandler; | ||
826 | pExecuteContext->pvContext = pvContext; | ||
827 | |||
828 | // If the external UI record is available (MSI version >= 3.1) use it but fall back to the standard external | ||
829 | // UI handler if necesary. | ||
830 | if (vpfnMsiSetExternalUIRecord) | ||
831 | { | ||
832 | er = vpfnMsiSetExternalUIRecord(InstallEngineRecordCallback, dwMessageFilter, pExecuteContext, &pExecuteContext->pfnPreviousExternalUIRecord); | ||
833 | WiuExitOnWin32Error(er, hr, "Failed to wire up external UI record handler."); | ||
834 | pExecuteContext->fSetPreviousExternalUIRecord = TRUE; | ||
835 | } | ||
836 | else | ||
837 | { | ||
838 | pExecuteContext->pfnPreviousExternalUI = vpfnMsiSetExternalUIW(InstallEngineCallback, dwMessageFilter, pExecuteContext); | ||
839 | pExecuteContext->fSetPreviousExternalUI = TRUE; | ||
840 | } | ||
841 | |||
842 | LExit: | ||
843 | return hr; | ||
844 | } | ||
845 | |||
846 | |||
847 | extern "C" void DAPI WiuUninitializeExternalUI( | ||
848 | __in WIU_MSI_EXECUTE_CONTEXT* pExecuteContext | ||
849 | ) | ||
850 | { | ||
851 | if (INSTALLUILEVEL_NOCHANGE != pExecuteContext->previousInstallUILevel) | ||
852 | { | ||
853 | pExecuteContext->previousInstallUILevel = vpfnMsiSetInternalUI(pExecuteContext->previousInstallUILevel, &pExecuteContext->hwndPreviousParentWindow); | ||
854 | } | ||
855 | |||
856 | if (pExecuteContext->fSetPreviousExternalUI) // unset the UI handler | ||
857 | { | ||
858 | vpfnMsiSetExternalUIW(pExecuteContext->pfnPreviousExternalUI, 0, NULL); | ||
859 | } | ||
860 | |||
861 | if (pExecuteContext->fSetPreviousExternalUIRecord) // unset the UI record handler | ||
862 | { | ||
863 | vpfnMsiSetExternalUIRecord(pExecuteContext->pfnPreviousExternalUIRecord, 0, NULL, NULL); | ||
864 | } | ||
865 | |||
866 | memset(pExecuteContext, 0, sizeof(WIU_MSI_EXECUTE_CONTEXT)); | ||
867 | } | ||
868 | |||
869 | |||
870 | extern "C" HRESULT DAPI WiuConfigureProductEx( | ||
871 | __in_z LPCWSTR wzProduct, | ||
872 | __in int iInstallLevel, | ||
873 | __in INSTALLSTATE eInstallState, | ||
874 | __in_z LPCWSTR wzCommandLine, | ||
875 | __out WIU_RESTART* pRestart | ||
876 | ) | ||
877 | { | ||
878 | HRESULT hr = S_OK; | ||
879 | DWORD er = ERROR_SUCCESS; | ||
880 | |||
881 | er = vpfnMsiConfigureProductExW(wzProduct, iInstallLevel, eInstallState, wzCommandLine); | ||
882 | er = CheckForRestartErrorCode(er, pRestart); | ||
883 | WiuExitOnWin32Error(er, hr, "Failed to configure product: %ls", wzProduct); | ||
884 | |||
885 | LExit: | ||
886 | return hr; | ||
887 | } | ||
888 | |||
889 | |||
890 | extern "C" HRESULT DAPI WiuInstallProduct( | ||
891 | __in_z LPCWSTR wzPackagePath, | ||
892 | __in_z LPCWSTR wzCommandLine, | ||
893 | __out WIU_RESTART* pRestart | ||
894 | ) | ||
895 | { | ||
896 | HRESULT hr = S_OK; | ||
897 | DWORD er = ERROR_SUCCESS; | ||
898 | |||
899 | er = vpfnMsiInstallProductW(wzPackagePath, wzCommandLine); | ||
900 | er = CheckForRestartErrorCode(er, pRestart); | ||
901 | WiuExitOnWin32Error(er, hr, "Failed to install product: %ls", wzPackagePath); | ||
902 | |||
903 | LExit: | ||
904 | return hr; | ||
905 | } | ||
906 | |||
907 | |||
908 | extern "C" HRESULT DAPI WiuRemovePatches( | ||
909 | __in_z LPCWSTR wzPatchList, | ||
910 | __in_z LPCWSTR wzProductCode, | ||
911 | __in_z LPCWSTR wzPropertyList, | ||
912 | __out WIU_RESTART* pRestart | ||
913 | ) | ||
914 | { | ||
915 | HRESULT hr = S_OK; | ||
916 | DWORD er = ERROR_SUCCESS; | ||
917 | |||
918 | er = vpfnMsiRemovePatchesW(wzPatchList, wzProductCode, INSTALLTYPE_SINGLE_INSTANCE, wzPropertyList); | ||
919 | er = CheckForRestartErrorCode(er, pRestart); | ||
920 | WiuExitOnWin32Error(er, hr, "Failed to remove patches."); | ||
921 | |||
922 | LExit: | ||
923 | return hr; | ||
924 | } | ||
925 | |||
926 | |||
927 | extern "C" HRESULT DAPI WiuSourceListAddSourceEx( | ||
928 | __in_z LPCWSTR wzProductCodeOrPatchCode, | ||
929 | __in_z_opt LPCWSTR wzUserSid, | ||
930 | __in MSIINSTALLCONTEXT dwContext, | ||
931 | __in DWORD dwCode, | ||
932 | __in_z LPCWSTR wzSource, | ||
933 | __in_opt DWORD dwIndex | ||
934 | ) | ||
935 | { | ||
936 | HRESULT hr = S_OK; | ||
937 | DWORD er = ERROR_SUCCESS; | ||
938 | |||
939 | er = vpfnMsiSourceListAddSourceExW(wzProductCodeOrPatchCode, wzUserSid, dwContext, MSISOURCETYPE_NETWORK | dwCode, wzSource, dwIndex); | ||
940 | WiuExitOnWin32Error(er, hr, "Failed to add source."); | ||
941 | |||
942 | LExit: | ||
943 | return hr; | ||
944 | } | ||
945 | |||
946 | extern "C" BOOL DAPI WiuIsMsiTransactionSupported( | ||
947 | ) | ||
948 | { | ||
949 | return vpfnMsiBeginTransaction && vpfnMsiEndTransaction; | ||
950 | } | ||
951 | |||
952 | extern "C" HRESULT DAPI WiuBeginTransaction( | ||
953 | __in_z LPCWSTR szName, | ||
954 | __in DWORD dwTransactionAttributes, | ||
955 | __out MSIHANDLE * phTransactionHandle, | ||
956 | __out HANDLE * phChangeOfOwnerEvent, | ||
957 | __in DWORD dwLogMode, | ||
958 | __in_z LPCWSTR szLogPath | ||
959 | ) | ||
960 | { | ||
961 | HRESULT hr = S_OK; | ||
962 | DWORD er = ERROR_SUCCESS; | ||
963 | |||
964 | if (!WiuIsMsiTransactionSupported()) | ||
965 | { | ||
966 | WiuExitOnFailure(hr = E_NOTIMPL, "Msi transactions are not supported"); | ||
967 | } | ||
968 | |||
969 | hr = WiuEnableLog(dwLogMode, szLogPath, INSTALLLOGATTRIBUTES_APPEND); | ||
970 | WiuExitOnFailure(hr, "Failed to enable logging for MSI transaction"); | ||
971 | |||
972 | er = vpfnMsiBeginTransaction(szName, dwTransactionAttributes, phTransactionHandle, phChangeOfOwnerEvent); | ||
973 | WiuExitOnWin32Error(er, hr, "Failed to begin transaction."); | ||
974 | |||
975 | LExit: | ||
976 | return hr; | ||
977 | } | ||
978 | |||
979 | extern "C" HRESULT DAPI WiuEndTransaction( | ||
980 | __in DWORD dwTransactionState, | ||
981 | __in DWORD dwLogMode, | ||
982 | __in_z LPCWSTR szLogPath | ||
983 | ) | ||
984 | { | ||
985 | HRESULT hr = S_OK; | ||
986 | DWORD er = ERROR_SUCCESS; | ||
987 | |||
988 | if (!WiuIsMsiTransactionSupported()) | ||
989 | { | ||
990 | WiuExitOnFailure(hr = E_NOTIMPL, "Msi transactions are not supported"); | ||
991 | } | ||
992 | |||
993 | hr = WiuEnableLog(dwLogMode, szLogPath, INSTALLLOGATTRIBUTES_APPEND); | ||
994 | WiuExitOnFailure(hr, "Failed to enable logging for MSI transaction"); | ||
995 | |||
996 | er = vpfnMsiEndTransaction(dwTransactionState); | ||
997 | WiuExitOnWin32Error(er, hr, "Failed to end transaction."); | ||
998 | |||
999 | LExit: | ||
1000 | return hr; | ||
1001 | } | ||
1002 | |||
1003 | |||
1004 | |||
1005 | static DWORD CheckForRestartErrorCode( | ||
1006 | __in DWORD dwErrorCode, | ||
1007 | __out WIU_RESTART* pRestart | ||
1008 | ) | ||
1009 | { | ||
1010 | switch (dwErrorCode) | ||
1011 | { | ||
1012 | case ERROR_SUCCESS_REBOOT_REQUIRED: | ||
1013 | case ERROR_SUCCESS_RESTART_REQUIRED: | ||
1014 | *pRestart = WIU_RESTART_REQUIRED; | ||
1015 | dwErrorCode = ERROR_SUCCESS; | ||
1016 | break; | ||
1017 | |||
1018 | case ERROR_SUCCESS_REBOOT_INITIATED: | ||
1019 | case ERROR_INSTALL_SUSPEND: | ||
1020 | *pRestart = WIU_RESTART_INITIATED; | ||
1021 | dwErrorCode = ERROR_SUCCESS; | ||
1022 | break; | ||
1023 | } | ||
1024 | |||
1025 | return dwErrorCode; | ||
1026 | } | ||
1027 | |||
1028 | static INT CALLBACK InstallEngineCallback( | ||
1029 | __in LPVOID pvContext, | ||
1030 | __in UINT uiMessage, | ||
1031 | __in_z_opt LPCWSTR wzMessage | ||
1032 | ) | ||
1033 | { | ||
1034 | INT nResult = IDNOACTION; | ||
1035 | WIU_MSI_EXECUTE_CONTEXT* pContext = (WIU_MSI_EXECUTE_CONTEXT*)pvContext; | ||
1036 | INSTALLMESSAGE mt = static_cast<INSTALLMESSAGE>(0xFF000000 & uiMessage); | ||
1037 | UINT uiFlags = 0x00FFFFFF & uiMessage; | ||
1038 | |||
1039 | if (wzMessage) | ||
1040 | { | ||
1041 | if (INSTALLMESSAGE_PROGRESS == mt) | ||
1042 | { | ||
1043 | nResult = HandleInstallProgress(pContext, wzMessage, NULL); | ||
1044 | } | ||
1045 | else | ||
1046 | { | ||
1047 | nResult = HandleInstallMessage(pContext, mt, uiFlags, wzMessage, NULL); | ||
1048 | } | ||
1049 | } | ||
1050 | |||
1051 | return nResult; | ||
1052 | } | ||
1053 | |||
1054 | static INT CALLBACK InstallEngineRecordCallback( | ||
1055 | __in LPVOID pvContext, | ||
1056 | __in UINT uiMessage, | ||
1057 | __in_opt MSIHANDLE hRecord | ||
1058 | ) | ||
1059 | { | ||
1060 | INT nResult = IDNOACTION; | ||
1061 | HRESULT hr = S_OK; | ||
1062 | WIU_MSI_EXECUTE_CONTEXT* pContext = (WIU_MSI_EXECUTE_CONTEXT*)pvContext; | ||
1063 | |||
1064 | INSTALLMESSAGE mt = static_cast<INSTALLMESSAGE>(0xFF000000 & uiMessage); | ||
1065 | UINT uiFlags = 0x00FFFFFF & uiMessage; | ||
1066 | LPWSTR sczMessage = NULL; | ||
1067 | DWORD cchMessage = 0; | ||
1068 | |||
1069 | if (hRecord) | ||
1070 | { | ||
1071 | if (INSTALLMESSAGE_PROGRESS == mt) | ||
1072 | { | ||
1073 | nResult = HandleInstallProgress(pContext, NULL, hRecord); | ||
1074 | } | ||
1075 | else | ||
1076 | { | ||
1077 | // create formated message string | ||
1078 | #pragma prefast(push) | ||
1079 | #pragma prefast(disable:6298) // docs explicitly say this is a valid option for getting the buffer size | ||
1080 | DWORD er = ::MsiFormatRecordW(NULL, hRecord, L"", &cchMessage); | ||
1081 | #pragma prefast(pop) | ||
1082 | if (ERROR_MORE_DATA == er || ERROR_SUCCESS == er) | ||
1083 | { | ||
1084 | hr = StrAlloc(&sczMessage, ++cchMessage); | ||
1085 | } | ||
1086 | else | ||
1087 | { | ||
1088 | hr = HRESULT_FROM_WIN32(er); | ||
1089 | } | ||
1090 | WiuExitOnFailure(hr, "Failed to allocate string for formated message."); | ||
1091 | |||
1092 | er = ::MsiFormatRecordW(NULL, hRecord, sczMessage, &cchMessage); | ||
1093 | WiuExitOnWin32Error(er, hr, "Failed to format message record."); | ||
1094 | |||
1095 | // Pass to handler including both the formated message and the original record. | ||
1096 | nResult = HandleInstallMessage(pContext, mt, uiFlags, sczMessage, hRecord); | ||
1097 | } | ||
1098 | } | ||
1099 | |||
1100 | LExit: | ||
1101 | ReleaseStr(sczMessage); | ||
1102 | return nResult; | ||
1103 | } | ||
1104 | |||
1105 | static INT HandleInstallMessage( | ||
1106 | __in WIU_MSI_EXECUTE_CONTEXT* pContext, | ||
1107 | __in INSTALLMESSAGE mt, | ||
1108 | __in UINT uiFlags, | ||
1109 | __in_z LPCWSTR wzMessage, | ||
1110 | __in_opt MSIHANDLE hRecord | ||
1111 | ) | ||
1112 | { | ||
1113 | INT nResult = IDNOACTION; | ||
1114 | |||
1115 | Trace(REPORT_STANDARD, "MSI install[%x]: %ls", pContext->dwCurrentProgressIndex, wzMessage); | ||
1116 | |||
1117 | // Handle the message. | ||
1118 | switch (mt) | ||
1119 | { | ||
1120 | case INSTALLMESSAGE_INITIALIZE: // this message is received prior to internal UI initialization, no string data | ||
1121 | ResetProgress(pContext); | ||
1122 | break; | ||
1123 | |||
1124 | case INSTALLMESSAGE_TERMINATE: // sent after UI termination, no string data | ||
1125 | break; | ||
1126 | |||
1127 | case INSTALLMESSAGE_ACTIONSTART: | ||
1128 | if (WIU_MSI_PROGRESS_INVALID != pContext->dwCurrentProgressIndex && pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData) | ||
1129 | { | ||
1130 | pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData = FALSE; | ||
1131 | } | ||
1132 | |||
1133 | nResult = SendMsiMessage(pContext, mt, uiFlags, wzMessage, hRecord); | ||
1134 | break; | ||
1135 | |||
1136 | case INSTALLMESSAGE_ACTIONDATA: | ||
1137 | if (WIU_MSI_PROGRESS_INVALID != pContext->dwCurrentProgressIndex && pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData) | ||
1138 | { | ||
1139 | if (pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fMoveForward) | ||
1140 | { | ||
1141 | pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted += pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwStep; | ||
1142 | } | ||
1143 | else // rollback. | ||
1144 | { | ||
1145 | pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted -= pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwStep; | ||
1146 | } | ||
1147 | |||
1148 | nResult = SendProgressUpdate(pContext); | ||
1149 | } | ||
1150 | else | ||
1151 | { | ||
1152 | nResult = SendMsiMessage(pContext, mt, uiFlags, wzMessage, hRecord); | ||
1153 | } | ||
1154 | break; | ||
1155 | |||
1156 | case INSTALLMESSAGE_OUTOFDISKSPACE: __fallthrough; | ||
1157 | case INSTALLMESSAGE_FATALEXIT: __fallthrough; | ||
1158 | case INSTALLMESSAGE_ERROR: | ||
1159 | nResult = SendErrorMessage(pContext, uiFlags, wzMessage, hRecord); | ||
1160 | break; | ||
1161 | |||
1162 | case INSTALLMESSAGE_FILESINUSE: | ||
1163 | case INSTALLMESSAGE_RMFILESINUSE: | ||
1164 | nResult = SendFilesInUseMessage(pContext, hRecord, INSTALLMESSAGE_RMFILESINUSE == mt); | ||
1165 | break; | ||
1166 | |||
1167 | /* | ||
1168 | #if 0 | ||
1169 | case INSTALLMESSAGE_COMMONDATA: | ||
1170 | if (L'1' == wzMessage[0] && L':' == wzMessage[1] && L' ' == wzMessage[2]) | ||
1171 | { | ||
1172 | if (L'0' == wzMessage[3]) | ||
1173 | { | ||
1174 | // TODO: handle the language common data message. | ||
1175 | lres = IDOK; | ||
1176 | return lres; | ||
1177 | } | ||
1178 | else if (L'1' == wzMessage[3]) | ||
1179 | { | ||
1180 | // TODO: really handle sending the caption. | ||
1181 | lres = ::SendSuxMessage(pInstallContext->pSetupUXInformation, SRM_EXEC_SET_CAPTION, uiFlags, reinterpret_cast<LPARAM>(wzMessage + 3)); | ||
1182 | return lres; | ||
1183 | } | ||
1184 | else if (L'2' == wzMessage[3]) | ||
1185 | { | ||
1186 | // TODO: really handle sending the cancel button status. | ||
1187 | lres = ::SendSuxMessage(pInstallContext->pSetupUXInformation, SRM_EXEC_SET_CANCEL, uiFlags, reinterpret_cast<LPARAM>(wzMessage + 3)); | ||
1188 | return lres; | ||
1189 | } | ||
1190 | } | ||
1191 | break; | ||
1192 | #endif | ||
1193 | */ | ||
1194 | |||
1195 | //case INSTALLMESSAGE_WARNING: | ||
1196 | //case INSTALLMESSAGE_USER: | ||
1197 | //case INSTALLMESSAGE_INFO: | ||
1198 | //case INSTALLMESSAGE_SHOWDIALOG: // sent prior to display of authored dialog or wizard | ||
1199 | default: | ||
1200 | nResult = SendMsiMessage(pContext, mt, uiFlags, wzMessage, hRecord); | ||
1201 | break; | ||
1202 | } | ||
1203 | |||
1204 | // Always return "no action" (0) for resolve source messages. | ||
1205 | return (INSTALLMESSAGE_RESOLVESOURCE == mt) ? IDNOACTION : nResult; | ||
1206 | } | ||
1207 | |||
1208 | static INT HandleInstallProgress( | ||
1209 | __in WIU_MSI_EXECUTE_CONTEXT* pContext, | ||
1210 | __in_z_opt LPCWSTR wzMessage, | ||
1211 | __in_opt MSIHANDLE hRecord | ||
1212 | ) | ||
1213 | { | ||
1214 | HRESULT hr = S_OK; | ||
1215 | INT nResult = IDNOACTION; | ||
1216 | INT iFields[4] = { }; | ||
1217 | INT cFields = 0; | ||
1218 | LPCWSTR pwz = NULL; | ||
1219 | DWORD cch = 0; | ||
1220 | |||
1221 | // get field values | ||
1222 | if (hRecord) | ||
1223 | { | ||
1224 | cFields = ::MsiRecordGetFieldCount(hRecord); | ||
1225 | cFields = min(cFields, countof(iFields)); // avoid buffer overrun if there are more fields than our buffer can hold | ||
1226 | for (INT i = 0; i < cFields; ++i) | ||
1227 | { | ||
1228 | iFields[i] = ::MsiRecordGetInteger(hRecord, i + 1); | ||
1229 | } | ||
1230 | } | ||
1231 | else | ||
1232 | { | ||
1233 | Assert(wzMessage); | ||
1234 | |||
1235 | // parse message string | ||
1236 | pwz = wzMessage; | ||
1237 | while (cFields < 4) | ||
1238 | { | ||
1239 | // check if we have the start of a valid part | ||
1240 | if ((L'1' + cFields) != pwz[0] || L':' != pwz[1] || L' ' != pwz[2]) | ||
1241 | { | ||
1242 | break; | ||
1243 | } | ||
1244 | pwz += 3; | ||
1245 | |||
1246 | // find character count of number | ||
1247 | cch = 0; | ||
1248 | while (pwz[cch] && L' ' != pwz[cch]) | ||
1249 | { | ||
1250 | ++cch; | ||
1251 | } | ||
1252 | |||
1253 | // parse number | ||
1254 | hr = StrStringToInt32(pwz, cch, &iFields[cFields]); | ||
1255 | WiuExitOnFailure(hr, "Failed to parse MSI message part."); | ||
1256 | |||
1257 | // increment field count | ||
1258 | ++cFields; | ||
1259 | } | ||
1260 | } | ||
1261 | |||
1262 | #ifdef _DEBUG | ||
1263 | WCHAR wz[256]; | ||
1264 | ::StringCchPrintfW(wz, countof(wz), L"1: %d 2: %d 3: %d 4: %d", iFields[0], iFields[1], iFields[2], iFields[3]); | ||
1265 | Trace(REPORT_STANDARD, "MSI progress[%x]: %ls", pContext->dwCurrentProgressIndex, wz); | ||
1266 | #endif | ||
1267 | |||
1268 | // Verify that we have the enough field values. | ||
1269 | if (1 > cFields) | ||
1270 | { | ||
1271 | ExitFunction(); // unknown message, bail | ||
1272 | } | ||
1273 | |||
1274 | // Handle based on message type. | ||
1275 | switch (iFields[0]) | ||
1276 | { | ||
1277 | case 0: // master progress reset | ||
1278 | if (4 > cFields) | ||
1279 | { | ||
1280 | Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - Invalid field count %d, '%ls'", cFields, wzMessage); | ||
1281 | ExitFunction(); | ||
1282 | } | ||
1283 | //Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - MASTER RESET - %d, %d, %d", iFields[1], iFields[2], iFields[3]); | ||
1284 | |||
1285 | // Update the index into progress array. | ||
1286 | if (WIU_MSI_PROGRESS_INVALID == pContext->dwCurrentProgressIndex) | ||
1287 | { | ||
1288 | pContext->dwCurrentProgressIndex = 0; | ||
1289 | } | ||
1290 | else if (pContext->dwCurrentProgressIndex + 1 < countof(pContext->rgMsiProgress)) | ||
1291 | { | ||
1292 | ++pContext->dwCurrentProgressIndex; | ||
1293 | } | ||
1294 | else | ||
1295 | { | ||
1296 | hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); | ||
1297 | WiuExitOnRootFailure(hr, "Insufficient space to hold progress information."); | ||
1298 | } | ||
1299 | |||
1300 | // we only care about the first stage after script execution has started | ||
1301 | //if (!pEngineInfo->fMsiProgressScriptInProgress && 1 != iFields[3]) | ||
1302 | //{ | ||
1303 | // pEngineInfo->fMsiProgressFinished = TRUE; | ||
1304 | //} | ||
1305 | |||
1306 | pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwTotal = iFields[1]; | ||
1307 | pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted = 0 == iFields[2] ? 0 : iFields[1]; // if forward start at 0, if backwards start at max | ||
1308 | pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fMoveForward = (0 == iFields[2]); | ||
1309 | pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData = FALSE; | ||
1310 | pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fScriptInProgress = (1 == iFields[3]); | ||
1311 | |||
1312 | if (0 == pContext->dwCurrentProgressIndex) | ||
1313 | { | ||
1314 | // HACK!!! this is a hack courtesy of the Windows Installer team. It seems the script planning phase | ||
1315 | // is always off by "about 50". So we'll toss an extra 50 ticks on so that the standard progress | ||
1316 | // doesn't go over 100%. If there are any custom actions, they may blow the total so we'll call this | ||
1317 | // "close" and deal with the rest. | ||
1318 | pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwTotal += 50; | ||
1319 | } | ||
1320 | break; | ||
1321 | |||
1322 | case 1: // action info. | ||
1323 | if (3 > cFields) | ||
1324 | { | ||
1325 | Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - Invalid field count %d, '%ls'", cFields, wzMessage); | ||
1326 | ExitFunction(); | ||
1327 | } | ||
1328 | //Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - ACTION INFO - %d, %d, %d", iFields[1], iFields[2], iFields[3]); | ||
1329 | |||
1330 | if (0 == iFields[2]) | ||
1331 | { | ||
1332 | pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData = FALSE; | ||
1333 | } | ||
1334 | else | ||
1335 | { | ||
1336 | pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fEnableActionData = TRUE; | ||
1337 | pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwStep = iFields[1]; | ||
1338 | } | ||
1339 | break; | ||
1340 | |||
1341 | case 2: // progress report. | ||
1342 | if (2 > cFields) | ||
1343 | { | ||
1344 | Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - Invalid field count %d, '%ls'", cFields, wzMessage); | ||
1345 | break; | ||
1346 | } | ||
1347 | |||
1348 | //Trace(REPORT_STANDARD, "INSTALLMESSAGE_PROGRESS - PROGRESS REPORT - %d, %d, %d", iFields[1], iFields[2], iFields[3]); | ||
1349 | |||
1350 | if (WIU_MSI_PROGRESS_INVALID == pContext->dwCurrentProgressIndex) | ||
1351 | { | ||
1352 | break; | ||
1353 | } | ||
1354 | else if (0 == pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwTotal) | ||
1355 | { | ||
1356 | break; | ||
1357 | } | ||
1358 | |||
1359 | // Update progress. | ||
1360 | if (pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].fMoveForward) | ||
1361 | { | ||
1362 | pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted += iFields[1]; | ||
1363 | } | ||
1364 | else // rollback. | ||
1365 | { | ||
1366 | pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted -= iFields[1]; | ||
1367 | } | ||
1368 | break; | ||
1369 | |||
1370 | case 3: // extend the progress bar. | ||
1371 | pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwTotal += iFields[1]; | ||
1372 | break; | ||
1373 | |||
1374 | default: | ||
1375 | ExitFunction(); // unknown message, bail | ||
1376 | } | ||
1377 | |||
1378 | // If we have a valid progress index, send an update. | ||
1379 | if (WIU_MSI_PROGRESS_INVALID != pContext->dwCurrentProgressIndex) | ||
1380 | { | ||
1381 | nResult = SendProgressUpdate(pContext); | ||
1382 | } | ||
1383 | |||
1384 | LExit: | ||
1385 | return nResult; | ||
1386 | } | ||
1387 | |||
1388 | static INT SendMsiMessage( | ||
1389 | __in WIU_MSI_EXECUTE_CONTEXT* pContext, | ||
1390 | __in INSTALLMESSAGE mt, | ||
1391 | __in UINT uiFlags, | ||
1392 | __in_z LPCWSTR wzMessage, | ||
1393 | __in_opt MSIHANDLE hRecord | ||
1394 | ) | ||
1395 | { | ||
1396 | INT nResult = IDNOACTION; | ||
1397 | WIU_MSI_EXECUTE_MESSAGE message = { }; | ||
1398 | LPWSTR* rgsczData = NULL; | ||
1399 | DWORD cData = 0; | ||
1400 | |||
1401 | InitializeMessageData(hRecord, &rgsczData, &cData); | ||
1402 | |||
1403 | message.type = WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE; | ||
1404 | message.dwAllowedResults = uiFlags; | ||
1405 | message.cData = cData; | ||
1406 | message.rgwzData = (LPCWSTR*)rgsczData; | ||
1407 | message.msiMessage.mt = mt; | ||
1408 | message.msiMessage.wzMessage = wzMessage; | ||
1409 | nResult = pContext->pfnMessageHandler(&message, pContext->pvContext); | ||
1410 | |||
1411 | UninitializeMessageData(rgsczData, cData); | ||
1412 | return nResult; | ||
1413 | } | ||
1414 | |||
1415 | static INT SendErrorMessage( | ||
1416 | __in WIU_MSI_EXECUTE_CONTEXT* pContext, | ||
1417 | __in UINT uiFlags, | ||
1418 | __in_z LPCWSTR wzMessage, | ||
1419 | __in_opt MSIHANDLE hRecord | ||
1420 | ) | ||
1421 | { | ||
1422 | INT nResult = IDNOACTION; | ||
1423 | WIU_MSI_EXECUTE_MESSAGE message = { }; | ||
1424 | DWORD dwErrorCode = 0; | ||
1425 | LPWSTR* rgsczData = NULL; | ||
1426 | DWORD cData = 0; | ||
1427 | |||
1428 | if (hRecord) | ||
1429 | { | ||
1430 | dwErrorCode = ::MsiRecordGetInteger(hRecord, 1); | ||
1431 | |||
1432 | // Set the recommendation if it's a known error code. | ||
1433 | switch (dwErrorCode) | ||
1434 | { | ||
1435 | case 1605: // continue with install even if there isn't enough room for rollback. | ||
1436 | nResult = IDIGNORE; | ||
1437 | break; | ||
1438 | |||
1439 | case 1704: // rollback suspended installs so our install can continue. | ||
1440 | nResult = IDOK; | ||
1441 | break; | ||
1442 | } | ||
1443 | } | ||
1444 | |||
1445 | InitializeMessageData(hRecord, &rgsczData, &cData); | ||
1446 | |||
1447 | message.type = WIU_MSI_EXECUTE_MESSAGE_ERROR; | ||
1448 | message.dwAllowedResults = uiFlags; | ||
1449 | message.nResultRecommendation = nResult; | ||
1450 | message.cData = cData; | ||
1451 | message.rgwzData = (LPCWSTR*)rgsczData; | ||
1452 | message.error.dwErrorCode = dwErrorCode; | ||
1453 | message.error.wzMessage = wzMessage; | ||
1454 | nResult = pContext->pfnMessageHandler(&message, pContext->pvContext); | ||
1455 | |||
1456 | UninitializeMessageData(rgsczData, cData); | ||
1457 | return nResult; | ||
1458 | } | ||
1459 | |||
1460 | static INT SendFilesInUseMessage( | ||
1461 | __in WIU_MSI_EXECUTE_CONTEXT* pContext, | ||
1462 | __in_opt MSIHANDLE hRecord, | ||
1463 | __in BOOL /*fRestartManagerRequest*/ | ||
1464 | ) | ||
1465 | { | ||
1466 | INT nResult = IDNOACTION; | ||
1467 | WIU_MSI_EXECUTE_MESSAGE message = { }; | ||
1468 | LPWSTR* rgsczData = NULL; | ||
1469 | DWORD cData = 0; | ||
1470 | |||
1471 | InitializeMessageData(hRecord, &rgsczData, &cData); | ||
1472 | |||
1473 | message.type = WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE; | ||
1474 | message.dwAllowedResults = WIU_MB_OKIGNORECANCELRETRY; | ||
1475 | message.cData = cData; | ||
1476 | message.rgwzData = (LPCWSTR*)rgsczData; | ||
1477 | message.msiFilesInUse.cFiles = message.cData; // point the files in use information to the message record information. | ||
1478 | message.msiFilesInUse.rgwzFiles = message.rgwzData; | ||
1479 | nResult = pContext->pfnMessageHandler(&message, pContext->pvContext); | ||
1480 | |||
1481 | UninitializeMessageData(rgsczData, cData); | ||
1482 | return nResult; | ||
1483 | } | ||
1484 | |||
1485 | static INT SendProgressUpdate( | ||
1486 | __in WIU_MSI_EXECUTE_CONTEXT* pContext | ||
1487 | ) | ||
1488 | { | ||
1489 | int nResult = IDNOACTION; | ||
1490 | DWORD dwPercentage = 0; // number representing 0 - 100% | ||
1491 | WIU_MSI_EXECUTE_MESSAGE message = { }; | ||
1492 | |||
1493 | //DWORD dwMsiProgressTotal = pEngineInfo->dwMsiProgressTotal; | ||
1494 | //DWORD dwMsiProgressComplete = pEngineInfo->dwMsiProgressComplete; //min(dwMsiProgressTotal, pEngineInfo->dwMsiProgressComplete); | ||
1495 | //double dProgressGauge = 0; | ||
1496 | //double dProgressStageTotal = (double)pEngineInfo->qwProgressStageTotal; | ||
1497 | |||
1498 | // Calculate progress for the phases of Windows Installer. | ||
1499 | // TODO: handle upgrade progress which would add another phase. | ||
1500 | dwPercentage += CalculatePhaseProgress(pContext, 0, 15); | ||
1501 | dwPercentage += CalculatePhaseProgress(pContext, 1, 80); | ||
1502 | dwPercentage += CalculatePhaseProgress(pContext, 2, 5); | ||
1503 | dwPercentage = min(dwPercentage, 100); // ensure the percentage never goes over 100%. | ||
1504 | |||
1505 | if (pContext->fRollback) | ||
1506 | { | ||
1507 | dwPercentage = 100 - dwPercentage; | ||
1508 | } | ||
1509 | |||
1510 | //if (qwTotal) // avoid "divide by zero" if the MSI range is blank. | ||
1511 | //{ | ||
1512 | // // calculate gauge. | ||
1513 | // double dProgressGauge = static_cast<double>(qwCompleted) / static_cast<double>(qwTotal); | ||
1514 | // dProgressGauge = (1.0 / (1.0 + exp(3.7 - dProgressGauge * 7.5)) - 0.024127021417669196) / 0.975872978582330804; | ||
1515 | // qwCompleted = (DWORD)(dProgressGauge * qwTotal); | ||
1516 | |||
1517 | // // calculate progress within range | ||
1518 | // //qwProgressComplete = (DWORD64)(dwMsiProgressComplete * (dProgressStageTotal / dwMsiProgressTotal)); | ||
1519 | // //qwProgressComplete = min(qwProgressComplete, pEngineInfo->qwProgressStageTotal); | ||
1520 | //} | ||
1521 | |||
1522 | #ifdef _DEBUG | ||
1523 | DWORD64 qwCompleted = pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwCompleted; | ||
1524 | DWORD64 qwTotal = pContext->rgMsiProgress[pContext->dwCurrentProgressIndex].dwTotal; | ||
1525 | Trace(REPORT_STANDARD, "MSI progress: %I64u/%I64u (%u%%)", qwCompleted, qwTotal, dwPercentage); | ||
1526 | //AssertSz(qwCompleted <= qwTotal, "Completed progress is larger than total progress."); | ||
1527 | #endif | ||
1528 | |||
1529 | message.type = WIU_MSI_EXECUTE_MESSAGE_PROGRESS; | ||
1530 | message.dwAllowedResults = MB_OKCANCEL; | ||
1531 | message.progress.dwPercentage = dwPercentage; | ||
1532 | nResult = pContext->pfnMessageHandler(&message, pContext->pvContext); | ||
1533 | |||
1534 | return nResult; | ||
1535 | } | ||
1536 | |||
1537 | static void ResetProgress( | ||
1538 | __in WIU_MSI_EXECUTE_CONTEXT* pContext | ||
1539 | ) | ||
1540 | { | ||
1541 | memset(pContext->rgMsiProgress, 0, sizeof(pContext->rgMsiProgress)); | ||
1542 | pContext->dwCurrentProgressIndex = WIU_MSI_PROGRESS_INVALID; | ||
1543 | } | ||
1544 | |||
1545 | static DWORD CalculatePhaseProgress( | ||
1546 | __in WIU_MSI_EXECUTE_CONTEXT* pContext, | ||
1547 | __in DWORD dwProgressIndex, | ||
1548 | __in DWORD dwWeightPercentage | ||
1549 | ) | ||
1550 | { | ||
1551 | DWORD dwPhasePercentage = 0; | ||
1552 | |||
1553 | // If we've already passed this progress index, return the maximum percentage possible (the weight) | ||
1554 | if (dwProgressIndex < pContext->dwCurrentProgressIndex) | ||
1555 | { | ||
1556 | dwPhasePercentage = dwWeightPercentage; | ||
1557 | } | ||
1558 | else if (dwProgressIndex == pContext->dwCurrentProgressIndex) // have to do the math for the current progress. | ||
1559 | { | ||
1560 | WIU_MSI_PROGRESS* pProgress = pContext->rgMsiProgress + dwProgressIndex; | ||
1561 | if (pProgress->dwTotal) | ||
1562 | { | ||
1563 | DWORD64 dw64Completed = pProgress->dwCompleted; | ||
1564 | dwPhasePercentage = static_cast<DWORD>(dw64Completed * dwWeightPercentage / pProgress->dwTotal); | ||
1565 | } | ||
1566 | } | ||
1567 | // else we're not there yet so it has to be zero. | ||
1568 | |||
1569 | return dwPhasePercentage; | ||
1570 | } | ||
1571 | |||
1572 | void InitializeMessageData( | ||
1573 | __in_opt MSIHANDLE hRecord, | ||
1574 | __deref_out_ecount(*pcData) LPWSTR** prgsczData, | ||
1575 | __out DWORD* pcData | ||
1576 | ) | ||
1577 | { | ||
1578 | DWORD cData = 0; | ||
1579 | LPWSTR* rgsczData = NULL; | ||
1580 | |||
1581 | // If we have a record based message, try to get the extra data. | ||
1582 | if (hRecord) | ||
1583 | { | ||
1584 | cData = ::MsiRecordGetFieldCount(hRecord); | ||
1585 | if (cData) | ||
1586 | { | ||
1587 | rgsczData = (LPWSTR*)MemAlloc(sizeof(LPWSTR*) * cData, TRUE); | ||
1588 | } | ||
1589 | |||
1590 | for (DWORD i = 0; rgsczData && i < cData; ++i) | ||
1591 | { | ||
1592 | DWORD cch = 0; | ||
1593 | |||
1594 | // get string from record | ||
1595 | #pragma prefast(push) | ||
1596 | #pragma prefast(disable:6298) | ||
1597 | DWORD er = ::MsiRecordGetStringW(hRecord, i + 1, L"", &cch); | ||
1598 | #pragma prefast(pop) | ||
1599 | if (ERROR_MORE_DATA == er) | ||
1600 | { | ||
1601 | HRESULT hr = StrAlloc(&rgsczData[i], ++cch); | ||
1602 | if (SUCCEEDED(hr)) | ||
1603 | { | ||
1604 | er = ::MsiRecordGetStringW(hRecord, i + 1, rgsczData[i], &cch); | ||
1605 | } | ||
1606 | } | ||
1607 | } | ||
1608 | } | ||
1609 | |||
1610 | *prgsczData = rgsczData; | ||
1611 | *pcData = cData; | ||
1612 | } | ||
1613 | |||
1614 | void UninitializeMessageData( | ||
1615 | __in LPWSTR* rgsczData, | ||
1616 | __in DWORD cData | ||
1617 | ) | ||
1618 | { | ||
1619 | // Clean up if there was any data allocated. | ||
1620 | if (rgsczData) | ||
1621 | { | ||
1622 | for (DWORD i = 0; i < cData; ++i) | ||
1623 | { | ||
1624 | ReleaseStr(rgsczData[i]); | ||
1625 | } | ||
1626 | |||
1627 | MemFree(rgsczData); | ||
1628 | } | ||
1629 | } | ||