diff options
author | Sean Hall <r.sean.hall@gmail.com> | 2019-02-03 13:20:43 -0600 |
---|---|---|
committer | Sean Hall <r.sean.hall@gmail.com> | 2019-02-03 13:20:43 -0600 |
commit | 72f3dceefa2e3893b061e074380794bc60b67b6f (patch) | |
tree | 74b812780e2e16b4e30764d92f6294958413ac6f /src | |
parent | d2f21865933c11b8af80b8383e1dbf611a175133 (diff) | |
download | wix-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.cpp | 27 | ||||
-rw-r--r-- | src/ca/precomp.h | 18 | ||||
-rw-r--r-- | src/ca/wixdepca.cpp | 516 | ||||
-rw-r--r-- | src/ca/wixdepca.def | 8 | ||||
-rw-r--r-- | src/ca/wixdepca.vcxproj | 57 | ||||
-rw-r--r-- | src/ca/wixdepca.vcxproj.filters | 40 | ||||
-rw-r--r-- | src/wixext/Dependency.xsd | 226 | ||||
-rw-r--r-- | src/wixext/DependencyBinder.cs | 169 | ||||
-rw-r--r-- | src/wixext/DependencyCommon.cs | 26 | ||||
-rw-r--r-- | src/wixext/DependencyCompiler.cs | 615 | ||||
-rw-r--r-- | src/wixext/DependencyDecompiler.cs | 345 | ||||
-rw-r--r-- | src/wixext/DependencyExtension.csproj | 50 | ||||
-rw-r--r-- | src/wixext/DependencyExtensionData.cs | 64 | ||||
-rw-r--r-- | src/wixext/messages.xml | 60 | ||||
-rw-r--r-- | src/wixext/tables.xml | 38 | ||||
-rw-r--r-- | src/wixlib/DependencyExtension.wixproj | 27 | ||||
-rw-r--r-- | src/wixlib/DependencyExtension.wxs | 22 | ||||
-rw-r--r-- | src/wixlib/DependencyExtension_Platform.wxi | 26 | ||||
-rw-r--r-- | src/wixlib/DependencyExtension_x86.wxs | 8 | ||||
-rw-r--r-- | src/wixlib/en-us.wxl | 8 |
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 | /******************************************************************** | ||
6 | DllMain - standard entry point for all WiX custom actions. | ||
7 | |||
8 | ********************************************************************/ | ||
9 | extern "C" BOOL WINAPI DllMain( | ||
10 | IN HINSTANCE hInstance, | ||
11 | IN ULONG ulReason, | ||
12 | IN LPVOID) | ||
13 | { | ||
14 | switch(ulReason) | ||
15 | { | ||
16 | case DLL_PROCESS_ATTACH: | ||
17 | WcaGlobalInitialize(hInstance); | ||
18 | ::DisableThreadLibraryCalls(hInstance); | ||
19 | break; | ||
20 | |||
21 | case DLL_PROCESS_DETACH: | ||
22 | WcaGlobalFinalize(); | ||
23 | break; | ||
24 | } | ||
25 | |||
26 | return TRUE; | ||
27 | } | ||
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 | |||
8 | LPCWSTR vcsDependencyProviderQuery = | ||
9 | L"SELECT `WixDependencyProvider`.`WixDependencyProvider`, `WixDependencyProvider`.`Component_`, `WixDependencyProvider`.`ProviderKey`, `WixDependencyProvider`.`Attributes` " | ||
10 | L"FROM `WixDependencyProvider`"; | ||
11 | enum eDependencyProviderQuery { dpqId = 1, dpqComponent, dpqProviderKey, dpqAttributes }; | ||
12 | |||
13 | LPCWSTR vcsDependencyQuery = | ||
14 | L"SELECT `WixDependency`.`WixDependency`, `WixDependencyProvider`.`Component_`, `WixDependency`.`ProviderKey`, `WixDependency`.`MinVersion`, `WixDependency`.`MaxVersion`, `WixDependency`.`Attributes` " | ||
15 | L"FROM `WixDependencyProvider`, `WixDependency`, `WixDependencyRef` " | ||
16 | L"WHERE `WixDependency`.`WixDependency` = `WixDependencyRef`.`WixDependency_` AND `WixDependencyProvider`.`WixDependencyProvider` = `WixDependencyRef`.`WixDependencyProvider_`"; | ||
17 | enum eDependencyComponentQuery { dqId = 1, dqComponent, dqProviderKey, dqMinVersion, dqMaxVersion, dqAttributes }; | ||
18 | |||
19 | static HRESULT EnsureRequiredDependencies( | ||
20 | __in MSIHANDLE hInstall, | ||
21 | __in BOOL fMachineContext | ||
22 | ); | ||
23 | |||
24 | static HRESULT EnsureAbsentDependents( | ||
25 | __in MSIHANDLE hInstall, | ||
26 | __in BOOL fMachineContext | ||
27 | ); | ||
28 | |||
29 | static HRESULT SplitIgnoredDependents( | ||
30 | __deref_inout STRINGDICT_HANDLE* psdIgnoredDependents | ||
31 | ); | ||
32 | |||
33 | static HRESULT CreateDependencyRecord( | ||
34 | __in int iMessageId, | ||
35 | __in_ecount(cDependencies) const DEPENDENCY* rgDependencies, | ||
36 | __in UINT cDependencies, | ||
37 | __out MSIHANDLE *phRecord | ||
38 | ); | ||
39 | |||
40 | static LPCWSTR LogDependencyName( | ||
41 | __in_z LPCWSTR wzName | ||
42 | ); | ||
43 | |||
44 | /*************************************************************************** | ||
45 | WixDependencyRequire - Checks that all required dependencies are installed. | ||
46 | |||
47 | ***************************************************************************/ | ||
48 | extern "C" UINT __stdcall WixDependencyRequire( | ||
49 | __in MSIHANDLE hInstall | ||
50 | ) | ||
51 | { | ||
52 | HRESULT hr = S_OK; | ||
53 | UINT er = ERROR_SUCCESS; | ||
54 | BOOL fMachineContext = FALSE; | ||
55 | |||
56 | hr = WcaInitialize(hInstall, "WixDependencyRequire"); | ||
57 | ExitOnFailure(hr, "Failed to initialize."); | ||
58 | |||
59 | hr = RegInitialize(); | ||
60 | ExitOnFailure(hr, "Failed to initialize the registry functions."); | ||
61 | |||
62 | // Determine whether we're installing per-user or per-machine. | ||
63 | fMachineContext = WcaIsPropertySet("ALLUSERS"); | ||
64 | |||
65 | // Check for any provider components being (re)installed that their requirements are already installed. | ||
66 | hr = EnsureRequiredDependencies(hInstall, fMachineContext); | ||
67 | ExitOnFailure(hr, "Failed to ensure required dependencies for (re)installing components."); | ||
68 | |||
69 | LExit: | ||
70 | RegUninitialize(); | ||
71 | |||
72 | er = FAILED(hr) ? ERROR_INSTALL_FAILURE : ERROR_SUCCESS; | ||
73 | return WcaFinalize(er); | ||
74 | } | ||
75 | |||
76 | /*************************************************************************** | ||
77 | WixDependencyCheck - Check dependencies based on component state. | ||
78 | |||
79 | Note: may return ERROR_NO_MORE_ITEMS to terminate the session early. | ||
80 | ***************************************************************************/ | ||
81 | extern "C" UINT __stdcall WixDependencyCheck( | ||
82 | __in MSIHANDLE hInstall | ||
83 | ) | ||
84 | { | ||
85 | HRESULT hr = S_OK; | ||
86 | UINT er = ERROR_SUCCESS; | ||
87 | BOOL fMachineContext = FALSE; | ||
88 | |||
89 | hr = WcaInitialize(hInstall, "WixDependencyCheck"); | ||
90 | ExitOnFailure(hr, "Failed to initialize."); | ||
91 | |||
92 | hr = RegInitialize(); | ||
93 | ExitOnFailure(hr, "Failed to initialize the registry functions."); | ||
94 | |||
95 | // Determine whether we're installing per-user or per-machine. | ||
96 | fMachineContext = WcaIsPropertySet("ALLUSERS"); | ||
97 | |||
98 | // Check for any dependents of provider components being uninstalled. | ||
99 | hr = EnsureAbsentDependents(hInstall, fMachineContext); | ||
100 | ExitOnFailure(hr, "Failed to ensure absent dependents for uninstalling components."); | ||
101 | |||
102 | LExit: | ||
103 | RegUninitialize(); | ||
104 | |||
105 | er = FAILED(hr) ? ERROR_INSTALL_FAILURE : ERROR_SUCCESS; | ||
106 | return WcaFinalize(er); | ||
107 | } | ||
108 | |||
109 | /*************************************************************************** | ||
110 | EnsureRequiredDependencies - Check that dependencies are installed for | ||
111 | any provider component that is being installed or reinstalled. | ||
112 | |||
113 | Note: Skipped if DISABLEDEPENDENCYCHECK is set. | ||
114 | ***************************************************************************/ | ||
115 | static HRESULT EnsureRequiredDependencies( | ||
116 | __in MSIHANDLE /*hInstall*/, | ||
117 | __in BOOL fMachineContext | ||
118 | ) | ||
119 | { | ||
120 | HRESULT hr = S_OK; | ||
121 | DWORD er = ERROR_SUCCESS; | ||
122 | STRINGDICT_HANDLE sdDependencies = NULL; | ||
123 | HKEY hkHive = NULL; | ||
124 | PMSIHANDLE hView = NULL; | ||
125 | PMSIHANDLE hRec = NULL; | ||
126 | LPWSTR sczId = NULL; | ||
127 | LPWSTR sczComponent = NULL; | ||
128 | LPWSTR sczProviderKey = NULL; | ||
129 | LPWSTR sczMinVersion = NULL; | ||
130 | LPWSTR sczMaxVersion = NULL; | ||
131 | int iAttributes = 0; | ||
132 | WCA_TODO tComponentAction = WCA_TODO_UNKNOWN; | ||
133 | DEPENDENCY* rgDependencies = NULL; | ||
134 | UINT cDependencies = 0; | ||
135 | PMSIHANDLE hDependencyRec = NULL; | ||
136 | |||
137 | // Skip the dependency check if the WixDependency table is missing (no dependencies to check for). | ||
138 | hr = WcaTableExists(L"WixDependency"); | ||
139 | if (S_FALSE == hr) | ||
140 | { | ||
141 | WcaLog(LOGMSG_STANDARD, "Skipping the dependency check since no dependencies are authored."); | ||
142 | ExitFunction1(hr = S_OK); | ||
143 | } | ||
144 | |||
145 | // If the table exists but not the others, the database was not authored correctly. | ||
146 | ExitOnFailure(hr, "Failed to check if the WixDependency table exists."); | ||
147 | |||
148 | // Initialize the dictionary to keep track of unique dependency keys. | ||
149 | hr = DictCreateStringList(&sdDependencies, INITIAL_STRINGDICT_SIZE, DICT_FLAG_CASEINSENSITIVE); | ||
150 | ExitOnFailure(hr, "Failed to initialize the unique dependency string list."); | ||
151 | |||
152 | // Set the registry hive to use depending on install context. | ||
153 | hkHive = fMachineContext ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; | ||
154 | |||
155 | // Loop over the provider components. | ||
156 | hr = WcaOpenExecuteView(vcsDependencyQuery, &hView); | ||
157 | ExitOnFailure(hr, "Failed to open the query view for dependencies."); | ||
158 | |||
159 | while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) | ||
160 | { | ||
161 | hr = WcaGetRecordString(hRec, dqId, &sczId); | ||
162 | ExitOnFailure(hr, "Failed to get WixDependency.WixDependency."); | ||
163 | |||
164 | hr = WcaGetRecordString(hRec, dqComponent, &sczComponent); | ||
165 | ExitOnFailure(hr, "Failed to get WixDependencyProvider.Component_."); | ||
166 | |||
167 | // Skip the current component if its not being installed or reinstalled. | ||
168 | tComponentAction = WcaGetComponentToDo(sczComponent); | ||
169 | if (WCA_TODO_INSTALL != tComponentAction && WCA_TODO_REINSTALL != tComponentAction) | ||
170 | { | ||
171 | WcaLog(LOGMSG_STANDARD, "Skipping dependency check for %ls because the component %ls is not being (re)installed.", sczId, sczComponent); | ||
172 | continue; | ||
173 | } | ||
174 | |||
175 | hr = WcaGetRecordString(hRec, dqProviderKey, &sczProviderKey); | ||
176 | ExitOnFailure(hr, "Failed to get WixDependency.ProviderKey."); | ||
177 | |||
178 | hr = WcaGetRecordString(hRec, dqMinVersion, &sczMinVersion); | ||
179 | ExitOnFailure(hr, "Failed to get WixDependency.MinVersion."); | ||
180 | |||
181 | hr = WcaGetRecordString(hRec, dqMaxVersion, &sczMaxVersion); | ||
182 | ExitOnFailure(hr, "Failed to get WixDependency.MaxVersion."); | ||
183 | |||
184 | hr = WcaGetRecordInteger(hRec, dqAttributes, &iAttributes); | ||
185 | ExitOnFailure(hr, "Failed to get WixDependency.Attributes."); | ||
186 | |||
187 | // Check the registry to see if the required providers (dependencies) exist. | ||
188 | hr = DepCheckDependency(hkHive, sczProviderKey, sczMinVersion, sczMaxVersion, iAttributes, sdDependencies, &rgDependencies, &cDependencies); | ||
189 | if (E_NOTFOUND != hr) | ||
190 | { | ||
191 | ExitOnFailure(hr, "Failed dependency check for %ls.", sczId); | ||
192 | } | ||
193 | } | ||
194 | |||
195 | if (E_NOMOREITEMS != hr) | ||
196 | { | ||
197 | ExitOnFailure(hr, "Failed to enumerate all of the rows in the dependency query view."); | ||
198 | } | ||
199 | else | ||
200 | { | ||
201 | hr = S_OK; | ||
202 | } | ||
203 | |||
204 | // If we collected any dependencies in the previous check, pump a message and prompt the user. | ||
205 | if (0 < cDependencies) | ||
206 | { | ||
207 | hr = CreateDependencyRecord(msierrDependencyMissingDependencies, rgDependencies, cDependencies, &hDependencyRec); | ||
208 | ExitOnFailure(hr, "Failed to create the dependency record for message %d.", msierrDependencyMissingDependencies); | ||
209 | |||
210 | // Send a yes/no message with a warning icon since continuing could be detrimental. | ||
211 | // This is sent as a USER message to better detect whether a user or dependency-aware bootstrapper is responding | ||
212 | // or if Windows Installer or a dependency-unaware boostrapper is returning a typical default response. | ||
213 | er = WcaProcessMessage(static_cast<INSTALLMESSAGE>(INSTALLMESSAGE_USER | MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2), hDependencyRec); | ||
214 | switch (er) | ||
215 | { | ||
216 | // Only a user or dependency-aware bootstrapper that prompted the user should return IDYES to continue anyway. | ||
217 | case IDYES: | ||
218 | ExitFunction1(hr = S_OK); | ||
219 | |||
220 | // Only a user or dependency-aware bootstrapper that prompted the user should return IDNO to terminate the operation. | ||
221 | case IDNO: | ||
222 | WcaSetReturnValue(ERROR_INSTALL_USEREXIT); | ||
223 | ExitFunction1(hr = S_OK); | ||
224 | |||
225 | // A dependency-aware bootstrapper should return IDCANCEL if running silently and the operation should be canceled. | ||
226 | case IDCANCEL: | ||
227 | __fallthrough; | ||
228 | |||
229 | // Bootstrappers which are not dependency-aware may return IDOK for unhandled messages. | ||
230 | case IDOK: | ||
231 | __fallthrough; | ||
232 | |||
233 | // Windows Installer returns 0 for USER messages when silent or passive, or when a bootstrapper does not handle the message. | ||
234 | case IDNOACTION: | ||
235 | WcaSetReturnValue(ERROR_INSTALL_FAILURE); | ||
236 | ExitFunction1(hr = S_OK); | ||
237 | |||
238 | default: | ||
239 | ExitOnFailure(hr = E_UNEXPECTED, "Unexpected message response %d from user or bootstrapper application.", er); | ||
240 | } | ||
241 | } | ||
242 | |||
243 | LExit: | ||
244 | ReleaseDependencyArray(rgDependencies, cDependencies); | ||
245 | ReleaseStr(sczId); | ||
246 | ReleaseStr(sczComponent); | ||
247 | ReleaseStr(sczProviderKey); | ||
248 | ReleaseStr(sczMinVersion); | ||
249 | ReleaseStr(sczMaxVersion); | ||
250 | ReleaseDict(sdDependencies); | ||
251 | |||
252 | return hr; | ||
253 | } | ||
254 | |||
255 | /*************************************************************************** | ||
256 | EnsureAbsentDependents - Checks that there are no dependents | ||
257 | registered for providers that are being uninstalled. | ||
258 | |||
259 | Note: Skipped if UPGRADINGPRODUCTCODE is set. | ||
260 | ***************************************************************************/ | ||
261 | static HRESULT EnsureAbsentDependents( | ||
262 | __in MSIHANDLE /*hInstall*/, | ||
263 | __in BOOL fMachineContext | ||
264 | ) | ||
265 | { | ||
266 | HRESULT hr = S_OK; | ||
267 | DWORD er = ERROR_SUCCESS; | ||
268 | STRINGDICT_HANDLE sdIgnoredDependents = NULL; | ||
269 | HKEY hkHive = NULL; | ||
270 | PMSIHANDLE hView = NULL; | ||
271 | PMSIHANDLE hRec = NULL; | ||
272 | LPWSTR sczId = NULL; | ||
273 | LPWSTR sczComponent = NULL; | ||
274 | LPWSTR sczProviderKey = NULL; | ||
275 | int iAttributes = 0; | ||
276 | WCA_TODO tComponentAction = WCA_TODO_UNKNOWN; | ||
277 | DEPENDENCY* rgDependents = NULL; | ||
278 | UINT cDependents = 0; | ||
279 | PMSIHANDLE hDependencyRec = NULL; | ||
280 | |||
281 | // Split the IGNOREDEPENDENCIES property for use below if set. If it is "ALL", then quit now. | ||
282 | hr = SplitIgnoredDependents(&sdIgnoredDependents); | ||
283 | ExitOnFailure(hr, "Failed to get the ignored dependents."); | ||
284 | |||
285 | hr = DictKeyExists(sdIgnoredDependents, L"ALL"); | ||
286 | if (E_NOTFOUND != hr) | ||
287 | { | ||
288 | ExitOnFailure(hr, "Failed to check if \"ALL\" was set in IGNOREDEPENDENCIES."); | ||
289 | |||
290 | // Otherwise... | ||
291 | WcaLog(LOGMSG_STANDARD, "Skipping the dependencies check since IGNOREDEPENDENCIES contains \"ALL\"."); | ||
292 | ExitFunction(); | ||
293 | } | ||
294 | else | ||
295 | { | ||
296 | // Key was not found, so proceed. | ||
297 | hr = S_OK; | ||
298 | } | ||
299 | |||
300 | // Skip the dependent check if the WixDependencyProvider table is missing (no dependency providers). | ||
301 | hr = WcaTableExists(L"WixDependencyProvider"); | ||
302 | if (S_FALSE == hr) | ||
303 | { | ||
304 | WcaLog(LOGMSG_STANDARD, "Skipping the dependents check since no dependency providers are authored."); | ||
305 | ExitFunction(); | ||
306 | } | ||
307 | |||
308 | ExitOnFailure(hr, "Failed to check if the WixDependencyProvider table exists."); | ||
309 | |||
310 | // Set the registry hive to use depending on install context. | ||
311 | hkHive = fMachineContext ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; | ||
312 | |||
313 | // Loop over the provider components. | ||
314 | hr = WcaOpenExecuteView(vcsDependencyProviderQuery, &hView); | ||
315 | ExitOnFailure(hr, "Failed to open the query view for dependency providers."); | ||
316 | |||
317 | while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) | ||
318 | { | ||
319 | hr = WcaGetRecordString(hRec, dpqId, &sczId); | ||
320 | ExitOnFailure(hr, "Failed to get WixDependencyProvider.WixDependencyProvider."); | ||
321 | |||
322 | hr = WcaGetRecordString(hRec, dpqComponent, &sczComponent); | ||
323 | ExitOnFailure(hr, "Failed to get WixDependencyProvider.Component."); | ||
324 | |||
325 | // Skip the current component if its not being uninstalled. | ||
326 | tComponentAction = WcaGetComponentToDo(sczComponent); | ||
327 | if (WCA_TODO_UNINSTALL != tComponentAction) | ||
328 | { | ||
329 | WcaLog(LOGMSG_STANDARD, "Skipping dependents check for %ls because the component %ls is not being uninstalled.", sczId, sczComponent); | ||
330 | continue; | ||
331 | } | ||
332 | |||
333 | hr = WcaGetRecordString(hRec, dpqProviderKey, &sczProviderKey); | ||
334 | ExitOnFailure(hr, "Failed to get WixDependencyProvider.ProviderKey."); | ||
335 | |||
336 | hr = WcaGetRecordInteger(hRec, dpqAttributes, &iAttributes); | ||
337 | ExitOnFailure(hr, "Failed to get WixDependencyProvider.Attributes."); | ||
338 | |||
339 | // Check the registry to see if the provider has any dependents registered. | ||
340 | hr = DepCheckDependents(hkHive, sczProviderKey, iAttributes, sdIgnoredDependents, &rgDependents, &cDependents); | ||
341 | ExitOnFailure(hr, "Failed dependents check for %ls.", sczId); | ||
342 | } | ||
343 | |||
344 | if (E_NOMOREITEMS != hr) | ||
345 | { | ||
346 | ExitOnFailure(hr, "Failed to enumerate all of the rows in the dependency provider query view."); | ||
347 | } | ||
348 | else | ||
349 | { | ||
350 | hr = S_OK; | ||
351 | } | ||
352 | |||
353 | // If we collected any providers with dependents in the previous check, pump a message and prompt the user. | ||
354 | if (0 < cDependents) | ||
355 | { | ||
356 | hr = CreateDependencyRecord(msierrDependencyHasDependents, rgDependents, cDependents, &hDependencyRec); | ||
357 | ExitOnFailure(hr, "Failed to create the dependency record for message %d.", msierrDependencyHasDependents); | ||
358 | |||
359 | // Send a yes/no message with a warning icon since continuing could be detrimental. | ||
360 | // This is sent as a USER message to better detect whether a user or dependency-aware bootstrapper is responding | ||
361 | // or if Windows Installer or a dependency-unaware boostrapper is returning a typical default response. | ||
362 | er = WcaProcessMessage(static_cast<INSTALLMESSAGE>(INSTALLMESSAGE_USER | MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2), hDependencyRec); | ||
363 | switch (er) | ||
364 | { | ||
365 | // Only a user or dependency-aware bootstrapper that prompted the user should return IDYES to continue anyway. | ||
366 | case IDYES: | ||
367 | ExitFunction1(hr = S_OK); | ||
368 | |||
369 | // Only a user or dependency-aware bootstrapper that prompted the user should return IDNO to terminate the operation. | ||
370 | case IDNO: | ||
371 | __fallthrough; | ||
372 | |||
373 | // Bootstrappers which are not dependency-aware may return IDOK for unhandled messages. | ||
374 | case IDOK: | ||
375 | __fallthrough; | ||
376 | |||
377 | // Windows Installer returns 0 for USER messages when silent or passive, or when a bootstrapper does not handle the message. | ||
378 | case IDNOACTION: | ||
379 | WcaSetReturnValue(ERROR_NO_MORE_ITEMS); | ||
380 | ExitFunction1(hr = S_OK); | ||
381 | |||
382 | // A dependency-aware bootstrapper should return IDCANCEL if running silently and the operation should be canceled. | ||
383 | case IDCANCEL: | ||
384 | WcaSetReturnValue(ERROR_INSTALL_FAILURE); | ||
385 | ExitFunction1(hr = S_OK); | ||
386 | |||
387 | default: | ||
388 | hr = E_UNEXPECTED; | ||
389 | ExitOnFailure(hr, "Unexpected message response %d from user or bootstrapper application.", er); | ||
390 | } | ||
391 | } | ||
392 | |||
393 | LExit: | ||
394 | ReleaseDependencyArray(rgDependents, cDependents); | ||
395 | ReleaseStr(sczId); | ||
396 | ReleaseStr(sczComponent); | ||
397 | ReleaseStr(sczProviderKey); | ||
398 | |||
399 | return hr; | ||
400 | } | ||
401 | |||
402 | /*************************************************************************** | ||
403 | SplitIgnoredDependents - Splits the IGNOREDEPENDENCIES property into a map. | ||
404 | |||
405 | ***************************************************************************/ | ||
406 | static HRESULT SplitIgnoredDependents( | ||
407 | __deref_inout STRINGDICT_HANDLE* psdIgnoredDependents | ||
408 | ) | ||
409 | { | ||
410 | HRESULT hr = S_OK; | ||
411 | LPWSTR sczIgnoreDependencies = NULL; | ||
412 | LPCWSTR wzDelim = L";"; | ||
413 | LPWSTR wzContext = NULL; | ||
414 | |||
415 | hr = WcaGetProperty(L"IGNOREDEPENDENCIES", &sczIgnoreDependencies); | ||
416 | ExitOnFailure(hr, "Failed to get the string value of the IGNOREDEPENDENCIES property."); | ||
417 | |||
418 | hr = DictCreateStringList(psdIgnoredDependents, INITIAL_STRINGDICT_SIZE, DICT_FLAG_CASEINSENSITIVE); | ||
419 | ExitOnFailure(hr, "Failed to create the string dictionary."); | ||
420 | |||
421 | // Parse through the semicolon-delimited tokens and add to the string dictionary. | ||
422 | for (LPCWSTR wzToken = ::wcstok_s(sczIgnoreDependencies, wzDelim, &wzContext); wzToken; wzToken = ::wcstok_s(NULL, wzDelim, &wzContext)) | ||
423 | { | ||
424 | hr = DictAddKey(*psdIgnoredDependents, wzToken); | ||
425 | ExitOnFailure(hr, "Failed to ignored dependency \"%ls\" to the string dictionary.", wzToken); | ||
426 | } | ||
427 | |||
428 | LExit: | ||
429 | ReleaseStr(sczIgnoreDependencies); | ||
430 | |||
431 | return hr; | ||
432 | } | ||
433 | |||
434 | /*************************************************************************** | ||
435 | CreateDependencyRecord - Creates a record containing the message template | ||
436 | and records to send to the UI handler. | ||
437 | |||
438 | Notes: Callers should call WcaProcessMessage and handle return codes. | ||
439 | ***************************************************************************/ | ||
440 | static HRESULT CreateDependencyRecord( | ||
441 | __in int iMessageId, | ||
442 | __in_ecount(cDependencies) const DEPENDENCY* rgDependencies, | ||
443 | __in UINT cDependencies, | ||
444 | __out MSIHANDLE *phRecord | ||
445 | ) | ||
446 | { | ||
447 | HRESULT hr = S_OK; | ||
448 | UINT er = ERROR_SUCCESS; | ||
449 | UINT cParams = 0; | ||
450 | UINT iParam = 0; | ||
451 | |||
452 | // Should not be PMSIHANDLE. | ||
453 | MSIHANDLE hRec = NULL; | ||
454 | |||
455 | // Calculate the number of parameters based on the format: | ||
456 | // msgId, count, key1, name1, key2, name2, etc. | ||
457 | cParams = 2 + 2 * cDependencies; | ||
458 | |||
459 | hRec = ::MsiCreateRecord(cParams); | ||
460 | ExitOnNull(hRec, hr, E_OUTOFMEMORY, "Not enough memory to create the message record."); | ||
461 | |||
462 | er = ::MsiRecordSetInteger(hRec, ++iParam, iMessageId); | ||
463 | ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to set the message identifier into the message record."); | ||
464 | |||
465 | er = ::MsiRecordSetInteger(hRec, ++iParam, cDependencies); | ||
466 | ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to set the number of dependencies into the message record."); | ||
467 | |||
468 | // Now loop through each dependency and add the key and name to the record. | ||
469 | for (UINT i = 0; i < cDependencies; i++) | ||
470 | { | ||
471 | const DEPENDENCY* pDependency = &rgDependencies[i]; | ||
472 | |||
473 | // Log message type-specific information. | ||
474 | switch (iMessageId) | ||
475 | { | ||
476 | // Send a user message when installing a component that is missing some dependencies. | ||
477 | case msierrDependencyMissingDependencies: | ||
478 | WcaLog(LOGMSG_VERBOSE, "The dependency \"%ls\" is missing or is not the required version.", pDependency->sczKey); | ||
479 | break; | ||
480 | |||
481 | // Send a user message when uninstalling a component that still has registered dependents. | ||
482 | case msierrDependencyHasDependents: | ||
483 | WcaLog(LOGMSG_VERBOSE, "Found dependent \"%ls\", name: \"%ls\".", pDependency->sczKey, LogDependencyName(pDependency->sczName)); | ||
484 | break; | ||
485 | } | ||
486 | |||
487 | er = ::MsiRecordSetStringW(hRec, ++iParam, pDependency->sczKey); | ||
488 | ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to set the dependency key \"%ls\" into the message record.", pDependency->sczKey); | ||
489 | |||
490 | er = ::MsiRecordSetStringW(hRec, ++iParam, pDependency->sczName); | ||
491 | ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Failed to set the dependency name \"%ls\" into the message record.", pDependency->sczName); | ||
492 | } | ||
493 | |||
494 | // Only assign the out parameter if successful to this point. | ||
495 | *phRecord = hRec; | ||
496 | hRec = NULL; | ||
497 | |||
498 | LExit: | ||
499 | if (hRec) | ||
500 | { | ||
501 | ::MsiCloseHandle(hRec); | ||
502 | } | ||
503 | |||
504 | return hr; | ||
505 | } | ||
506 | |||
507 | /*************************************************************************** | ||
508 | LogDependencyName - Returns the dependency name or "Unknown" if null. | ||
509 | |||
510 | ***************************************************************************/ | ||
511 | static LPCWSTR LogDependencyName( | ||
512 | __in_z LPCWSTR wzName | ||
513 | ) | ||
514 | { | ||
515 | return wzName ? wzName : L"Unknown"; | ||
516 | } | ||
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 | |||
4 | LIBRARY "wixdepca" | ||
5 | |||
6 | EXPORTS | ||
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 | |||
3 | namespace WixToolset.Extensions | ||
4 | { | ||
5 | using System; | ||
6 | using System.Collections.ObjectModel; | ||
7 | using System.Globalization; | ||
8 | using WixToolset.Data; | ||
9 | using WixToolset.Extensibility; | ||
10 | |||
11 | /// <summary> | ||
12 | /// The compiler for the WiX toolset dependency extension. | ||
13 | /// </summary> | ||
14 | public sealed class DependencyBinder : BinderExtension | ||
15 | { | ||
16 | private Output output; | ||
17 | |||
18 | /// <summary> | ||
19 | /// Called after all output changes occur and right before the output is bound into its final format. | ||
20 | /// </summary> | ||
21 | public override void Finish(Output output) | ||
22 | { | ||
23 | // Only process MSI packages. | ||
24 | if (OutputType.Product != output.Type) | ||
25 | { | ||
26 | return; | ||
27 | } | ||
28 | |||
29 | this.output = output; | ||
30 | |||
31 | Table wixDependencyTable = output.Tables["WixDependency"]; | ||
32 | Table wixDependencyProviderTable = output.Tables["WixDependencyProvider"]; | ||
33 | Table wixDependencyRefTable = output.Tables["WixDependencyRef"]; | ||
34 | |||
35 | // Make sure there's something to do. | ||
36 | if (null != wixDependencyRefTable) | ||
37 | { | ||
38 | KeyedRowCollection wixDependencyRows = new KeyedRowCollection(wixDependencyTable); | ||
39 | KeyedRowCollection wixDependencyProviderRows = new KeyedRowCollection(wixDependencyProviderTable); | ||
40 | |||
41 | // For each relationship, get the provides and requires rows to generate registry values. | ||
42 | foreach (Row wixDependencyRefRow in wixDependencyRefTable.Rows) | ||
43 | { | ||
44 | string providesId = (string)wixDependencyRefRow[0]; | ||
45 | string requiresId = (string)wixDependencyRefRow[1]; | ||
46 | |||
47 | Row wixDependencyRow = null; | ||
48 | if (wixDependencyRows.Contains(requiresId)) | ||
49 | { | ||
50 | wixDependencyRow = wixDependencyRows[requiresId]; | ||
51 | } | ||
52 | |||
53 | Row wixDependencyProviderRow = null; | ||
54 | if (wixDependencyProviderRows.Contains(providesId)) | ||
55 | { | ||
56 | wixDependencyProviderRow = wixDependencyProviderRows[providesId]; | ||
57 | } | ||
58 | |||
59 | // If we found both rows, generate the registry values. | ||
60 | if (null != wixDependencyRow && null != wixDependencyProviderRow) | ||
61 | { | ||
62 | // Format the root registry key using the required provider key and the current provider key. | ||
63 | string requiresKey = (string)wixDependencyRow[1]; | ||
64 | string providesKey = (string)wixDependencyProviderRow[2]; | ||
65 | string keyRequires = String.Format(@"{0}{1}\{2}\{3}", DependencyCommon.RegistryRoot, requiresKey, DependencyCommon.RegistryDependents, providesKey); | ||
66 | |||
67 | // Get the component ID from the provider. | ||
68 | string componentId = (string)wixDependencyProviderRow[1]; | ||
69 | |||
70 | Row row = this.CreateRegistryRow(wixDependencyRow); | ||
71 | row[0] = this.Core.CreateIdentifier("reg", providesId, requiresId, "(Default)"); | ||
72 | row[1] = -1; | ||
73 | row[2] = keyRequires; | ||
74 | row[3] = "*"; | ||
75 | row[4] = null; | ||
76 | row[5] = componentId; | ||
77 | |||
78 | string minVersion = (string)wixDependencyRow[2]; | ||
79 | if (!String.IsNullOrEmpty(minVersion)) | ||
80 | { | ||
81 | row = this.CreateRegistryRow(wixDependencyRow); | ||
82 | row[0] = this.Core.CreateIdentifier("reg", providesId, requiresId, "MinVersion"); | ||
83 | row[1] = -1; | ||
84 | row[2] = keyRequires; | ||
85 | row[3] = "MinVersion"; | ||
86 | row[4] = minVersion; | ||
87 | row[5] = componentId; | ||
88 | } | ||
89 | |||
90 | string maxVersion = (string)wixDependencyRow[3]; | ||
91 | if (!String.IsNullOrEmpty(minVersion)) | ||
92 | { | ||
93 | row = this.CreateRegistryRow(wixDependencyRow); | ||
94 | row[0] = this.Core.CreateIdentifier("reg", providesId, requiresId, "MaxVersion"); | ||
95 | row[1] = -1; | ||
96 | row[2] = keyRequires; | ||
97 | row[3] = "MaxVersion"; | ||
98 | row[4] = maxVersion; | ||
99 | row[5] = componentId; | ||
100 | } | ||
101 | |||
102 | if (null != wixDependencyRow[4]) | ||
103 | { | ||
104 | int attributes = (int)wixDependencyRow[4]; | ||
105 | |||
106 | row = this.CreateRegistryRow(wixDependencyRow); | ||
107 | row[0] = this.Core.CreateIdentifier("reg", providesId, requiresId, "Attributes"); | ||
108 | row[1] = -1; | ||
109 | row[2] = keyRequires; | ||
110 | row[3] = "Attributes"; | ||
111 | row[4] = String.Concat("#", attributes.ToString(CultureInfo.InvariantCulture.NumberFormat)); | ||
112 | row[5] = componentId; | ||
113 | } | ||
114 | } | ||
115 | } | ||
116 | } | ||
117 | } | ||
118 | |||
119 | /// <summary> | ||
120 | /// Creates a registry row using source information from the given <see cref="Row"/>. | ||
121 | /// </summary> | ||
122 | /// <param name="referenceRow">The <see cref="Row"/> from which the section and source line information are retrieved.</param> | ||
123 | /// <returns>A new Registry row.</returns> | ||
124 | private Row CreateRegistryRow(Row referenceRow) | ||
125 | { | ||
126 | TableDefinition tableDefinition = this.Core.TableDefinitions["Registry"]; | ||
127 | |||
128 | // Create the row from the main tables, which were populated during link anyway. | ||
129 | // We still associate the table with the dependency row's section to maintain servicing. | ||
130 | Table table = this.output.EnsureTable(tableDefinition, referenceRow.Table.Section); | ||
131 | Row row = table.CreateRow(referenceRow.SourceLineNumbers); | ||
132 | |||
133 | // Set the section ID for patching and return the new row. | ||
134 | row.SectionId = referenceRow.SectionId; | ||
135 | return row; | ||
136 | } | ||
137 | |||
138 | /// <summary> | ||
139 | /// A keyed collection of <see cref="Row"/> instances for O(1) lookup. | ||
140 | /// </summary> | ||
141 | private sealed class KeyedRowCollection : KeyedCollection<string, Row> | ||
142 | { | ||
143 | /// <summary> | ||
144 | /// Initializes the <see cref="KeyedRowCollection"/> class with all rows from the specified <paramref name="table"/>. | ||
145 | /// </summary> | ||
146 | /// <param name="table">The <see cref="Table"/> containing rows to index.</param> | ||
147 | internal KeyedRowCollection(Table table) | ||
148 | { | ||
149 | if (null != table) | ||
150 | { | ||
151 | foreach (Row row in table.Rows) | ||
152 | { | ||
153 | this.Add(row); | ||
154 | } | ||
155 | } | ||
156 | } | ||
157 | |||
158 | /// <summary> | ||
159 | /// Gets the primary key for the <see cref="Row"/>. | ||
160 | /// </summary> | ||
161 | /// <param name="row">The <see cref="Row"/> to index.</param> | ||
162 | /// <returns>The primary key for the <see cref="Row"/>.</returns> | ||
163 | protected override string GetKeyForItem(Row row) | ||
164 | { | ||
165 | return row.GetPrimaryKey('/'); | ||
166 | } | ||
167 | } | ||
168 | } | ||
169 | } | ||
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 | |||
3 | namespace WixToolset.Extensions | ||
4 | { | ||
5 | using System; | ||
6 | using WixToolset; | ||
7 | |||
8 | internal static class DependencyCommon | ||
9 | { | ||
10 | // Bundle attributes are in the upper 32-bits. | ||
11 | internal const int ProvidesAttributesBundle = 0x10000; | ||
12 | |||
13 | // Same values as for the Upgrade table in Windows Installer. | ||
14 | internal const int RequiresAttributesMinVersionInclusive = 256; | ||
15 | internal const int RequiresAttributesMaxVersionInclusive = 512; | ||
16 | |||
17 | // The root registry key for the dependency extension. We write to Software\Classes explicitly | ||
18 | // based on the current security context instead of HKCR. See | ||
19 | // http://msdn.microsoft.com/en-us/library/ms724475(VS.85).aspx for more information. | ||
20 | internal static readonly string RegistryRoot = @"Software\Classes\Installer\Dependencies\"; | ||
21 | internal static readonly string RegistryDependents = "Dependents"; | ||
22 | |||
23 | // The following characters cannot be used in a provider key. | ||
24 | internal static readonly char[] InvalidCharacters = new char[] { ' ', '\"', ';', '\\' }; | ||
25 | } | ||
26 | } | ||
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 | |||
3 | namespace WixToolset.Extensions | ||
4 | { | ||
5 | using System; | ||
6 | using System.Collections.Generic; | ||
7 | using System.Globalization; | ||
8 | using System.Text; | ||
9 | using System.Xml.Linq; | ||
10 | using WixToolset.Data; | ||
11 | using WixToolset.Extensibility; | ||
12 | |||
13 | /// <summary> | ||
14 | /// The compiler for the WiX toolset dependency extension. | ||
15 | /// </summary> | ||
16 | public sealed class DependencyCompiler : CompilerExtension | ||
17 | { | ||
18 | /// <summary> | ||
19 | /// Package type when parsing the Provides element. | ||
20 | /// </summary> | ||
21 | private enum PackageType | ||
22 | { | ||
23 | None, | ||
24 | ExePackage, | ||
25 | MsiPackage, | ||
26 | MspPackage, | ||
27 | MsuPackage | ||
28 | } | ||
29 | |||
30 | public DependencyCompiler() | ||
31 | { | ||
32 | this.Namespace = "http://wixtoolset.org/schemas/v4/wxs/dependency"; | ||
33 | } | ||
34 | |||
35 | /// <summary> | ||
36 | /// Processes an attribute for the Compiler. | ||
37 | /// </summary> | ||
38 | /// <param name="sourceLineNumbers">Source line number for the parent element.</param> | ||
39 | /// <param name="parentElement">Parent element of attribute.</param> | ||
40 | /// <param name="attribute">Attribute to process.</param> | ||
41 | public override void ParseAttribute(XElement parentElement, XAttribute attribute, IDictionary<string, string> context) | ||
42 | { | ||
43 | SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(parentElement); | ||
44 | switch (parentElement.Name.LocalName) | ||
45 | { | ||
46 | case "Bundle": | ||
47 | switch (attribute.Name.LocalName) | ||
48 | { | ||
49 | case "ProviderKey": | ||
50 | this.ParseProviderKeyAttribute(sourceLineNumbers, parentElement, attribute); | ||
51 | break; | ||
52 | default: | ||
53 | this.Core.UnexpectedAttribute(parentElement, attribute); | ||
54 | break; | ||
55 | } | ||
56 | break; | ||
57 | default: | ||
58 | this.Core.UnexpectedAttribute(parentElement, attribute); | ||
59 | break; | ||
60 | } | ||
61 | } | ||
62 | |||
63 | /// <summary> | ||
64 | /// Processes an element for the Compiler. | ||
65 | /// </summary> | ||
66 | /// <param name="sourceLineNumbers">Source line number for the parent element.</param> | ||
67 | /// <param name="parentElement">Parent element of element to process.</param> | ||
68 | /// <param name="element">Element to process.</param> | ||
69 | /// <param name="contextValues">Extra information about the context in which this element is being parsed.</param> | ||
70 | public override void ParseElement(XElement parentElement, XElement element, IDictionary<string, string> context) | ||
71 | { | ||
72 | PackageType packageType = PackageType.None; | ||
73 | |||
74 | switch (parentElement.Name.LocalName) | ||
75 | { | ||
76 | case "Bundle": | ||
77 | case "Fragment": | ||
78 | case "Module": | ||
79 | case "Product": | ||
80 | switch (element.Name.LocalName) | ||
81 | { | ||
82 | case "Requires": | ||
83 | this.ParseRequiresElement(element, null, false); | ||
84 | break; | ||
85 | default: | ||
86 | this.Core.UnexpectedElement(parentElement, element); | ||
87 | break; | ||
88 | } | ||
89 | break; | ||
90 | case "ExePackage": | ||
91 | packageType = PackageType.ExePackage; | ||
92 | break; | ||
93 | case "MsiPackage": | ||
94 | packageType = PackageType.MsiPackage; | ||
95 | break; | ||
96 | case "MspPackage": | ||
97 | packageType = PackageType.MspPackage; | ||
98 | break; | ||
99 | case "MsuPackage": | ||
100 | packageType = PackageType.MsuPackage; | ||
101 | break; | ||
102 | default: | ||
103 | this.Core.UnexpectedElement(parentElement, element); | ||
104 | break; | ||
105 | } | ||
106 | |||
107 | if (PackageType.None != packageType) | ||
108 | { | ||
109 | string packageId = context["PackageId"]; | ||
110 | |||
111 | switch (element.Name.LocalName) | ||
112 | { | ||
113 | case "Provides": | ||
114 | this.ParseProvidesElement(element, packageType, packageId); | ||
115 | break; | ||
116 | default: | ||
117 | this.Core.UnexpectedElement(parentElement, element); | ||
118 | break; | ||
119 | } | ||
120 | } | ||
121 | } | ||
122 | |||
123 | /// <summary> | ||
124 | /// Processes a child element of a Component for the Compiler. | ||
125 | /// </summary> | ||
126 | /// <param name="parentElement">Parent element of element to process.</param> | ||
127 | /// <param name="element">Element to process.</param> | ||
128 | /// <param name="context">Extra information about the context in which this element is being parsed.</param> | ||
129 | /// <returns>The component key path type if set.</returns> | ||
130 | public override ComponentKeyPath ParsePossibleKeyPathElement(XElement parentElement, XElement element, IDictionary<string, string> context) | ||
131 | { | ||
132 | SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(parentElement); | ||
133 | ComponentKeyPath keyPath = null; | ||
134 | |||
135 | switch (parentElement.Name.LocalName) | ||
136 | { | ||
137 | case "Component": | ||
138 | string componentId = context["ComponentId"]; | ||
139 | |||
140 | // 64-bit components may cause issues downlevel. | ||
141 | bool win64 = false; | ||
142 | Boolean.TryParse(context["Win64"], out win64); | ||
143 | |||
144 | switch (element.Name.LocalName) | ||
145 | { | ||
146 | case "Provides": | ||
147 | if (win64) | ||
148 | { | ||
149 | this.Core.OnMessage(DependencyWarnings.Win64Component(sourceLineNumbers, componentId)); | ||
150 | } | ||
151 | |||
152 | keyPath = this.ParseProvidesElement(element, PackageType.None, componentId); | ||
153 | break; | ||
154 | default: | ||
155 | this.Core.UnexpectedElement(parentElement, element); | ||
156 | break; | ||
157 | } | ||
158 | break; | ||
159 | default: | ||
160 | this.Core.UnexpectedElement(parentElement, element); | ||
161 | break; | ||
162 | } | ||
163 | |||
164 | return keyPath; | ||
165 | } | ||
166 | |||
167 | /// <summary> | ||
168 | /// Processes the ProviderKey bundle attribute. | ||
169 | /// </summary> | ||
170 | /// <param name="sourceLineNumbers">Source line number for the parent element.</param> | ||
171 | /// <param name="parentElement">Parent element of attribute.</param> | ||
172 | /// <param name="attribute">The XML attribute for the ProviderKey attribute.</param> | ||
173 | private void ParseProviderKeyAttribute(SourceLineNumber sourceLineNumbers, XElement parentElement, XAttribute attribute) | ||
174 | { | ||
175 | Identifier id = null; | ||
176 | string providerKey = null; | ||
177 | int illegalChar = -1; | ||
178 | |||
179 | switch (attribute.Name.LocalName) | ||
180 | { | ||
181 | case "ProviderKey": | ||
182 | providerKey = this.Core.GetAttributeValue(sourceLineNumbers, attribute); | ||
183 | break; | ||
184 | default: | ||
185 | this.Core.UnexpectedAttribute(parentElement, attribute); | ||
186 | break; | ||
187 | } | ||
188 | |||
189 | // Make sure the key does not contain any illegal characters or values. | ||
190 | if (String.IsNullOrEmpty(providerKey)) | ||
191 | { | ||
192 | this.Core.OnMessage(WixErrors.IllegalEmptyAttributeValue(sourceLineNumbers, parentElement.Name.LocalName, attribute.Name.LocalName)); | ||
193 | } | ||
194 | else if (0 <= (illegalChar = providerKey.IndexOfAny(DependencyCommon.InvalidCharacters))) | ||
195 | { | ||
196 | StringBuilder sb = new StringBuilder(DependencyCommon.InvalidCharacters.Length * 2); | ||
197 | Array.ForEach<char>(DependencyCommon.InvalidCharacters, c => sb.Append(c).Append(" ")); | ||
198 | |||
199 | this.Core.OnMessage(DependencyErrors.IllegalCharactersInProvider(sourceLineNumbers, "ProviderKey", providerKey[illegalChar], sb.ToString())); | ||
200 | } | ||
201 | else if ("ALL" == providerKey) | ||
202 | { | ||
203 | this.Core.OnMessage(DependencyErrors.ReservedValue(sourceLineNumbers, parentElement.Name.LocalName, "ProviderKey", providerKey)); | ||
204 | } | ||
205 | |||
206 | // Generate the primary key for the row. | ||
207 | id = this.Core.CreateIdentifier("dep", attribute.Name.LocalName, providerKey); | ||
208 | |||
209 | if (!this.Core.EncounteredError) | ||
210 | { | ||
211 | // Create the provider row for the bundle. The Component_ field is required | ||
212 | // in the table definition but unused for bundles, so just set it to the valid ID. | ||
213 | Row row = this.Core.CreateRow(sourceLineNumbers, "WixDependencyProvider", id); | ||
214 | row[1] = id.Id; | ||
215 | row[2] = providerKey; | ||
216 | row[5] = DependencyCommon.ProvidesAttributesBundle; | ||
217 | } | ||
218 | } | ||
219 | |||
220 | /// <summary> | ||
221 | /// Processes the Provides element. | ||
222 | /// </summary> | ||
223 | /// <param name="node">The XML node for the Provides element.</param> | ||
224 | /// <param name="packageType">The type of the package being chained into a bundle, or "None" if building an MSI package.</param> | ||
225 | /// <param name="keyPath">Explicit key path.</param> | ||
226 | /// <param name="parentId">The identifier of the parent component or package.</param> | ||
227 | /// <returns>The type of key path if set.</returns> | ||
228 | private ComponentKeyPath ParseProvidesElement(XElement node, PackageType packageType, string parentId) | ||
229 | { | ||
230 | SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); | ||
231 | ComponentKeyPath keyPath = null; | ||
232 | Identifier id = null; | ||
233 | string key = null; | ||
234 | string version = null; | ||
235 | string displayName = null; | ||
236 | int attributes = 0; | ||
237 | int illegalChar = -1; | ||
238 | |||
239 | foreach (XAttribute attrib in node.Attributes()) | ||
240 | { | ||
241 | if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) | ||
242 | { | ||
243 | switch (attrib.Name.LocalName) | ||
244 | { | ||
245 | case "Id": | ||
246 | id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); | ||
247 | break; | ||
248 | case "Key": | ||
249 | key = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
250 | break; | ||
251 | case "Version": | ||
252 | version = this.Core.GetAttributeVersionValue(sourceLineNumbers, attrib); | ||
253 | break; | ||
254 | case "DisplayName": | ||
255 | displayName = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
256 | break; | ||
257 | default: | ||
258 | this.Core.UnexpectedAttribute(node, attrib); | ||
259 | break; | ||
260 | } | ||
261 | } | ||
262 | else | ||
263 | { | ||
264 | this.Core.ParseExtensionAttribute(node, attrib); | ||
265 | } | ||
266 | } | ||
267 | |||
268 | // Make sure the key is valid. The key will default to the ProductCode for MSI packages | ||
269 | // and the package code for MSP packages in the binder if not specified. | ||
270 | if (!String.IsNullOrEmpty(key)) | ||
271 | { | ||
272 | // Make sure the key does not contain any illegal characters or values. | ||
273 | if (0 <= (illegalChar = key.IndexOfAny(DependencyCommon.InvalidCharacters))) | ||
274 | { | ||
275 | StringBuilder sb = new StringBuilder(DependencyCommon.InvalidCharacters.Length * 2); | ||
276 | Array.ForEach<char>(DependencyCommon.InvalidCharacters, c => sb.Append(c).Append(" ")); | ||
277 | |||
278 | this.Core.OnMessage(DependencyErrors.IllegalCharactersInProvider(sourceLineNumbers, "Key", key[illegalChar], sb.ToString())); | ||
279 | } | ||
280 | else if ("ALL" == key) | ||
281 | { | ||
282 | this.Core.OnMessage(DependencyErrors.ReservedValue(sourceLineNumbers, node.Name.LocalName, "Key", key)); | ||
283 | } | ||
284 | } | ||
285 | else if (PackageType.ExePackage == packageType || PackageType.MsuPackage == packageType) | ||
286 | { | ||
287 | // Must specify the provider key when authored for a package. | ||
288 | this.Core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Key")); | ||
289 | } | ||
290 | else if (PackageType.None == packageType) | ||
291 | { | ||
292 | // Make sure the ProductCode is authored and set the key. | ||
293 | this.Core.CreateSimpleReference(sourceLineNumbers, "Property", "ProductCode"); | ||
294 | key = "!(bind.property.ProductCode)"; | ||
295 | } | ||
296 | |||
297 | // The Version attribute should not be authored in or for an MSI package. | ||
298 | if (!String.IsNullOrEmpty(version)) | ||
299 | { | ||
300 | switch (packageType) | ||
301 | { | ||
302 | case PackageType.None: | ||
303 | this.Core.OnMessage(DependencyWarnings.DiscouragedVersionAttribute(sourceLineNumbers)); | ||
304 | break; | ||
305 | case PackageType.MsiPackage: | ||
306 | this.Core.OnMessage(DependencyWarnings.DiscouragedVersionAttribute(sourceLineNumbers, parentId)); | ||
307 | break; | ||
308 | } | ||
309 | } | ||
310 | else if (PackageType.MspPackage == packageType || PackageType.MsuPackage == packageType) | ||
311 | { | ||
312 | // Must specify the Version when authored for packages that do not contain a version. | ||
313 | this.Core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Version")); | ||
314 | } | ||
315 | |||
316 | // Need the element ID for child element processing, so generate now if not authored. | ||
317 | if (null == id) | ||
318 | { | ||
319 | id = this.Core.CreateIdentifier("dep", node.Name.LocalName, parentId, key); | ||
320 | } | ||
321 | |||
322 | foreach (XElement child in node.Elements()) | ||
323 | { | ||
324 | if (this.Namespace == child.Name.Namespace) | ||
325 | { | ||
326 | switch (child.Name.LocalName) | ||
327 | { | ||
328 | case "Requires": | ||
329 | this.ParseRequiresElement(child, id.Id, PackageType.None == packageType); | ||
330 | break; | ||
331 | case "RequiresRef": | ||
332 | this.ParseRequiresRefElement(child, id.Id, PackageType.None == packageType); | ||
333 | break; | ||
334 | default: | ||
335 | this.Core.UnexpectedElement(node, child); | ||
336 | break; | ||
337 | } | ||
338 | } | ||
339 | else | ||
340 | { | ||
341 | this.Core.ParseExtensionElement(node, child); | ||
342 | } | ||
343 | } | ||
344 | |||
345 | if (!this.Core.EncounteredError) | ||
346 | { | ||
347 | // Create the row in the provider table. | ||
348 | Row row = this.Core.CreateRow(sourceLineNumbers, "WixDependencyProvider", id); | ||
349 | row[1] = parentId; | ||
350 | row[2] = key; | ||
351 | |||
352 | if (!String.IsNullOrEmpty(version)) | ||
353 | { | ||
354 | row[3] = version; | ||
355 | } | ||
356 | |||
357 | if (!String.IsNullOrEmpty(displayName)) | ||
358 | { | ||
359 | row[4] = displayName; | ||
360 | } | ||
361 | |||
362 | if (0 != attributes) | ||
363 | { | ||
364 | row[5] = attributes; | ||
365 | } | ||
366 | |||
367 | if (PackageType.None == packageType) | ||
368 | { | ||
369 | // Reference the Check custom action to check for dependencies on the current provider. | ||
370 | if (Platform.ARM == this.Core.CurrentPlatform) | ||
371 | { | ||
372 | // Ensure the ARM version of the CA is referenced. | ||
373 | this.Core.CreateSimpleReference(sourceLineNumbers, "CustomAction", "WixDependencyCheck_ARM"); | ||
374 | } | ||
375 | else | ||
376 | { | ||
377 | // All other supported platforms use x86. | ||
378 | this.Core.CreateSimpleReference(sourceLineNumbers, "CustomAction", "WixDependencyCheck"); | ||
379 | } | ||
380 | |||
381 | // Generate registry rows for the provider using binder properties. | ||
382 | string keyProvides = String.Concat(DependencyCommon.RegistryRoot, key); | ||
383 | |||
384 | row = this.Core.CreateRow(sourceLineNumbers, "Registry", this.Core.CreateIdentifier("reg", id.Id, "(Default)")); | ||
385 | row[1] = -1; | ||
386 | row[2] = keyProvides; | ||
387 | row[3] = null; | ||
388 | row[4] = "[ProductCode]"; | ||
389 | row[5] = parentId; | ||
390 | |||
391 | // Use the Version registry value and use that as a potential key path. | ||
392 | Identifier idVersion = this.Core.CreateIdentifier("reg", id.Id, "Version"); | ||
393 | keyPath = new ComponentKeyPath() { Id = idVersion.Id, Explicit = false, Type = ComponentKeyPathType.Registry }; | ||
394 | |||
395 | row = this.Core.CreateRow(sourceLineNumbers, "Registry", idVersion); | ||
396 | row[1] = -1; | ||
397 | row[2] = keyProvides; | ||
398 | row[3] = "Version"; | ||
399 | row[4] = !String.IsNullOrEmpty(version) ? version : "[ProductVersion]"; | ||
400 | row[5] = parentId; | ||
401 | |||
402 | row = this.Core.CreateRow(sourceLineNumbers, "Registry", this.Core.CreateIdentifier("reg", id.Id, "DisplayName")); | ||
403 | row[1] = -1; | ||
404 | row[2] = keyProvides; | ||
405 | row[3] = "DisplayName"; | ||
406 | row[4] = !String.IsNullOrEmpty(displayName) ? displayName : "[ProductName]"; | ||
407 | row[5] = parentId; | ||
408 | |||
409 | if (0 != attributes) | ||
410 | { | ||
411 | row = this.Core.CreateRow(sourceLineNumbers, "Registry", this.Core.CreateIdentifier("reg", id.Id, "Attributes")); | ||
412 | row[1] = -1; | ||
413 | row[2] = keyProvides; | ||
414 | row[3] = "Attributes"; | ||
415 | row[4] = String.Concat("#", attributes.ToString(CultureInfo.InvariantCulture.NumberFormat)); | ||
416 | row[5] = parentId; | ||
417 | } | ||
418 | } | ||
419 | } | ||
420 | |||
421 | return keyPath; | ||
422 | } | ||
423 | |||
424 | /// <summary> | ||
425 | /// Processes the Requires element. | ||
426 | /// </summary> | ||
427 | /// <param name="node">The XML node for the Requires element.</param> | ||
428 | /// <param name="providerId">The parent provider identifier.</param> | ||
429 | /// <param name="requiresAction">Whether the Requires custom action should be referenced.</param> | ||
430 | private void ParseRequiresElement(XElement node, string providerId, bool requiresAction) | ||
431 | { | ||
432 | SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); | ||
433 | Identifier id = null; | ||
434 | string providerKey = null; | ||
435 | string minVersion = null; | ||
436 | string maxVersion = null; | ||
437 | int attributes = 0; | ||
438 | int illegalChar = -1; | ||
439 | |||
440 | foreach (XAttribute attrib in node.Attributes()) | ||
441 | { | ||
442 | if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) | ||
443 | { | ||
444 | switch (attrib.Name.LocalName) | ||
445 | { | ||
446 | case "Id": | ||
447 | id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); | ||
448 | break; | ||
449 | case "ProviderKey": | ||
450 | providerKey = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
451 | break; | ||
452 | case "Minimum": | ||
453 | minVersion = this.Core.GetAttributeVersionValue(sourceLineNumbers, attrib); | ||
454 | break; | ||
455 | case "Maximum": | ||
456 | maxVersion = this.Core.GetAttributeVersionValue(sourceLineNumbers, attrib); | ||
457 | break; | ||
458 | case "IncludeMinimum": | ||
459 | if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) | ||
460 | { | ||
461 | attributes |= DependencyCommon.RequiresAttributesMinVersionInclusive; | ||
462 | } | ||
463 | break; | ||
464 | case "IncludeMaximum": | ||
465 | if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) | ||
466 | { | ||
467 | attributes |= DependencyCommon.RequiresAttributesMaxVersionInclusive; | ||
468 | } | ||
469 | break; | ||
470 | default: | ||
471 | this.Core.UnexpectedAttribute(node, attrib); | ||
472 | break; | ||
473 | } | ||
474 | } | ||
475 | else | ||
476 | { | ||
477 | this.Core.ParseExtensionAttribute(node, attrib); | ||
478 | } | ||
479 | } | ||
480 | |||
481 | this.Core.ParseForExtensionElements(node); | ||
482 | |||
483 | if (null == id) | ||
484 | { | ||
485 | // Generate an ID only if this element is authored under a Provides element; otherwise, a RequiresRef | ||
486 | // element will be necessary and the Id attribute will be required. | ||
487 | if (!String.IsNullOrEmpty(providerId)) | ||
488 | { | ||
489 | id = this.Core.CreateIdentifier("dep", node.Name.LocalName, providerKey); | ||
490 | } | ||
491 | else | ||
492 | { | ||
493 | this.Core.OnMessage(WixErrors.ExpectedAttributeWhenElementNotUnderElement(sourceLineNumbers, node.Name.LocalName, "Id", "Provides")); | ||
494 | id = Identifier.Invalid; | ||
495 | } | ||
496 | } | ||
497 | |||
498 | if (String.IsNullOrEmpty(providerKey)) | ||
499 | { | ||
500 | this.Core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "ProviderKey")); | ||
501 | } | ||
502 | // Make sure the key does not contain any illegal characters. | ||
503 | else if (0 <= (illegalChar = providerKey.IndexOfAny(DependencyCommon.InvalidCharacters))) | ||
504 | { | ||
505 | StringBuilder sb = new StringBuilder(DependencyCommon.InvalidCharacters.Length * 2); | ||
506 | Array.ForEach<char>(DependencyCommon.InvalidCharacters, c => sb.Append(c).Append(" ")); | ||
507 | |||
508 | this.Core.OnMessage(DependencyErrors.IllegalCharactersInProvider(sourceLineNumbers, "ProviderKey", providerKey[illegalChar], sb.ToString())); | ||
509 | } | ||
510 | |||
511 | |||
512 | if (!this.Core.EncounteredError) | ||
513 | { | ||
514 | // Reference the Require custom action if required. | ||
515 | if (requiresAction) | ||
516 | { | ||
517 | if (Platform.ARM == this.Core.CurrentPlatform) | ||
518 | { | ||
519 | // Ensure the ARM version of the CA is referenced. | ||
520 | this.Core.CreateSimpleReference(sourceLineNumbers, "CustomAction", "WixDependencyRequire_ARM"); | ||
521 | } | ||
522 | else | ||
523 | { | ||
524 | // All other supported platforms use x86. | ||
525 | this.Core.CreateSimpleReference(sourceLineNumbers, "CustomAction", "WixDependencyRequire"); | ||
526 | } | ||
527 | } | ||
528 | |||
529 | Row row = this.Core.CreateRow(sourceLineNumbers, "WixDependency", id); | ||
530 | row[1] = providerKey; | ||
531 | row[2] = minVersion; | ||
532 | row[3] = maxVersion; | ||
533 | |||
534 | if (0 != attributes) | ||
535 | { | ||
536 | row[4] = attributes; | ||
537 | } | ||
538 | |||
539 | // Create the relationship between this WixDependency row and the WixDependencyProvider row. | ||
540 | if (!String.IsNullOrEmpty(providerId)) | ||
541 | { | ||
542 | // Create the relationship between the WixDependency row and the parent WixDependencyProvider row. | ||
543 | row = this.Core.CreateRow(sourceLineNumbers, "WixDependencyRef"); | ||
544 | row[0] = providerId; | ||
545 | row[1] = id.Id; | ||
546 | } | ||
547 | } | ||
548 | } | ||
549 | |||
550 | /// <summary> | ||
551 | /// Processes the RequiresRef element. | ||
552 | /// </summary> | ||
553 | /// <param name="node">The XML node for the RequiresRef element.</param> | ||
554 | /// <param name="providerId">The parent provider identifier.</param> | ||
555 | /// <param name="requiresAction">Whether the Requires custom action should be referenced.</param> | ||
556 | private void ParseRequiresRefElement(XElement node, string providerId, bool requiresAction) | ||
557 | { | ||
558 | SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); | ||
559 | string id = null; | ||
560 | |||
561 | foreach (XAttribute attrib in node.Attributes()) | ||
562 | { | ||
563 | if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) | ||
564 | { | ||
565 | switch (attrib.Name.LocalName) | ||
566 | { | ||
567 | case "Id": | ||
568 | id = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); | ||
569 | break; | ||
570 | default: | ||
571 | this.Core.UnexpectedAttribute(node, attrib); | ||
572 | break; | ||
573 | } | ||
574 | } | ||
575 | else | ||
576 | { | ||
577 | this.Core.ParseExtensionAttribute(node, attrib); | ||
578 | } | ||
579 | } | ||
580 | |||
581 | this.Core.ParseForExtensionElements(node); | ||
582 | |||
583 | if (String.IsNullOrEmpty(id)) | ||
584 | { | ||
585 | this.Core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); | ||
586 | } | ||
587 | |||
588 | if (!this.Core.EncounteredError) | ||
589 | { | ||
590 | // Reference the Require custom action if required. | ||
591 | if (requiresAction) | ||
592 | { | ||
593 | if (Platform.ARM == this.Core.CurrentPlatform) | ||
594 | { | ||
595 | // Ensure the ARM version of the CA is referenced. | ||
596 | this.Core.CreateSimpleReference(sourceLineNumbers, "CustomAction", "WixDependencyRequire_ARM"); | ||
597 | } | ||
598 | else | ||
599 | { | ||
600 | // All other supported platforms use x86. | ||
601 | this.Core.CreateSimpleReference(sourceLineNumbers, "CustomAction", "WixDependencyRequire"); | ||
602 | } | ||
603 | } | ||
604 | |||
605 | // Create a link dependency on the row that contains information we'll need during bind. | ||
606 | this.Core.CreateSimpleReference(sourceLineNumbers, "WixDependency", id); | ||
607 | |||
608 | // Create the relationship between the WixDependency row and the parent WixDependencyProvider row. | ||
609 | Row row = this.Core.CreateRow(sourceLineNumbers, "WixDependencyRef"); | ||
610 | row[0] = providerId; | ||
611 | row[1] = id; | ||
612 | } | ||
613 | } | ||
614 | } | ||
615 | } | ||
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 | |||
3 | namespace WixToolset.Extensions | ||
4 | { | ||
5 | using System; | ||
6 | using System.Collections.Generic; | ||
7 | using System.Collections.ObjectModel; | ||
8 | using WixToolset; | ||
9 | using WixToolset.Data; | ||
10 | using WixToolset.Extensibility; | ||
11 | using WixToolset.Extensions.Serialize.Dependency; | ||
12 | using Dependency = WixToolset.Extensions.Serialize.Dependency; | ||
13 | using Wix = WixToolset.Data.Serialize; | ||
14 | |||
15 | /// <summary> | ||
16 | /// The decompiler for the WiX toolset dependency extension. | ||
17 | /// </summary> | ||
18 | public sealed class DependencyDecompiler : DecompilerExtension | ||
19 | { | ||
20 | private RegistryKeyValueCollection registryValues; | ||
21 | private Dictionary<string, string> keyCache; | ||
22 | |||
23 | /// <summary> | ||
24 | /// Creates a new instance of the <see cref="DependencyDecompiler"/> class. | ||
25 | /// </summary> | ||
26 | public DependencyDecompiler() | ||
27 | { | ||
28 | this.registryValues = new RegistryKeyValueCollection(); | ||
29 | this.keyCache = new Dictionary<string, string>(); | ||
30 | |||
31 | this.TableDefinitions = DependencyExtensionData.GetExtensionTableDefinitions(); | ||
32 | } | ||
33 | |||
34 | /// <summary> | ||
35 | /// Get the extensions library to be removed. | ||
36 | /// </summary> | ||
37 | /// <param name="tableDefinitions">Table definitions for library.</param> | ||
38 | /// <returns>Library to remove from decompiled output.</returns> | ||
39 | public override Library GetLibraryToRemove(TableDefinitionCollection tableDefinitions) | ||
40 | { | ||
41 | return DependencyExtensionData.GetExtensionLibrary(tableDefinitions); | ||
42 | } | ||
43 | |||
44 | /// <summary> | ||
45 | /// Decompiles an extension table. | ||
46 | /// </summary> | ||
47 | /// <param name="table">The table to decompile.</param> | ||
48 | public override void DecompileTable(Table table) | ||
49 | { | ||
50 | switch (table.Name) | ||
51 | { | ||
52 | case "WixDependencyProvider": | ||
53 | this.DecompileWixDependencyProviderTable(table); | ||
54 | break; | ||
55 | |||
56 | case "WixDependency": | ||
57 | this.DecompileWixDependencyTable(table); | ||
58 | break; | ||
59 | |||
60 | case "WixDependencyRef": | ||
61 | this.DecompileWixDependencyRefTable(table); | ||
62 | break; | ||
63 | |||
64 | default: | ||
65 | base.DecompileTable(table); | ||
66 | break; | ||
67 | } | ||
68 | } | ||
69 | |||
70 | /// <summary> | ||
71 | /// Finalize decompilation by removing registry values that the compiler writes. | ||
72 | /// </summary> | ||
73 | /// <param name="tables">The collection of all tables.</param> | ||
74 | public override void Finish(TableIndexedCollection tables) | ||
75 | { | ||
76 | // Remove generated registry rows. | ||
77 | this.FinalizeRegistryTable(tables); | ||
78 | |||
79 | // Remove extension properties. | ||
80 | this.FinalizeProperties(); | ||
81 | } | ||
82 | |||
83 | /// <summary> | ||
84 | /// Decompiles the WixDependencyProvider table. | ||
85 | /// </summary> | ||
86 | /// <param name="table">The table to decompile.</param> | ||
87 | private void DecompileWixDependencyProviderTable(Table table) | ||
88 | { | ||
89 | foreach (Row row in table.Rows) | ||
90 | { | ||
91 | Provides provides = new Provides(); | ||
92 | |||
93 | provides.Id = (string)row[0]; | ||
94 | provides.Key = (string)row[2]; | ||
95 | |||
96 | if (null != row[3]) | ||
97 | { | ||
98 | provides.Version = (string)row[3]; | ||
99 | } | ||
100 | |||
101 | if (null != row[4]) | ||
102 | { | ||
103 | provides.DisplayName = (string)row[4]; | ||
104 | } | ||
105 | |||
106 | // Nothing to parse for attributes currently. | ||
107 | |||
108 | Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", (string)row[1]); | ||
109 | if (null != component) | ||
110 | { | ||
111 | component.AddChild(provides); | ||
112 | } | ||
113 | else | ||
114 | { | ||
115 | this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", (string)row[1], "Component")); | ||
116 | } | ||
117 | |||
118 | // Index the provider to parent the RequiresRef elements. | ||
119 | this.Core.IndexElement(row, provides); | ||
120 | |||
121 | // Add the provider-specific registry keys to be removed during finalization. | ||
122 | // Only remove specific keys that the compiler writes. | ||
123 | string keyProvides = String.Concat(DependencyCommon.RegistryRoot, provides.Key); | ||
124 | |||
125 | this.registryValues.Add(keyProvides, null); | ||
126 | this.registryValues.Add(keyProvides, "Version"); | ||
127 | this.registryValues.Add(keyProvides, "DisplayName"); | ||
128 | this.registryValues.Add(keyProvides, "Attributes"); | ||
129 | |||
130 | // Cache the provider key. | ||
131 | this.keyCache[provides.Id] = provides.Key; | ||
132 | } | ||
133 | } | ||
134 | |||
135 | /// <summary> | ||
136 | /// Decompiles the WixDependency table. | ||
137 | /// </summary> | ||
138 | /// <param name="table">The table to decompile.</param> | ||
139 | private void DecompileWixDependencyTable(Table table) | ||
140 | { | ||
141 | foreach (Row row in table.Rows) | ||
142 | { | ||
143 | Requires requires = new Requires(); | ||
144 | |||
145 | requires.Id = (string)row[0]; | ||
146 | requires.ProviderKey = (string)row[1]; | ||
147 | |||
148 | if (null != row[2]) | ||
149 | { | ||
150 | requires.Minimum = (string)row[2]; | ||
151 | } | ||
152 | |||
153 | if (null != row[3]) | ||
154 | { | ||
155 | requires.Maximum = (string)row[3]; | ||
156 | } | ||
157 | |||
158 | if (null != row[4]) | ||
159 | { | ||
160 | int attributes = (int)row[4]; | ||
161 | |||
162 | if (0 != (attributes & DependencyCommon.RequiresAttributesMinVersionInclusive)) | ||
163 | { | ||
164 | requires.IncludeMinimum = Dependency.YesNoType.yes; | ||
165 | } | ||
166 | |||
167 | if (0 != (attributes & DependencyCommon.RequiresAttributesMaxVersionInclusive)) | ||
168 | { | ||
169 | requires.IncludeMaximum = Dependency.YesNoType.yes; | ||
170 | } | ||
171 | } | ||
172 | |||
173 | this.Core.RootElement.AddChild(requires); | ||
174 | |||
175 | // Cache the requires key. | ||
176 | this.keyCache[requires.Id] = requires.ProviderKey; | ||
177 | } | ||
178 | } | ||
179 | |||
180 | /// <summary> | ||
181 | /// Decompiles the WixDependencyRef table. | ||
182 | /// </summary> | ||
183 | /// <param name="table">The table to decompile.</param> | ||
184 | private void DecompileWixDependencyRefTable(Table table) | ||
185 | { | ||
186 | foreach (Row row in table.Rows) | ||
187 | { | ||
188 | RequiresRef requiresRef = new RequiresRef(); | ||
189 | |||
190 | requiresRef.Id = (string)row[1]; | ||
191 | |||
192 | Provides provides = (Provides)this.Core.GetIndexedElement("WixDependencyProvider", (string)row[0]); | ||
193 | if (null != provides) | ||
194 | { | ||
195 | provides.AddChild(requiresRef); | ||
196 | } | ||
197 | else | ||
198 | { | ||
199 | this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "WixDependencyProvider_", (string)row[0], "WixDependencyProvider")); | ||
200 | } | ||
201 | |||
202 | // Get the cached keys for the provider and dependency IDs and generate registry rows. | ||
203 | string providesKey = null; | ||
204 | string requiresKey = null; | ||
205 | |||
206 | if (null != provides && this.keyCache.ContainsKey(provides.Id)) | ||
207 | { | ||
208 | providesKey = this.keyCache[provides.Id]; | ||
209 | } | ||
210 | else | ||
211 | { | ||
212 | this.Core.OnMessage(DependencyWarnings.ProvidesKeyNotFound(row.SourceLineNumbers, provides.Id)); | ||
213 | } | ||
214 | |||
215 | if (this.keyCache.ContainsKey(requiresRef.Id)) | ||
216 | { | ||
217 | requiresKey = this.keyCache[requiresRef.Id]; | ||
218 | } | ||
219 | else | ||
220 | { | ||
221 | this.Core.OnMessage(DependencyWarnings.RequiresKeyNotFound(row.SourceLineNumbers, requiresRef.Id)); | ||
222 | } | ||
223 | |||
224 | if (!this.Core.EncounteredError) | ||
225 | { | ||
226 | // Add the dependency-specific registry keys to be removed during finalization. | ||
227 | // Only remove specific keys that the compiler writes. | ||
228 | string keyRequires = String.Format(@"{0}{1}\{2}\{3}", DependencyCommon.RegistryRoot, requiresKey, DependencyCommon.RegistryDependents, providesKey); | ||
229 | |||
230 | this.registryValues.Add(keyRequires, "*"); | ||
231 | this.registryValues.Add(keyRequires, "MinVersion"); | ||
232 | this.registryValues.Add(keyRequires, "MaxVersion"); | ||
233 | this.registryValues.Add(keyRequires, "Attributes"); | ||
234 | } | ||
235 | } | ||
236 | } | ||
237 | |||
238 | /// <summary> | ||
239 | /// Removes rows from the Registry table that are generated by this extension. | ||
240 | /// </summary> | ||
241 | /// <param name="tables">The collection of tables.</param> | ||
242 | private void FinalizeRegistryTable(TableIndexedCollection tables) | ||
243 | { | ||
244 | Table registryTable = tables["Registry"]; | ||
245 | if (null != registryTable) | ||
246 | { | ||
247 | foreach (Row registryRow in registryTable.Rows) | ||
248 | { | ||
249 | // Check if the compiler writes this registry value; if so, it should be removed. | ||
250 | if (this.registryValues.Contains(registryRow)) | ||
251 | { | ||
252 | Wix.ISchemaElement elem = this.Core.GetIndexedElement(registryRow); | ||
253 | |||
254 | // If the registry row was found, remove it from its parent. | ||
255 | if (null != elem && null != elem.ParentElement) | ||
256 | { | ||
257 | Wix.IParentElement elemParent = elem.ParentElement as Wix.IParentElement; | ||
258 | if (null != elemParent) | ||
259 | { | ||
260 | elemParent.RemoveChild(elem); | ||
261 | } | ||
262 | } | ||
263 | } | ||
264 | } | ||
265 | } | ||
266 | } | ||
267 | |||
268 | /// <summary> | ||
269 | /// Removes properties defined by this extension. | ||
270 | /// </summary> | ||
271 | /// <param name="tables">The collection of tables.</param> | ||
272 | private void FinalizeProperties() | ||
273 | { | ||
274 | string[] properties = new string[] { "DISABLEDEPENDENCYCHECK", "IGNOREDEPENDENCIES" }; | ||
275 | foreach (string property in properties) | ||
276 | { | ||
277 | Wix.Property elem = this.Core.GetIndexedElement("Property", property) as Wix.Property; | ||
278 | if (null != elem) | ||
279 | { | ||
280 | // If a value is defined, log a warning we're removing it. | ||
281 | if (!String.IsNullOrEmpty(elem.Value)) | ||
282 | { | ||
283 | this.Core.OnMessage(DependencyWarnings.PropertyRemoved(elem.Id)); | ||
284 | } | ||
285 | |||
286 | // If the property row was found, remove it from its parent. | ||
287 | if (null != elem.ParentElement) | ||
288 | { | ||
289 | Wix.IParentElement elemParent = elem.ParentElement as Wix.IParentElement; | ||
290 | if (null != elemParent) | ||
291 | { | ||
292 | elemParent.RemoveChild(elem); | ||
293 | } | ||
294 | } | ||
295 | } | ||
296 | } | ||
297 | } | ||
298 | |||
299 | /// <summary> | ||
300 | /// Provides an O(1) lookup for registry key and value name pairs for use in the decompiler. | ||
301 | /// </summary> | ||
302 | private sealed class RegistryKeyValueCollection : KeyedCollection<int, KeyValuePair<string, string>> | ||
303 | { | ||
304 | /// <summary> | ||
305 | /// Adds the registry key and value name pair to the collection if it doesn't already exist. | ||
306 | /// </summary> | ||
307 | /// <param name="key">The registry key to add.</param> | ||
308 | /// <param name="name">The registry value name to add.</param> | ||
309 | internal void Add(string key, string name) | ||
310 | { | ||
311 | KeyValuePair<string, string> pair = new KeyValuePair<string, string>(key, name); | ||
312 | if (!this.Contains(pair)) | ||
313 | { | ||
314 | this.Add(pair); | ||
315 | } | ||
316 | } | ||
317 | |||
318 | /// <summary> | ||
319 | /// Returns whether the collection contains the registry key and value name pair from the <see cref="Row"/>. | ||
320 | /// </summary> | ||
321 | /// <param name="row">The registry <see cref="Row"/> to search for.</param> | ||
322 | /// <returns>True if the collection contains the registry key and value name pair from the <see cref="Row"/>; otherwise, false.</returns> | ||
323 | internal bool Contains(Row row) | ||
324 | { | ||
325 | if (null == row) | ||
326 | { | ||
327 | return false; | ||
328 | } | ||
329 | |||
330 | KeyValuePair<string, string> pair = new KeyValuePair<string, string>((string)row[2], (string)row[3]); | ||
331 | return this.Contains(pair); | ||
332 | } | ||
333 | |||
334 | /// <summary> | ||
335 | /// Return the hash code of the key and value pair concatenated with a colon as a delimiter. | ||
336 | /// </summary> | ||
337 | /// <param name="pair">The registry key and value name pair.</param> | ||
338 | /// <returns></returns> | ||
339 | protected override int GetKeyForItem(KeyValuePair<string, string> pair) | ||
340 | { | ||
341 | return String.Concat(pair.Key, ":", pair.Value).GetHashCode(); | ||
342 | } | ||
343 | } | ||
344 | } | ||
345 | } | ||
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 | |||
3 | namespace WixToolset.Extensions | ||
4 | { | ||
5 | using System; | ||
6 | using System.Reflection; | ||
7 | using WixToolset.Data; | ||
8 | using WixToolset.Extensibility; | ||
9 | |||
10 | /// <summary> | ||
11 | /// The WiX toolset dependency extension. | ||
12 | /// </summary> | ||
13 | public sealed class DependencyExtensionData : ExtensionData | ||
14 | { | ||
15 | /// <summary> | ||
16 | /// Gets the default culture. | ||
17 | /// </summary> | ||
18 | /// <value>The default culture.</value> | ||
19 | public override string DefaultCulture | ||
20 | { | ||
21 | get { return "en-us"; } | ||
22 | } | ||
23 | |||
24 | /// <summary> | ||
25 | /// Gets the optional table definitions for this extension. | ||
26 | /// </summary> | ||
27 | /// <value>The optional table definitions for this extension.</value> | ||
28 | public override TableDefinitionCollection TableDefinitions | ||
29 | { | ||
30 | get | ||
31 | { | ||
32 | return DependencyExtensionData.GetExtensionTableDefinitions(); | ||
33 | } | ||
34 | } | ||
35 | |||
36 | /// <summary> | ||
37 | /// Gets the library associated with this extension. | ||
38 | /// </summary> | ||
39 | /// <param name="tableDefinitions">The table definitions to use while loading the library.</param> | ||
40 | /// <returns>The loaded library.</returns> | ||
41 | public override Library GetLibrary(TableDefinitionCollection tableDefinitions) | ||
42 | { | ||
43 | return DependencyExtensionData.GetExtensionLibrary(tableDefinitions); | ||
44 | } | ||
45 | |||
46 | /// <summary> | ||
47 | /// Internal mechanism to access the extension's table definitions. | ||
48 | /// </summary> | ||
49 | /// <returns>Extension's table definitions.</returns> | ||
50 | internal static TableDefinitionCollection GetExtensionTableDefinitions() | ||
51 | { | ||
52 | return ExtensionData.LoadTableDefinitionHelper(Assembly.GetExecutingAssembly(), "WixToolset.Extensions.Data.tables.xml"); | ||
53 | } | ||
54 | |||
55 | /// <summary> | ||
56 | /// Internal mechanism to access the extension's library. | ||
57 | /// </summary> | ||
58 | /// <returns>Extension's library.</returns> | ||
59 | internal static Library GetExtensionLibrary(TableDefinitionCollection tableDefinitions) | ||
60 | { | ||
61 | return ExtensionData.LoadLibraryHelper(Assembly.GetExecutingAssembly(), "WixToolset.Extensions.Data.Dependency.wixlib", tableDefinitions); | ||
62 | } | ||
63 | } | ||
64 | } | ||
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> | ||