diff options
| author | Bob Arnson <bob@firegiant.com> | 2021-08-22 22:26:23 -0400 |
|---|---|---|
| committer | Bob Arnson <bob@firegiant.com> | 2021-08-22 22:55:41 -0400 |
| commit | 19c52f14644ac666c12f1a44bc2c0cce1586b8c5 (patch) | |
| tree | be6ef8e61e7fe6c37ae9e7bafd3f2fda64934883 /src | |
| parent | bb18c9c4f0e6da640775b85ebda68b31f2b391ed (diff) | |
| download | wix-19c52f14644ac666c12f1a44bc2c0cce1586b8c5.tar.gz wix-19c52f14644ac666c12f1a44bc2c0cce1586b8c5.tar.bz2 wix-19c52f14644ac666c12f1a44bc2c0cce1586b8c5.zip | |
On SuppressDowngradeFailure, quit after detect.
Implements https://github.com/wixtoolset/issues/issues/6537.
Diffstat (limited to 'src')
7 files changed, 95 insertions, 40 deletions
diff --git a/src/ext/Bal/Bal.wixext.sln b/src/ext/Bal/Bal.wixext.sln index ddf9ef88..ee14dd97 100644 --- a/src/ext/Bal/Bal.wixext.sln +++ b/src/ext/Bal/Bal.wixext.sln | |||
| @@ -17,7 +17,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.Bal.wixext", "wi | |||
| 17 | EndProject | 17 | EndProject |
| 18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.Dnc.Host", "WixToolset.Dnc.Host\WixToolset.Dnc.Host.csproj", "{0D780900-C2FF-4FA2-8CB5-8A19768724C5}" | 18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.Dnc.Host", "WixToolset.Dnc.Host\WixToolset.Dnc.Host.csproj", "{0D780900-C2FF-4FA2-8CB5-8A19768724C5}" |
| 19 | EndProject | 19 | EndProject |
| 20 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WixToolset.Mba.Host", "WixToolset.Mba.Host\WixToolset.Mba.Host.csproj", "{F2BA1935-70FA-4156-B161-FD03850B4FAA}" | 20 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.Mba.Host", "WixToolset.Mba.Host\WixToolset.Mba.Host.csproj", "{F2BA1935-70FA-4156-B161-FD03850B4FAA}" |
| 21 | EndProject | 21 | EndProject |
| 22 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Example.FullFramework2MBA", "test\examples\FullFramework2MBA\Example.FullFramework2MBA.csproj", "{CC4236FC-226E-4232-AB50-24CBEC4D314D}" | 22 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Example.FullFramework2MBA", "test\examples\FullFramework2MBA\Example.FullFramework2MBA.csproj", "{CC4236FC-226E-4232-AB50-24CBEC4D314D}" |
| 23 | EndProject | 23 | EndProject |
diff --git a/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp b/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp index be2dde1f..74e0b7d3 100644 --- a/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp +++ b/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp | |||
| @@ -286,6 +286,7 @@ public: // IBootstrapperApplication | |||
| 286 | // If we're not doing a prerequisite install, remember when our bundle would cause a downgrade. | 286 | // If we're not doing a prerequisite install, remember when our bundle would cause a downgrade. |
| 287 | if (!m_fPrereq && BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE == operation) | 287 | if (!m_fPrereq && BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE == operation) |
| 288 | { | 288 | { |
| 289 | BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "A newer version (v%ls) of this product is installed.", wzVersion); | ||
| 289 | m_fDowngrading = TRUE; | 290 | m_fDowngrading = TRUE; |
| 290 | } | 291 | } |
| 291 | } | 292 | } |
| @@ -321,12 +322,14 @@ public: // IBootstrapperApplication | |||
| 321 | ) | 322 | ) |
| 322 | { | 323 | { |
| 323 | HRESULT hr = S_OK; | 324 | HRESULT hr = S_OK; |
| 325 | |||
| 324 | // If we're not interacting with the user or we're doing a layout or we're resuming just after a force restart | 326 | // If we're not interacting with the user or we're doing a layout or we're resuming just after a force restart |
| 325 | // then automatically start planning. | 327 | // then automatically start planning. |
| 326 | BOOL fSkipToPlan = SUCCEEDED(hrStatus) && | 328 | BOOL fSkipToPlan = SUCCEEDED(hrStatus) && |
| 327 | (BOOTSTRAPPER_DISPLAY_FULL > m_command.display || | 329 | (BOOTSTRAPPER_DISPLAY_FULL > m_command.display || |
| 328 | BOOTSTRAPPER_ACTION_LAYOUT == m_command.action || | 330 | BOOTSTRAPPER_ACTION_LAYOUT == m_command.action || |
| 329 | BOOTSTRAPPER_RESUME_TYPE_REBOOT == m_command.resumeType); | 331 | BOOTSTRAPPER_RESUME_TYPE_REBOOT == m_command.resumeType); |
| 332 | |||
| 330 | // If we're requiring user input (which currently means Install, Repair, or Uninstall) | 333 | // If we're requiring user input (which currently means Install, Repair, or Uninstall) |
| 331 | // or if we're skipping to an action that modifies machine state | 334 | // or if we're skipping to an action that modifies machine state |
| 332 | // then evaluate conditions. | 335 | // then evaluate conditions. |
| @@ -360,6 +363,23 @@ public: // IBootstrapperApplication | |||
| 360 | } | 363 | } |
| 361 | } | 364 | } |
| 362 | } | 365 | } |
| 366 | else if (m_fDowngrading && BOOTSTRAPPER_ACTION_UNINSTALL < m_command.action) | ||
| 367 | { | ||
| 368 | if (m_fSuppressDowngradeFailure) | ||
| 369 | { | ||
| 370 | BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Downgrade failure has been suppressed; exiting bundle."); | ||
| 371 | |||
| 372 | hr = S_OK; | ||
| 373 | SetState(WIXSTDBA_STATE_APPLIED, hr); | ||
| 374 | ExitFunction(); | ||
| 375 | } | ||
| 376 | else | ||
| 377 | { | ||
| 378 | // If we are going to apply a downgrade, bail. | ||
| 379 | hr = HRESULT_FROM_WIN32(ERROR_PRODUCT_VERSION); | ||
| 380 | BalExitOnFailure(hr, "Cannot install a product when a newer version is installed."); | ||
| 381 | } | ||
| 382 | } | ||
| 363 | } | 383 | } |
| 364 | 384 | ||
| 365 | SetState(WIXSTDBA_STATE_DETECTED, hrStatus); | 385 | SetState(WIXSTDBA_STATE_DETECTED, hrStatus); |
| @@ -369,6 +389,7 @@ public: // IBootstrapperApplication | |||
| 369 | ::PostMessageW(m_hWnd, WM_WIXSTDBA_PLAN_PACKAGES, 0, m_command.action); | 389 | ::PostMessageW(m_hWnd, WM_WIXSTDBA_PLAN_PACKAGES, 0, m_command.action); |
| 370 | } | 390 | } |
| 371 | 391 | ||
| 392 | LExit: | ||
| 372 | return hr; | 393 | return hr; |
| 373 | } | 394 | } |
| 374 | 395 | ||
| @@ -2162,7 +2183,7 @@ private: | |||
| 2162 | { | 2183 | { |
| 2163 | HRESULT hr = S_OK; | 2184 | HRESULT hr = S_OK; |
| 2164 | LPWSTR sczModulePath = NULL; | 2185 | LPWSTR sczModulePath = NULL; |
| 2165 | IXMLDOMDocument *pixdManifest = NULL; | 2186 | IXMLDOMDocument* pixdManifest = NULL; |
| 2166 | 2187 | ||
| 2167 | hr = BalManifestLoad(m_hModule, &pixdManifest); | 2188 | hr = BalManifestLoad(m_hModule, &pixdManifest); |
| 2168 | BalExitOnFailure(hr, "Failed to load bootstrapper application manifest."); | 2189 | BalExitOnFailure(hr, "Failed to load bootstrapper application manifest."); |
| @@ -2789,21 +2810,21 @@ private: | |||
| 2789 | switch (uMsg) | 2810 | switch (uMsg) |
| 2790 | { | 2811 | { |
| 2791 | case WM_NCCREATE: | 2812 | case WM_NCCREATE: |
| 2792 | { | 2813 | { |
| 2793 | LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam); | 2814 | LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam); |
| 2794 | pBA = reinterpret_cast<CWixStandardBootstrapperApplication*>(lpcs->lpCreateParams); | 2815 | pBA = reinterpret_cast<CWixStandardBootstrapperApplication*>(lpcs->lpCreateParams); |
| 2795 | #pragma warning(suppress:4244) | 2816 | #pragma warning(suppress:4244) |
| 2796 | ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pBA)); | 2817 | ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pBA)); |
| 2797 | } | 2818 | } |
| 2798 | break; | 2819 | break; |
| 2799 | 2820 | ||
| 2800 | case WM_NCDESTROY: | 2821 | case WM_NCDESTROY: |
| 2801 | { | 2822 | { |
| 2802 | LRESULT lres = CallDefaultWndProc(pBA, hWnd, uMsg, wParam, lParam); | 2823 | LRESULT lres = CallDefaultWndProc(pBA, hWnd, uMsg, wParam, lParam); |
| 2803 | ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0); | 2824 | ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0); |
| 2804 | ::PostQuitMessage(0); | 2825 | ::PostQuitMessage(0); |
| 2805 | return lres; | 2826 | return lres; |
| 2806 | } | 2827 | } |
| 2807 | 2828 | ||
| 2808 | case WM_CREATE: | 2829 | case WM_CREATE: |
| 2809 | if (!pBA->OnCreate(hWnd)) | 2830 | if (!pBA->OnCreate(hWnd)) |
| @@ -3023,8 +3044,6 @@ private: | |||
| 3023 | } | 3044 | } |
| 3024 | 3045 | ||
| 3025 | m_pEngine->CloseSplashScreen(); | 3046 | m_pEngine->CloseSplashScreen(); |
| 3026 | |||
| 3027 | return; | ||
| 3028 | } | 3047 | } |
| 3029 | 3048 | ||
| 3030 | 3049 | ||
| @@ -3042,8 +3061,6 @@ private: | |||
| 3042 | } | 3061 | } |
| 3043 | 3062 | ||
| 3044 | m_pEngine->CloseSplashScreen(); | 3063 | m_pEngine->CloseSplashScreen(); |
| 3045 | |||
| 3046 | return; | ||
| 3047 | } | 3064 | } |
| 3048 | 3065 | ||
| 3049 | 3066 | ||
| @@ -3073,8 +3090,6 @@ private: | |||
| 3073 | { | 3090 | { |
| 3074 | SetState(WIXSTDBA_STATE_DETECTING, hr); | 3091 | SetState(WIXSTDBA_STATE_DETECTING, hr); |
| 3075 | } | 3092 | } |
| 3076 | |||
| 3077 | return; | ||
| 3078 | } | 3093 | } |
| 3079 | 3094 | ||
| 3080 | 3095 | ||
| @@ -3089,20 +3104,6 @@ private: | |||
| 3089 | 3104 | ||
| 3090 | m_plannedAction = action; | 3105 | m_plannedAction = action; |
| 3091 | 3106 | ||
| 3092 | // If we are going to apply a downgrade, bail. | ||
| 3093 | if (m_fDowngrading && BOOTSTRAPPER_ACTION_UNINSTALL < action) | ||
| 3094 | { | ||
| 3095 | if (m_fSuppressDowngradeFailure) | ||
| 3096 | { | ||
| 3097 | BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "A newer version of this product is installed but downgrade failure has been suppressed; continuing..."); | ||
| 3098 | } | ||
| 3099 | else | ||
| 3100 | { | ||
| 3101 | hr = HRESULT_FROM_WIN32(ERROR_PRODUCT_VERSION); | ||
| 3102 | BalExitOnFailure(hr, "Cannot install a product when a newer version is installed."); | ||
| 3103 | } | ||
| 3104 | } | ||
| 3105 | |||
| 3106 | SetState(WIXSTDBA_STATE_PLANNING, hr); | 3107 | SetState(WIXSTDBA_STATE_PLANNING, hr); |
| 3107 | 3108 | ||
| 3108 | hr = m_pEngine->Plan(action); | 3109 | hr = m_pEngine->Plan(action); |
| @@ -3113,8 +3114,6 @@ private: | |||
| 3113 | { | 3114 | { |
| 3114 | SetState(WIXSTDBA_STATE_PLANNING, hr); | 3115 | SetState(WIXSTDBA_STATE_PLANNING, hr); |
| 3115 | } | 3116 | } |
| 3116 | |||
| 3117 | return; | ||
| 3118 | } | 3117 | } |
| 3119 | 3118 | ||
| 3120 | 3119 | ||
| @@ -3139,8 +3138,6 @@ private: | |||
| 3139 | { | 3138 | { |
| 3140 | SetState(WIXSTDBA_STATE_APPLYING, hr); | 3139 | SetState(WIXSTDBA_STATE_APPLYING, hr); |
| 3141 | } | 3140 | } |
| 3142 | |||
| 3143 | return; | ||
| 3144 | } | 3141 | } |
| 3145 | 3142 | ||
| 3146 | 3143 | ||
| @@ -3490,8 +3487,6 @@ private: | |||
| 3490 | ReleaseStr(sczLicenseUrl); | 3487 | ReleaseStr(sczLicenseUrl); |
| 3491 | ReleaseStr(sczLicenseDirectory); | 3488 | ReleaseStr(sczLicenseDirectory); |
| 3492 | ReleaseStr(sczLicenseFilename); | 3489 | ReleaseStr(sczLicenseFilename); |
| 3493 | |||
| 3494 | return; | ||
| 3495 | } | 3490 | } |
| 3496 | 3491 | ||
| 3497 | 3492 | ||
| @@ -3579,8 +3574,6 @@ private: | |||
| 3579 | ReleaseStr(sczLaunchTargetElevatedId); | 3574 | ReleaseStr(sczLaunchTargetElevatedId); |
| 3580 | StrSecureZeroFreeString(sczLaunchTarget); | 3575 | StrSecureZeroFreeString(sczLaunchTarget); |
| 3581 | ReleaseStr(sczUnformattedLaunchTarget); | 3576 | ReleaseStr(sczUnformattedLaunchTarget); |
| 3582 | |||
| 3583 | return; | ||
| 3584 | } | 3577 | } |
| 3585 | 3578 | ||
| 3586 | 3579 | ||
| @@ -3593,8 +3586,6 @@ private: | |||
| 3593 | 3586 | ||
| 3594 | m_fAllowRestart = TRUE; | 3587 | m_fAllowRestart = TRUE; |
| 3595 | ::SendMessageW(m_hWnd, WM_CLOSE, 0, 0); | 3588 | ::SendMessageW(m_hWnd, WM_CLOSE, 0, 0); |
| 3596 | |||
| 3597 | return; | ||
| 3598 | } | 3589 | } |
| 3599 | 3590 | ||
| 3600 | 3591 | ||
| @@ -3614,8 +3605,6 @@ private: | |||
| 3614 | 3605 | ||
| 3615 | LExit: | 3606 | LExit: |
| 3616 | ReleaseStr(sczLogFile); | 3607 | ReleaseStr(sczLogFile); |
| 3617 | |||
| 3618 | return; | ||
| 3619 | } | 3608 | } |
| 3620 | 3609 | ||
| 3621 | 3610 | ||
| @@ -3902,7 +3891,7 @@ public: | |||
| 3902 | __in BOOL fPrereq, | 3891 | __in BOOL fPrereq, |
| 3903 | __in HRESULT hrHostInitialization, | 3892 | __in HRESULT hrHostInitialization, |
| 3904 | __in IBootstrapperEngine* pEngine | 3893 | __in IBootstrapperEngine* pEngine |
| 3905 | ) : CBalBaseBootstrapperApplication(pEngine, 3, 3000) | 3894 | ) : CBalBaseBootstrapperApplication(pEngine, 3, 3000) |
| 3906 | { | 3895 | { |
| 3907 | m_hModule = hModule; | 3896 | m_hModule = hModule; |
| 3908 | m_command = { }; | 3897 | m_command = { }; |
diff --git a/src/test/burn/TestData/WixStdBaTests/BundleA/Bundle.wxs b/src/test/burn/TestData/WixStdBaTests/BundleA/Bundle.wxs new file mode 100644 index 00000000..dded1f40 --- /dev/null +++ b/src/test/burn/TestData/WixStdBaTests/BundleA/Bundle.wxs | |||
| @@ -0,0 +1,17 @@ | |||
| 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 | <Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:bal="http://wixtoolset.org/schemas/v4/wxs/bal"> | ||
| 4 | <Bundle Name="~$(var.TestGroupName) - $(var.BundleName)" Version="$(var.Version)" UpgradeCode="$(var.UpgradeCode)" Compressed="yes" CommandLineVariables="caseSensitive"> | ||
| 5 | <Log Prefix="~$(var.TestGroupName)_$(var.BundleName)" /> | ||
| 6 | |||
| 7 | <Variable Name="TestGroupName" Value="$(var.TestGroupName)" /> | ||
| 8 | |||
| 9 | <BootstrapperApplication> | ||
| 10 | <bal:WixStandardBootstrapperApplication LicenseUrl="" Theme="hyperlinkLicense" SuppressDowngradeFailure="yes" /> | ||
| 11 | </BootstrapperApplication> | ||
| 12 | |||
| 13 | <Chain> | ||
| 14 | <PackageGroupRef Id="BundlePackages" /> | ||
| 15 | </Chain> | ||
| 16 | </Bundle> | ||
| 17 | </Wix> | ||
diff --git a/src/test/burn/TestData/WixStdBaTests/BundleA/BundleA.wxs b/src/test/burn/TestData/WixStdBaTests/BundleA/BundleA.wxs new file mode 100644 index 00000000..b3345b5d --- /dev/null +++ b/src/test/burn/TestData/WixStdBaTests/BundleA/BundleA.wxs | |||
| @@ -0,0 +1,10 @@ | |||
| 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 | <Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" | ||
| 4 | xmlns:bal="http://wixtoolset.org/schemas/v4/wxs/bal"> | ||
| 5 | <Fragment> | ||
| 6 | <PackageGroup Id="BundlePackages"> | ||
| 7 | <MsiPackage Id="PackageA" SourceFile="$(var.PackageA.TargetPath)" /> | ||
| 8 | </PackageGroup> | ||
| 9 | </Fragment> | ||
| 10 | </Wix> | ||
diff --git a/src/test/burn/TestData/WixStdBaTests/BundleA/BundleA_v11.wixproj b/src/test/burn/TestData/WixStdBaTests/BundleA/BundleA_v11.wixproj new file mode 100644 index 00000000..5fa77495 --- /dev/null +++ b/src/test/burn/TestData/WixStdBaTests/BundleA/BundleA_v11.wixproj | |||
| @@ -0,0 +1,15 @@ | |||
| 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 | <Project Sdk="WixToolset.Sdk"> | ||
| 3 | <PropertyGroup> | ||
| 4 | <OutputType>Bundle</OutputType> | ||
| 5 | <UpgradeCode>{7D977157-06C9-4176-A931-AC16E18AAB51}</UpgradeCode> | ||
| 6 | <DefineConstants>$(DefineConstants);Version=1.1</DefineConstants> | ||
| 7 | <OutputName>WixStdBaTest1_v11</OutputName> | ||
| 8 | </PropertyGroup> | ||
| 9 | <ItemGroup> | ||
| 10 | <ProjectReference Include="..\PackageA\PackageA.wixproj" /> | ||
| 11 | </ItemGroup> | ||
| 12 | <ItemGroup> | ||
| 13 | <PackageReference Include="WixToolset.Bal.wixext" /> | ||
| 14 | </ItemGroup> | ||
| 15 | </Project> \ No newline at end of file | ||
diff --git a/src/test/burn/TestData/WixStdBaTests/BundleA/BundleA_v12.wixproj b/src/test/burn/TestData/WixStdBaTests/BundleA/BundleA_v12.wixproj new file mode 100644 index 00000000..5918df51 --- /dev/null +++ b/src/test/burn/TestData/WixStdBaTests/BundleA/BundleA_v12.wixproj | |||
| @@ -0,0 +1,15 @@ | |||
| 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 | <Project Sdk="WixToolset.Sdk"> | ||
| 3 | <PropertyGroup> | ||
| 4 | <OutputType>Bundle</OutputType> | ||
| 5 | <UpgradeCode>{7D977157-06C9-4176-A931-AC16E18AAB51}</UpgradeCode> | ||
| 6 | <DefineConstants>$(DefineConstants);Version=1.2</DefineConstants> | ||
| 7 | <OutputName>WixStdBaTest1_v12</OutputName> | ||
| 8 | </PropertyGroup> | ||
| 9 | <ItemGroup> | ||
| 10 | <ProjectReference Include="..\PackageA\PackageA.wixproj" /> | ||
| 11 | </ItemGroup> | ||
| 12 | <ItemGroup> | ||
| 13 | <PackageReference Include="WixToolset.Bal.wixext" /> | ||
| 14 | </ItemGroup> | ||
| 15 | </Project> \ No newline at end of file | ||
diff --git a/src/test/burn/TestData/WixStdBaTests/PackageA/PackageA.wixproj b/src/test/burn/TestData/WixStdBaTests/PackageA/PackageA.wixproj new file mode 100644 index 00000000..a028c152 --- /dev/null +++ b/src/test/burn/TestData/WixStdBaTests/PackageA/PackageA.wixproj | |||
| @@ -0,0 +1,9 @@ | |||
| 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 | <Project Sdk="WixToolset.Sdk"> | ||
| 3 | <PropertyGroup> | ||
| 4 | <UpgradeCode>{78FBE6B9-7BA5-444B-A2B8-489820FA2C89}</UpgradeCode> | ||
| 5 | </PropertyGroup> | ||
| 6 | <ItemGroup> | ||
| 7 | <Compile Include="..\..\Templates\Package.wxs" Link="Package.wxs" /> | ||
| 8 | </ItemGroup> | ||
| 9 | </Project> \ No newline at end of file | ||
