diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 839b1cd0a..9a759c661 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,7 +24,7 @@ jobs: id-token: write steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4.2.2 with: fetch-depth: 0 @@ -66,7 +66,10 @@ jobs: run: ./src/build_official.cmd env: RuntimeTestsEnabled: true - SigningVaultUri: ${{ github.ref == 'refs/heads/master' && secrets.WIX_SIGNING_VAULTURI || '' }} + SigningKeyVaultUri: ${{ github.ref == 'refs/heads/master' && secrets.WIX_SIGNING_VAULTURI || '' }} + SigningTenantId: ${{ github.ref == 'refs/heads/master' && secrets.WIX_SIGNING_TENANTID || '' }} + SigningClientId: ${{ github.ref == 'refs/heads/master' && secrets.WIX_SIGNING_CLIENTID || '' }} + SigningClientSecret: ${{ github.ref == 'refs/heads/master' && secrets.WIX_SIGNING_SECRET || '' }} SigningCertName: ${{ github.ref == 'refs/heads/master' && secrets.WIX_SIGNING_CERTNAME || '' }} BuildRunNumber: ${{ github.run_number }} @@ -85,7 +88,7 @@ jobs: - name: Save build if: always() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4.4.3 with: name: artifacts path: build/artifacts/ @@ -108,7 +111,7 @@ jobs: # Do NOT publish logs on `master` branch as they may contain secrets in them. - name: Save logs if: github.ref != 'refs/heads/master' && (success() || failure()) - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4.4.3 with: name: logs_${{ github.run_id }} path: build/logs/ diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets index b63d59f1a..132f47c6c 100644 --- a/src/Directory.Build.targets +++ b/src/Directory.Build.targets @@ -3,10 +3,9 @@ - $(ToolsFolder) - $(SigningToolFolder)\sign.exe - $(MSBuildThisFileDirectory)signing-empty-file-list.txt - --description "WiX Toolset" --description-url "https://wixtoolset.org/" --timestamp-url "http://timestamp.digicert.com" --file-list "$(SigningFilelist)" --azure-key-vault-managed-identity true --azure-key-vault-url "$(SigningVaultUri)" --azure-key-vault-certificate "$(SigningCertName)" + $(ToolsFolder)\sign.exe + code azure-key-vault + --description "WiX Toolset" --description-url "https://wixtoolset.org/" --file-list "$(MSBuildThisFileDirectory)signing-empty-file-list.txt" --azure-key-vault-url $(SigningKeyVaultUri) --azure-key-vault-tenant-id $(SigningTenantId) --azure-key-vault-client-id $(SigningClientId) --azure-key-vault-client-secret $(SigningClientSecret) --azure-key-vault-certificate $(SigningCertName) --timestamp-url "http://timestamp.digicert.com" diff --git a/src/Directory.vcxproj.props b/src/Directory.vcxproj.props index 9064973db..078309a25 100644 --- a/src/Directory.vcxproj.props +++ b/src/Directory.vcxproj.props @@ -10,9 +10,8 @@ $(OutputPath)$(PlatformFolder)\ - - $([Microsoft.Build.Utilities.ToolLocationHelper]::GetLatestSDKTargetPlatformVersion('Windows', '10.0')) + 10.0 diff --git a/src/api/wix/WixToolset.Data/Intermediate.cs b/src/api/wix/WixToolset.Data/Intermediate.cs index 64f9810d2..ad4e49f9b 100644 --- a/src/api/wix/WixToolset.Data/Intermediate.cs +++ b/src/api/wix/WixToolset.Data/Intermediate.cs @@ -244,6 +244,20 @@ public void Save(string path) } } + /// + /// Saves an intermediate that can only be written to to a path on disk. + /// + /// Path to save intermediate file to disk. + public void SaveNew(string path) + { + Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(path))); + + using (var wixout = WixOutput.CreateNew(path)) + { + this.Save(wixout); + } + } + /// /// Saves an intermediate to a WixOutput. /// diff --git a/src/api/wix/WixToolset.Data/WixOutput.cs b/src/api/wix/WixToolset.Data/WixOutput.cs index 43359f247..72b922c9e 100644 --- a/src/api/wix/WixToolset.Data/WixOutput.cs +++ b/src/api/wix/WixToolset.Data/WixOutput.cs @@ -25,7 +25,7 @@ private WixOutput(Uri uri, ZipArchive archive, Stream stream) } /// - /// + /// /// public Uri Uri { get; } @@ -189,7 +189,10 @@ public void ExtractEmbeddedFile(string embeddedId, string outputPath) /// Stream to the data of the file. public Stream CreateDataStream(string name) { - this.DeleteExistingEntry(name); + if (this.archive.Mode == ZipArchiveMode.Update) + { + this.DeleteExistingEntry(name); + } var entry = this.archive.CreateEntry(name); @@ -203,7 +206,10 @@ public Stream CreateDataStream(string name) /// Path to file on disk to include in the output. public void ImportDataStream(string name, string path) { - this.DeleteExistingEntry(name); + if (this.archive.Mode == ZipArchiveMode.Update) + { + this.DeleteExistingEntry(name); + } this.archive.CreateEntryFromFile(path, name, System.IO.Compression.CompressionLevel.Optimal); } @@ -240,6 +246,26 @@ public string GetData(string name) } } + /// + /// Creates a new file structure on disk that can only be written to. + /// + /// Path to write file structure to. + /// Newly created WixOutput. + internal static WixOutput CreateNew(string path) + { + var fullPath = Path.GetFullPath(path); + + Directory.CreateDirectory(Path.GetDirectoryName(fullPath)); + + var uri = new Uri(fullPath); + + var stream = File.Create(path); + + var archive = new ZipArchive(stream, ZipArchiveMode.Create, leaveOpen: true); + + return new WixOutput(uri, archive, stream); + } + /// /// Disposes of the internal state of the file structure. /// diff --git a/src/dtf/WixToolset.Dtf.WindowsInstaller/CustomActionProxy.cs b/src/dtf/WixToolset.Dtf.WindowsInstaller/CustomActionProxy.cs index d3fd7d1b7..ae2673ca9 100644 --- a/src/dtf/WixToolset.Dtf.WindowsInstaller/CustomActionProxy.cs +++ b/src/dtf/WixToolset.Dtf.WindowsInstaller/CustomActionProxy.cs @@ -101,8 +101,13 @@ public static int InvokeCustomAction(int sessionHandle, string entryPoint, return (int) ActionResult.Failure; } + string originalDirectory = null; + try { + // Remember the original directory so we can restore it later. + originalDirectory = Environment.CurrentDirectory; + // Set the current directory to the location of the extracted files. Environment.CurrentDirectory = AppDomain.CurrentDomain.BaseDirectory; @@ -142,6 +147,20 @@ public static int InvokeCustomAction(int sessionHandle, string entryPoint, session.Log(ex.ToString()); return (int) ActionResult.Failure; } + finally + { + try + { + if (!String.IsNullOrEmpty(originalDirectory)) + { + Environment.CurrentDirectory = originalDirectory; + } + } + catch (Exception ex) + { + session.Log("Failed to restore current directory after running custom action: {0}", ex.Message); + } + } } /// diff --git a/src/ext/Util/ca/RemoveFoldersEx.cpp b/src/ext/Util/ca/RemoveFoldersEx.cpp index 7cba4e976..992302c60 100644 --- a/src/ext/Util/ca/RemoveFoldersEx.cpp +++ b/src/ext/Util/ca/RemoveFoldersEx.cpp @@ -238,10 +238,10 @@ extern "C" UINT WINAPI WixRemoveFoldersEx( hr = PathExpand(&sczExpandedPath, sczPath, PATH_EXPAND_ENVIRONMENT); ExitOnFailure(hr, "Failed to expand path: %S for row: %S", sczPath, sczId); - + hr = PathBackslashTerminate(&sczExpandedPath); ExitOnFailure(hr, "Failed to backslash-terminate path: %S", sczExpandedPath); - + WcaLog(LOGMSG_STANDARD, "Recursing path: %S for row: %S.", sczExpandedPath, sczId); hr = RecursePath(sczExpandedPath, sczId, sczComponent, sczProperty, iMode, f64BitComponent, &dwCounter, &hTable, &hColumns); ExitOnFailure(hr, "Failed while navigating path: %S for row: %S", sczPath, sczId); diff --git a/src/ext/Util/ca/precomp.h b/src/ext/Util/ca/precomp.h index efde32a61..9456c6916 100644 --- a/src/ext/Util/ca/precomp.h +++ b/src/ext/Util/ca/precomp.h @@ -15,7 +15,7 @@ #include #include -#include +#include #include // NetApi32.lib #include #include @@ -50,5 +50,6 @@ #include "scauser.h" #include "scasmb.h" #include "scasmbexec.h" +#include "utilca.h" #include "..\..\caDecor.h" diff --git a/src/ext/Util/ca/scaexec.cpp b/src/ext/Util/ca/scaexec.cpp index 5119bc11e..8579b8bba 100644 --- a/src/ext/Util/ca/scaexec.cpp +++ b/src/ext/Util/ca/scaexec.cpp @@ -613,8 +613,7 @@ static HRESULT RemoveUserInternal( LPWSTR pwz = NULL; LPWSTR pwzGroup = NULL; LPWSTR pwzGroupDomain = NULL; - LPCWSTR wz = NULL; - PDOMAIN_CONTROLLER_INFOW pDomainControllerInfo = NULL; + LPWSTR pwzDomainName = NULL; // // Remove the logon as service privilege. @@ -644,30 +643,10 @@ static HRESULT RemoveUserInternal( // if (!(SCAU_DONT_CREATE_USER & iAttributes)) { - if (wzDomain && *wzDomain) - { - er = ::DsGetDcNameW(NULL, (LPCWSTR)wzDomain, NULL, NULL, NULL, &pDomainControllerInfo); - if (RPC_S_SERVER_UNAVAILABLE == er) - { - // MSDN says, if we get the above error code, try again with the "DS_FORCE_REDISCOVERY" flag - er = ::DsGetDcNameW(NULL, (LPCWSTR)wzDomain, NULL, NULL, DS_FORCE_REDISCOVERY, &pDomainControllerInfo); - } - if (ERROR_SUCCESS == er) - { - if (2 <= wcslen(pDomainControllerInfo->DomainControllerName)) - { - wz = pDomainControllerInfo->DomainControllerName + 2; // Add 2 so that we don't get the \\ prefix. - // Pass the entire string if it is too short - // to have a \\ prefix. - } - } - else - { - wz = wzDomain; - } - } + hr = GetDomainFromServerName(&pwzDomainName, wzDomain, 0); + ExitOnFailure(hr, "Failed to get domain to remove user from server name: %ls", wzDomain); - er = ::NetUserDel(wz, wzName); + er = ::NetUserDel(pwzDomainName, wzName); if (NERR_UserNotFound == er) { er = NERR_Success; @@ -707,52 +686,13 @@ static HRESULT RemoveUserInternal( } LExit: - if (pDomainControllerInfo) - { - ::NetApiBufferFree(static_cast(pDomainControllerInfo)); - } + ReleaseStr(pwzDomainName); + ReleaseStr(pwzGroupDomain); + ReleaseStr(pwzGroup); return hr; } -static void GetServerName(LPWSTR pwzDomain, LPWSTR* ppwzServerName) -{ - DWORD er = ERROR_SUCCESS; - PDOMAIN_CONTROLLER_INFOW pDomainControllerInfo = NULL; - - if (pwzDomain && *pwzDomain) - { - er = ::DsGetDcNameW(NULL, (LPCWSTR)pwzDomain, NULL, NULL, NULL, &pDomainControllerInfo); - if (RPC_S_SERVER_UNAVAILABLE == er) - { - // MSDN says, if we get the above error code, try again with the "DS_FORCE_REDISCOVERY" flag - er = ::DsGetDcNameW(NULL, (LPCWSTR)pwzDomain, NULL, NULL, DS_FORCE_REDISCOVERY, &pDomainControllerInfo); - } - - if (ERROR_SUCCESS == er && pDomainControllerInfo->DomainControllerName) - { - // Skip the \\ prefix if present. - if ('\\' == *pDomainControllerInfo->DomainControllerName && '\\' == *pDomainControllerInfo->DomainControllerName + 1) - { - *ppwzServerName = pDomainControllerInfo->DomainControllerName + 2; - } - else - { - *ppwzServerName = pDomainControllerInfo->DomainControllerName; - } - } - else - { - *ppwzServerName = pwzDomain; - } - } - - if (pDomainControllerInfo) - { - ::NetApiBufferFree((LPVOID)pDomainControllerInfo); - } -} - /******************************************************************** CreateUser - CUSTOM ACTION ENTRY POINT for creating users @@ -776,6 +716,7 @@ extern "C" UINT __stdcall CreateUser( LPWSTR pwzPassword = NULL; LPWSTR pwzGroup = NULL; LPWSTR pwzGroupDomain = NULL; + LPWSTR pwzDomainName = NULL; int iAttributes = 0; BOOL fInitializedCom = FALSE; @@ -786,7 +727,6 @@ extern "C" UINT __stdcall CreateUser( USER_INFO_1 userInfo1; USER_INFO_1* pUserInfo1 = NULL; DWORD dw; - LPWSTR pwzServerName = NULL; hr = WcaInitialize(hInstall, "CreateUser"); ExitOnFailure(hr, "failed to initialize"); @@ -845,9 +785,10 @@ extern "C" UINT __stdcall CreateUser( // // Create the User // - GetServerName(pwzDomain, &pwzServerName); + hr = GetDomainFromServerName(&pwzDomainName, pwzDomain, 0); + ExitOnFailure(hr, "Failed to get domain from server name: %ls", pwzDomain); - er = ::NetUserAdd(pwzServerName, 1, reinterpret_cast(pUserInfo1), &dw); + er = ::NetUserAdd(pwzDomainName, 1, reinterpret_cast(pUserInfo1), &dw); if (NERR_UserExists == er) { if (SCAU_FAIL_IF_EXISTS & iAttributes) @@ -862,7 +803,7 @@ extern "C" UINT __stdcall CreateUser( if (SCAU_UPDATE_IF_EXISTS & iAttributes) { pUserInfo1 = NULL; - er = ::NetUserGetInfo(pwzServerName, pwzName, 1, reinterpret_cast(&pUserInfo1)); + er = ::NetUserGetInfo(pwzDomainName, pwzName, 1, reinterpret_cast(&pUserInfo1)); if (ERROR_SUCCESS == er) { // There is no rollback scheduled if the key is empty. @@ -922,28 +863,28 @@ extern "C" UINT __stdcall CreateUser( if (ERROR_SUCCESS == er) { - hr = SetUserPassword(pwzServerName, pwzName, pwzPassword); + hr = SetUserPassword(pwzDomainName, pwzName, pwzPassword); if (FAILED(hr)) { - WcaLogError(hr, "failed to set user password for user %ls\\%ls, continuing anyway.", pwzServerName, pwzName); + WcaLogError(hr, "failed to set user password for user %ls\\%ls, continuing anyway.", pwzDomainName, pwzName); hr = S_OK; } if (SCAU_REMOVE_COMMENT & iAttributes) { - hr = SetUserComment(pwzServerName, pwzName, L""); + hr = SetUserComment(pwzDomainName, pwzName, L""); if (FAILED(hr)) { - WcaLogError(hr, "failed to clear user comment for user %ls\\%ls, continuing anyway.", pwzServerName, pwzName); + WcaLogError(hr, "failed to clear user comment for user %ls\\%ls, continuing anyway.", pwzDomainName, pwzName); hr = S_OK; } } else if (pwzComment && *pwzComment) { - hr = SetUserComment(pwzServerName, pwzName, pwzComment); + hr = SetUserComment(pwzDomainName, pwzName, pwzComment); if (FAILED(hr)) { - WcaLogError(hr, "failed to set user comment to %ls for user %ls\\%ls, continuing anyway.", pwzComment, pwzServerName, pwzName); + WcaLogError(hr, "failed to set user comment to %ls for user %ls\\%ls, continuing anyway.", pwzComment, pwzDomainName, pwzName); hr = S_OK; } } @@ -952,10 +893,10 @@ extern "C" UINT __stdcall CreateUser( ApplyAttributes(iAttributes, &flags); - hr = SetUserFlags(pwzServerName, pwzName, flags); + hr = SetUserFlags(pwzDomainName, pwzName, flags); if (FAILED(hr)) { - WcaLogError(hr, "failed to set user flags for user %ls\\%ls, continuing anyway.", pwzServerName, pwzName); + WcaLogError(hr, "failed to set user flags for user %ls\\%ls, continuing anyway.", pwzDomainName, pwzName); hr = S_OK; } } @@ -1018,6 +959,7 @@ extern "C" UINT __stdcall CreateUser( ReleaseStr(pwzPassword); ReleaseStr(pwzGroup); ReleaseStr(pwzGroupDomain); + ReleaseStr(pwzDomainName) if (fInitializedCom) { diff --git a/src/ext/Util/ca/scauser.cpp b/src/ext/Util/ca/scauser.cpp index b643a8429..79da155fa 100644 --- a/src/ext/Util/ca/scauser.cpp +++ b/src/ext/Util/ca/scauser.cpp @@ -487,7 +487,7 @@ HRESULT ScaUserExecute( { HRESULT hr = S_OK; DWORD er = 0; - PDOMAIN_CONTROLLER_INFOW pDomainControllerInfo = NULL; + LPWSTR pwzDomainName = NULL; LPWSTR pwzBaseScriptKey = NULL; DWORD cScriptKey = 0; @@ -518,36 +518,11 @@ HRESULT ScaUserExecute( ExitOnFailure(hr, "Failed to add user comment to custom action data: %ls", psu->wzComment); // Check to see if the user already exists since we have to be very careful when adding - // and removing users. Note: MSDN says that it is safe to call these APIs from any - // user, so we should be safe calling it during immediate mode. - er = ::NetApiBufferAllocate(sizeof(USER_INFO_0), reinterpret_cast(&pUserInfo)); - hr = HRESULT_FROM_WIN32(er); - ExitOnFailure(hr, "Failed to allocate memory to check existence of user: %ls", psu->wzName); - - LPCWSTR wzDomain = psu->wzDomain; - if (wzDomain && *wzDomain) - { - er = ::DsGetDcNameW(NULL, wzDomain, NULL, NULL, NULL, &pDomainControllerInfo); - if (RPC_S_SERVER_UNAVAILABLE == er) - { - // MSDN says, if we get the above error code, try again with the "DS_FORCE_REDISCOVERY" flag - er = ::DsGetDcNameW(NULL, wzDomain, NULL, NULL, DS_FORCE_REDISCOVERY, &pDomainControllerInfo); - } - if (ERROR_SUCCESS == er && pDomainControllerInfo->DomainControllerName) - { - // If the \\ prefix on the queried domain was present, skip it. - if ('\\' == *pDomainControllerInfo->DomainControllerName && '\\' == *pDomainControllerInfo->DomainControllerName + 1) - { - wzDomain = pDomainControllerInfo->DomainControllerName + 2; - } - else - { - wzDomain = pDomainControllerInfo->DomainControllerName; - } - } - } + // and removing users. + hr = GetDomainFromServerName(&pwzDomainName, psu->wzDomain, 0); + ExitOnFailure(hr, "Failed to get domain from server name: %ls", psu->wzDomain); - er = ::NetUserGetInfo(wzDomain, psu->wzName, 0, reinterpret_cast(pUserInfo)); + er = ::NetUserGetInfo(pwzDomainName, psu->wzName, 0, reinterpret_cast(&pUserInfo)); if (NERR_Success == er) { ueUserExists = USER_EXISTS_YES; @@ -560,7 +535,7 @@ HRESULT ScaUserExecute( { ueUserExists = USER_EXISTS_INDETERMINATE; hr = HRESULT_FROM_WIN32(er); - WcaLog(LOGMSG_VERBOSE, "Failed to check existence of domain: %ls, user: %ls (error code 0x%x) - continuing", wzDomain, psu->wzName, hr); + WcaLog(LOGMSG_VERBOSE, "Failed to check existence of domain: %ls, user: %ls (error code 0x%x) - continuing", pwzDomainName, psu->wzName, hr); hr = S_OK; er = ERROR_SUCCESS; } @@ -685,11 +660,6 @@ HRESULT ScaUserExecute( ::NetApiBufferFree(static_cast(pUserInfo)); pUserInfo = NULL; } - if (pDomainControllerInfo) - { - ::NetApiBufferFree(static_cast(pDomainControllerInfo)); - pDomainControllerInfo = NULL; - } } LExit: @@ -697,14 +667,12 @@ HRESULT ScaUserExecute( ReleaseStr(pwzScriptKey); ReleaseStr(pwzActionData); ReleaseStr(pwzRollbackData); + ReleaseStr(pwzDomainName); + if (pUserInfo) { ::NetApiBufferFree(static_cast(pUserInfo)); } - if (pDomainControllerInfo) - { - ::NetApiBufferFree(static_cast(pDomainControllerInfo)); - } return hr; } diff --git a/src/ext/Util/ca/utilca.cpp b/src/ext/Util/ca/utilca.cpp index 37664a1c8..d41f00c29 100644 --- a/src/ext/Util/ca/utilca.cpp +++ b/src/ext/Util/ca/utilca.cpp @@ -1,3 +1,59 @@ // 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. #include "precomp.h" + +HRESULT GetDomainFromServerName( + __deref_out_z LPWSTR* ppwzDomainName, + __in_z LPCWSTR wzServerName, + __in DWORD dwFlags + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + PDOMAIN_CONTROLLER_INFOW pDomainControllerInfo = NULL; + LPCWSTR wz = wzServerName ? wzServerName : L""; // initialize the domain to the provided server name (or empty string). + + // If the server name was not empty, try to get the domain name out of it. + if (*wz) + { + er = ::DsGetDcNameW(NULL, wz, NULL, NULL, dwFlags, &pDomainControllerInfo); + if (RPC_S_SERVER_UNAVAILABLE == er) + { + // MSDN says, if we get the above error code, try again with the "DS_FORCE_REDISCOVERY" flag. + er = ::DsGetDcNameW(NULL, wz, NULL, NULL, dwFlags | DS_FORCE_REDISCOVERY, &pDomainControllerInfo); + } + ExitOnWin32Error(er, hr, "Could not get domain name from server name: %ls", wz); + + if (pDomainControllerInfo->DomainControllerName) + { + // Skip the \\ prefix if present. + if ('\\' == *pDomainControllerInfo->DomainControllerName && '\\' == *(pDomainControllerInfo->DomainControllerName + 1)) + { + wz = pDomainControllerInfo->DomainControllerName + 2; + } + else + { + wz = pDomainControllerInfo->DomainControllerName; + } + } + } + +LExit: + // Note: we overwrite the error code here as failure to contact domain controller above is not a fatal error. + if (wz && *wz) + { + hr = StrAllocString(ppwzDomainName, wz, 0); + } + else // return NULL the server name ended up empty. + { + ReleaseNullStr(*ppwzDomainName); + hr = S_OK; + } + + if (pDomainControllerInfo) + { + ::NetApiBufferFree((LPVOID)pDomainControllerInfo); + } + + return hr; +} diff --git a/src/ext/Util/ca/utilca.h b/src/ext/Util/ca/utilca.h new file mode 100644 index 000000000..97c545619 --- /dev/null +++ b/src/ext/Util/ca/utilca.h @@ -0,0 +1,8 @@ +#pragma once +// 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. + +HRESULT GetDomainFromServerName( + __deref_out_z LPWSTR* ppwzDomainName, + __in_z LPCWSTR wzServerName, + __in DWORD dwFlags + ); diff --git a/src/internal/SetBuildNumber/Directory.Packages.props.pp b/src/internal/SetBuildNumber/Directory.Packages.props.pp index 10ee9e0d9..e9438d065 100644 --- a/src/internal/SetBuildNumber/Directory.Packages.props.pp +++ b/src/internal/SetBuildNumber/Directory.Packages.props.pp @@ -41,20 +41,20 @@ - + - + - - + + - + @@ -70,16 +70,16 @@ - - - + + + - - + + @@ -88,12 +88,12 @@ - + - - - - + + + + diff --git a/src/internal/WixInternal.TestSupport.Native/build/WixInternal.TestSupport.Native.props b/src/internal/WixInternal.TestSupport.Native/build/WixInternal.TestSupport.Native.props index 11f3baed7..2362a8deb 100644 --- a/src/internal/WixInternal.TestSupport.Native/build/WixInternal.TestSupport.Native.props +++ b/src/internal/WixInternal.TestSupport.Native/build/WixInternal.TestSupport.Native.props @@ -5,8 +5,8 @@ $([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), .gitignore)) - - + + v4.7.2 diff --git a/src/internal/WixInternal.TestSupport.Native/build/WixInternal.TestSupport.Native.targets b/src/internal/WixInternal.TestSupport.Native/build/WixInternal.TestSupport.Native.targets index 15bdb33e2..a882ad8de 100644 --- a/src/internal/WixInternal.TestSupport.Native/build/WixInternal.TestSupport.Native.targets +++ b/src/internal/WixInternal.TestSupport.Native/build/WixInternal.TestSupport.Native.targets @@ -22,28 +22,28 @@ $(RootPackagesFolder)xunit.abstractions.2.0.3\lib\netstandard2.0\xunit.abstractions.dll - $(RootPackagesFolder)xunit.assert.2.5.0\lib\netstandard1.1\xunit.assert.dll + $(RootPackagesFolder)xunit.assert.2.8.1\lib\netstandard1.1\xunit.assert.dll - $(RootPackagesFolder)xunit.extensibility.core.2.5.0\lib\netstandard1.1\xunit.core.dll + $(RootPackagesFolder)xunit.extensibility.core.2.8.1\lib\netstandard1.1\xunit.core.dll - $(RootPackagesFolder)xunit.extensibility.execution.2.5.0\lib\net452\xunit.execution.desktop.dll + $(RootPackagesFolder)xunit.extensibility.execution.2.8.1\lib\net452\xunit.execution.desktop.dll - + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - + + + - - + + diff --git a/src/internal/WixInternal.TestSupport.Native/packages.config b/src/internal/WixInternal.TestSupport.Native/packages.config index a9afa1faf..21567bf36 100644 --- a/src/internal/WixInternal.TestSupport.Native/packages.config +++ b/src/internal/WixInternal.TestSupport.Native/packages.config @@ -7,10 +7,10 @@ when any of these versions are updated. --> - - - - - - + + + + + + diff --git a/src/libs/dutil/WixToolset.DUtil/thmutil.cpp b/src/libs/dutil/WixToolset.DUtil/thmutil.cpp index 7938cd0bd..7cea3a173 100644 --- a/src/libs/dutil/WixToolset.DUtil/thmutil.cpp +++ b/src/libs/dutil/WixToolset.DUtil/thmutil.cpp @@ -312,13 +312,14 @@ static HRESULT ShowControl( __in BOOL fSaveEditboxes, __in THEME_SHOW_PAGE_REASON reason, __in DWORD dwPageId, - __out_opt HWND* phwndFocus + __inout_opt HWND* phwndFocus ); static HRESULT ShowControls( __in THEME* pTheme, __in_opt const THEME_CONTROL* pParentControl, __in int nCmdShow, __in BOOL fSaveEditboxes, + __in BOOL fSetFocus, __in THEME_SHOW_PAGE_REASON reason, __in DWORD dwPageId ); @@ -1192,8 +1193,9 @@ DAPI_(HRESULT) ThemeShowPageEx( BOOL fHide = SW_HIDE == nCmdShow; BOOL fSaveEditboxes = FALSE; THEME_SAVEDVARIABLE* pSavedVariable = NULL; - THEME_PAGE* pPage = ThemeGetPage(pTheme, dwPage); SIZE_T cb = 0; + BOOL fSetFocus = dwPage != pTheme->dwCurrentPageId; + THEME_PAGE* pPage = ThemeGetPage(pTheme, dwPage); if (pPage) { @@ -1257,7 +1259,7 @@ DAPI_(HRESULT) ThemeShowPageEx( } } - hr = ShowControls(pTheme, NULL, nCmdShow, fSaveEditboxes, reason, dwPage); + hr = ShowControls(pTheme, NULL, nCmdShow, fSaveEditboxes, fSetFocus, reason, dwPage); ThmExitOnFailure(hr, "Failed to show page controls."); LExit: @@ -5561,7 +5563,7 @@ static HRESULT ShowControl( __in BOOL fSaveEditboxes, __in THEME_SHOW_PAGE_REASON reason, __in DWORD dwPageId, - __out_opt HWND* phwndFocus + __inout_opt HWND* phwndFocus ) { HRESULT hr = S_OK; @@ -5810,7 +5812,7 @@ static HRESULT ShowControl( if (0 < pControl->cControls) { - ShowControls(pTheme, pControl, nCmdShow, fSaveEditboxes, reason, dwPageId); + ShowControls(pTheme, pControl, nCmdShow, fSaveEditboxes, FALSE/*fSetFocus*/, reason, dwPageId); } if (THEME_CONTROL_TYPE_BILLBOARD == pControl->type && pControl->wPageId) @@ -5842,6 +5844,7 @@ static HRESULT ShowControls( __in_opt const THEME_CONTROL* pParentControl, __in int nCmdShow, __in BOOL fSaveEditboxes, + __in BOOL fSetFocus, __in THEME_SHOW_PAGE_REASON reason, __in DWORD dwPageId ) @@ -5865,7 +5868,7 @@ static HRESULT ShowControls( } } - if (hwndFocus) + if (fSetFocus && hwndFocus) { ::SendMessage(pTheme->hwndParent, WM_NEXTDLGCTL, (WPARAM)hwndFocus, TRUE); } diff --git a/src/setup/MetadataTask/GenerateMetadata.cs b/src/setup/MetadataTask/GenerateMetadata.cs index efe4ee399..65e6f7a46 100644 --- a/src/setup/MetadataTask/GenerateMetadata.cs +++ b/src/setup/MetadataTask/GenerateMetadata.cs @@ -19,7 +19,7 @@ public class GenerateMetadata : Task private static readonly JsonSerializerOptions SerializerOptions = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, - IgnoreNullValues = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, WriteIndented = true, Converters = { new JsonStringEnumConverter(JsonNamingPolicy.CamelCase) }, }; diff --git a/src/wix.vsconfig b/src/wix.vsconfig index f72cc5e4c..8fb74dfc2 100644 --- a/src/wix.vsconfig +++ b/src/wix.vsconfig @@ -1,6 +1,5 @@ { "version": "1.0", "components": [ - "Microsoft.VisualStudio.Component.VC.v141.ARM64.Spectre" ] } diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/PatchFilterMap.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/PatchFilterMap.cs index 4822f3a5c..fe4dfde40 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/PatchFilterMap.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/PatchFilterMap.cs @@ -1,4 +1,4 @@ -// 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. +// 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. namespace WixToolset.Core.WindowsInstaller.Bind { @@ -53,6 +53,7 @@ internal bool TryGetPatchFiltersForRow(Row row, out string targetFilterId, out s targetFilterId = patchFilter?.TargetFilterId; updatedFilterId = patchFilter?.UpdatedFilterId; + return patchFilter != null; } diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/ReduceTransformCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/ReduceTransformCommand.cs index e7d936601..f10134808 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/ReduceTransformCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/ReduceTransformCommand.cs @@ -101,11 +101,10 @@ private bool ReduceTransform(WindowsInstallerData transform, IEnumerable t.Rows.Count == 0).Select(t => t.Name).ToList(); diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/UpdateMediaSequencesCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/UpdateMediaSequencesCommand.cs index e0ed56b10..8cf2df3a6 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/UpdateMediaSequencesCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/UpdateMediaSequencesCommand.cs @@ -60,7 +60,7 @@ public void Execute() if (facade.PatchGroup.HasValue) { - if (patchGroups.TryGetValue(facade.PatchGroup.Value, out var patchGroup)) + if (!patchGroups.TryGetValue(facade.PatchGroup.Value, out var patchGroup)) { patchGroup = new List(); patchGroups.Add(facade.PatchGroup.Value, patchGroup); diff --git a/src/wix/WixToolset.Core/CommandLine/BuildCommand.cs b/src/wix/WixToolset.Core/CommandLine/BuildCommand.cs index 2ad180739..2e2ef1719 100644 --- a/src/wix/WixToolset.Core/CommandLine/BuildCommand.cs +++ b/src/wix/WixToolset.Core/CommandLine/BuildCommand.cs @@ -251,7 +251,7 @@ private void LibraryPhase(IReadOnlyCollection intermediates, IRead if (!this.Messaging.EncounteredError) { - result.Library.Save(outputPath); + result.Library.SaveNew(outputPath); this.LayoutFiles(result.TrackedFiles, null, cancellationToken); } diff --git a/src/wix/WixToolset.Core/ExtensibilityServices/BackendHelper.cs b/src/wix/WixToolset.Core/ExtensibilityServices/BackendHelper.cs index 43f7aa016..988ad60a3 100644 --- a/src/wix/WixToolset.Core/ExtensibilityServices/BackendHelper.cs +++ b/src/wix/WixToolset.Core/ExtensibilityServices/BackendHelper.cs @@ -120,16 +120,13 @@ public bool TryParseMsiProductVersion(string version, bool strict, out string pa { if (WixVersion.TryParse(version, out var wixVersion) && wixVersion.HasMajor && wixVersion.Major < 256 && wixVersion.Minor < 256 && wixVersion.Patch < 65536 && wixVersion.Labels == null && String.IsNullOrEmpty(wixVersion.Metadata)) { - parsedVersion = $"{wixVersion.Major}.{wixVersion.Minor}"; - - if (strict || wixVersion.HasPatch) + if (strict) { - parsedVersion += $".{wixVersion.Patch}"; + parsedVersion = $"{wixVersion.Major}.{wixVersion.Minor}.{wixVersion.Patch}"; } - - if (!strict && wixVersion.HasRevision) + else { - parsedVersion += $".{wixVersion.Revision}"; + parsedVersion = wixVersion.Prefix.HasValue ? version.Substring(1) : version; } return true; diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/PatchFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/PatchFixture.cs index 49303df19..0ce4674d9 100644 --- a/src/wix/test/WixToolsetTest.CoreIntegration/PatchFixture.cs +++ b/src/wix/test/WixToolsetTest.CoreIntegration/PatchFixture.cs @@ -44,9 +44,9 @@ public PatchFixture() var tempFolderUpdate = Path.Combine(this.tempBaseFolder, "PatchTemplatePackage", "update"); var tempFolderUpdateNoFileChanges = Path.Combine(this.tempBaseFolder, "PatchTemplatePackage", "updatewithoutfilechanges"); - this.templateBaselinePdb = BuildMsi("Baseline.msi", this.templateSourceFolder, tempFolderBaseline, "1.0.0", "1.0.0", "1.0.0", new[] { Path.Combine(this.templateSourceFolder, ".baseline-data") }); - this.templateUpdatePdb = BuildMsi("Update.msi", this.templateSourceFolder, tempFolderUpdate, "1.0.1", "1.0.1", "1.0.1", new[] { Path.Combine(this.templateSourceFolder, ".update-data") }); - this.templateUpdateNoFilesChangedPdb = BuildMsi("Update.msi", this.templateSourceFolder, tempFolderUpdateNoFileChanges, "1.0.1", "1.0.1", "1.0.1", new[] { Path.Combine(this.templateSourceFolder, ".baseline-data") }); + this.templateBaselinePdb = BuildMsi("Baseline.msi", this.templateSourceFolder, tempFolderBaseline, "1.0.0", "1.0.0", "1.0.0", bindpaths: new[] { Path.Combine(this.templateSourceFolder, ".baseline-data") }); + this.templateUpdatePdb = BuildMsi("Update.msi", this.templateSourceFolder, tempFolderUpdate, "1.0.1", "1.0.1", "1.0.1", bindpaths: new[] { Path.Combine(this.templateSourceFolder, ".update-data") }); + this.templateUpdateNoFilesChangedPdb = BuildMsi("Update.msi", this.templateSourceFolder, tempFolderUpdateNoFileChanges, "1.0.1", "1.0.1", "1.0.1", bindpaths: new[] { Path.Combine(this.templateSourceFolder, ".baseline-data") }); } public void Dispose() @@ -81,6 +81,33 @@ public void CanBuildSimplePatchUsingWixpdbs() } } + [Fact] + public void CanBuildSimplePatchWithNewFileAndFilteringUsingWixpdbs() + { + var folder = TestData.Get(@"TestData", "PatchWithAddedFile"); + + using (var fs = new DisposableFileSystem()) + { + var tempFolder = fs.GetFolder(); + + var baselinePath = BuildMsi("Baseline.msi", folder, tempFolder, "1.0.0", "1.0.0", "1.0.0"); + var update1Path = BuildMsi("Update.msi", folder, tempFolder, "1.0.1", "1.0.1", "1.0.1", "TRUE"); + var patchPath = BuildMsp("Patch1.msp", folder, tempFolder, "1.0.1", warningsAsErrors: false); + + var doc = GetExtractPatchXml(patchPath); + WixAssert.StringEqual("{7D326855-E790-4A94-8611-5351F8321FCA}", doc.Root.Element(TargetProductCodeName).Value); + + var names = Query.GetSubStorageNames(patchPath); + WixAssert.CompareLineByLine(new[] { "#RTM.1", "RTM.1" }, names); + + var cab = Path.Combine(tempFolder, "foo.cab"); + Query.ExtractStream(patchPath, "foo.cab", cab); + + var files = Query.GetCabinetFiles(cab); + WixAssert.CompareLineByLine(new[] { "c.txt" }, files.Select(f => f.Name).ToArray()); + } + } + [Fact] public void CanBuildSimplePatchWithFileChangesUsingMsi() { @@ -452,7 +479,7 @@ public void CanBuildPatchWithRegistryFiltering() } } - private static string BuildMsi(string outputName, string sourceFolder, string baseFolder, string defineV, string defineA, string defineB, IEnumerable bindpaths = null) + private static string BuildMsi(string outputName, string sourceFolder, string baseFolder, string defineV, string defineA, string defineB, string defineC = null, IEnumerable bindpaths = null) { var outputPath = Path.Combine(baseFolder, Path.Combine("bin", outputName)); @@ -463,6 +490,7 @@ private static string BuildMsi(string outputName, string sourceFolder, string ba "-d", "V=" + defineV, "-d", "A=" + defineA, "-d", "B=" + defineB, + "-d", "C=" + defineC ?? String.Empty, "-bindpath", Path.Combine(sourceFolder, ".data"), "-intermediateFolder", Path.Combine(baseFolder, "obj"), "-o", outputPath, diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchFromWixlib/Package.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchFromWixlib/Package.wxs index 5cab1dbd7..1e2efb40f 100644 --- a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchFromWixlib/Package.wxs +++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchFromWixlib/Package.wxs @@ -17,7 +17,7 @@ - + diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchWithAddedFile/.data/Av1.0.0.txt b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchWithAddedFile/.data/Av1.0.0.txt new file mode 100644 index 000000000..6fd385bd0 --- /dev/null +++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchWithAddedFile/.data/Av1.0.0.txt @@ -0,0 +1 @@ +This is A v1.0.0 diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchWithAddedFile/.data/Av1.0.1.txt b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchWithAddedFile/.data/Av1.0.1.txt new file mode 100644 index 000000000..b1f0bc01c --- /dev/null +++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchWithAddedFile/.data/Av1.0.1.txt @@ -0,0 +1 @@ +This ia A v1.0.1 diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchWithAddedFile/.data/Bv1.0.0.txt b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchWithAddedFile/.data/Bv1.0.0.txt new file mode 100644 index 000000000..ece55fec6 --- /dev/null +++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchWithAddedFile/.data/Bv1.0.0.txt @@ -0,0 +1 @@ +This is B v1.0.0 diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchWithAddedFile/.data/Bv1.0.1.txt b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchWithAddedFile/.data/Bv1.0.1.txt new file mode 100644 index 000000000..cf3372fd6 --- /dev/null +++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchWithAddedFile/.data/Bv1.0.1.txt @@ -0,0 +1 @@ +This ia B v1.0.1 diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchWithAddedFile/.data/C.txt b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchWithAddedFile/.data/C.txt new file mode 100644 index 000000000..da7bfac11 --- /dev/null +++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchWithAddedFile/.data/C.txt @@ -0,0 +1 @@ +This is C, unversioned. diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchWithAddedFile/Package.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchWithAddedFile/Package.wxs new file mode 100644 index 000000000..2a970193f --- /dev/null +++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchWithAddedFile/Package.wxs @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchWithAddedFile/Patch.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchWithAddedFile/Patch.wxs new file mode 100644 index 000000000..679f0c10f --- /dev/null +++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchWithAddedFile/Patch.wxs @@ -0,0 +1,18 @@ + + + + + + + + + + + + diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/VersionFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/VersionFixture.cs index cf8466aa1..4934933fa 100644 --- a/src/wix/test/WixToolsetTest.CoreIntegration/VersionFixture.cs +++ b/src/wix/test/WixToolsetTest.CoreIntegration/VersionFixture.cs @@ -40,6 +40,62 @@ public void CanBuildMsiWithPrefixedVersion() } } + [Fact] + public void CanBuildMsiWithInsignificantZeroesVersion() + { + var folder = TestData.Get(@"TestData"); + + using (var fs = new DisposableFileSystem()) + { + var baseFolder = fs.GetFolder(); + var intermediateFolder = Path.Combine(baseFolder, "obj"); + var msiPath = Path.Combine(baseFolder, "bin", "test1.msi"); + + var result = WixRunner.Execute(new[] + { + "build", + Path.Combine(folder, "Version", "PackageWithReplaceableVersion.wxs"), + "-bindpath", Path.Combine(folder, "SingleFile", "data"), + "-intermediateFolder", intermediateFolder, + "-d", "Version=0001.002.0003.04", + "-o", msiPath + }); + + result.AssertSuccess(); + + var productVersion = GetProductVersionFromMsi(msiPath); + Assert.Equal("0001.002.0003.04", productVersion); + } + } + + [Fact] + public void CanBuildMsiWithPrefixedInsignificantZeroesVersion() + { + var folder = TestData.Get(@"TestData"); + + using (var fs = new DisposableFileSystem()) + { + var baseFolder = fs.GetFolder(); + var intermediateFolder = Path.Combine(baseFolder, "obj"); + var msiPath = Path.Combine(baseFolder, "bin", "test1.msi"); + + var result = WixRunner.Execute(new[] + { + "build", + Path.Combine(folder, "Version", "PackageWithReplaceableVersion.wxs"), + "-bindpath", Path.Combine(folder, "SingleFile", "data"), + "-intermediateFolder", intermediateFolder, + "-d", "Version=v01.002.0003.000004", + "-o", msiPath + }); + + result.AssertSuccess(); + + var productVersion = GetProductVersionFromMsi(msiPath); + Assert.Equal("01.002.0003.000004", productVersion); + } + } + [Fact] public void CanBuildMsiWithPrefixedVersionBindVariable() {