aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSean Hall <r.sean.hall@gmail.com>2019-02-03 13:20:43 -0600
committerSean Hall <r.sean.hall@gmail.com>2019-02-03 13:20:43 -0600
commit72f3dceefa2e3893b061e074380794bc60b67b6f (patch)
tree74b812780e2e16b4e30764d92f6294958413ac6f /src
parentd2f21865933c11b8af80b8383e1dbf611a175133 (diff)
downloadwix-72f3dceefa2e3893b061e074380794bc60b67b6f.tar.gz
wix-72f3dceefa2e3893b061e074380794bc60b67b6f.tar.bz2
wix-72f3dceefa2e3893b061e074380794bc60b67b6f.zip
Import code from old v4 repo
Diffstat (limited to 'src')
-rw-r--r--src/ca/dllmain.cpp27
-rw-r--r--src/ca/precomp.h18
-rw-r--r--src/ca/wixdepca.cpp516
-rw-r--r--src/ca/wixdepca.def8
-rw-r--r--src/ca/wixdepca.vcxproj57
-rw-r--r--src/ca/wixdepca.vcxproj.filters40
-rw-r--r--src/wixext/Dependency.xsd226
-rw-r--r--src/wixext/DependencyBinder.cs169
-rw-r--r--src/wixext/DependencyCommon.cs26
-rw-r--r--src/wixext/DependencyCompiler.cs615
-rw-r--r--src/wixext/DependencyDecompiler.cs345
-rw-r--r--src/wixext/DependencyExtension.csproj50
-rw-r--r--src/wixext/DependencyExtensionData.cs64
-rw-r--r--src/wixext/messages.xml60
-rw-r--r--src/wixext/tables.xml38
-rw-r--r--src/wixlib/DependencyExtension.wixproj27
-rw-r--r--src/wixlib/DependencyExtension.wxs22
-rw-r--r--src/wixlib/DependencyExtension_Platform.wxi26
-rw-r--r--src/wixlib/DependencyExtension_x86.wxs8
-rw-r--r--src/wixlib/en-us.wxl8
20 files changed, 2350 insertions, 0 deletions
diff --git a/src/ca/dllmain.cpp b/src/ca/dllmain.cpp
new file mode 100644
index 00000000..7d299feb
--- /dev/null
+++ b/src/ca/dllmain.cpp
@@ -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/********************************************************************
6DllMain - standard entry point for all WiX custom actions.
7
8********************************************************************/
9extern "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}
diff --git a/src/ca/precomp.h b/src/ca/precomp.h
new file mode 100644
index 00000000..5fd06cff
--- /dev/null
+++ b/src/ca/precomp.h
@@ -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"
diff --git a/src/ca/wixdepca.cpp b/src/ca/wixdepca.cpp
new file mode 100644
index 00000000..154b73f2
--- /dev/null
+++ b/src/ca/wixdepca.cpp
@@ -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
8LPCWSTR vcsDependencyProviderQuery =
9 L"SELECT `WixDependencyProvider`.`WixDependencyProvider`, `WixDependencyProvider`.`Component_`, `WixDependencyProvider`.`ProviderKey`, `WixDependencyProvider`.`Attributes` "
10 L"FROM `WixDependencyProvider`";
11enum eDependencyProviderQuery { dpqId = 1, dpqComponent, dpqProviderKey, dpqAttributes };
12
13LPCWSTR 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_`";
17enum eDependencyComponentQuery { dqId = 1, dqComponent, dqProviderKey, dqMinVersion, dqMaxVersion, dqAttributes };
18
19static HRESULT EnsureRequiredDependencies(
20 __in MSIHANDLE hInstall,
21 __in BOOL fMachineContext
22 );
23
24static HRESULT EnsureAbsentDependents(
25 __in MSIHANDLE hInstall,
26 __in BOOL fMachineContext
27 );
28
29static HRESULT SplitIgnoredDependents(
30 __deref_inout STRINGDICT_HANDLE* psdIgnoredDependents
31 );
32
33static HRESULT CreateDependencyRecord(
34 __in int iMessageId,
35 __in_ecount(cDependencies) const DEPENDENCY* rgDependencies,
36 __in UINT cDependencies,
37 __out MSIHANDLE *phRecord
38 );
39
40static LPCWSTR LogDependencyName(
41 __in_z LPCWSTR wzName
42 );
43
44/***************************************************************************
45 WixDependencyRequire - Checks that all required dependencies are installed.
46
47***************************************************************************/
48extern "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
69LExit:
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***************************************************************************/
81extern "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
102LExit:
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***************************************************************************/
115static 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
243LExit:
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***************************************************************************/
261static 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
393LExit:
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***************************************************************************/
406static 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
428LExit:
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***************************************************************************/
440static 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
498LExit:
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***************************************************************************/
511static LPCWSTR LogDependencyName(
512 __in_z LPCWSTR wzName
513 )
514{
515 return wzName ? wzName : L"Unknown";
516}
diff --git a/src/ca/wixdepca.def b/src/ca/wixdepca.def
new file mode 100644
index 00000000..df50e992
--- /dev/null
+++ b/src/ca/wixdepca.def
@@ -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
4LIBRARY "wixdepca"
5
6EXPORTS
7 WixDependencyRequire
8 WixDependencyCheck
diff --git a/src/ca/wixdepca.vcxproj b/src/ca/wixdepca.vcxproj
new file mode 100644
index 00000000..b757a35f
--- /dev/null
+++ b/src/ca/wixdepca.vcxproj
@@ -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>
diff --git a/src/ca/wixdepca.vcxproj.filters b/src/ca/wixdepca.vcxproj.filters
new file mode 100644
index 00000000..1fdb0236
--- /dev/null
+++ b/src/ca/wixdepca.vcxproj.filters
@@ -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
diff --git a/src/wixext/Dependency.xsd b/src/wixext/Dependency.xsd
new file mode 100644
index 00000000..0c36cb88
--- /dev/null
+++ b/src/wixext/Dependency.xsd
@@ -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>
diff --git a/src/wixext/DependencyBinder.cs b/src/wixext/DependencyBinder.cs
new file mode 100644
index 00000000..13fea203
--- /dev/null
+++ b/src/wixext/DependencyBinder.cs
@@ -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
3namespace 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}
diff --git a/src/wixext/DependencyCommon.cs b/src/wixext/DependencyCommon.cs
new file mode 100644
index 00000000..4826d8b0
--- /dev/null
+++ b/src/wixext/DependencyCommon.cs
@@ -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
3namespace 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}
diff --git a/src/wixext/DependencyCompiler.cs b/src/wixext/DependencyCompiler.cs
new file mode 100644
index 00000000..a138c047
--- /dev/null
+++ b/src/wixext/DependencyCompiler.cs
@@ -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
3namespace 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}
diff --git a/src/wixext/DependencyDecompiler.cs b/src/wixext/DependencyDecompiler.cs
new file mode 100644
index 00000000..3013cf7c
--- /dev/null
+++ b/src/wixext/DependencyDecompiler.cs
@@ -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
3namespace 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}
diff --git a/src/wixext/DependencyExtension.csproj b/src/wixext/DependencyExtension.csproj
new file mode 100644
index 00000000..050e8662
--- /dev/null
+++ b/src/wixext/DependencyExtension.csproj
@@ -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>
diff --git a/src/wixext/DependencyExtensionData.cs b/src/wixext/DependencyExtensionData.cs
new file mode 100644
index 00000000..da2215ce
--- /dev/null
+++ b/src/wixext/DependencyExtensionData.cs
@@ -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
3namespace 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}
diff --git a/src/wixext/messages.xml b/src/wixext/messages.xml
new file mode 100644
index 00000000..bd6eb602
--- /dev/null
+++ b/src/wixext/messages.xml
@@ -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>
diff --git a/src/wixext/tables.xml b/src/wixext/tables.xml
new file mode 100644
index 00000000..03c9f267
--- /dev/null
+++ b/src/wixext/tables.xml
@@ -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>
diff --git a/src/wixlib/DependencyExtension.wixproj b/src/wixlib/DependencyExtension.wixproj
new file mode 100644
index 00000000..e52218ed
--- /dev/null
+++ b/src/wixlib/DependencyExtension.wixproj
@@ -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>
diff --git a/src/wixlib/DependencyExtension.wxs b/src/wixlib/DependencyExtension.wxs
new file mode 100644
index 00000000..21f863d2
--- /dev/null
+++ b/src/wixlib/DependencyExtension.wxs
@@ -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>
diff --git a/src/wixlib/DependencyExtension_Platform.wxi b/src/wixlib/DependencyExtension_Platform.wxi
new file mode 100644
index 00000000..d06b0055
--- /dev/null
+++ b/src/wixlib/DependencyExtension_Platform.wxi
@@ -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>
diff --git a/src/wixlib/DependencyExtension_x86.wxs b/src/wixlib/DependencyExtension_x86.wxs
new file mode 100644
index 00000000..715eba02
--- /dev/null
+++ b/src/wixlib/DependencyExtension_x86.wxs
@@ -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>
diff --git a/src/wixlib/en-us.wxl b/src/wixlib/en-us.wxl
new file mode 100644
index 00000000..ce29a153
--- /dev/null
+++ b/src/wixlib/en-us.wxl
@@ -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>