@joebigelow / wix-1 / commits / 72f3dcee

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>