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