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> | ||
