Import code from old v4 repo
Sean Hall committed
Feb 3, 2019 at 13:20 UTC
72f3dceefa2e3893b061e074380794bc60b67b6f
20 files changed
+2350
src/ca/dllmain.cpp
new
+27
@@ -0,0 +1,27 @@
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
+DllMain - standard entry point for all WiX custom actions.
7
+
8
+********************************************************************/
9
+extern "C" BOOL WINAPI DllMain(
10
+ IN HINSTANCE hInstance,
11
+ IN ULONG ulReason,
12
+ IN LPVOID)
13
+{
14
+ switch(ulReason)
15
+ {
16
+ case DLL_PROCESS_ATTACH:
17
+ WcaGlobalInitialize(hInstance);
18
+ ::DisableThreadLibraryCalls(hInstance);
19
+ break;
20
+
21
+ case DLL_PROCESS_DETACH:
22
+ WcaGlobalFinalize();
23
+ break;
24
+ }
25
+
26
+ return TRUE;
27
+}
src/ca/precomp.h
new
+18
@@ -0,0 +1,18 @@
1
+#pragma once
2
+// 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.
3
+
4
+
5
+#include <windows.h>
6
+#include <msiquery.h>
7
+#include <intsafe.h>
8
+#include <strsafe.h>
9
+
10
+#include "wcautil.h"
11
+#include "fileutil.h"
12
+#include "strutil.h"
13
+#include "memutil.h"
14
+#include "regutil.h"
15
+#include "dictutil.h"
16
+#include "deputil.h"
17
+
18
+#include "CustomMsiErrors.h"
src/ca/wixdepca.cpp
new
+516
@@ -0,0 +1,516 @@
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
+#define IDNOACTION 0
6
+#define INITIAL_STRINGDICT_SIZE 4
7
+
8
+LPCWSTR vcsDependencyProviderQuery =
9
+ L"SELECT `WixDependencyProvider`.`WixDependencyProvider`, `WixDependencyProvider`.`Component_`, `WixDependencyProvider`.`ProviderKey`, `WixDependencyProvider`.`Attributes` "
10
+ L"FROM `WixDependencyProvider`";
11
+enum eDependencyProviderQuery { dpqId = 1, dpqComponent, dpqProviderKey, dpqAttributes };
12
+
13
+LPCWSTR vcsDependencyQuery =
14
+ L"SELECT `WixDependency`.`WixDependency`, `WixDependencyProvider`.`Component_`, `WixDependency`.`ProviderKey`, `WixDependency`.`MinVersion`, `WixDependency`.`MaxVersion`, `WixDependency`.`Attributes` "
15
+ L"FROM `WixDependencyProvider`, `WixDependency`, `WixDependencyRef` "
16
+ L"WHERE `WixDependency`.`WixDependency` = `WixDependencyRef`.`WixDependency_` AND `WixDependencyProvider`.`WixDependencyProvider` = `WixDependencyRef`.`WixDependencyProvider_`";
17
+enum eDependencyComponentQuery { dqId = 1, dqComponent, dqProviderKey, dqMinVersion, dqMaxVersion, dqAttributes };
18
+
19
+static HRESULT EnsureRequiredDependencies(
20
+ __in MSIHANDLE hInstall,
21
+ __in BOOL fMachineContext
22
+ );
23
+
24
+static HRESULT EnsureAbsentDependents(
25
+ __in MSIHANDLE hInstall,
26
+ __in BOOL fMachineContext
27
+ );
28
+
29
+static HRESULT SplitIgnoredDependents(
30
+ __deref_inout STRINGDICT_HANDLE* psdIgnoredDependents
31
+ );
32
+
33
+static HRESULT CreateDependencyRecord(
34
+ __in int iMessageId,
35
+ __in_ecount(cDependencies) const DEPENDENCY* rgDependencies,
36
+ __in UINT cDependencies,
37
+ __out MSIHANDLE *phRecord
38
+ );
39
+
40
+static LPCWSTR LogDependencyName(
41
+ __in_z LPCWSTR wzName
42
+ );
43
+
44
+/***************************************************************************
45
+ WixDependencyRequire - Checks that all required dependencies are installed.
46
+
47
+***************************************************************************/
48
+extern "C" UINT __stdcall WixDependencyRequire(
49
+ __in MSIHANDLE hInstall
50
+ )
51
+{
52
+ HRESULT hr = S_OK;
53
+ UINT er = ERROR_SUCCESS;
54
+ BOOL fMachineContext = FALSE;
55
+
56
+ hr = WcaInitialize(hInstall, "WixDependencyRequire");
57
+ ExitOnFailure(hr, "Failed to initialize.");
58
+
59
+ hr = RegInitialize();
60
+ ExitOnFailure(hr, "Failed to initialize the registry functions.");
61
+
62
+ // Determine whether we're installing per-user or per-machine.
63
+ fMachineContext = WcaIsPropertySet("ALLUSERS");
64
+
65
+ // Check for any provider components being (re)installed that their requirements are already installed.
66
+ hr = EnsureRequiredDependencies(hInstall, fMachineContext);
67
+ ExitOnFailure(hr, "Failed to ensure required dependencies for (re)installing components.");
68
+
69
+LExit:
70
+ RegUninitialize();
71
+
72
+ er = FAILED(hr) ? ERROR_INSTALL_FAILURE : ERROR_SUCCESS;
73
+ return WcaFinalize(er);
74
+}
75
+
76
+/***************************************************************************
77
+ WixDependencyCheck - Check dependencies based on component state.
78
+
79
+ Note: may return ERROR_NO_MORE_ITEMS to terminate the session early.
80
+***************************************************************************/
81
+extern "C" UINT __stdcall WixDependencyCheck(
82
+ __in MSIHANDLE hInstall
83
+ )
84
+{
85
+ HRESULT hr = S_OK;
86
+ UINT er = ERROR_SUCCESS;
87
+ BOOL fMachineContext = FALSE;
88
+
89
+ hr = WcaInitialize(hInstall, "WixDependencyCheck");
90
+ ExitOnFailure(hr, "Failed to initialize.");
91
+
92
+ hr = RegInitialize();
93
+ ExitOnFailure(hr, "Failed to initialize the registry functions.");
94
+
95
+ // Determine whether we're installing per-user or per-machine.
96
+ fMachineContext = WcaIsPropertySet("ALLUSERS");
97
+
98
+ // Check for any dependents of provider components being uninstalled.
99
+ hr = EnsureAbsentDependents(hInstall, fMachineContext);
100
+ ExitOnFailure(hr, "Failed to ensure absent dependents for uninstalling components.");
101
+
102
+LExit:
103
+ RegUninitialize();
104
+
105
+ er = FAILED(hr) ? ERROR_INSTALL_FAILURE : ERROR_SUCCESS;
106
+ return WcaFinalize(er);
107
+}
108
+
109
+/***************************************************************************
110
+ EnsureRequiredDependencies - Check that dependencies are installed for
111
+ any provider component that is being installed or reinstalled.
112
+
113
+ Note: Skipped if DISABLEDEPENDENCYCHECK is set.
114
+***************************************************************************/
115
+static HRESULT EnsureRequiredDependencies(
116
+ __in MSIHANDLE /*hInstall*/,
117
+ __in BOOL fMachineContext
118
+ )
119
+{
120
+ HRESULT hr = S_OK;
121
+ DWORD er = ERROR_SUCCESS;
122
+ STRINGDICT_HANDLE sdDependencies = NULL;
123
+ HKEY hkHive = NULL;
124
+ PMSIHANDLE hView = NULL;
125
+ PMSIHANDLE hRec = NULL;
126
+ LPWSTR sczId = NULL;
127
+ LPWSTR sczComponent = NULL;
128
+ LPWSTR sczProviderKey = NULL;
129
+ LPWSTR sczMinVersion = NULL;
130
+ LPWSTR sczMaxVersion = NULL;
131
+ int iAttributes = 0;
132
+ WCA_TODO tComponentAction = WCA_TODO_UNKNOWN;
133
+ DEPENDENCY* rgDependencies = NULL;
134
+ UINT cDependencies = 0;
135
+ PMSIHANDLE hDependencyRec = NULL;
136
+
137
+ // Skip the dependency check if the WixDependency table is missing (no dependencies to check for).
138
+ hr = WcaTableExists(L"WixDependency");
139
+ if (S_FALSE == hr)
140
+ {
141
+ WcaLog(LOGMSG_STANDARD, "Skipping the dependency check since no dependencies are authored.");
142
+ ExitFunction1(hr = S_OK);
143
+ }
144
+
145
+ // If the table exists but not the others, the database was not authored correctly.
146
+ ExitOnFailure(hr, "Failed to check if the WixDependency table exists.");
147
+
148
+ // Initialize the dictionary to keep track of unique dependency keys.
149
+ hr = DictCreateStringList(&sdDependencies, INITIAL_STRINGDICT_SIZE, DICT_FLAG_CASEINSENSITIVE);
150
+ ExitOnFailure(hr, "Failed to initialize the unique dependency string list.");
151
+
152
+ // Set the registry hive to use depending on install context.
153
+ hkHive = fMachineContext ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
154
+
155
+ // Loop over the provider components.
156
+ hr = WcaOpenExecuteView(vcsDependencyQuery, &hView);
157
+ ExitOnFailure(hr, "Failed to open the query view for dependencies.");
158
+
159
+ while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
160
+ {
161
+ hr = WcaGetRecordString(hRec, dqId, &sczId);
162
+ ExitOnFailure(hr, "Failed to get WixDependency.WixDependency.");
163
+
164
+ hr = WcaGetRecordString(hRec, dqComponent, &sczComponent);
165
+ ExitOnFailure(hr, "Failed to get WixDependencyProvider.Component_.");
166
+
167
+ // Skip the current component if its not being installed or reinstalled.
168
+ tComponentAction = WcaGetComponentToDo(sczComponent);
169
+ if (WCA_TODO_INSTALL != tComponentAction && WCA_TODO_REINSTALL != tComponentAction)
170
+ {
171
+ WcaLog(LOGMSG_STANDARD, "Skipping dependency check for %ls because the component %ls is not being (re)installed.", sczId, sczComponent);
172
+ continue;
173
+ }
174
+
175
+ hr = WcaGetRecordString(hRec, dqProviderKey, &sczProviderKey);
176
+ ExitOnFailure(hr, "Failed to get WixDependency.ProviderKey.");
177
+
178
+ hr = WcaGetRecordString(hRec, dqMinVersion, &sczMinVersion);
179
+ ExitOnFailure(hr, "Failed to get WixDependency.MinVersion.");
180
+
181
+ hr = WcaGetRecordString(hRec, dqMaxVersion, &sczMaxVersion);
182
+ ExitOnFailure(hr, "Failed to get WixDependency.MaxVersion.");
183
+
184
+ hr = WcaGetRecordInteger(hRec, dqAttributes, &iAttributes);
185
+ ExitOnFailure(hr, "Failed to get WixDependency.Attributes.");
186
+
187
+ // Check the registry to see if the required providers (dependencies) exist.
188
+ hr = DepCheckDependency(hkHive, sczProviderKey, sczMinVersion, sczMaxVersion, iAttributes, sdDependencies, &rgDependencies, &cDependencies);
189
+ if (E_NOTFOUND != hr)
190
+ {
191
+ ExitOnFailure(hr, "Failed dependency check for %ls.", sczId);
192
+ }
193
+ }
194
+
195
+ if (E_NOMOREITEMS != hr)
196
+ {
197
+ ExitOnFailure(hr, "Failed to enumerate all of the rows in the dependency query view.");
198
+ }
199
+ else
200
+ {
201
+ hr = S_OK;
202
+ }
203
+
204
+ // If we collected any dependencies in the previous check, pump a message and prompt the user.
205
+ if (0 < cDependencies)
206
+ {
207
+ hr = CreateDependencyRecord(msierrDependencyMissingDependencies, rgDependencies, cDependencies, &hDependencyRec);
208
+ ExitOnFailure(hr, "Failed to create the dependency record for message %d.", msierrDependencyMissingDependencies);
209
+
210
+ // Send a yes/no message with a warning icon since continuing could be detrimental.
211
+ // This is sent as a USER message to better detect whether a user or dependency-aware bootstrapper is responding
212
+ // or if Windows Installer or a dependency-unaware boostrapper is returning a typical default response.
213
+ er = WcaProcessMessage(static_cast<INSTALLMESSAGE>(INSTALLMESSAGE_USER | MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2), hDependencyRec);
214
+ switch (er)
215
+ {
216
+ // Only a user or dependency-aware bootstrapper that prompted the user should return IDYES to continue anyway.
217
+ case IDYES:
218
+ ExitFunction1(hr = S_OK);
219
+
220
+ // Only a user or dependency-aware bootstrapper that prompted the user should return IDNO to terminate the operation.
221
+ case IDNO:
222
+ WcaSetReturnValue(ERROR_INSTALL_USEREXIT);
223
+ ExitFunction1(hr = S_OK);
224
+
225
+ // A dependency-aware bootstrapper should return IDCANCEL if running silently and the operation should be canceled.
226
+ case IDCANCEL:
227
+ __fallthrough;
228
+
229
+ // Bootstrappers which are not dependency-aware may return IDOK for unhandled messages.
230
+ case IDOK:
231
+ __fallthrough;
232
+
233
+ // Windows Installer returns 0 for USER messages when silent or passive, or when a bootstrapper does not handle the message.
234
+ case IDNOACTION:
235
+ WcaSetReturnValue(ERROR_INSTALL_FAILURE);
236
+ ExitFunction1(hr = S_OK);
237
+
238
+ default:
239
+ ExitOnFailure(hr = E_UNEXPECTED, "Unexpected message response %d from user or bootstrapper application.", er);
240
+ }
241
+ }
242
+
243
+LExit:
244
+ ReleaseDependencyArray(rgDependencies, cDependencies);
245
+ ReleaseStr(sczId);
246
+ ReleaseStr(sczComponent);
247
+ ReleaseStr(sczProviderKey);
248
+ ReleaseStr(sczMinVersion);
249
+ ReleaseStr(sczMaxVersion);
250
+ ReleaseDict(sdDependencies);
251
+
252
+ return hr;
253
+}
254
+
255
+/***************************************************************************
256
+ EnsureAbsentDependents - Checks that there are no dependents
257
+ registered for providers that are being uninstalled.
258
+
259
+ Note: Skipped if UPGRADINGPRODUCTCODE is set.
260
+***************************************************************************/
261
+static HRESULT EnsureAbsentDependents(
262
+ __in MSIHANDLE /*hInstall*/,
263
+ __in BOOL fMachineContext
264
+ )
265
+{
266
+ HRESULT hr = S_OK;
267
+ DWORD er = ERROR_SUCCESS;
268
+ STRINGDICT_HANDLE sdIgnoredDependents = NULL;
269
+ HKEY hkHive = NULL;
270
+ PMSIHANDLE hView = NULL;
271
+ PMSIHANDLE hRec = NULL;
272
+ LPWSTR sczId = NULL;
273
+ LPWSTR sczComponent = NULL;
274
+ LPWSTR sczProviderKey = NULL;
275
+ int iAttributes = 0;
276
+ WCA_TODO tComponentAction = WCA_TODO_UNKNOWN;
277
+ DEPENDENCY* rgDependents = NULL;
278
+ UINT cDependents = 0;
279
+ PMSIHANDLE hDependencyRec = NULL;
280
+
281
+ // Split the IGNOREDEPENDENCIES property for use below if set. If it is "ALL", then quit now.
282
+ hr = SplitIgnoredDependents(&sdIgnoredDependents);
283
+ ExitOnFailure(hr, "Failed to get the ignored dependents.");
284
+
285
+ hr = DictKeyExists(sdIgnoredDependents, L"ALL");
286
+ if (E_NOTFOUND != hr)
287
+ {
288
+ ExitOnFailure(hr, "Failed to check if \"ALL\" was set in IGNOREDEPENDENCIES.");
289
+
290
+ // Otherwise...
291
+ WcaLog(LOGMSG_STANDARD, "Skipping the dependencies check since IGNOREDEPENDENCIES contains \"ALL\".");
292
+ ExitFunction();
293
+ }
294
+ else
295
+ {
296
+ // Key was not found, so proceed.
297
+ hr = S_OK;
298
+ }
299
+
300
+ // Skip the dependent check if the WixDependencyProvider table is missing (no dependency providers).
301
+ hr = WcaTableExists(L"WixDependencyProvider");
302
+ if (S_FALSE == hr)
303
+ {
304
+ WcaLog(LOGMSG_STANDARD, "Skipping the dependents check since no dependency providers are authored.");
305
+ ExitFunction();
306
+ }
307
+
308
+ ExitOnFailure(hr, "Failed to check if the WixDependencyProvider table exists.");
309
+
310
+ // Set the registry hive to use depending on install context.
311
+ hkHive = fMachineContext ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
312
+
313
+ // Loop over the provider components.
314
+ hr = WcaOpenExecuteView(vcsDependencyProviderQuery, &hView);
315
+ ExitOnFailure(hr, "Failed to open the query view for dependency providers.");
316
+
317
+ while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
318
+ {
319
+ hr = WcaGetRecordString(hRec, dpqId, &sczId);
320
+ ExitOnFailure(hr, "Failed to get WixDependencyProvider.WixDependencyProvider.");
321
+
322
+ hr = WcaGetRecordString(hRec, dpqComponent, &sczComponent);
323
+ ExitOnFailure(hr, "Failed to get WixDependencyProvider.Component.");
324
+
325
+ // Skip the current component if its not being uninstalled.
326
+ tComponentAction = WcaGetComponentToDo(sczComponent);
327
+ if (WCA_TODO_UNINSTALL != tComponentAction)
328
+ {
329
+ WcaLog(LOGMSG_STANDARD, "Skipping dependents check for %ls because the component %ls is not being uninstalled.", sczId, sczComponent);
330
+ continue;
331
+ }
332
+
333
+ hr = WcaGetRecordString(hRec, dpqProviderKey, &sczProviderKey);
334
+ ExitOnFailure(hr, "Failed to get WixDependencyProvider.ProviderKey.");
335
+
336
+ hr = WcaGetRecordInteger(hRec, dpqAttributes, &iAttributes);
337
+ ExitOnFailure(hr, "Failed to get WixDependencyProvider.Attributes.");
338
+
339
+ // Check the registry to see if the provider has any dependents registered.
340
+ hr = DepCheckDependents(hkHive, sczProviderKey, iAttributes, sdIgnoredDependents, &rgDependents, &cDependents);
341
+ ExitOnFailure(hr, "Failed dependents check for %ls.", sczId);
342
+ }
343
+
344
+ if (E_NOMOREITEMS != hr)
345
+ {
346
+ ExitOnFailure(hr, "Failed to enumerate all of the rows in the dependency provider query view.");
347
+ }
348
+ else
349
+ {
350
+ hr = S_OK;
351
+ }
352
+
353
+ // If we collected any providers with dependents in the previous check, pump a message and prompt the user.
354
+ if (0 < cDependents)
355
+ {
356
+ hr = CreateDependencyRecord(msierrDependencyHasDependents, rgDependents, cDependents, &hDependencyRec);
357
+ ExitOnFailure(hr, "Failed to create the dependency record for message %d.", msierrDependencyHasDependents);
358
+
359
+ // Send a yes/no message with a warning icon since continuing could be detrimental.
360
+ // This is sent as a USER message to better detect whether a user or dependency-aware bootstrapper is responding
361
+ // or if Windows Installer or a dependency-unaware boostrapper is returning a typical default response.
362
+ er = WcaProcessMessage(static_cast<INSTALLMESSAGE>(INSTALLMESSAGE_USER | MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2), hDependencyRec);
363
+ switch (er)
364
+ {
365
+ // Only a user or dependency-aware bootstrapper that prompted the user should return IDYES to continue anyway.
366
+ case IDYES:
367
+ ExitFunction1(hr = S_OK);
368
+
369
+ // Only a user or dependency-aware bootstrapper that prompted the user should return IDNO to terminate the operation.
370
+ case IDNO:
371
+ __fallthrough;
372
+
373
+ // Bootstrappers which are not dependency-aware may return IDOK for unhandled messages.
374
+ case IDOK:
375
+ __fallthrough;
376
+
377
+ // Windows Installer returns 0 for USER messages when silent or passive, or when a bootstrapper does not handle the message.
378
+ case IDNOACTION:
379
+ WcaSetReturnValue(ERROR_NO_MORE_ITEMS);
380
+ ExitFunction1(hr = S_OK);
381
+
382
+ // A dependency-aware bootstrapper should return IDCANCEL if running silently and the operation should be canceled.
383
+ case IDCANCEL:
384
+ WcaSetReturnValue(ERROR_INSTALL_FAILURE);
385
+ ExitFunction1(hr = S_OK);
386
+
387
+ default:
388
+ hr = E_UNEXPECTED;
389
+ ExitOnFailure(hr, "Unexpected message response %d from user or bootstrapper application.", er);
390
+ }
391
+ }
392
+
393
+LExit:
394
+ ReleaseDependencyArray(rgDependents, cDependents);
395
+ ReleaseStr(sczId);
396
+ ReleaseStr(sczComponent);
397
+ ReleaseStr(sczProviderKey);
398
+
399
+ return hr;
400
+}
401
+
402
+/***************************************************************************
403
+ SplitIgnoredDependents - Splits the IGNOREDEPENDENCIES property into a map.
404
+
405
+***************************************************************************/
406
+static HRESULT SplitIgnoredDependents(
407
+ __deref_inout STRINGDICT_HANDLE* psdIgnoredDependents
408
+ )
409
+{
410
+ HRESULT hr = S_OK;
411
+ LPWSTR sczIgnoreDependencies = NULL;
412
+ LPCWSTR wzDelim = L";";
413
+ LPWSTR wzContext = NULL;
414
+
415
+ hr = WcaGetProperty(L"IGNOREDEPENDENCIES", &sczIgnoreDependencies);
416
+ ExitOnFailure(hr, "Failed to get the string value of the IGNOREDEPENDENCIES property.");
417
+
418
+ hr = DictCreateStringList(psdIgnoredDependents, INITIAL_STRINGDICT_SIZE, DICT_FLAG_CASEINSENSITIVE);
419
+ ExitOnFailure(hr, "Failed to create the string dictionary.");
420
+
421
+ // Parse through the semicolon-delimited tokens and add to the string dictionary.
422
+ for (LPCWSTR wzToken = ::wcstok_s(sczIgnoreDependencies, wzDelim, &wzContext); wzToken; wzToken = ::wcstok_s(NULL, wzDelim, &wzContext))
423
+ {
424
+ hr = DictAddKey(*psdIgnoredDependents, wzToken);
425
+ ExitOnFailure(hr, "Failed to ignored dependency \"%ls\" to the string dictionary.", wzToken);
426
+ }
427
+
428
+LExit:
429
+ ReleaseStr(sczIgnoreDependencies);
430
+
431
+ return hr;
432
+}
433
+
434
+/***************************************************************************
435
+ CreateDependencyRecord - Creates a record containing the message template
436
+ and records to send to the UI handler.
437
+
438
+ Notes: Callers should call WcaProcessMessage and handle return codes.
439
+***************************************************************************/
440
+static HRESULT CreateDependencyRecord(
441
+ __in int iMessageId,
442
+ __in_ecount(cDependencies) const DEPENDENCY* rgDependencies,
443
+ __in UINT cDependencies,
444
+ __out MSIHANDLE *phRecord
445
+ )
446
+{
447
+ HRESULT hr = S_OK;
448
+ UINT er = ERROR_SUCCESS;
449
+ UINT cParams = 0;
450
+ UINT iParam = 0;
451
+
452
+ // Should not be PMSIHANDLE.
453
+ MSIHANDLE hRec = NULL;
454
+
455
+ // Calculate the number of parameters based on the format:
456
+ // msgId, count, key1, name1, key2, name2, etc.
457
+ cParams = 2 + 2 * cDependencies;
458
+
459
+ hRec = ::MsiCreateRecord(cParams);
460
+ ExitOnNull(hRec, hr, E_OUTOFMEMORY, "Not enough memory to create the message record.");
461
+
462
+ er = ::MsiRecordSetInteger(hRec, ++iParam, iMessageId);
463
+ ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to set the message identifier into the message record.");
464
+
465
+ er = ::MsiRecordSetInteger(hRec, ++iParam, cDependencies);
466
+ ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to set the number of dependencies into the message record.");
467
+
468
+ // Now loop through each dependency and add the key and name to the record.
469
+ for (UINT i = 0; i < cDependencies; i++)
470
+ {
471
+ const DEPENDENCY* pDependency = &rgDependencies[i];
472
+
473
+ // Log message type-specific information.
474
+ switch (iMessageId)
475
+ {
476
+ // Send a user message when installing a component that is missing some dependencies.
477
+ case msierrDependencyMissingDependencies:
478
+ WcaLog(LOGMSG_VERBOSE, "The dependency \"%ls\" is missing or is not the required version.", pDependency->sczKey);
479
+ break;
480
+
481
+ // Send a user message when uninstalling a component that still has registered dependents.
482
+ case msierrDependencyHasDependents:
483
+ WcaLog(LOGMSG_VERBOSE, "Found dependent \"%ls\", name: \"%ls\".", pDependency->sczKey, LogDependencyName(pDependency->sczName));
484
+ break;
485
+ }
486
+
487
+ er = ::MsiRecordSetStringW(hRec, ++iParam, pDependency->sczKey);
488
+ ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to set the dependency key \"%ls\" into the message record.", pDependency->sczKey);
489
+
490
+ er = ::MsiRecordSetStringW(hRec, ++iParam, pDependency->sczName);
491
+ ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to set the dependency name \"%ls\" into the message record.", pDependency->sczName);
492
+ }
493
+
494
+ // Only assign the out parameter if successful to this point.
495
+ *phRecord = hRec;
496
+ hRec = NULL;
497
+
498
+LExit:
499
+ if (hRec)
500
+ {
501
+ ::MsiCloseHandle(hRec);
502
+ }
503
+
504
+ return hr;
505
+}
506
+
507
+/***************************************************************************
508
+ LogDependencyName - Returns the dependency name or "Unknown" if null.
509
+
510
+***************************************************************************/
511
+static LPCWSTR LogDependencyName(
512
+ __in_z LPCWSTR wzName
513
+ )
514
+{
515
+ return wzName ? wzName : L"Unknown";
516
+}
src/ca/wixdepca.def
new
+8
@@ -0,0 +1,8 @@
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
+
4
+LIBRARY "wixdepca"
5
+
6
+EXPORTS
7
+ WixDependencyRequire
8
+ WixDependencyCheck
src/ca/wixdepca.vcxproj
new
+57
@@ -0,0 +1,57 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<!-- 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. -->
3
+
4
+
5
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
6
+ <ItemGroup Label="ProjectConfigurations">
7
+ <ProjectConfiguration Include="Debug|Win32">
8
+ <Configuration>Debug</Configuration>
9
+ <Platform>Win32</Platform>
10
+ </ProjectConfiguration>
11
+ <ProjectConfiguration Include="Release|Win32">
12
+ <Configuration>Release</Configuration>
13
+ <Platform>Win32</Platform>
14
+ </ProjectConfiguration>
15
+ </ItemGroup>
16
+ <ItemGroup Label="ProjectConfigurations">
17
+ <ProjectConfiguration Include="Debug|ARM">
18
+ <Configuration>Debug</Configuration>
19
+ <Platform>ARM</Platform>
20
+ </ProjectConfiguration>
21
+ <ProjectConfiguration Include="Release|ARM">
22
+ <Configuration>Release</Configuration>
23
+ <Platform>ARM</Platform>
24
+ </ProjectConfiguration>
25
+ </ItemGroup>
26
+
27
+ <PropertyGroup Label="Globals">
28
+ <ProjectGuid>{B86AF46C-0F90-49CC-923F-A800B088D015}</ProjectGuid>
29
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
30
+ <CharacterSet>Unicode</CharacterSet>
31
+ <TargetName>WixDepCA</TargetName>
32
+ <ProjectModuleDefinitionFile>wixdepca.def</ProjectModuleDefinitionFile>
33
+ </PropertyGroup>
34
+
35
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), wix.proj))\tools\WixBuild.props" />
36
+
37
+ <PropertyGroup>
38
+ <ProjectAdditionalIncludeDirectories>$(WixRoot)src\libs\dutil\inc;$(WixRoot)src\libs\wcautil;$(WixRoot)src\libs\deputil\inc</ProjectAdditionalIncludeDirectories>
39
+ <ProjectAdditionalLinkLibraries>msi.lib;dutil.lib;deputil.lib;wcautil.lib</ProjectAdditionalLinkLibraries>
40
+ </PropertyGroup>
41
+
42
+ <ItemGroup>
43
+ <ClCompile Include="dllmain.cpp" />
44
+ <ClCompile Include="wixdepca.cpp" />
45
+ </ItemGroup>
46
+ <ItemGroup>
47
+ <ClInclude Include="precomp.h" />
48
+ </ItemGroup>
49
+ <ItemGroup>
50
+ <None Include="wixdepca.def" />
51
+ </ItemGroup>
52
+ <ItemGroup>
53
+ <ResourceCompile Include="wixdepca.rc" />
54
+ </ItemGroup>
55
+
56
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), wix.proj))\tools\WixBuild.targets" />
57
+</Project>
src/ca/wixdepca.vcxproj.filters
new
+40
@@ -0,0 +1,40 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3
+ <ItemGroup>
4
+ <Filter Include="Source Files">
5
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
6
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
7
+ </Filter>
8
+ <Filter Include="Header Files">
9
+ <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
10
+ <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
11
+ </Filter>
12
+ <Filter Include="Resource Files">
13
+ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
14
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
15
+ </Filter>
16
+ </ItemGroup>
17
+ <ItemGroup>
18
+ <ClCompile Include="wixdepca.cpp">
19
+ <Filter>Source Files</Filter>
20
+ </ClCompile>
21
+ <ClCompile Include="dllmain.cpp">
22
+ <Filter>Source Files</Filter>
23
+ </ClCompile>
24
+ </ItemGroup>
25
+ <ItemGroup>
26
+ <ClInclude Include="precomp.h">
27
+ <Filter>Header Files</Filter>
28
+ </ClInclude>
29
+ </ItemGroup>
30
+ <ItemGroup>
31
+ <ResourceCompile Include="wixdepca.rc">
32
+ <Filter>Resource Files</Filter>
33
+ </ResourceCompile>
34
+ </ItemGroup>
35
+ <ItemGroup>
36
+ <None Include="wixdepca.def">
37
+ <Filter>Source Files</Filter>
38
+ </None>
39
+ </ItemGroup>
40
+</Project>
\ No newline at end of file
src/wixext/Dependency.xsd
new
+226
@@ -0,0 +1,226 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<!-- 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. -->
3
+
4
+
5
+<xs:schema xmlns:html="http://www.w3.org/1999/xhtml"
6
+ xmlns:wix="http://wixtoolset.org/schemas/v4/wxs"
7
+ xmlns:xs="http://www.w3.org/2001/XMLSchema"
8
+ xmlns:xse=" http://wixtoolset.org/schemas/XmlSchemaExtension"
9
+ targetNamespace="http://wixtoolset.org/schemas/v4/wxs/dependency"
10
+ xmlns="http://wixtoolset.org/schemas/v4/wxs/dependency">
11
+ <xs:annotation>
12
+ <xs:documentation>
13
+ The source code schema for the WiX Toolset Dependency Extension.
14
+ </xs:documentation>
15
+ </xs:annotation>
16
+ <xs:element name="Provides">
17
+ <xs:annotation>
18
+ <xs:documentation>
19
+ Describes the information for this product or feature that serves as a dependency of other products or features.
20
+ </xs:documentation>
21
+ <xs:appinfo>
22
+ <xse:parent namespace="http://wixtoolset.org/schemas/v4/wxs" ref="Component" />
23
+ <xse:parent namespace="http://wixtoolset.org/schemas/v4/wxs" ref="ExePackage" />
24
+ <xse:parent namespace="http://wixtoolset.org/schemas/v4/wxs" ref="MsiPackage" />
25
+ <xse:parent namespace="http://wixtoolset.org/schemas/v4/wxs" ref="MspPackage" />
26
+ <xse:parent namespace="http://wixtoolset.org/schemas/v4/wxs" ref="MsuPackage" />
27
+ <xse:remarks>
28
+ <html:p>
29
+ This element is required for any product, feature, or bundle that will use the Dependency feature to properly reference count
30
+ other products or features. It should be authored into a component that is always installed and removed with the
31
+ product or features that contain it. This guarantees that product dependencies are not removed before those products that
32
+ depend on them.
33
+ </html:p>
34
+ <html:p>
35
+ The @Key attribute should identify a version range for your product that you guarantee will be backward compatible.
36
+ This key is designed to persist throughout compatible upgrades so that dependent products do not have to be reinstalled
37
+ and will not prevent your product from being upgraded. If this attribute is not authored, the value is the ProductCode
38
+ and will not automatically support upgrades.
39
+ </html:p>
40
+ <html:p>
41
+ By default this uses the Product/@Id attribute value, which may be automatically generated.
42
+ </html:p>
43
+ </xse:remarks>
44
+ <xse:howtoRef href="author_product_dependencies.html">How To: Author product dependencies</xse:howtoRef>
45
+ </xs:appinfo>
46
+ </xs:annotation>
47
+ <xs:complexType>
48
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
49
+ <xs:element ref="Requires" />
50
+ <xs:element ref="RequiresRef" />
51
+ </xs:choice>
52
+ <xs:attribute name="Id" type="xs:string">
53
+ <xs:annotation>
54
+ <xs:documentation>
55
+ Dependency provider identity. If this attribute is not specified, an identifier will be generated automatically.
56
+ </xs:documentation>
57
+ </xs:annotation>
58
+ </xs:attribute>
59
+ <xs:attribute name="Key" type="xs:string">
60
+ <xs:annotation>
61
+ <xs:documentation>
62
+ Optional unique registry key name that identifies a product version range on which other products can depend.
63
+ This attribute is required in package authoring, but optional for components.
64
+ </xs:documentation>
65
+ </xs:annotation>
66
+ </xs:attribute>
67
+ <xs:attribute name="Version" type="VersionType">
68
+ <xs:annotation>
69
+ <xs:documentation>
70
+ The version of the package. For MSI packages, the ProductVersion will be used by default
71
+ and this attribute should not be specified.
72
+ </xs:documentation>
73
+ </xs:annotation>
74
+ </xs:attribute>
75
+ <xs:attribute name="DisplayName" type="xs:string">
76
+ <xs:annotation>
77
+ <xs:documentation>
78
+ Optional display name of the package. For MSI packages, the ProductName will be used by default.
79
+ </xs:documentation>
80
+ </xs:annotation>
81
+ </xs:attribute>
82
+ </xs:complexType>
83
+ </xs:element>
84
+ <xs:element name="Requires">
85
+ <xs:annotation>
86
+ <xs:documentation>
87
+ Describes a dependency on a provider for the current component or package.
88
+ </xs:documentation>
89
+ <xs:appinfo>
90
+ <xse:parent namespace="http://wixtoolset.org/schemas/v4/wxs" ref="Bundle" />
91
+ <xse:parent namespace="http://wixtoolset.org/schemas/v4/wxs" ref="Fragment" />
92
+ <xse:parent namespace="http://wixtoolset.org/schemas/v4/wxs" ref="Module" />
93
+ <xse:parent namespace="http://wixtoolset.org/schemas/v4/wxs" ref="Product" />
94
+ <xse:remarks>
95
+ <html:p>
96
+ This element declares a dependency on any product that uses the Provides element. If that product is uninstalled
97
+ before a product that requires it, the uninstall will err or warn the user that other products are installed
98
+ which depend on that product. This behavior can be modified by changing the attribute values on the Requires element.
99
+ </html:p>
100
+ <html:p>
101
+ If you do not nest this element under a Provides element, you must specify the @Id attribute
102
+ so that it can be referenced by a RequiresRef element nested under a Provides element.
103
+ </html:p>
104
+ </xse:remarks>
105
+ <xse:seeAlso ref="RequiresRef" />
106
+ <xse:howtoRef href="author_product_dependencies.html">How To: Author product dependencies</xse:howtoRef>
107
+ </xs:appinfo>
108
+ </xs:annotation>
109
+ <xs:complexType>
110
+ <xs:attribute name="Id" type="xs:string">
111
+ <xs:annotation>
112
+ <xs:documentation>
113
+ Dependency requirement identity. If this attribute is not specified, an identifier will be generated automatically.
114
+ If this element is not authored under a Provides element, this attribute is required.
115
+ </xs:documentation>
116
+ </xs:annotation>
117
+ </xs:attribute>
118
+ <xs:attribute name="ProviderKey" type="xs:string" use="required">
119
+ <xs:annotation>
120
+ <xs:documentation>
121
+ The unique registry key name for the dependency provider to require during installation of this product.
122
+ </xs:documentation>
123
+ </xs:annotation>
124
+ </xs:attribute>
125
+ <xs:attribute name="Minimum" type="VersionType">
126
+ <xs:annotation>
127
+ <xs:documentation>
128
+ The minimum version of the dependency provider required to be installed. The default is unbound.
129
+ </xs:documentation>
130
+ </xs:annotation>
131
+ </xs:attribute>
132
+ <xs:attribute name="Maximum" type="VersionType">
133
+ <xs:annotation>
134
+ <xs:documentation>
135
+ The maximum version of the dependency provider required to be installed. The default is unbound.
136
+ </xs:documentation>
137
+ </xs:annotation>
138
+ </xs:attribute>
139
+ <xs:attribute name="IncludeMinimum" type="YesNoType">
140
+ <xs:annotation>
141
+ <xs:documentation>
142
+ Set to "yes" to make the range of dependency provider versions required include the value specified in Minimum.
143
+ </xs:documentation>
144
+ </xs:annotation>
145
+ </xs:attribute>
146
+ <xs:attribute name="IncludeMaximum" type="YesNoType">
147
+ <xs:annotation>
148
+ <xs:documentation>
149
+ Set to "yes" to make the range of dependency provider versions required include the value specified in Maximum.
150
+ </xs:documentation>
151
+ </xs:annotation>
152
+ </xs:attribute>
153
+ </xs:complexType>
154
+ </xs:element>
155
+ <xs:element name="RequiresRef">
156
+ <xs:annotation>
157
+ <xs:documentation>
158
+ References existing authoring for a dependency on a provider for the current component or package.
159
+ </xs:documentation>
160
+ <xs:appinfo>
161
+ <xse:remarks>
162
+ <html:p>
163
+ This element references a dependency on any product that uses the Provides element. If that product is uninstalled
164
+ before a product that requires it, the uninstall will err or warn the user that other products are installed
165
+ which depend on that product. This behavior can be modified by changing the attribute values on the Requires element.
166
+ </html:p>
167
+ </xse:remarks>
168
+ <xse:seeAlso ref="Requires" />
169
+ <xse:howtoRef href="author_product_dependencies.html">How To: Author product dependencies</xse:howtoRef>
170
+ </xs:appinfo>
171
+ </xs:annotation>
172
+ <xs:complexType>
173
+ <xs:attribute name="Id" type="xs:string" use="required">
174
+ <xs:annotation>
175
+ <xs:documentation>
176
+ The identifier of the Requires element to reference.
177
+ </xs:documentation>
178
+ </xs:annotation>
179
+ </xs:attribute>
180
+ </xs:complexType>
181
+ </xs:element>
182
+ <xs:attribute name="ProviderKey" type="xs:string">
183
+ <xs:annotation>
184
+ <xs:documentation>
185
+ Optional attribute to explicitly author the provider key for the entire bundle.
186
+ </xs:documentation>
187
+ <xs:appinfo>
188
+ <xse:parent namespace="http://wixtoolset.org/schemas/v4/wxs" ref="Bundle" />
189
+ <xse:remarks>
190
+ <html:p>
191
+ This provider key is designed to persist throughout compatible upgrades so that dependent bundles do not have to be reinstalled
192
+ and will not prevent your product from being upgraded. If this attribute is not authored, the value is the
193
+ automatically-generated bundle ID and will not automatically support upgrades.
194
+ </html:p>
195
+ <html:p>
196
+ Only a single provider key is supported for bundles. To author that your bundle provides additional features via
197
+ packages, author different provider keys for your packages.
198
+ </html:p>
199
+ </xse:remarks>
200
+ <xse:seeAlso ref="Provides" />
201
+ </xs:appinfo>
202
+ </xs:annotation>
203
+ </xs:attribute>
204
+ <xs:simpleType name="VersionType">
205
+ <xs:annotation>
206
+ <xs:documentation>
207
+ Values of this type will look like: "x.x.x.x" where x is an integer from 0 to 65534.
208
+ This can also be a preprocessor, binder, or WiX variable.
209
+ </xs:documentation>
210
+ </xs:annotation>
211
+ <xs:restriction base="xs:string">
212
+ <xs:pattern value="(\d{1,5}\.){3}\d{1,5}|[!$]\((var|bind|wix)\.[_A-Za-z][\w\.]*\)" />
213
+ </xs:restriction>
214
+ </xs:simpleType>
215
+ <xs:simpleType name="YesNoType">
216
+ <xs:annotation>
217
+ <xs:documentation>
218
+ Values of this type will either be "yes" or "no".
219
+ </xs:documentation>
220
+ </xs:annotation>
221
+ <xs:restriction base="xs:NMTOKEN">
222
+ <xs:enumeration value="no" />
223
+ <xs:enumeration value="yes" />
224
+ </xs:restriction>
225
+ </xs:simpleType>
226
+</xs:schema>
src/wixext/DependencyBinder.cs
new
+169
@@ -0,0 +1,169 @@
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
+namespace WixToolset.Extensions
4
+{
5
+ using System;
6
+ using System.Collections.ObjectModel;
7
+ using System.Globalization;
8
+ using WixToolset.Data;
9
+ using WixToolset.Extensibility;
10
+
11
+ /// <summary>
12
+ /// The compiler for the WiX toolset dependency extension.
13
+ /// </summary>
14
+ public sealed class DependencyBinder : BinderExtension
15
+ {
16
+ private Output output;
17
+
18
+ /// <summary>
19
+ /// Called after all output changes occur and right before the output is bound into its final format.
20
+ /// </summary>
21
+ public override void Finish(Output output)
22
+ {
23
+ // Only process MSI packages.
24
+ if (OutputType.Product != output.Type)
25
+ {
26
+ return;
27
+ }
28
+
29
+ this.output = output;
30
+
31
+ Table wixDependencyTable = output.Tables["WixDependency"];
32
+ Table wixDependencyProviderTable = output.Tables["WixDependencyProvider"];
33
+ Table wixDependencyRefTable = output.Tables["WixDependencyRef"];
34
+
35
+ // Make sure there's something to do.
36
+ if (null != wixDependencyRefTable)
37
+ {
38
+ KeyedRowCollection wixDependencyRows = new KeyedRowCollection(wixDependencyTable);
39
+ KeyedRowCollection wixDependencyProviderRows = new KeyedRowCollection(wixDependencyProviderTable);
40
+
41
+ // For each relationship, get the provides and requires rows to generate registry values.
42
+ foreach (Row wixDependencyRefRow in wixDependencyRefTable.Rows)
43
+ {
44
+ string providesId = (string)wixDependencyRefRow[0];
45
+ string requiresId = (string)wixDependencyRefRow[1];
46
+
47
+ Row wixDependencyRow = null;
48
+ if (wixDependencyRows.Contains(requiresId))
49
+ {
50
+ wixDependencyRow = wixDependencyRows[requiresId];
51
+ }
52
+
53
+ Row wixDependencyProviderRow = null;
54
+ if (wixDependencyProviderRows.Contains(providesId))
55
+ {
56
+ wixDependencyProviderRow = wixDependencyProviderRows[providesId];
57
+ }
58
+
59
+ // If we found both rows, generate the registry values.
60
+ if (null != wixDependencyRow && null != wixDependencyProviderRow)
61
+ {
62
+ // Format the root registry key using the required provider key and the current provider key.
63
+ string requiresKey = (string)wixDependencyRow[1];
64
+ string providesKey = (string)wixDependencyProviderRow[2];
65
+ string keyRequires = String.Format(@"{0}{1}\{2}\{3}", DependencyCommon.RegistryRoot, requiresKey, DependencyCommon.RegistryDependents, providesKey);
66
+
67
+ // Get the component ID from the provider.
68
+ string componentId = (string)wixDependencyProviderRow[1];
69
+
70
+ Row row = this.CreateRegistryRow(wixDependencyRow);
71
+ row[0] = this.Core.CreateIdentifier("reg", providesId, requiresId, "(Default)");
72
+ row[1] = -1;
73
+ row[2] = keyRequires;
74
+ row[3] = "*";
75
+ row[4] = null;
76
+ row[5] = componentId;
77
+
78
+ string minVersion = (string)wixDependencyRow[2];
79
+ if (!String.IsNullOrEmpty(minVersion))
80
+ {
81
+ row = this.CreateRegistryRow(wixDependencyRow);
82
+ row[0] = this.Core.CreateIdentifier("reg", providesId, requiresId, "MinVersion");
83
+ row[1] = -1;
84
+ row[2] = keyRequires;
85
+ row[3] = "MinVersion";
86
+ row[4] = minVersion;
87
+ row[5] = componentId;
88
+ }
89
+
90
+ string maxVersion = (string)wixDependencyRow[3];
91
+ if (!String.IsNullOrEmpty(minVersion))
92
+ {
93
+ row = this.CreateRegistryRow(wixDependencyRow);
94
+ row[0] = this.Core.CreateIdentifier("reg", providesId, requiresId, "MaxVersion");
95
+ row[1] = -1;
96
+ row[2] = keyRequires;
97
+ row[3] = "MaxVersion";
98
+ row[4] = maxVersion;
99
+ row[5] = componentId;
100
+ }
101
+
102
+ if (null != wixDependencyRow[4])
103
+ {
104
+ int attributes = (int)wixDependencyRow[4];
105
+
106
+ row = this.CreateRegistryRow(wixDependencyRow);
107
+ row[0] = this.Core.CreateIdentifier("reg", providesId, requiresId, "Attributes");
108
+ row[1] = -1;
109
+ row[2] = keyRequires;
110
+ row[3] = "Attributes";
111
+ row[4] = String.Concat("#", attributes.ToString(CultureInfo.InvariantCulture.NumberFormat));
112
+ row[5] = componentId;
113
+ }
114
+ }
115
+ }
116
+ }
117
+ }
118
+
119
+ /// <summary>
120
+ /// Creates a registry row using source information from the given <see cref="Row"/>.
121
+ /// </summary>
122
+ /// <param name="referenceRow">The <see cref="Row"/> from which the section and source line information are retrieved.</param>
123
+ /// <returns>A new Registry row.</returns>
124
+ private Row CreateRegistryRow(Row referenceRow)
125
+ {
126
+ TableDefinition tableDefinition = this.Core.TableDefinitions["Registry"];
127
+
128
+ // Create the row from the main tables, which were populated during link anyway.
129
+ // We still associate the table with the dependency row's section to maintain servicing.
130
+ Table table = this.output.EnsureTable(tableDefinition, referenceRow.Table.Section);
131
+ Row row = table.CreateRow(referenceRow.SourceLineNumbers);
132
+
133
+ // Set the section ID for patching and return the new row.
134
+ row.SectionId = referenceRow.SectionId;
135
+ return row;
136
+ }
137
+
138
+ /// <summary>
139
+ /// A keyed collection of <see cref="Row"/> instances for O(1) lookup.
140
+ /// </summary>
141
+ private sealed class KeyedRowCollection : KeyedCollection<string, Row>
142
+ {
143
+ /// <summary>
144
+ /// Initializes the <see cref="KeyedRowCollection"/> class with all rows from the specified <paramref name="table"/>.
145
+ /// </summary>
146
+ /// <param name="table">The <see cref="Table"/> containing rows to index.</param>
147
+ internal KeyedRowCollection(Table table)
148
+ {
149
+ if (null != table)
150
+ {
151
+ foreach (Row row in table.Rows)
152
+ {
153
+ this.Add(row);
154
+ }
155
+ }
156
+ }
157
+
158
+ /// <summary>
159
+ /// Gets the primary key for the <see cref="Row"/>.
160
+ /// </summary>
161
+ /// <param name="row">The <see cref="Row"/> to index.</param>
162
+ /// <returns>The primary key for the <see cref="Row"/>.</returns>
163
+ protected override string GetKeyForItem(Row row)
164
+ {
165
+ return row.GetPrimaryKey('/');
166
+ }
167
+ }
168
+ }
169
+}
src/wixext/DependencyCommon.cs
new
+26
@@ -0,0 +1,26 @@
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
+namespace WixToolset.Extensions
4
+{
5
+ using System;
6
+ using WixToolset;
7
+
8
+ internal static class DependencyCommon
9
+ {
10
+ // Bundle attributes are in the upper 32-bits.
11
+ internal const int ProvidesAttributesBundle = 0x10000;
12
+
13
+ // Same values as for the Upgrade table in Windows Installer.
14
+ internal const int RequiresAttributesMinVersionInclusive = 256;
15
+ internal const int RequiresAttributesMaxVersionInclusive = 512;
16
+
17
+ // The root registry key for the dependency extension. We write to Software\Classes explicitly
18
+ // based on the current security context instead of HKCR. See
19
+ // http://msdn.microsoft.com/en-us/library/ms724475(VS.85).aspx for more information.
20
+ internal static readonly string RegistryRoot = @"Software\Classes\Installer\Dependencies\";
21
+ internal static readonly string RegistryDependents = "Dependents";
22
+
23
+ // The following characters cannot be used in a provider key.
24
+ internal static readonly char[] InvalidCharacters = new char[] { ' ', '\"', ';', '\\' };
25
+ }
26
+}
src/wixext/DependencyCompiler.cs
new
+615
@@ -0,0 +1,615 @@
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
+namespace WixToolset.Extensions
4
+{
5
+ using System;
6
+ using System.Collections.Generic;
7
+ using System.Globalization;
8
+ using System.Text;
9
+ using System.Xml.Linq;
10
+ using WixToolset.Data;
11
+ using WixToolset.Extensibility;
12
+
13
+ /// <summary>
14
+ /// The compiler for the WiX toolset dependency extension.
15
+ /// </summary>
16
+ public sealed class DependencyCompiler : CompilerExtension
17
+ {
18
+ /// <summary>
19
+ /// Package type when parsing the Provides element.
20
+ /// </summary>
21
+ private enum PackageType
22
+ {
23
+ None,
24
+ ExePackage,
25
+ MsiPackage,
26
+ MspPackage,
27
+ MsuPackage
28
+ }
29
+
30
+ public DependencyCompiler()
31
+ {
32
+ this.Namespace = "http://wixtoolset.org/schemas/v4/wxs/dependency";
33
+ }
34
+
35
+ /// <summary>
36
+ /// Processes an attribute for the Compiler.
37
+ /// </summary>
38
+ /// <param name="sourceLineNumbers">Source line number for the parent element.</param>
39
+ /// <param name="parentElement">Parent element of attribute.</param>
40
+ /// <param name="attribute">Attribute to process.</param>
41
+ public override void ParseAttribute(XElement parentElement, XAttribute attribute, IDictionary<string, string> context)
42
+ {
43
+ SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(parentElement);
44
+ switch (parentElement.Name.LocalName)
45
+ {
46
+ case "Bundle":
47
+ switch (attribute.Name.LocalName)
48
+ {
49
+ case "ProviderKey":
50
+ this.ParseProviderKeyAttribute(sourceLineNumbers, parentElement, attribute);
51
+ break;
52
+ default:
53
+ this.Core.UnexpectedAttribute(parentElement, attribute);
54
+ break;
55
+ }
56
+ break;
57
+ default:
58
+ this.Core.UnexpectedAttribute(parentElement, attribute);
59
+ break;
60
+ }
61
+ }
62
+
63
+ /// <summary>
64
+ /// Processes an element for the Compiler.
65
+ /// </summary>
66
+ /// <param name="sourceLineNumbers">Source line number for the parent element.</param>
67
+ /// <param name="parentElement">Parent element of element to process.</param>
68
+ /// <param name="element">Element to process.</param>
69
+ /// <param name="contextValues">Extra information about the context in which this element is being parsed.</param>
70
+ public override void ParseElement(XElement parentElement, XElement element, IDictionary<string, string> context)
71
+ {
72
+ PackageType packageType = PackageType.None;
73
+
74
+ switch (parentElement.Name.LocalName)
75
+ {
76
+ case "Bundle":
77
+ case "Fragment":
78
+ case "Module":
79
+ case "Product":
80
+ switch (element.Name.LocalName)
81
+ {
82
+ case "Requires":
83
+ this.ParseRequiresElement(element, null, false);
84
+ break;
85
+ default:
86
+ this.Core.UnexpectedElement(parentElement, element);
87
+ break;
88
+ }
89
+ break;
90
+ case "ExePackage":
91
+ packageType = PackageType.ExePackage;
92
+ break;
93
+ case "MsiPackage":
94
+ packageType = PackageType.MsiPackage;
95
+ break;
96
+ case "MspPackage":
97
+ packageType = PackageType.MspPackage;
98
+ break;
99
+ case "MsuPackage":
100
+ packageType = PackageType.MsuPackage;
101
+ break;
102
+ default:
103
+ this.Core.UnexpectedElement(parentElement, element);
104
+ break;
105
+ }
106
+
107
+ if (PackageType.None != packageType)
108
+ {
109
+ string packageId = context["PackageId"];
110
+
111
+ switch (element.Name.LocalName)
112
+ {
113
+ case "Provides":
114
+ this.ParseProvidesElement(element, packageType, packageId);
115
+ break;
116
+ default:
117
+ this.Core.UnexpectedElement(parentElement, element);
118
+ break;
119
+ }
120
+ }
121
+ }
122
+
123
+ /// <summary>
124
+ /// Processes a child element of a Component for the Compiler.
125
+ /// </summary>
126
+ /// <param name="parentElement">Parent element of element to process.</param>
127
+ /// <param name="element">Element to process.</param>
128
+ /// <param name="context">Extra information about the context in which this element is being parsed.</param>
129
+ /// <returns>The component key path type if set.</returns>
130
+ public override ComponentKeyPath ParsePossibleKeyPathElement(XElement parentElement, XElement element, IDictionary<string, string> context)
131
+ {
132
+ SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(parentElement);
133
+ ComponentKeyPath keyPath = null;
134
+
135
+ switch (parentElement.Name.LocalName)
136
+ {
137
+ case "Component":
138
+ string componentId = context["ComponentId"];
139
+
140
+ // 64-bit components may cause issues downlevel.
141
+ bool win64 = false;
142
+ Boolean.TryParse(context["Win64"], out win64);
143
+
144
+ switch (element.Name.LocalName)
145
+ {
146
+ case "Provides":
147
+ if (win64)
148
+ {
149
+ this.Core.OnMessage(DependencyWarnings.Win64Component(sourceLineNumbers, componentId));
150
+ }
151
+
152
+ keyPath = this.ParseProvidesElement(element, PackageType.None, componentId);
153
+ break;
154
+ default:
155
+ this.Core.UnexpectedElement(parentElement, element);
156
+ break;
157
+ }
158
+ break;
159
+ default:
160
+ this.Core.UnexpectedElement(parentElement, element);
161
+ break;
162
+ }
163
+
164
+ return keyPath;
165
+ }
166
+
167
+ /// <summary>
168
+ /// Processes the ProviderKey bundle attribute.
169
+ /// </summary>
170
+ /// <param name="sourceLineNumbers">Source line number for the parent element.</param>
171
+ /// <param name="parentElement">Parent element of attribute.</param>
172
+ /// <param name="attribute">The XML attribute for the ProviderKey attribute.</param>
173
+ private void ParseProviderKeyAttribute(SourceLineNumber sourceLineNumbers, XElement parentElement, XAttribute attribute)
174
+ {
175
+ Identifier id = null;
176
+ string providerKey = null;
177
+ int illegalChar = -1;
178
+
179
+ switch (attribute.Name.LocalName)
180
+ {
181
+ case "ProviderKey":
182
+ providerKey = this.Core.GetAttributeValue(sourceLineNumbers, attribute);
183
+ break;
184
+ default:
185
+ this.Core.UnexpectedAttribute(parentElement, attribute);
186
+ break;
187
+ }
188
+
189
+ // Make sure the key does not contain any illegal characters or values.
190
+ if (String.IsNullOrEmpty(providerKey))
191
+ {
192
+ this.Core.OnMessage(WixErrors.IllegalEmptyAttributeValue(sourceLineNumbers, parentElement.Name.LocalName, attribute.Name.LocalName));
193
+ }
194
+ else if (0 <= (illegalChar = providerKey.IndexOfAny(DependencyCommon.InvalidCharacters)))
195
+ {
196
+ StringBuilder sb = new StringBuilder(DependencyCommon.InvalidCharacters.Length * 2);
197
+ Array.ForEach<char>(DependencyCommon.InvalidCharacters, c => sb.Append(c).Append(" "));
198
+
199
+ this.Core.OnMessage(DependencyErrors.IllegalCharactersInProvider(sourceLineNumbers, "ProviderKey", providerKey[illegalChar], sb.ToString()));
200
+ }
201
+ else if ("ALL" == providerKey)
202
+ {
203
+ this.Core.OnMessage(DependencyErrors.ReservedValue(sourceLineNumbers, parentElement.Name.LocalName, "ProviderKey", providerKey));
204
+ }
205
+
206
+ // Generate the primary key for the row.
207
+ id = this.Core.CreateIdentifier("dep", attribute.Name.LocalName, providerKey);
208
+
209
+ if (!this.Core.EncounteredError)
210
+ {
211
+ // Create the provider row for the bundle. The Component_ field is required
212
+ // in the table definition but unused for bundles, so just set it to the valid ID.
213
+ Row row = this.Core.CreateRow(sourceLineNumbers, "WixDependencyProvider", id);
214
+ row[1] = id.Id;
215
+ row[2] = providerKey;
216
+ row[5] = DependencyCommon.ProvidesAttributesBundle;
217
+ }
218
+ }
219
+
220
+ /// <summary>
221
+ /// Processes the Provides element.
222
+ /// </summary>
223
+ /// <param name="node">The XML node for the Provides element.</param>
224
+ /// <param name="packageType">The type of the package being chained into a bundle, or "None" if building an MSI package.</param>
225
+ /// <param name="keyPath">Explicit key path.</param>
226
+ /// <param name="parentId">The identifier of the parent component or package.</param>
227
+ /// <returns>The type of key path if set.</returns>
228
+ private ComponentKeyPath ParseProvidesElement(XElement node, PackageType packageType, string parentId)
229
+ {
230
+ SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
231
+ ComponentKeyPath keyPath = null;
232
+ Identifier id = null;
233
+ string key = null;
234
+ string version = null;
235
+ string displayName = null;
236
+ int attributes = 0;
237
+ int illegalChar = -1;
238
+
239
+ foreach (XAttribute attrib in node.Attributes())
240
+ {
241
+ if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
242
+ {
243
+ switch (attrib.Name.LocalName)
244
+ {
245
+ case "Id":
246
+ id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
247
+ break;
248
+ case "Key":
249
+ key = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
250
+ break;
251
+ case "Version":
252
+ version = this.Core.GetAttributeVersionValue(sourceLineNumbers, attrib);
253
+ break;
254
+ case "DisplayName":
255
+ displayName = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
256
+ break;
257
+ default:
258
+ this.Core.UnexpectedAttribute(node, attrib);
259
+ break;
260
+ }
261
+ }
262
+ else
263
+ {
264
+ this.Core.ParseExtensionAttribute(node, attrib);
265
+ }
266
+ }
267
+
268
+ // Make sure the key is valid. The key will default to the ProductCode for MSI packages
269
+ // and the package code for MSP packages in the binder if not specified.
270
+ if (!String.IsNullOrEmpty(key))
271
+ {
272
+ // Make sure the key does not contain any illegal characters or values.
273
+ if (0 <= (illegalChar = key.IndexOfAny(DependencyCommon.InvalidCharacters)))
274
+ {
275
+ StringBuilder sb = new StringBuilder(DependencyCommon.InvalidCharacters.Length * 2);
276
+ Array.ForEach<char>(DependencyCommon.InvalidCharacters, c => sb.Append(c).Append(" "));
277
+
278
+ this.Core.OnMessage(DependencyErrors.IllegalCharactersInProvider(sourceLineNumbers, "Key", key[illegalChar], sb.ToString()));
279
+ }
280
+ else if ("ALL" == key)
281
+ {
282
+ this.Core.OnMessage(DependencyErrors.ReservedValue(sourceLineNumbers, node.Name.LocalName, "Key", key));
283
+ }
284
+ }
285
+ else if (PackageType.ExePackage == packageType || PackageType.MsuPackage == packageType)
286
+ {
287
+ // Must specify the provider key when authored for a package.
288
+ this.Core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Key"));
289
+ }
290
+ else if (PackageType.None == packageType)
291
+ {
292
+ // Make sure the ProductCode is authored and set the key.
293
+ this.Core.CreateSimpleReference(sourceLineNumbers, "Property", "ProductCode");
294
+ key = "!(bind.property.ProductCode)";
295
+ }
296
+
297
+ // The Version attribute should not be authored in or for an MSI package.
298
+ if (!String.IsNullOrEmpty(version))
299
+ {
300
+ switch (packageType)
301
+ {
302
+ case PackageType.None:
303
+ this.Core.OnMessage(DependencyWarnings.DiscouragedVersionAttribute(sourceLineNumbers));
304
+ break;
305
+ case PackageType.MsiPackage:
306
+ this.Core.OnMessage(DependencyWarnings.DiscouragedVersionAttribute(sourceLineNumbers, parentId));
307
+ break;
308
+ }
309
+ }
310
+ else if (PackageType.MspPackage == packageType || PackageType.MsuPackage == packageType)
311
+ {
312
+ // Must specify the Version when authored for packages that do not contain a version.
313
+ this.Core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Version"));
314
+ }
315
+
316
+ // Need the element ID for child element processing, so generate now if not authored.
317
+ if (null == id)
318
+ {
319
+ id = this.Core.CreateIdentifier("dep", node.Name.LocalName, parentId, key);
320
+ }
321
+
322
+ foreach (XElement child in node.Elements())
323
+ {
324
+ if (this.Namespace == child.Name.Namespace)
325
+ {
326
+ switch (child.Name.LocalName)
327
+ {
328
+ case "Requires":
329
+ this.ParseRequiresElement(child, id.Id, PackageType.None == packageType);
330
+ break;
331
+ case "RequiresRef":
332
+ this.ParseRequiresRefElement(child, id.Id, PackageType.None == packageType);
333
+ break;
334
+ default:
335
+ this.Core.UnexpectedElement(node, child);
336
+ break;
337
+ }
338
+ }
339
+ else
340
+ {
341
+ this.Core.ParseExtensionElement(node, child);
342
+ }
343
+ }
344
+
345
+ if (!this.Core.EncounteredError)
346
+ {
347
+ // Create the row in the provider table.
348
+ Row row = this.Core.CreateRow(sourceLineNumbers, "WixDependencyProvider", id);
349
+ row[1] = parentId;
350
+ row[2] = key;
351
+
352
+ if (!String.IsNullOrEmpty(version))
353
+ {
354
+ row[3] = version;
355
+ }
356
+
357
+ if (!String.IsNullOrEmpty(displayName))
358
+ {
359
+ row[4] = displayName;
360
+ }
361
+
362
+ if (0 != attributes)
363
+ {
364
+ row[5] = attributes;
365
+ }
366
+
367
+ if (PackageType.None == packageType)
368
+ {
369
+ // Reference the Check custom action to check for dependencies on the current provider.
370
+ if (Platform.ARM == this.Core.CurrentPlatform)
371
+ {
372
+ // Ensure the ARM version of the CA is referenced.
373
+ this.Core.CreateSimpleReference(sourceLineNumbers, "CustomAction", "WixDependencyCheck_ARM");
374
+ }
375
+ else
376
+ {
377
+ // All other supported platforms use x86.
378
+ this.Core.CreateSimpleReference(sourceLineNumbers, "CustomAction", "WixDependencyCheck");
379
+ }
380
+
381
+ // Generate registry rows for the provider using binder properties.
382
+ string keyProvides = String.Concat(DependencyCommon.RegistryRoot, key);
383
+
384
+ row = this.Core.CreateRow(sourceLineNumbers, "Registry", this.Core.CreateIdentifier("reg", id.Id, "(Default)"));
385
+ row[1] = -1;
386
+ row[2] = keyProvides;
387
+ row[3] = null;
388
+ row[4] = "[ProductCode]";
389
+ row[5] = parentId;
390
+
391
+ // Use the Version registry value and use that as a potential key path.
392
+ Identifier idVersion = this.Core.CreateIdentifier("reg", id.Id, "Version");
393
+ keyPath = new ComponentKeyPath() { Id = idVersion.Id, Explicit = false, Type = ComponentKeyPathType.Registry };
394
+
395
+ row = this.Core.CreateRow(sourceLineNumbers, "Registry", idVersion);
396
+ row[1] = -1;
397
+ row[2] = keyProvides;
398
+ row[3] = "Version";
399
+ row[4] = !String.IsNullOrEmpty(version) ? version : "[ProductVersion]";
400
+ row[5] = parentId;
401
+
402
+ row = this.Core.CreateRow(sourceLineNumbers, "Registry", this.Core.CreateIdentifier("reg", id.Id, "DisplayName"));
403
+ row[1] = -1;
404
+ row[2] = keyProvides;
405
+ row[3] = "DisplayName";
406
+ row[4] = !String.IsNullOrEmpty(displayName) ? displayName : "[ProductName]";
407
+ row[5] = parentId;
408
+
409
+ if (0 != attributes)
410
+ {
411
+ row = this.Core.CreateRow(sourceLineNumbers, "Registry", this.Core.CreateIdentifier("reg", id.Id, "Attributes"));
412
+ row[1] = -1;
413
+ row[2] = keyProvides;
414
+ row[3] = "Attributes";
415
+ row[4] = String.Concat("#", attributes.ToString(CultureInfo.InvariantCulture.NumberFormat));
416
+ row[5] = parentId;
417
+ }
418
+ }
419
+ }
420
+
421
+ return keyPath;
422
+ }
423
+
424
+ /// <summary>
425
+ /// Processes the Requires element.
426
+ /// </summary>
427
+ /// <param name="node">The XML node for the Requires element.</param>
428
+ /// <param name="providerId">The parent provider identifier.</param>
429
+ /// <param name="requiresAction">Whether the Requires custom action should be referenced.</param>
430
+ private void ParseRequiresElement(XElement node, string providerId, bool requiresAction)
431
+ {
432
+ SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
433
+ Identifier id = null;
434
+ string providerKey = null;
435
+ string minVersion = null;
436
+ string maxVersion = null;
437
+ int attributes = 0;
438
+ int illegalChar = -1;
439
+
440
+ foreach (XAttribute attrib in node.Attributes())
441
+ {
442
+ if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
443
+ {
444
+ switch (attrib.Name.LocalName)
445
+ {
446
+ case "Id":
447
+ id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
448
+ break;
449
+ case "ProviderKey":
450
+ providerKey = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
451
+ break;
452
+ case "Minimum":
453
+ minVersion = this.Core.GetAttributeVersionValue(sourceLineNumbers, attrib);
454
+ break;
455
+ case "Maximum":
456
+ maxVersion = this.Core.GetAttributeVersionValue(sourceLineNumbers, attrib);
457
+ break;
458
+ case "IncludeMinimum":
459
+ if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
460
+ {
461
+ attributes |= DependencyCommon.RequiresAttributesMinVersionInclusive;
462
+ }
463
+ break;
464
+ case "IncludeMaximum":
465
+ if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
466
+ {
467
+ attributes |= DependencyCommon.RequiresAttributesMaxVersionInclusive;
468
+ }
469
+ break;
470
+ default:
471
+ this.Core.UnexpectedAttribute(node, attrib);
472
+ break;
473
+ }
474
+ }
475
+ else
476
+ {
477
+ this.Core.ParseExtensionAttribute(node, attrib);
478
+ }
479
+ }
480
+
481
+ this.Core.ParseForExtensionElements(node);
482
+
483
+ if (null == id)
484
+ {
485
+ // Generate an ID only if this element is authored under a Provides element; otherwise, a RequiresRef
486
+ // element will be necessary and the Id attribute will be required.
487
+ if (!String.IsNullOrEmpty(providerId))
488
+ {
489
+ id = this.Core.CreateIdentifier("dep", node.Name.LocalName, providerKey);
490
+ }
491
+ else
492
+ {
493
+ this.Core.OnMessage(WixErrors.ExpectedAttributeWhenElementNotUnderElement(sourceLineNumbers, node.Name.LocalName, "Id", "Provides"));
494
+ id = Identifier.Invalid;
495
+ }
496
+ }
497
+
498
+ if (String.IsNullOrEmpty(providerKey))
499
+ {
500
+ this.Core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "ProviderKey"));
501
+ }
502
+ // Make sure the key does not contain any illegal characters.
503
+ else if (0 <= (illegalChar = providerKey.IndexOfAny(DependencyCommon.InvalidCharacters)))
504
+ {
505
+ StringBuilder sb = new StringBuilder(DependencyCommon.InvalidCharacters.Length * 2);
506
+ Array.ForEach<char>(DependencyCommon.InvalidCharacters, c => sb.Append(c).Append(" "));
507
+
508
+ this.Core.OnMessage(DependencyErrors.IllegalCharactersInProvider(sourceLineNumbers, "ProviderKey", providerKey[illegalChar], sb.ToString()));
509
+ }
510
+
511
+
512
+ if (!this.Core.EncounteredError)
513
+ {
514
+ // Reference the Require custom action if required.
515
+ if (requiresAction)
516
+ {
517
+ if (Platform.ARM == this.Core.CurrentPlatform)
518
+ {
519
+ // Ensure the ARM version of the CA is referenced.
520
+ this.Core.CreateSimpleReference(sourceLineNumbers, "CustomAction", "WixDependencyRequire_ARM");
521
+ }
522
+ else
523
+ {
524
+ // All other supported platforms use x86.
525
+ this.Core.CreateSimpleReference(sourceLineNumbers, "CustomAction", "WixDependencyRequire");
526
+ }
527
+ }
528
+
529
+ Row row = this.Core.CreateRow(sourceLineNumbers, "WixDependency", id);
530
+ row[1] = providerKey;
531
+ row[2] = minVersion;
532
+ row[3] = maxVersion;
533
+
534
+ if (0 != attributes)
535
+ {
536
+ row[4] = attributes;
537
+ }
538
+
539
+ // Create the relationship between this WixDependency row and the WixDependencyProvider row.
540
+ if (!String.IsNullOrEmpty(providerId))
541
+ {
542
+ // Create the relationship between the WixDependency row and the parent WixDependencyProvider row.
543
+ row = this.Core.CreateRow(sourceLineNumbers, "WixDependencyRef");
544
+ row[0] = providerId;
545
+ row[1] = id.Id;
546
+ }
547
+ }
548
+ }
549
+
550
+ /// <summary>
551
+ /// Processes the RequiresRef element.
552
+ /// </summary>
553
+ /// <param name="node">The XML node for the RequiresRef element.</param>
554
+ /// <param name="providerId">The parent provider identifier.</param>
555
+ /// <param name="requiresAction">Whether the Requires custom action should be referenced.</param>
556
+ private void ParseRequiresRefElement(XElement node, string providerId, bool requiresAction)
557
+ {
558
+ SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
559
+ string id = null;
560
+
561
+ foreach (XAttribute attrib in node.Attributes())
562
+ {
563
+ if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
564
+ {
565
+ switch (attrib.Name.LocalName)
566
+ {
567
+ case "Id":
568
+ id = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
569
+ break;
570
+ default:
571
+ this.Core.UnexpectedAttribute(node, attrib);
572
+ break;
573
+ }
574
+ }
575
+ else
576
+ {
577
+ this.Core.ParseExtensionAttribute(node, attrib);
578
+ }
579
+ }
580
+
581
+ this.Core.ParseForExtensionElements(node);
582
+
583
+ if (String.IsNullOrEmpty(id))
584
+ {
585
+ this.Core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
586
+ }
587
+
588
+ if (!this.Core.EncounteredError)
589
+ {
590
+ // Reference the Require custom action if required.
591
+ if (requiresAction)
592
+ {
593
+ if (Platform.ARM == this.Core.CurrentPlatform)
594
+ {
595
+ // Ensure the ARM version of the CA is referenced.
596
+ this.Core.CreateSimpleReference(sourceLineNumbers, "CustomAction", "WixDependencyRequire_ARM");
597
+ }
598
+ else
599
+ {
600
+ // All other supported platforms use x86.
601
+ this.Core.CreateSimpleReference(sourceLineNumbers, "CustomAction", "WixDependencyRequire");
602
+ }
603
+ }
604
+
605
+ // Create a link dependency on the row that contains information we'll need during bind.
606
+ this.Core.CreateSimpleReference(sourceLineNumbers, "WixDependency", id);
607
+
608
+ // Create the relationship between the WixDependency row and the parent WixDependencyProvider row.
609
+ Row row = this.Core.CreateRow(sourceLineNumbers, "WixDependencyRef");
610
+ row[0] = providerId;
611
+ row[1] = id;
612
+ }
613
+ }
614
+ }
615
+}
src/wixext/DependencyDecompiler.cs
new
+345
@@ -0,0 +1,345 @@
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
+namespace WixToolset.Extensions
4
+{
5
+ using System;
6
+ using System.Collections.Generic;
7
+ using System.Collections.ObjectModel;
8
+ using WixToolset;
9
+ using WixToolset.Data;
10
+ using WixToolset.Extensibility;
11
+ using WixToolset.Extensions.Serialize.Dependency;
12
+ using Dependency = WixToolset.Extensions.Serialize.Dependency;
13
+ using Wix = WixToolset.Data.Serialize;
14
+
15
+ /// <summary>
16
+ /// The decompiler for the WiX toolset dependency extension.
17
+ /// </summary>
18
+ public sealed class DependencyDecompiler : DecompilerExtension
19
+ {
20
+ private RegistryKeyValueCollection registryValues;
21
+ private Dictionary<string, string> keyCache;
22
+
23
+ /// <summary>
24
+ /// Creates a new instance of the <see cref="DependencyDecompiler"/> class.
25
+ /// </summary>
26
+ public DependencyDecompiler()
27
+ {
28
+ this.registryValues = new RegistryKeyValueCollection();
29
+ this.keyCache = new Dictionary<string, string>();
30
+
31
+ this.TableDefinitions = DependencyExtensionData.GetExtensionTableDefinitions();
32
+ }
33
+
34
+ /// <summary>
35
+ /// Get the extensions library to be removed.
36
+ /// </summary>
37
+ /// <param name="tableDefinitions">Table definitions for library.</param>
38
+ /// <returns>Library to remove from decompiled output.</returns>
39
+ public override Library GetLibraryToRemove(TableDefinitionCollection tableDefinitions)
40
+ {
41
+ return DependencyExtensionData.GetExtensionLibrary(tableDefinitions);
42
+ }
43
+
44
+ /// <summary>
45
+ /// Decompiles an extension table.
46
+ /// </summary>
47
+ /// <param name="table">The table to decompile.</param>
48
+ public override void DecompileTable(Table table)
49
+ {
50
+ switch (table.Name)
51
+ {
52
+ case "WixDependencyProvider":
53
+ this.DecompileWixDependencyProviderTable(table);
54
+ break;
55
+
56
+ case "WixDependency":
57
+ this.DecompileWixDependencyTable(table);
58
+ break;
59
+
60
+ case "WixDependencyRef":
61
+ this.DecompileWixDependencyRefTable(table);
62
+ break;
63
+
64
+ default:
65
+ base.DecompileTable(table);
66
+ break;
67
+ }
68
+ }
69
+
70
+ /// <summary>
71
+ /// Finalize decompilation by removing registry values that the compiler writes.
72
+ /// </summary>
73
+ /// <param name="tables">The collection of all tables.</param>
74
+ public override void Finish(TableIndexedCollection tables)
75
+ {
76
+ // Remove generated registry rows.
77
+ this.FinalizeRegistryTable(tables);
78
+
79
+ // Remove extension properties.
80
+ this.FinalizeProperties();
81
+ }
82
+
83
+ /// <summary>
84
+ /// Decompiles the WixDependencyProvider table.
85
+ /// </summary>
86
+ /// <param name="table">The table to decompile.</param>
87
+ private void DecompileWixDependencyProviderTable(Table table)
88
+ {
89
+ foreach (Row row in table.Rows)
90
+ {
91
+ Provides provides = new Provides();
92
+
93
+ provides.Id = (string)row[0];
94
+ provides.Key = (string)row[2];
95
+
96
+ if (null != row[3])
97
+ {
98
+ provides.Version = (string)row[3];
99
+ }
100
+
101
+ if (null != row[4])
102
+ {
103
+ provides.DisplayName = (string)row[4];
104
+ }
105
+
106
+ // Nothing to parse for attributes currently.
107
+
108
+ Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", (string)row[1]);
109
+ if (null != component)
110
+ {
111
+ component.AddChild(provides);
112
+ }
113
+ else
114
+ {
115
+ this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", (string)row[1], "Component"));
116
+ }
117
+
118
+ // Index the provider to parent the RequiresRef elements.
119
+ this.Core.IndexElement(row, provides);
120
+
121
+ // Add the provider-specific registry keys to be removed during finalization.
122
+ // Only remove specific keys that the compiler writes.
123
+ string keyProvides = String.Concat(DependencyCommon.RegistryRoot, provides.Key);
124
+
125
+ this.registryValues.Add(keyProvides, null);
126
+ this.registryValues.Add(keyProvides, "Version");
127
+ this.registryValues.Add(keyProvides, "DisplayName");
128
+ this.registryValues.Add(keyProvides, "Attributes");
129
+
130
+ // Cache the provider key.
131
+ this.keyCache[provides.Id] = provides.Key;
132
+ }
133
+ }
134
+
135
+ /// <summary>
136
+ /// Decompiles the WixDependency table.
137
+ /// </summary>
138
+ /// <param name="table">The table to decompile.</param>
139
+ private void DecompileWixDependencyTable(Table table)
140
+ {
141
+ foreach (Row row in table.Rows)
142
+ {
143
+ Requires requires = new Requires();
144
+
145
+ requires.Id = (string)row[0];
146
+ requires.ProviderKey = (string)row[1];
147
+
148
+ if (null != row[2])
149
+ {
150
+ requires.Minimum = (string)row[2];
151
+ }
152
+
153
+ if (null != row[3])
154
+ {
155
+ requires.Maximum = (string)row[3];
156
+ }
157
+
158
+ if (null != row[4])
159
+ {
160
+ int attributes = (int)row[4];
161
+
162
+ if (0 != (attributes & DependencyCommon.RequiresAttributesMinVersionInclusive))
163
+ {
164
+ requires.IncludeMinimum = Dependency.YesNoType.yes;
165
+ }
166
+
167
+ if (0 != (attributes & DependencyCommon.RequiresAttributesMaxVersionInclusive))
168
+ {
169
+ requires.IncludeMaximum = Dependency.YesNoType.yes;
170
+ }
171
+ }
172
+
173
+ this.Core.RootElement.AddChild(requires);
174
+
175
+ // Cache the requires key.
176
+ this.keyCache[requires.Id] = requires.ProviderKey;
177
+ }
178
+ }
179
+
180
+ /// <summary>
181
+ /// Decompiles the WixDependencyRef table.
182
+ /// </summary>
183
+ /// <param name="table">The table to decompile.</param>
184
+ private void DecompileWixDependencyRefTable(Table table)
185
+ {
186
+ foreach (Row row in table.Rows)
187
+ {
188
+ RequiresRef requiresRef = new RequiresRef();
189
+
190
+ requiresRef.Id = (string)row[1];
191
+
192
+ Provides provides = (Provides)this.Core.GetIndexedElement("WixDependencyProvider", (string)row[0]);
193
+ if (null != provides)
194
+ {
195
+ provides.AddChild(requiresRef);
196
+ }
197
+ else
198
+ {
199
+ this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "WixDependencyProvider_", (string)row[0], "WixDependencyProvider"));
200
+ }
201
+
202
+ // Get the cached keys for the provider and dependency IDs and generate registry rows.
203
+ string providesKey = null;
204
+ string requiresKey = null;
205
+
206
+ if (null != provides && this.keyCache.ContainsKey(provides.Id))
207
+ {
208
+ providesKey = this.keyCache[provides.Id];
209
+ }
210
+ else
211
+ {
212
+ this.Core.OnMessage(DependencyWarnings.ProvidesKeyNotFound(row.SourceLineNumbers, provides.Id));
213
+ }
214
+
215
+ if (this.keyCache.ContainsKey(requiresRef.Id))
216
+ {
217
+ requiresKey = this.keyCache[requiresRef.Id];
218
+ }
219
+ else
220
+ {
221
+ this.Core.OnMessage(DependencyWarnings.RequiresKeyNotFound(row.SourceLineNumbers, requiresRef.Id));
222
+ }
223
+
224
+ if (!this.Core.EncounteredError)
225
+ {
226
+ // Add the dependency-specific registry keys to be removed during finalization.
227
+ // Only remove specific keys that the compiler writes.
228
+ string keyRequires = String.Format(@"{0}{1}\{2}\{3}", DependencyCommon.RegistryRoot, requiresKey, DependencyCommon.RegistryDependents, providesKey);
229
+
230
+ this.registryValues.Add(keyRequires, "*");
231
+ this.registryValues.Add(keyRequires, "MinVersion");
232
+ this.registryValues.Add(keyRequires, "MaxVersion");
233
+ this.registryValues.Add(keyRequires, "Attributes");
234
+ }
235
+ }
236
+ }
237
+
238
+ /// <summary>
239
+ /// Removes rows from the Registry table that are generated by this extension.
240
+ /// </summary>
241
+ /// <param name="tables">The collection of tables.</param>
242
+ private void FinalizeRegistryTable(TableIndexedCollection tables)
243
+ {
244
+ Table registryTable = tables["Registry"];
245
+ if (null != registryTable)
246
+ {
247
+ foreach (Row registryRow in registryTable.Rows)
248
+ {
249
+ // Check if the compiler writes this registry value; if so, it should be removed.
250
+ if (this.registryValues.Contains(registryRow))
251
+ {
252
+ Wix.ISchemaElement elem = this.Core.GetIndexedElement(registryRow);
253
+
254
+ // If the registry row was found, remove it from its parent.
255
+ if (null != elem && null != elem.ParentElement)
256
+ {
257
+ Wix.IParentElement elemParent = elem.ParentElement as Wix.IParentElement;
258
+ if (null != elemParent)
259
+ {
260
+ elemParent.RemoveChild(elem);
261
+ }
262
+ }
263
+ }
264
+ }
265
+ }
266
+ }
267
+
268
+ /// <summary>
269
+ /// Removes properties defined by this extension.
270
+ /// </summary>
271
+ /// <param name="tables">The collection of tables.</param>
272
+ private void FinalizeProperties()
273
+ {
274
+ string[] properties = new string[] { "DISABLEDEPENDENCYCHECK", "IGNOREDEPENDENCIES" };
275
+ foreach (string property in properties)
276
+ {
277
+ Wix.Property elem = this.Core.GetIndexedElement("Property", property) as Wix.Property;
278
+ if (null != elem)
279
+ {
280
+ // If a value is defined, log a warning we're removing it.
281
+ if (!String.IsNullOrEmpty(elem.Value))
282
+ {
283
+ this.Core.OnMessage(DependencyWarnings.PropertyRemoved(elem.Id));
284
+ }
285
+
286
+ // If the property row was found, remove it from its parent.
287
+ if (null != elem.ParentElement)
288
+ {
289
+ Wix.IParentElement elemParent = elem.ParentElement as Wix.IParentElement;
290
+ if (null != elemParent)
291
+ {
292
+ elemParent.RemoveChild(elem);
293
+ }
294
+ }
295
+ }
296
+ }
297
+ }
298
+
299
+ /// <summary>
300
+ /// Provides an O(1) lookup for registry key and value name pairs for use in the decompiler.
301
+ /// </summary>
302
+ private sealed class RegistryKeyValueCollection : KeyedCollection<int, KeyValuePair<string, string>>
303
+ {
304
+ /// <summary>
305
+ /// Adds the registry key and value name pair to the collection if it doesn't already exist.
306
+ /// </summary>
307
+ /// <param name="key">The registry key to add.</param>
308
+ /// <param name="name">The registry value name to add.</param>
309
+ internal void Add(string key, string name)
310
+ {
311
+ KeyValuePair<string, string> pair = new KeyValuePair<string, string>(key, name);
312
+ if (!this.Contains(pair))
313
+ {
314
+ this.Add(pair);
315
+ }
316
+ }
317
+
318
+ /// <summary>
319
+ /// Returns whether the collection contains the registry key and value name pair from the <see cref="Row"/>.
320
+ /// </summary>
321
+ /// <param name="row">The registry <see cref="Row"/> to search for.</param>
322
+ /// <returns>True if the collection contains the registry key and value name pair from the <see cref="Row"/>; otherwise, false.</returns>
323
+ internal bool Contains(Row row)
324
+ {
325
+ if (null == row)
326
+ {
327
+ return false;
328
+ }
329
+
330
+ KeyValuePair<string, string> pair = new KeyValuePair<string, string>((string)row[2], (string)row[3]);
331
+ return this.Contains(pair);
332
+ }
333
+
334
+ /// <summary>
335
+ /// Return the hash code of the key and value pair concatenated with a colon as a delimiter.
336
+ /// </summary>
337
+ /// <param name="pair">The registry key and value name pair.</param>
338
+ /// <returns></returns>
339
+ protected override int GetKeyForItem(KeyValuePair<string, string> pair)
340
+ {
341
+ return String.Concat(pair.Key, ":", pair.Value).GetHashCode();
342
+ }
343
+ }
344
+ }
345
+}
src/wixext/DependencyExtension.csproj
new
+50
@@ -0,0 +1,50 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<!-- 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. -->
3
+
4
+
5
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
6
+ <PropertyGroup>
7
+ <ProjectGuid>{A0B6D3F1-AE5E-423B-BA92-60C9926CA498}</ProjectGuid>
8
+ <AssemblyName>WixDependencyExtension</AssemblyName>
9
+ <OutputType>Library</OutputType>
10
+ <RootNamespace>WixToolset.Extensions</RootNamespace>
11
+ </PropertyGroup>
12
+ <ItemGroup>
13
+ <Compile Include="AssemblyInfo.cs" />
14
+ <Compile Include="DependencyBinder.cs" />
15
+ <Compile Include="DependencyCommon.cs" />
16
+ <Compile Include="DependencyCompiler.cs" />
17
+ <Compile Include="DependencyDecompiler.cs" />
18
+ <Compile Include="DependencyExtensionData.cs" />
19
+ <EmbeddedFlattenedResource Include="Data\tables.xml">
20
+ <LogicalName>$(RootNamespace).Data.tables.xml</LogicalName>
21
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
22
+ </EmbeddedFlattenedResource>
23
+ <MsgGenSource Include="Data\messages.xml">
24
+ <ResourcesLogicalName>$(RootNamespace).Data.Messages.resources</ResourcesLogicalName>
25
+ </MsgGenSource>
26
+ <EmbeddedFlattenedResource Include="Xsd\Dependency.xsd">
27
+ <LogicalName>$(RootNamespace).Xsd.Dependency.xsd</LogicalName>
28
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
29
+ </EmbeddedFlattenedResource>
30
+ <XsdGenSource Include="Xsd\Dependency.xsd">
31
+ <CommonNamespace>WixToolset.Data.Serialize</CommonNamespace>
32
+ <Namespace>WixToolset.Extensions.Serialize.Dependency</Namespace>
33
+ </XsdGenSource>
34
+ <EmbeddedResource Include="$(OutputPath)Dependency.wixlib">
35
+ <Link>Data\Dependency.wixlib</Link>
36
+ </EmbeddedResource>
37
+ </ItemGroup>
38
+ <ItemGroup>
39
+ <Reference Include="System" />
40
+ <Reference Include="System.Xml" />
41
+ <Reference Include="System.Xml.Linq" />
42
+ <ProjectReference Include="..\..\..\libs\WixToolset.Data\WixToolset.Data.csproj" />
43
+ <ProjectReference Include="..\..\..\libs\WixToolset.Extensibility\WixToolset.Extensibility.csproj" />
44
+ <ProjectReference Include="..\..\..\tools\wix\Wix.csproj" />
45
+ <ProjectReference Include="..\wixlib\DependencyExtension.wixproj">
46
+ <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
47
+ </ProjectReference>
48
+ </ItemGroup>
49
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), wix.proj))\tools\WixBuild.targets" />
50
+</Project>
src/wixext/DependencyExtensionData.cs
new
+64
@@ -0,0 +1,64 @@
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
+namespace WixToolset.Extensions
4
+{
5
+ using System;
6
+ using System.Reflection;
7
+ using WixToolset.Data;
8
+ using WixToolset.Extensibility;
9
+
10
+ /// <summary>
11
+ /// The WiX toolset dependency extension.
12
+ /// </summary>
13
+ public sealed class DependencyExtensionData : ExtensionData
14
+ {
15
+ /// <summary>
16
+ /// Gets the default culture.
17
+ /// </summary>
18
+ /// <value>The default culture.</value>
19
+ public override string DefaultCulture
20
+ {
21
+ get { return "en-us"; }
22
+ }
23
+
24
+ /// <summary>
25
+ /// Gets the optional table definitions for this extension.
26
+ /// </summary>
27
+ /// <value>The optional table definitions for this extension.</value>
28
+ public override TableDefinitionCollection TableDefinitions
29
+ {
30
+ get
31
+ {
32
+ return DependencyExtensionData.GetExtensionTableDefinitions();
33
+ }
34
+ }
35
+
36
+ /// <summary>
37
+ /// Gets the library associated with this extension.
38
+ /// </summary>
39
+ /// <param name="tableDefinitions">The table definitions to use while loading the library.</param>
40
+ /// <returns>The loaded library.</returns>
41
+ public override Library GetLibrary(TableDefinitionCollection tableDefinitions)
42
+ {
43
+ return DependencyExtensionData.GetExtensionLibrary(tableDefinitions);
44
+ }
45
+
46
+ /// <summary>
47
+ /// Internal mechanism to access the extension's table definitions.
48
+ /// </summary>
49
+ /// <returns>Extension's table definitions.</returns>
50
+ internal static TableDefinitionCollection GetExtensionTableDefinitions()
51
+ {
52
+ return ExtensionData.LoadTableDefinitionHelper(Assembly.GetExecutingAssembly(), "WixToolset.Extensions.Data.tables.xml");
53
+ }
54
+
55
+ /// <summary>
56
+ /// Internal mechanism to access the extension's library.
57
+ /// </summary>
58
+ /// <returns>Extension's library.</returns>
59
+ internal static Library GetExtensionLibrary(TableDefinitionCollection tableDefinitions)
60
+ {
61
+ return ExtensionData.LoadLibraryHelper(Assembly.GetExecutingAssembly(), "WixToolset.Extensions.Data.Dependency.wixlib", tableDefinitions);
62
+ }
63
+ }
64
+}
src/wixext/messages.xml
new
+60
@@ -0,0 +1,60 @@
1
+<?xml version='1.0' encoding='utf-8'?>
2
+<!-- 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. -->
3
+
4
+
5
+<Messages Namespace="WixToolset.Extensions" Resources="Data.Messages" xmlns="http://schemas.microsoft.com/genmsgs/2004/07/messages">
6
+ <Class Name="DependencyErrors" ContainerName="DependencyErrorEventArgs" BaseContainerName="MessageEventArgs">
7
+ <Message Id="IllegalCharactersInProvider" Number="5400">
8
+ <Instance>
9
+ The provider key authored into the {0} attribute contains an illegal character, '{1}'. Please author the provider key without any of the following characters: {2}
10
+ <Parameter Type="System.String" Name="attributeName" />
11
+ <Parameter Type="System.Char" Name="illegalChar" />
12
+ <Parameter Type="System.String" Name="illegalChars" />
13
+ </Instance>
14
+ </Message>
15
+ <Message Id="ReservedValue" Number="5401">
16
+ <Instance>
17
+ The {0}/@{1} attribute value '{2}' is reserved and cannot be used here. Please choose a different value.
18
+ <Parameter Type="System.String" Name="elementName" />
19
+ <Parameter Type="System.String" Name="attributeName" />
20
+ <Parameter Type="System.String" Name="attributeValue" />
21
+ </Instance>
22
+ </Message>
23
+ </Class>
24
+ <Class Name="DependencyWarnings" ContainerName="DependencyWarningEventArgs" BaseContainerName="MessageEventArgs">
25
+ <Message Id="ProvidesKeyNotFound" Number="5431">
26
+ <Instance>
27
+ The provider key with identifier {0} was not found in the WixDependencyProvider table. Related registry rows will not be removed from authoring.
28
+ <Parameter Type="System.String" Name="id" />
29
+ </Instance>
30
+ </Message>
31
+ <Message Id="RequiresKeyNotFound" Number="5432">
32
+ <Instance>
33
+ The dependency key with identifier {0} was not found in the WixDependency table. Related registry rows will not be removed from authoring.
34
+ <Parameter Type="System.String" Name="id" />
35
+ </Instance>
36
+ </Message>
37
+ <Message Id="PropertyRemoved" Number="5433" SourceLineNumbers="no">
38
+ <Instance>
39
+ The property {0} was authored in the package with a value and will be removed. The property should not be authored.
40
+ <Parameter Type="System.String" Name="name" />
41
+ </Instance>
42
+ </Message>
43
+ <Message Id="DiscouragedVersionAttribute" Number="5434">
44
+ <Instance>
45
+ The Provides/@Version attribute should not be specified in an MSI package. The ProductVersion will be used by default.
46
+ </Instance>
47
+ <Instance>
48
+ The Provides/@Version attribute should not be specified for MSI package {0}. The ProductVersion will be used by default.
49
+ <Parameter Type="System.String" Name="id" />
50
+ </Instance>
51
+ </Message>
52
+ <Message Id="Win64Component" Number="5435">
53
+ <Instance>
54
+ The Provides element should not be authored in the 64-bit component with identifier {0}. The dependency feature may not work if installing this package on 64-bit Windows operating systems prior to Windows 7 and Windows Server 2008 R2. Set the Component/@Win64 attribute to "no" to make sure the dependency feature works correctly on all supported operating systems.
55
+ <Parameter Type="System.String" Name="componentId" />
56
+ </Instance>
57
+ </Message>
58
+ </Class>
59
+ <Class Name="DependencyVerboses" ContainerName="DependencyVerboseEventArgs" BaseContainerName="MessageEventArgs" />
60
+</Messages>
src/wixext/tables.xml
new
+38
@@ -0,0 +1,38 @@
1
+<?xml version="1.0" encoding="utf-8" ?>
2
+<!-- 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. -->
3
+
4
+
5
+<tableDefinitions xmlns="http://wixtoolset.org/schemas/v4/wi/tables">
6
+ <tableDefinition name="WixDependencyProvider" createSymbols="yes">
7
+ <columnDefinition name="WixDependencyProvider" type="string" length="72" primaryKey="yes" modularize="column"
8
+ category="identifier" description="The non-localized primary key for the table."/>
9
+ <columnDefinition name="Component_" type="string" length="72" keyTable="Component" keyColumn="1" modularize="column"
10
+ category="identifier" description="The foreign key into the Component table used to determine install state."/>
11
+ <columnDefinition name="ProviderKey" type="string" length="255"
12
+ category="text" description="The name of the registry key that holds the provider identity."/>
13
+ <columnDefinition name="Version" type="string" length="72" nullable="yes"
14
+ category="version" description="The version of the package."/>
15
+ <columnDefinition name="DisplayName" type="string" length="255" nullable="yes"
16
+ category="text" description="The display name of the package."/>
17
+ <columnDefinition name="Attributes" type="number" length="4" nullable="yes"
18
+ minValue="0" maxValue="2147483647" description="A 32-bit word that specifies the attribute flags to be applied."/>
19
+ </tableDefinition>
20
+ <tableDefinition name="WixDependency" createSymbols="yes">
21
+ <columnDefinition name="WixDependency" type="string" length="72" primaryKey="yes" modularize="column"
22
+ category="identifier" description="The non-localized primary key for the table."/>
23
+ <columnDefinition name="ProviderKey" type="string" length="255"
24
+ category="text" description="The name of the registry key that holds the provider identity."/>
25
+ <columnDefinition name="MinVersion" type="string" length="72" nullable="yes"
26
+ category="version" description="The minimum version of the provider supported."/>
27
+ <columnDefinition name="MaxVersion" type="string" length="72" nullable="yes"
28
+ category="version" description="The maximum version of the provider supported."/>
29
+ <columnDefinition name="Attributes" type="number" length="4" nullable="yes"
30
+ minValue="0" maxValue="2147483647" description="A 32-bit word that specifies the attribute flags to be applied."/>
31
+ </tableDefinition>
32
+ <tableDefinition name="WixDependencyRef" createSymbols="yes">
33
+ <columnDefinition name="WixDependencyProvider_" type="string" length="72" primaryKey="yes" keyTable="WixDependencyProvider" keyColumn="1" modularize="column"
34
+ category="identifier" description="Foreign key into the Component table." />
35
+ <columnDefinition name="WixDependency_" type="string" length="72" primaryKey="yes" keyTable="WixDependency" keyColumn="1" modularize="column"
36
+ category="identifier" description="Foreign key into the WixDependency table." />
37
+ </tableDefinition>
38
+</tableDefinitions>
src/wixlib/DependencyExtension.wixproj
new
+27
@@ -0,0 +1,27 @@
1
+<?xml version="1.0" encoding="utf-8" ?>
2
+<!-- 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. -->
3
+
4
+
5
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
6
+ <PropertyGroup>
7
+ <ProjectGuid>{58ED0EC8-73F8-4EE1-8664-A53486D38EC8}</ProjectGuid>
8
+ <OutputName>dependency</OutputName>
9
+ <OutputType>Library</OutputType>
10
+ <BindFiles>true</BindFiles>
11
+ <Pedantic>true</Pedantic>
12
+ <SuppressSpecificWarnings>1086</SuppressSpecificWarnings>
13
+ <Cultures>en-us</Cultures>
14
+ </PropertyGroup>
15
+
16
+ <ItemGroup>
17
+ <Compile Include="DependencyExtension.wxs" />
18
+ <Compile Include="DependencyExtension_x86.wxs" />
19
+ <EmbeddedResource Include="en-us.wxl" />
20
+ </ItemGroup>
21
+
22
+ <ItemGroup>
23
+ <ProjectReference Include="..\ca\wixdepca.vcxproj" />
24
+ </ItemGroup>
25
+
26
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), wix.proj))\tools\WixBuild.targets" />
27
+</Project>
src/wixlib/DependencyExtension.wxs
new
+22
@@ -0,0 +1,22 @@
1
+<?xml version="1.0"?>
2
+<!-- 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. -->
3
+
4
+
5
+<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
6
+ <?include caerr.wxi ?>
7
+
8
+ <Fragment>
9
+ <UI Id="WixDependencyErrors">
10
+ <Error Id="$(var.msierrDependencyMissingDependencies)">!(loc.msierrDependencyMissingDependencies)</Error>
11
+ <Error Id="$(var.msierrDependencyHasDependents)">!(loc.msierrDependencyHasDependents)</Error>
12
+ </UI>
13
+ </Fragment>
14
+
15
+ <Fragment>
16
+ <Property Id="DISABLEDEPENDENCYCHECK" Secure="yes" SuppressModularization="yes"/>
17
+ </Fragment>
18
+
19
+ <Fragment>
20
+ <Property Id="IGNOREDEPENDENCIES" Secure="yes" SuppressModularization="yes"/>
21
+ </Fragment>
22
+</Wix>
src/wixlib/DependencyExtension_Platform.wxi
new
+26
@@ -0,0 +1,26 @@
1
+<?xml version="1.0"?>
2
+<!-- 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. -->
3
+
4
+
5
+<Include xmlns="http://wixtoolset.org/schemas/v4/wxs">
6
+ <?include caSuffix.wxi ?>
7
+ <Fragment>
8
+ <CustomAction Id="WixDependencyRequire$(var.Suffix)" BinaryKey="WixDepCA$(var.Suffix)" DllEntry="WixDependencyRequire" Execute="immediate" Return="check" SuppressModularization="yes"/>
9
+ <InstallExecuteSequence>
10
+ <Custom Action="WixDependencyRequire$(var.Suffix)" Before="WixDependencyCheck$(var.Suffix)" Overridable="yes"><![CDATA[NOT DISABLEDEPENDENCYCHECK]]></Custom>
11
+ </InstallExecuteSequence>
12
+ <UIRef Id="WixDependencyErrors"/>
13
+ <PropertyRef Id="DISABLEDEPENDENCYCHECK"/>
14
+ </Fragment>
15
+ <Fragment>
16
+ <CustomAction Id="WixDependencyCheck$(var.Suffix)" BinaryKey="WixDepCA$(var.Suffix)" DllEntry="WixDependencyCheck" Execute="immediate" Return="check" SuppressModularization="yes"/>
17
+ <InstallExecuteSequence>
18
+ <Custom Action="WixDependencyCheck$(var.Suffix)" Before="InstallInitialize" Overridable="yes"><![CDATA[(REMOVE OR MsiPatchRemovalList) AND NOT (UPGRADINGPRODUCTCODE OR IGNOREDEPENDENCIES="ALL")]]></Custom>
19
+ </InstallExecuteSequence>
20
+ <UIRef Id="WixDependencyErrors"/>
21
+ <PropertyRef Id="IGNOREDEPENDENCIES"/>
22
+ </Fragment>
23
+ <Fragment>
24
+ <Binary Id="WixDepCA$(var.Suffix)" SourceFile="!(bindpath.$(var.platform))wixdepca.dll"/>
25
+ </Fragment>
26
+</Include>
src/wixlib/DependencyExtension_x86.wxs
new
+8
@@ -0,0 +1,8 @@
1
+<?xml version="1.0"?>
2
+<!-- 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. -->
3
+
4
+
5
+<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
6
+ <?define platform=x86 ?>
7
+ <?include DependencyExtension_Platform.wxi ?>
8
+</Wix>
src/wixlib/en-us.wxl
new
+8
@@ -0,0 +1,8 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<!-- 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. -->
3
+
4
+
5
+<WixLocalization Culture="en-US" xmlns="http://wixtoolset.org/schemas/v4/wxl">
6
+ <String Id="msierrDependencyMissingDependencies" Overridable="yes">If you continue with this install, the product may not work properly because [2] or more dependencies are missing. Do you want to continue with this install anyway?</String>
7
+ <String Id="msierrDependencyHasDependents" Overridable="yes">If you continue with this uninstall, [2] or more products may stop working properly. Do you want to continue with this uninstall anyway?</String>
8
+</WixLocalization>