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