aboutsummaryrefslogtreecommitdiff
path: root/src/ext/Msmq/ca/mqqueuesched.cpp
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2021-05-04 22:48:58 -0700
committerRob Mensching <rob@firegiant.com>2021-05-04 22:48:58 -0700
commitd77dca43e87e8711b19910a8fd49138f939bf0a4 (patch)
tree650235e3577bde24518af53e001b7bcbac055da3 /src/ext/Msmq/ca/mqqueuesched.cpp
parent4426b8745595b6e2c709e08871d04efbf574c1f5 (diff)
downloadwix-d77dca43e87e8711b19910a8fd49138f939bf0a4.tar.gz
wix-d77dca43e87e8711b19910a8fd49138f939bf0a4.tar.bz2
wix-d77dca43e87e8711b19910a8fd49138f939bf0a4.zip
Move Msmq.wixext into ext
Diffstat (limited to 'src/ext/Msmq/ca/mqqueuesched.cpp')
-rw-r--r--src/ext/Msmq/ca/mqqueuesched.cpp582
1 files changed, 582 insertions, 0 deletions
diff --git a/src/ext/Msmq/ca/mqqueuesched.cpp b/src/ext/Msmq/ca/mqqueuesched.cpp
new file mode 100644
index 00000000..01777ea4
--- /dev/null
+++ b/src/ext/Msmq/ca/mqqueuesched.cpp
@@ -0,0 +1,582 @@
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// sql queries
7
8LPCWSTR vcsMessageQueueQuery =
9 L"SELECT `MessageQueue`, `Component_`, `BasePriority`, `JournalQuota`, `Label`, `MulticastAddress`, `PathName`, `PrivLevel`, `Quota`, `ServiceTypeGuid`, `Attributes` FROM `MessageQueue`";
10enum eMessageQueueQuery { mqqMessageQueue = 1, mqqComponent, mqqBasePriority, mqqJournalQuota, mqqLabel, mqqMulticastAddress, mqqPathName, mqqPrivLevel, mqqQuota, mqqServiceTypeGuid, mqqAttributes };
11
12LPCWSTR vcsMessageQueueUserPermissionQuery =
13 L"SELECT `MessageQueueUserPermission`, `MessageQueue_`, `MessageQueueUserPermission`.`Component_`, `Domain`, `Name`, `Permissions` FROM `MessageQueueUserPermission`, `User` WHERE `User_` = `User`";
14LPCWSTR vcsMessageQueueGroupPermissionQuery =
15 L"SELECT `MessageQueueGroupPermission`, `MessageQueue_`, `MessageQueueGroupPermission`.`Component_`, `Domain`, `Name`, `Permissions` FROM `MessageQueueGroupPermission`, `Group` WHERE `Group_` = `Group`";
16enum eMessageQueuePermissionQuery { mqpqMessageQueuePermission = 1, mqpqMessageQueue, mqpqComponent, mqpqDomain, mqpqName, mqpqPermissions };
17
18
19// prototypes for private helper functions
20
21static HRESULT MqiMessageQueueFindByKey(
22 MQI_MESSAGE_QUEUE_LIST* pList,
23 LPCWSTR pwzKey,
24 MQI_MESSAGE_QUEUE** ppItm
25 );
26static HRESULT AddMessageQueueToActionData(
27 MQI_MESSAGE_QUEUE* pItm,
28 LPWSTR* ppwzActionData
29 );
30static HRESULT MessageQueueTrusteePermissionsRead(
31 LPCWSTR pwzQuery,
32 MQI_MESSAGE_QUEUE_LIST* pMessageQueueList,
33 MQI_MESSAGE_QUEUE_PERMISSION_LIST* pList
34 );
35static HRESULT AddMessageQueuePermissionToActionData(
36 MQI_MESSAGE_QUEUE_PERMISSION* pItm,
37 LPWSTR* ppwzActionData
38 );
39
40
41// private typedefs
42
43typedef HRESULT (__stdcall *MQPathNameToFormatNameFunc)(LPCWSTR, LPWSTR, LPDWORD);
44
45
46// private variables
47
48static HMODULE ghMQRT;
49static MQPathNameToFormatNameFunc gpfnMQPathNameToFormatName;
50
51
52// function definitions
53
54HRESULT MqiSchedInitialize()
55{
56 HRESULT hr = S_OK;
57
58 // load mqrt.dll
59 ghMQRT = ::LoadLibraryW(L"mqrt.dll");
60 if (!ghMQRT)
61 {
62 ExitFunction1(hr = S_FALSE);
63 }
64
65 // get MQPathNameToFormatName function address
66 gpfnMQPathNameToFormatName = (MQPathNameToFormatNameFunc)::GetProcAddress(ghMQRT, "MQPathNameToFormatName");
67 ExitOnNullWithLastError(gpfnMQPathNameToFormatName, hr, "Failed get address for MQPathNameToFormatName() function");
68
69 hr = S_OK;
70
71LExit:
72 return hr;
73}
74
75void MqiSchedUninitialize()
76{
77 if (ghMQRT)
78 {
79 ::FreeLibrary(ghMQRT);
80 }
81}
82
83HRESULT MqiMessageQueueRead(
84 MQI_MESSAGE_QUEUE_LIST* pList
85 )
86{
87 HRESULT hr = S_OK;
88 UINT er = ERROR_SUCCESS;
89
90 PMSIHANDLE hView, hRec;
91
92 MQI_MESSAGE_QUEUE* pItm = NULL;
93 LPWSTR pwzData = NULL;
94
95 // loop through all partitions
96 hr = WcaOpenExecuteView(vcsMessageQueueQuery, &hView);
97 ExitOnFailure(hr, "Failed to execute view on MessageQueue table");
98
99 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
100 {
101 // create entry
102 pItm = (MQI_MESSAGE_QUEUE*)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MQI_MESSAGE_QUEUE));
103 if (!pItm)
104 ExitFunction1(hr = E_OUTOFMEMORY);
105
106 // get key
107 hr = WcaGetRecordString(hRec, mqqMessageQueue, &pwzData);
108 ExitOnFailure(hr, "Failed to get key");
109 StringCchCopyW(pItm->wzKey, countof(pItm->wzKey), pwzData);
110
111 // get component install state
112 hr = WcaGetRecordString(hRec, mqqComponent, &pwzData);
113 ExitOnFailure(hr, "Failed to get component");
114
115 // get component install state
116 er = ::MsiGetComponentStateW(WcaGetInstallHandle(), pwzData, &pItm->isInstalled, &pItm->isAction);
117 ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to get component state");
118
119 // get base priority
120 hr = WcaGetRecordInteger(hRec, mqqBasePriority, &pItm->iBasePriority);
121 ExitOnFailure(hr, "Failed to get base priority");
122
123 // get journal quota
124 hr = WcaGetRecordInteger(hRec, mqqJournalQuota, &pItm->iJournalQuota);
125 ExitOnFailure(hr, "Failed to get journal quota");
126
127 // get label
128 hr = WcaGetRecordFormattedString(hRec, mqqLabel, &pwzData);
129 ExitOnFailure(hr, "Failed to get label");
130 StringCchCopyW(pItm->wzLabel, countof(pItm->wzLabel), pwzData);
131
132 // get multicast address
133 hr = WcaGetRecordFormattedString(hRec, mqqMulticastAddress, &pwzData);
134 ExitOnFailure(hr, "Failed to get multicast address");
135 StringCchCopyW(pItm->wzMulticastAddress, countof(pItm->wzMulticastAddress), pwzData);
136
137 // get path name
138 hr = WcaGetRecordFormattedString(hRec, mqqPathName, &pwzData);
139 ExitOnFailure(hr, "Failed to get path name");
140 StringCchCopyW(pItm->wzPathName, countof(pItm->wzPathName), pwzData);
141
142 // get privacy level
143 hr = WcaGetRecordInteger(hRec, mqqPrivLevel, &pItm->iPrivLevel);
144 ExitOnFailure(hr, "Failed to get privacy level");
145
146 // get quota
147 hr = WcaGetRecordInteger(hRec, mqqQuota, &pItm->iQuota);
148 ExitOnFailure(hr, "Failed to get quota");
149
150 // get service type guid
151 hr = WcaGetRecordFormattedString(hRec, mqqServiceTypeGuid, &pwzData);
152 ExitOnFailure(hr, "Failed to get service type guid");
153 StringCchCopyW(pItm->wzServiceTypeGuid, countof(pItm->wzServiceTypeGuid), pwzData);
154
155 // get attributes
156 hr = WcaGetRecordInteger(hRec, mqqAttributes, &pItm->iAttributes);
157 ExitOnFailure(hr, "Failed to get attributes");
158
159 // increment counters
160 if (WcaIsInstalling(pItm->isInstalled, pItm->isAction))
161 pList->iInstallCount++;
162 if (WcaIsUninstalling(pItm->isInstalled, pItm->isAction))
163 pList->iUninstallCount++;
164
165 // add entry
166 pItm->pNext = pList->pFirst;
167 pList->pFirst = pItm;
168 pItm = NULL;
169 }
170
171 if (E_NOMOREITEMS == hr)
172 hr = S_OK;
173
174LExit:
175 // clean up
176 if (pItm)
177 ::HeapFree(::GetProcessHeap(), 0, pItm);
178
179 ReleaseStr(pwzData);
180
181 return hr;
182}
183
184HRESULT MqiMessageQueueVerify(
185 MQI_MESSAGE_QUEUE_LIST* pList
186 )
187{
188 HRESULT hr = S_OK;
189 LPWSTR pwzFormatName = NULL;
190 DWORD dwCount = 128;
191
192 for (MQI_MESSAGE_QUEUE* pItm = pList->pFirst; pItm; pItm = pItm->pNext)
193 {
194 // queues that are being installed only
195 if (!WcaIsInstalling(pItm->isInstalled, pItm->isAction))
196 continue;
197
198 // get format name
199 hr = StrAlloc(&pwzFormatName, dwCount);
200 ExitOnFailure(hr, "Failed to allocate format name string");
201 do {
202 hr = gpfnMQPathNameToFormatName(pItm->wzPathName, pwzFormatName, &dwCount);
203 switch (hr)
204 {
205 case MQ_ERROR_QUEUE_NOT_FOUND:
206 break; // break
207 case MQ_ERROR_FORMATNAME_BUFFER_TOO_SMALL:
208 hr = StrAlloc(&pwzFormatName, dwCount);
209 ExitOnFailure(hr, "Failed to reallocate format name string");
210 hr = S_FALSE; // retry
211 break;
212 default:
213 ExitOnFailure(hr, "Failed to get format name");
214 hr = S_OK;
215 }
216 } while (S_FALSE == hr);
217
218 if (MQ_ERROR_QUEUE_NOT_FOUND == hr)
219 {
220 continue;
221 }
222 pItm->fExists = TRUE;
223 pList->iInstallCount--;
224
225 // clean up
226 ReleaseNullStr(pwzFormatName);
227 }
228
229 hr = S_OK;
230
231LExit:
232 ReleaseStr(pwzFormatName);
233 return hr;
234}
235
236HRESULT MqiMessageQueueInstall(
237 MQI_MESSAGE_QUEUE_LIST* pList,
238 BOOL fRollback,
239 LPWSTR* ppwzActionData
240 )
241{
242 HRESULT hr = S_OK;
243
244 // add count to action data
245 hr = WcaWriteIntegerToCaData(pList->iInstallCount, ppwzActionData);
246 ExitOnFailure(hr, "Failed to add count to custom action data");
247
248 for (MQI_MESSAGE_QUEUE* pItm = pList->pFirst; pItm; pItm = pItm->pNext)
249 {
250 // queues that are being installed only
251 if (!WcaIsInstalling(pItm->isInstalled, pItm->isAction))
252 continue;
253
254 // if the queue exists we should not try to create it
255 if (pItm->fExists && !fRollback)
256 {
257 continue;
258 }
259
260 // add message queue to action data
261 hr = AddMessageQueueToActionData(pItm, ppwzActionData);
262 ExitOnFailure(hr, "Failed to add message queue to action data");
263 }
264
265 hr = S_OK;
266
267LExit:
268 return hr;
269}
270
271HRESULT MqiMessageQueueUninstall(
272 MQI_MESSAGE_QUEUE_LIST* pList,
273 BOOL fRollback,
274 LPWSTR* ppwzActionData
275 )
276{
277 HRESULT hr = S_OK;
278
279 // add count to action data
280 hr = WcaWriteIntegerToCaData(pList->iUninstallCount, ppwzActionData);
281 ExitOnFailure(hr, "Failed to add count to custom action data");
282
283 for (MQI_MESSAGE_QUEUE* pItm = pList->pFirst; pItm; pItm = pItm->pNext)
284 {
285 // queues that are being uninstalled only
286 if (!WcaIsUninstalling(pItm->isInstalled, pItm->isAction))
287 continue;
288
289 // if we did not create the queue we should not try to delete it
290 if (pItm->fExists && fRollback)
291 {
292 continue;
293 }
294
295 // add message queue to action data
296 hr = AddMessageQueueToActionData(pItm, ppwzActionData);
297 ExitOnFailure(hr, "Failed to add message queue to action data");
298 }
299
300 hr = S_OK;
301
302LExit:
303 return hr;
304}
305
306void MqiMessageQueueFreeList(
307 MQI_MESSAGE_QUEUE_LIST* pList
308 )
309{
310 MQI_MESSAGE_QUEUE* pItm = pList->pFirst;
311 while (pItm)
312 {
313 MQI_MESSAGE_QUEUE* pDelete = pItm;
314 pItm = pItm->pNext;
315 ::HeapFree(::GetProcessHeap(), 0, pDelete);
316 }
317}
318
319HRESULT MqiMessageQueuePermissionRead(
320 MQI_MESSAGE_QUEUE_LIST* pMessageQueueList,
321 MQI_MESSAGE_QUEUE_PERMISSION_LIST* pList
322 )
323{
324 HRESULT hr = S_OK;
325
326 // read message queue user permissions
327 if (S_OK == WcaTableExists(L"MessageQueueUserPermission"))
328 {
329 hr = MessageQueueTrusteePermissionsRead(vcsMessageQueueUserPermissionQuery, pMessageQueueList, pList);
330 ExitOnFailure(hr, "Failed to read message queue user permissions");
331 }
332
333 // read message queue group permissions
334 if (S_OK == WcaTableExists(L"MessageQueueGroupPermission"))
335 {
336 hr = MessageQueueTrusteePermissionsRead(vcsMessageQueueGroupPermissionQuery, pMessageQueueList, pList);
337 ExitOnFailure(hr, "Failed to read message queue group permissions");
338 }
339
340 hr = S_OK;
341
342LExit:
343 return hr;
344}
345
346HRESULT MqiMessageQueuePermissionInstall(
347 MQI_MESSAGE_QUEUE_PERMISSION_LIST* pList,
348 LPWSTR* ppwzActionData
349 )
350{
351 HRESULT hr = S_OK;
352
353 // add count to action data
354 hr = WcaWriteIntegerToCaData(pList->iInstallCount, ppwzActionData);
355 ExitOnFailure(hr, "Failed to add count to custom action data");
356
357 for (MQI_MESSAGE_QUEUE_PERMISSION* pItm = pList->pFirst; pItm; pItm = pItm->pNext)
358 {
359 // queue permissions that are being installed only
360 if (!WcaIsInstalling(pItm->isInstalled, pItm->isAction))
361 continue;
362
363 // add message queue permission to action data
364 hr = AddMessageQueuePermissionToActionData(pItm, ppwzActionData);
365 ExitOnFailure(hr, "Failed to add message queue permission to action data");
366 }
367
368 hr = S_OK;
369
370LExit:
371 return hr;
372}
373
374HRESULT MqiMessageQueuePermissionUninstall(
375 MQI_MESSAGE_QUEUE_PERMISSION_LIST* pList,
376 LPWSTR* ppwzActionData
377 )
378{
379 HRESULT hr = S_OK;
380
381 // add count to action data
382 hr = WcaWriteIntegerToCaData(pList->iUninstallCount, ppwzActionData);
383 ExitOnFailure(hr, "Failed to add count to custom action data");
384
385 for (MQI_MESSAGE_QUEUE_PERMISSION* pItm = pList->pFirst; pItm; pItm = pItm->pNext)
386 {
387 // queue permissions that are being uninstalled only
388 if (!WcaIsUninstalling(pItm->isInstalled, pItm->isAction))
389 continue;
390
391 // add message queue permission to action data
392 hr = AddMessageQueuePermissionToActionData(pItm, ppwzActionData);
393 ExitOnFailure(hr, "Failed to add message queue permission to action data");
394 }
395
396 hr = S_OK;
397
398LExit:
399 return hr;
400}
401
402void MqiMessageQueuePermissionFreeList(
403 MQI_MESSAGE_QUEUE_PERMISSION_LIST* pList
404 )
405{
406 MQI_MESSAGE_QUEUE_PERMISSION* pItm = pList->pFirst;
407 while (pItm)
408 {
409 MQI_MESSAGE_QUEUE_PERMISSION* pDelete = pItm;
410 pItm = pItm->pNext;
411 ::HeapFree(::GetProcessHeap(), 0, pDelete);
412 }
413}
414
415
416// helper function definitions
417
418static HRESULT MqiMessageQueueFindByKey(
419 MQI_MESSAGE_QUEUE_LIST* pList,
420 LPCWSTR pwzKey,
421 MQI_MESSAGE_QUEUE** ppItm
422 )
423{
424 for (MQI_MESSAGE_QUEUE* pItm = pList->pFirst; pItm; pItm = pItm->pNext)
425 {
426 if (0 == lstrcmpW(pItm->wzKey, pwzKey))
427 {
428 *ppItm = pItm;
429 return S_OK;
430 }
431 }
432
433 return S_FALSE;
434}
435
436static HRESULT AddMessageQueueToActionData(
437 MQI_MESSAGE_QUEUE* pItm,
438 LPWSTR* ppwzActionData
439 )
440{
441 HRESULT hr = S_OK;
442
443 // add message queue information to custom action data
444 hr = WcaWriteStringToCaData(pItm->wzKey, ppwzActionData);
445 ExitOnFailure(hr, "Failed to add key to custom action data");
446 hr = WcaWriteIntegerToCaData(pItm->iBasePriority, ppwzActionData);
447 ExitOnFailure(hr, "Failed to add base priority to custom action data");
448 hr = WcaWriteIntegerToCaData(pItm->iJournalQuota, ppwzActionData);
449 ExitOnFailure(hr, "Failed to add journal quota to custom action data");
450 hr = WcaWriteStringToCaData(pItm->wzLabel, ppwzActionData);
451 ExitOnFailure(hr, "Failed to add label to custom action data");
452 hr = WcaWriteStringToCaData(pItm->wzMulticastAddress, ppwzActionData);
453 ExitOnFailure(hr, "Failed to add multicast address to custom action data");
454 hr = WcaWriteStringToCaData(pItm->wzPathName, ppwzActionData);
455 ExitOnFailure(hr, "Failed to add path name to custom action data");
456 hr = WcaWriteIntegerToCaData(pItm->iPrivLevel, ppwzActionData);
457 ExitOnFailure(hr, "Failed to add privacy level to custom action data");
458 hr = WcaWriteIntegerToCaData(pItm->iQuota, ppwzActionData);
459 ExitOnFailure(hr, "Failed to add quota to custom action data");
460 hr = WcaWriteStringToCaData(pItm->wzServiceTypeGuid, ppwzActionData);
461 ExitOnFailure(hr, "Failed to add service type guid to custom action data");
462 hr = WcaWriteIntegerToCaData(pItm->iAttributes, ppwzActionData);
463 ExitOnFailure(hr, "Failed to add attributes to custom action data");
464
465 hr = S_OK;
466
467LExit:
468 return hr;
469}
470
471static HRESULT MessageQueueTrusteePermissionsRead(
472 LPCWSTR pwzQuery,
473 MQI_MESSAGE_QUEUE_LIST* pMessageQueueList,
474 MQI_MESSAGE_QUEUE_PERMISSION_LIST* pList
475 )
476{
477 HRESULT hr = S_OK;
478 UINT er = ERROR_SUCCESS;
479
480 PMSIHANDLE hView, hRec;
481
482 LPWSTR pwzData = NULL;
483
484 MQI_MESSAGE_QUEUE_PERMISSION* pItm = NULL;
485
486 // loop through all application roles
487 hr = WcaOpenExecuteView(pwzQuery, &hView);
488 ExitOnFailure(hr, "Failed to execute view on table");
489
490 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
491 {
492 // create entry
493 pItm = (MQI_MESSAGE_QUEUE_PERMISSION*)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MQI_MESSAGE_QUEUE_PERMISSION));
494 if (!pItm)
495 ExitFunction1(hr = E_OUTOFMEMORY);
496
497 // get key
498 hr = WcaGetRecordString(hRec, mqpqMessageQueuePermission, &pwzData);
499 ExitOnFailure(hr, "Failed to get key");
500 StringCchCopyW(pItm->wzKey, countof(pItm->wzKey), pwzData);
501
502 // get component
503 hr = WcaGetRecordString(hRec, mqpqComponent, &pwzData);
504 ExitOnFailure(hr, "Failed to get component");
505
506 // get component install state
507 er = ::MsiGetComponentStateW(WcaGetInstallHandle(), pwzData, &pItm->isInstalled, &pItm->isAction);
508 ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to get component state");
509
510 // get message queue
511 hr = WcaGetRecordString(hRec, mqpqMessageQueue, &pwzData);
512 ExitOnFailure(hr, "Failed to get application role");
513
514 hr = MqiMessageQueueFindByKey(pMessageQueueList, pwzData, &pItm->pMessageQueue);
515 if (S_FALSE == hr)
516 hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
517 ExitOnFailure(hr, "Failed to find message queue, key: %S", pwzData);
518
519 // get user domain
520 hr = WcaGetRecordFormattedString(hRec, mqpqDomain, &pwzData);
521 ExitOnFailure(hr, "Failed to get domain");
522 StringCchCopyW(pItm->wzDomain, countof(pItm->wzDomain), pwzData);
523
524 // get user name
525 hr = WcaGetRecordFormattedString(hRec, mqpqName, &pwzData);
526 ExitOnFailure(hr, "Failed to get name");
527 StringCchCopyW(pItm->wzName, countof(pItm->wzName), pwzData);
528
529 // get permissions
530 hr = WcaGetRecordInteger(hRec, mqpqPermissions, &pItm->iPermissions);
531 ExitOnFailure(hr, "Failed to get permissions");
532
533 // set references & increment counters
534 if (WcaIsInstalling(pItm->isInstalled, pItm->isAction))
535 pList->iInstallCount++;
536 if (WcaIsUninstalling(pItm->isInstalled, pItm->isAction))
537 pList->iUninstallCount++;
538
539 // add entry
540 if (pList->pFirst)
541 pItm->pNext = pList->pFirst;
542 pList->pFirst = pItm;
543 pItm = NULL;
544 }
545
546 if (E_NOMOREITEMS == hr)
547 hr = S_OK;
548
549LExit:
550 // clean up
551 ReleaseStr(pwzData);
552
553 if (pItm)
554 ::HeapFree(::GetProcessHeap(), 0, pItm);
555
556 return hr;
557}
558
559static HRESULT AddMessageQueuePermissionToActionData(
560 MQI_MESSAGE_QUEUE_PERMISSION* pItm,
561 LPWSTR* ppwzActionData
562 )
563{
564 HRESULT hr = S_OK;
565
566 // add message queue information to custom action data
567 hr = WcaWriteStringToCaData(pItm->wzKey, ppwzActionData);
568 ExitOnFailure(hr, "Failed to add key to custom action data");
569 hr = WcaWriteStringToCaData(pItm->pMessageQueue->wzPathName, ppwzActionData);
570 ExitOnFailure(hr, "Failed to add path name to custom action data");
571 hr = WcaWriteStringToCaData(pItm->wzDomain, ppwzActionData);
572 ExitOnFailure(hr, "Failed to add domain to custom action data");
573 hr = WcaWriteStringToCaData(pItm->wzName, ppwzActionData);
574 ExitOnFailure(hr, "Failed to add name to custom action data");
575 hr = WcaWriteIntegerToCaData(pItm->iPermissions, ppwzActionData);
576 ExitOnFailure(hr, "Failed to add permissions to custom action data");
577
578 hr = S_OK;
579
580LExit:
581 return hr;
582}