From bbc1298ff897811aea1324fada12f9bb66dcf621 Mon Sep 17 00:00:00 2001 From: JohnMcPMS Date: Thu, 24 Mar 2022 15:12:29 -0700 Subject: [PATCH 1/3] Script can setup, install, and wait for completion to loop through items --- tools/CorrelationTestbed/InSandboxScript.ps1 | 41 ++++ .../Test-CorrelationInSandbox.ps1 | 211 ++++++++++++++++++ 2 files changed, 252 insertions(+) create mode 100644 tools/CorrelationTestbed/InSandboxScript.ps1 create mode 100644 tools/CorrelationTestbed/Test-CorrelationInSandbox.ps1 diff --git a/tools/CorrelationTestbed/InSandboxScript.ps1 b/tools/CorrelationTestbed/InSandboxScript.ps1 new file mode 100644 index 0000000000..2c862b6753 --- /dev/null +++ b/tools/CorrelationTestbed/InSandboxScript.ps1 @@ -0,0 +1,41 @@ +Param( + [String] $DesktopAppInstallerPath, + [String[]] $DesktopAppInstallerDependencyPath, + [String] $PackageIdentifier, + [String] $SourceName, + [String] $OutputPath +) + +function Get-ARPTable { + $registry_paths = @('HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*','HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*', 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*', 'HKCU:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*') + return Get-ItemProperty $registry_paths -ErrorAction SilentlyContinue | + Select-Object DisplayName, DisplayVersion, Publisher, @{N='ProductCode'; E={$_.PSChildName}} | + Where-Object {$null -ne $_.DisplayName } +} + +Write-Host @" +--> Installing WinGet + +"@ + +$ProgressPreference = 'SilentlyContinue' +Add-AppxPackage -Path $DesktopAppInstallerPath -DependencyPath $DesktopAppInstallerDependencyPath + +$originalARP = Get-ARPTable + +Write-Host @" + +--> Installing $PackageIdentifier + +"@ + +winget install --id "$PackageIdentifier" -s "$SourceName" --verbose-logs --accept-source-agreements --accept-package-agreements + +Write-Host @" + +--> Comparing ARP Entries +"@ + +(Compare-Object (Get-ARPTable) $originalARP -Property DisplayName,DisplayVersion,Publisher,ProductCode)| Select-Object -Property * -ExcludeProperty SideIndicator | Format-Table + +"Test output" | Out-File (Join-Path $OutputPath "test-out.txt") diff --git a/tools/CorrelationTestbed/Test-CorrelationInSandbox.ps1 b/tools/CorrelationTestbed/Test-CorrelationInSandbox.ps1 new file mode 100644 index 0000000000..c1ed34925b --- /dev/null +++ b/tools/CorrelationTestbed/Test-CorrelationInSandbox.ps1 @@ -0,0 +1,211 @@ +# Started by copying from: +# https://github.com/microsoft/winget-pkgs/blob/c393e50b66448cc25a5cd27aa754d37677f42ce2/Tools/SandboxTest.ps1 + +Param( + [Parameter(Position = 0, HelpMessage = "The package identifiers to test.")] + [String[]] $PackageIdentifiers, + [Parameter(Position = 1, HelpMessage = "The source name that the package identifiers are from.")] + [String] $Source +) + +$ErrorActionPreference = "Stop" + +# Check if Windows Sandbox is enabled + +if (-Not (Get-Command 'WindowsSandbox' -ErrorAction SilentlyContinue)) { + Write-Error -Category NotInstalled -Message @' +Windows Sandbox does not seem to be available. Check the following URL for prerequisites and further details: +https://docs.microsoft.com/windows/security/threat-protection/windows-sandbox/windows-sandbox-overview + +You can run the following command in an elevated PowerShell for enabling Windows Sandbox: +$ Enable-WindowsOptionalFeature -Online -FeatureName 'Containers-DisposableClientVM' +'@ +} + +# Close Windows Sandbox + +function Close-WindowsSandbox { + $sandbox = Get-Process 'WindowsSandboxClient' -ErrorAction SilentlyContinue + if ($sandbox) { + Write-Host '--> Closing Windows Sandbox' + + $sandbox | Stop-Process + $sandbox | Wait-Process -Timeout 30 + + Write-Host + } + Remove-Variable sandbox +} + +Close-WindowsSandbox + +# Initialize Temp Folder + +$tempFolderName = 'CorrelationTestStaging' +$tempFolder = Join-Path -Path ([System.IO.Path]::GetTempPath()) -ChildPath $tempFolderName + +New-Item $tempFolder -ItemType Directory -ErrorAction SilentlyContinue | Out-Null + +# Set dependencies + +$apiLatestUrl = 'https://api.github.com/repos/microsoft/winget-cli/releases/latest' + +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +$WebClient = New-Object System.Net.WebClient + +function Get-LatestUrl { + ((Invoke-WebRequest $apiLatestUrl -UseBasicParsing | ConvertFrom-Json).assets | Where-Object { $_.name -match '^Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle$' }).browser_download_url +} + +function Get-LatestHash { + $shaUrl = ((Invoke-WebRequest $apiLatestUrl -UseBasicParsing | ConvertFrom-Json).assets | Where-Object { $_.name -match '^Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.txt$' }).browser_download_url + + $shaFile = Join-Path -Path $tempFolder -ChildPath 'Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.txt' + $WebClient.DownloadFile($shaUrl, $shaFile) + + Get-Content $shaFile +} + +# Hide the progress bar of Invoke-WebRequest +$oldProgressPreference = $ProgressPreference +$ProgressPreference = 'SilentlyContinue' + +$desktopAppInstaller = @{ + fileName = 'Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle' + url = $(Get-LatestUrl) + hash = $(Get-LatestHash) +} + +$ProgressPreference = $oldProgressPreference + +$vcLibsUwp = @{ + fileName = 'Microsoft.VCLibs.x64.14.00.Desktop.appx' + url = 'https://aka.ms/Microsoft.VCLibs.x64.14.00.Desktop.appx' + hash = 'A39CEC0E70BE9E3E48801B871C034872F1D7E5E8EEBE986198C019CF2C271040' +} +$uiLibsUwp = @{ + fileName = 'Microsoft.UI.Xaml.2.7.zip' + url = 'https://www.nuget.org/api/v2/package/Microsoft.UI.Xaml/2.7.0' + hash = "422FD24B231E87A842C4DAEABC6A335112E0D35B86FAC91F5CE7CF327E36A591" +} + +$dependencies = @($desktopAppInstaller, $vcLibsUwp, $uiLibsUwp) + +# Clean temp directory + +Get-ChildItem $tempFolder -Recurse -Exclude $dependencies.fileName | Remove-Item -Force -Recurse + +if (-Not [String]::IsNullOrWhiteSpace($Manifest)) { + Copy-Item -Path $Manifest -Recurse -Destination $tempFolder +} + +# Download dependencies + +Write-Host '--> Checking dependencies' + +$desktopInSandbox = 'C:\Users\WDAGUtilityAccount\Desktop' + +foreach ($dependency in $dependencies) { + $dependency.file = Join-Path -Path $tempFolder -ChildPath $dependency.fileName + $dependency.pathInSandbox = Join-Path -Path $desktopInSandbox -ChildPath (Join-Path -Path $tempFolderName -ChildPath $dependency.fileName) + + # Only download if the file does not exist, or its hash does not match. + if (-Not ((Test-Path -Path $dependency.file -PathType Leaf) -And $dependency.hash -eq $(Get-FileHash $dependency.file).Hash)) { + Write-Host @" + - Downloading: + $($dependency.url) +"@ + + try { + $WebClient.DownloadFile($dependency.url, $dependency.file) + } + catch { + #Pass the exception as an inner exception + throw [System.Net.WebException]::new("Error downloading $($dependency.url).",$_.Exception) + } + if (-not ($dependency.hash -eq $(Get-FileHash $dependency.file).Hash)) { + throw [System.Activities.VersionMismatchException]::new('Dependency hash does not match the downloaded file') + } + } +} + +# Extract Microsoft.UI.Xaml from zip (if freshly downloaded). +# This is a workaround until https://github.com/microsoft/winget-cli/issues/1861 is resolved. + +if (-Not (Test-Path (Join-Path -Path $tempFolder -ChildPath \Microsoft.UI.Xaml.2.7\tools\AppX\x64\Release\Microsoft.UI.Xaml.2.7.appx))){ + Expand-Archive -Path $uiLibsUwp.file -DestinationPath ($tempFolder + "\Microsoft.UI.Xaml.2.7") -Force +} +$uiLibsUwp.file = (Join-Path -Path $tempFolder -ChildPath \Microsoft.UI.Xaml.2.7\tools\AppX\x64\Release\Microsoft.UI.Xaml.2.7.appx) +$uiLibsUwp.pathInSandbox = Join-Path -Path $desktopInSandbox -ChildPath (Join-Path -Path $tempFolderName -ChildPath \Microsoft.UI.Xaml.2.7\tools\AppX\x64\Release\Microsoft.UI.Xaml.2.7.appx) +Write-Host + +# Copy main script + +$mainPs1FileName = 'InSandboxScript.ps1' +Copy-Item (Join-Path $PSScriptRoot $mainPs1FileName) (Join-Path $tempFolder $mainPs1FileName) + +foreach ($packageIdentifier in $PackageIdentifiers) +{ + + # Create temporary location for output + $outPath = Join-Path ([System.IO.Path]::GetTempPath()) (New-Guid) + New-Item -ItemType Directory $outPath > $nul + + $outPathInSandbox = Join-Path -Path $desktopInSandbox -ChildPath (Split-Path -Path $outPath -Leaf) + + $bootstrapPs1Content = @" +.\$mainPs1FileName -DesktopAppInstallerPath '$($desktopAppInstaller.pathInSandbox)' -DesktopAppInstallerDependencyPath @('$($vcLibsUwp.pathInSandbox)', '$($uiLibsUwp.pathInSandbox)') -PackageIdentifier '$packageIdentifier' -SourceName '$Source'-OutputPath '$outPathInSandbox' +"@ + + $bootstrapPs1FileName = 'Bootstrap.ps1' + $bootstrapPs1Content | Out-File (Join-Path $tempFolder $bootstrapPs1FileName) -Force + + # Create Wsb file + + $bootstrapPs1InSandbox = Join-Path -Path $desktopInSandbox -ChildPath (Join-Path -Path $tempFolderName -ChildPath $bootstrapPs1FileName) + $tempFolderInSandbox = Join-Path -Path $desktopInSandbox -ChildPath $tempFolderName + + $sandboxTestWsbContent = @" + + + + $tempFolder + true + + + $outPath + + + + PowerShell Start-Process PowerShell -WindowStyle Maximized -WorkingDirectory '$tempFolderInSandbox' -ArgumentList '-ExecutionPolicy Bypass -NoExit -NoLogo -File $bootstrapPs1InSandbox' + + +"@ + + $sandboxTestWsbFileName = 'SandboxTest.wsb' + $sandboxTestWsbFile = Join-Path -Path $tempFolder -ChildPath $sandboxTestWsbFileName + $sandboxTestWsbContent | Out-File $sandboxTestWsbFile -Force + + Write-Host @" +--> Starting Windows Sandbox, and: + - Mounting the following directories: + - $tempFolder as read-only + - $outPath as read-write + - Installing WinGet + - Installing the package: $packageIdentifier + - Comparing ARP Entries +"@ + + Write-Host + + WindowsSandbox $SandboxTestWsbFile + + $outputFileBlockerPath = Join-Path $outPath "test-out.txt" + + while (-not (Test-Path $outputFileBlockerPath)) + { + Start-Sleep 1 + } + + Close-WindowsSandbox +} From 34b8b360ffca9c38cde328136cf5e7617e2ced36 Mon Sep 17 00:00:00 2001 From: JohnMcPMS Date: Tue, 5 Apr 2022 16:57:32 -0700 Subject: [PATCH 2/3] Pretty much all of it --- .../Package.appxmanifest | 1 - src/AppInstallerCLITests/TestSource.cpp | 2 + .../Microsoft/SQLiteIndexSource.cpp | 3 +- .../Microsoft/Schema/1_1/Interface.h | 3 + .../Microsoft/Schema/1_1/Interface_1_1.cpp | 20 + .../Schema/1_1/ManifestMetadataTable.cpp | 19 + .../Schema/1_1/ManifestMetadataTable.h | 4 + .../Public/winget/RepositorySearch.h | 1 + .../Rest/RestSource.cpp | 2 + .../PackageManager.idl | 2 + .../PackageVersionInfo.cpp | 4 + .../PackageVersionInfo.h | 1 + tools/CorrelationTestbed/InSandboxScript.ps1 | 82 ++- .../InstallAndCheckCorrelation.sln | 31 + .../InstallAndCheckCorrelation.cpp | 552 ++++++++++++++++++ .../InstallAndCheckCorrelation.vcxproj | 181 ++++++ ...InstallAndCheckCorrelation.vcxproj.filters | 28 + .../Microsoft.Management.Deployment.winmd | Bin 0 -> 20480 bytes .../packages.config | 5 + .../Process-CorrelationResults.ps1 | 37 ++ .../Test-CorrelationInSandbox.ps1 | 147 ++++- 21 files changed, 1109 insertions(+), 16 deletions(-) create mode 100644 tools/CorrelationTestbed/InstallAndCheckCorrelation/InstallAndCheckCorrelation.sln create mode 100644 tools/CorrelationTestbed/InstallAndCheckCorrelation/InstallAndCheckCorrelation/InstallAndCheckCorrelation.cpp create mode 100644 tools/CorrelationTestbed/InstallAndCheckCorrelation/InstallAndCheckCorrelation/InstallAndCheckCorrelation.vcxproj create mode 100644 tools/CorrelationTestbed/InstallAndCheckCorrelation/InstallAndCheckCorrelation/InstallAndCheckCorrelation.vcxproj.filters create mode 100644 tools/CorrelationTestbed/InstallAndCheckCorrelation/InstallAndCheckCorrelation/Microsoft.Management.Deployment.winmd create mode 100644 tools/CorrelationTestbed/InstallAndCheckCorrelation/InstallAndCheckCorrelation/packages.config create mode 100644 tools/CorrelationTestbed/Process-CorrelationResults.ps1 diff --git a/src/AppInstallerCLIPackage/Package.appxmanifest b/src/AppInstallerCLIPackage/Package.appxmanifest index 58a2ff098d..4fafddc46d 100644 --- a/src/AppInstallerCLIPackage/Package.appxmanifest +++ b/src/AppInstallerCLIPackage/Package.appxmanifest @@ -16,7 +16,6 @@ - diff --git a/src/AppInstallerCLITests/TestSource.cpp b/src/AppInstallerCLITests/TestSource.cpp index 77c6d6322f..9b42173dc3 100644 --- a/src/AppInstallerCLITests/TestSource.cpp +++ b/src/AppInstallerCLITests/TestSource.cpp @@ -50,6 +50,8 @@ namespace TestCommon return LocIndString{ VersionManifest.Channel }; case PackageVersionProperty::SourceIdentifier: return LocIndString{ Source.lock()->GetIdentifier() }; + case PackageVersionProperty::Publisher: + return LocIndString{ VersionManifest.DefaultLocalization.Get() }; default: return {}; } diff --git a/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.cpp b/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.cpp index 3362a11bcf..d457a59df5 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.cpp @@ -48,7 +48,8 @@ namespace AppInstaller::Repository::Microsoft return LocIndString{ GetReferenceSource()->GetDetails().Name }; default: // Values coming from the index will always be localized/independent. - return LocIndString{ GetReferenceSource()->GetIndex().GetPropertyByManifestId(m_manifestId, property).value() }; + std::optional optValue = GetReferenceSource()->GetIndex().GetPropertyByManifestId(m_manifestId, property); + return LocIndString{ optValue ? optValue.value() :std::string{} }; } } diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_1/Interface.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_1/Interface.h index e21536ea13..2e8bc227fc 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_1/Interface.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_1/Interface.h @@ -30,5 +30,8 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_1 void PerformQuerySearch(V1_0::SearchResultsTable& resultsTable, const RequestMatch& query) const override; virtual SearchResult SearchInternal(const SQLite::Connection& connection, SearchRequest& request) const; virtual void PrepareForPackaging(SQLite::Connection& connection, bool vacuum); + + // Gets a property already knowing that the manifest id is valid. + virtual std::optional GetPropertyByManifestIdInternal(const SQLite::Connection& connection, SQLite::rowid_t manifestId, PackageVersionProperty property) const; }; } diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_1/Interface_1_1.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_1/Interface_1_1.cpp index 7254e20591..543db5001b 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_1/Interface_1_1.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_1/Interface_1_1.cpp @@ -292,4 +292,24 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_1 builder.Execute(connection); } } + + std::optional Interface::GetPropertyByManifestIdInternal(const SQLite::Connection& connection, SQLite::rowid_t manifestId, PackageVersionProperty property) const + { + switch (property) + { + case AppInstaller::Repository::PackageVersionProperty::Publisher: + { + // Publisher is not a primary data member in this version, but it may be stored in the metadata + if (ManifestMetadataTable::Exists(connection)) + { + return ManifestMetadataTable::GetMetadataByManifestIdAndMetadata(connection, manifestId, PackageVersionMetadata::Publisher); + } + + // No metadata, so no publisher + return {}; + } + default: + return V1_0::Interface::GetPropertyByManifestIdInternal(connection, manifestId, property); + } + } } diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_1/ManifestMetadataTable.cpp b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_1/ManifestMetadataTable.cpp index a11ae0f612..8f57310ab9 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_1/ManifestMetadataTable.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_1/ManifestMetadataTable.cpp @@ -68,6 +68,25 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_1 return result; } + std::optional ManifestMetadataTable::GetMetadataByManifestIdAndMetadata(const SQLite::Connection& connection, SQLite::rowid_t manifestId, PackageVersionMetadata metadata) + { + using namespace Builder; + + StatementBuilder builder; + builder.Select(s_ManifestMetadataTable_Value_Column).From(s_ManifestMetadataTable_Table_Name). + Where(s_ManifestMetadataTable_Manifest_Column).Equals(manifestId). + And(s_ManifestMetadataTable_Metadata_Column).Equals(metadata); + + Statement statement = builder.Prepare(connection); + + if (statement.Step()) + { + return statement.GetColumn(0); + } + + return {}; + } + void ManifestMetadataTable::SetMetadataByManifestId(SQLite::Connection& connection, SQLite::rowid_t manifestId, PackageVersionMetadata metadata, std::string_view value) { using namespace Builder; diff --git a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_1/ManifestMetadataTable.h b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_1/ManifestMetadataTable.h index eea2ff1b72..37f14df3ac 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/Schema/1_1/ManifestMetadataTable.h +++ b/src/AppInstallerRepositoryCore/Microsoft/Schema/1_1/ManifestMetadataTable.h @@ -26,6 +26,10 @@ namespace AppInstaller::Repository::Microsoft::Schema::V1_1 // The table must exist. static ISQLiteIndex::MetadataResult GetMetadataByManifestId(const SQLite::Connection& connection, SQLite::rowid_t manifestId); + // Gets the specific metadata value for the manifest, if it exists. + // The table must exist. + static std::optional GetMetadataByManifestIdAndMetadata(const SQLite::Connection& connection, SQLite::rowid_t manifestId, PackageVersionMetadata metadata); + // Sets the metadata value for the given manifest. // The table must exist. static void SetMetadataByManifestId(SQLite::Connection& connection, SQLite::rowid_t manifestId, PackageVersionMetadata metadata, std::string_view value); diff --git a/src/AppInstallerRepositoryCore/Public/winget/RepositorySearch.h b/src/AppInstallerRepositoryCore/Public/winget/RepositorySearch.h index df294ba8df..996ded1efa 100644 --- a/src/AppInstallerRepositoryCore/Public/winget/RepositorySearch.h +++ b/src/AppInstallerRepositoryCore/Public/winget/RepositorySearch.h @@ -134,6 +134,7 @@ namespace AppInstaller::Repository RelativePath, // Returned in hexadecimal format ManifestSHA256Hash, + Publisher, }; // A property of a package version that can have multiple values. diff --git a/src/AppInstallerRepositoryCore/Rest/RestSource.cpp b/src/AppInstallerRepositoryCore/Rest/RestSource.cpp index 9dd08c949f..e002b89b2d 100644 --- a/src/AppInstallerRepositoryCore/Rest/RestSource.cpp +++ b/src/AppInstallerRepositoryCore/Rest/RestSource.cpp @@ -201,6 +201,8 @@ namespace AppInstaller::Repository::Rest return Utility::LocIndString{ m_versionInfo.VersionAndChannel.GetVersion().ToString() }; case PackageVersionProperty::Channel: return Utility::LocIndString{ m_versionInfo.VersionAndChannel.GetChannel().ToString() }; + case PackageVersionProperty::Publisher: + return Utility::LocIndString{ m_package->PackageInfo().Publisher }; default: return Utility::LocIndString{}; } diff --git a/src/Microsoft.Management.Deployment/PackageManager.idl b/src/Microsoft.Management.Deployment/PackageManager.idl index f39b28accd..3a1fb3c54a 100644 --- a/src/Microsoft.Management.Deployment/PackageManager.idl +++ b/src/Microsoft.Management.Deployment/PackageManager.idl @@ -270,6 +270,8 @@ namespace Microsoft.Management.Deployment { /// Checks if this package version has at least one applicable installer. Boolean HasApplicableInstaller { get; }; + + String Publisher { get; }; } /// DESIGN NOTE: diff --git a/src/Microsoft.Management.Deployment/PackageVersionInfo.cpp b/src/Microsoft.Management.Deployment/PackageVersionInfo.cpp index a2e4812cc2..3debdf0ff8 100644 --- a/src/Microsoft.Management.Deployment/PackageVersionInfo.cpp +++ b/src/Microsoft.Management.Deployment/PackageVersionInfo.cpp @@ -43,6 +43,10 @@ namespace winrt::Microsoft::Management::Deployment::implementation { return winrt::to_hstring(m_packageVersion->GetProperty(::AppInstaller::Repository::PackageVersionProperty::Name).get()); } + hstring PackageVersionInfo::Publisher() + { + return winrt::to_hstring(m_packageVersion->GetProperty(::AppInstaller::Repository::PackageVersionProperty::Publisher).get()); + } hstring PackageVersionInfo::Version() { return winrt::to_hstring(m_packageVersion->GetProperty(::AppInstaller::Repository::PackageVersionProperty::Version).get()); diff --git a/src/Microsoft.Management.Deployment/PackageVersionInfo.h b/src/Microsoft.Management.Deployment/PackageVersionInfo.h index 8d7c5e189f..cf92c5715f 100644 --- a/src/Microsoft.Management.Deployment/PackageVersionInfo.h +++ b/src/Microsoft.Management.Deployment/PackageVersionInfo.h @@ -17,6 +17,7 @@ namespace winrt::Microsoft::Management::Deployment::implementation hstring GetMetadata(winrt::Microsoft::Management::Deployment::PackageVersionMetadataField const& metadataField); hstring Id(); hstring DisplayName(); + hstring Publisher(); hstring Version(); hstring Channel(); winrt::Windows::Foundation::Collections::IVectorView PackageFamilyNames(); diff --git a/tools/CorrelationTestbed/InSandboxScript.ps1 b/tools/CorrelationTestbed/InSandboxScript.ps1 index 2c862b6753..6e425b3d50 100644 --- a/tools/CorrelationTestbed/InSandboxScript.ps1 +++ b/tools/CorrelationTestbed/InSandboxScript.ps1 @@ -3,7 +3,8 @@ Param( [String[]] $DesktopAppInstallerDependencyPath, [String] $PackageIdentifier, [String] $SourceName, - [String] $OutputPath + [String] $OutputPath, + [Switch] $UseDev ) function Get-ARPTable { @@ -13,13 +14,54 @@ function Get-ARPTable { Where-Object {$null -ne $_.DisplayName } } +$ProgressPreference = 'SilentlyContinue' + +$desktopPath = "C:\Users\WDAGUtilityAccount\Desktop" + +$regFilesDirPath = Join-Path $desktopPath "RegFiles" + +if (Test-Path $regFilesDirPath) +{ + foreach ($regFile in (Get-ChildItem $regFilesDirPath)) + { + + Write-Host @" +--> Importing reg file $($regFile.FullName) +"@ + reg import $($regFile.FullName) + } +} + Write-Host @" --> Installing WinGet "@ -$ProgressPreference = 'SilentlyContinue' -Add-AppxPackage -Path $DesktopAppInstallerPath -DependencyPath $DesktopAppInstallerDependencyPath +if ($UseDev) +{ + foreach($dependency in $DesktopAppInstallerDependencyPath) + { + Write-Host @" + ----> Installing $dependency +"@ + Add-AppxPackage -Path $dependency + } + + Write-Host @" + ----> Enabling dev mode +"@ + reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock" /t REG_DWORD /f /v "AllowDevelopmentWithoutDevLicense" /d "1" + + $devPackageManifestPath = Join-Path $desktopPath "DevPackage\AppxManifest.xml" + Write-Host @" + ----> Installing $devPackageManifestPath +"@ + Add-AppxPackage -Path $devPackageManifestPath -Register +} +else +{ + Add-AppxPackage -Path $DesktopAppInstallerPath -DependencyPath $DesktopAppInstallerDependencyPath +} $originalARP = Get-ARPTable @@ -29,13 +71,41 @@ Write-Host @" "@ -winget install --id "$PackageIdentifier" -s "$SourceName" --verbose-logs --accept-source-agreements --accept-package-agreements +$installAndCorrelateOutPath = Join-Path $OutputPath "install_and_correlate.json" + +$installAndCorrelationExpression = Join-Path $desktopPath "InstallAndCheckCorrelation\InstallAndCheckCorrelation.exe" +$installAndCorrelationExpression = -join($installAndCorrelationExpression, ' -id "', $PackageIdentifier, '" -src "', $SourceName, '" -out "', $installAndCorrelateOutPath, '"') + +if ($UseDev) +{ + $installAndCorrelationExpression = -join($installAndCorrelationExpression, ' -dev') +} + +Invoke-Expression $installAndCorrelationExpression + +Write-Host @" + +--> Copying logs +"@ + +if ($UseDev) +{ + Copy-Item -Recurse (Join-Path $env:LOCALAPPDATA "Packages\WinGetDevCLI_8wekyb3d8bbwe\LocalState\DiagOutputDir") $OutputPath +} +else +{ + Copy-Item -Recurse (Join-Path $env:LOCALAPPDATA "Packages\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe\LocalState\DiagOutputDir") $OutputPath +} + Write-Host @" --> Comparing ARP Entries "@ -(Compare-Object (Get-ARPTable) $originalARP -Property DisplayName,DisplayVersion,Publisher,ProductCode)| Select-Object -Property * -ExcludeProperty SideIndicator | Format-Table +$arpCompared = (Compare-Object (Get-ARPTable) $originalARP -Property DisplayName,DisplayVersion,Publisher,ProductCode) +$arpCompared | Select-Object -Property * -ExcludeProperty SideIndicator | Format-Table + +$arpCompared | Select-Object -Property * -ExcludeProperty SideIndicator | Format-Table | Out-File (Join-Path $OutputPath "ARPCompare.txt") -"Test output" | Out-File (Join-Path $OutputPath "test-out.txt") +"Done" | Out-File (Join-Path $OutputPath "done.txt") diff --git a/tools/CorrelationTestbed/InstallAndCheckCorrelation/InstallAndCheckCorrelation.sln b/tools/CorrelationTestbed/InstallAndCheckCorrelation/InstallAndCheckCorrelation.sln new file mode 100644 index 0000000000..03abce3ce3 --- /dev/null +++ b/tools/CorrelationTestbed/InstallAndCheckCorrelation/InstallAndCheckCorrelation.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31911.196 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "InstallAndCheckCorrelation", "InstallAndCheckCorrelation\InstallAndCheckCorrelation.vcxproj", "{204CD25F-AAEA-4CA1-AB9F-A26747976932}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {204CD25F-AAEA-4CA1-AB9F-A26747976932}.Debug|x64.ActiveCfg = Debug|x64 + {204CD25F-AAEA-4CA1-AB9F-A26747976932}.Debug|x64.Build.0 = Debug|x64 + {204CD25F-AAEA-4CA1-AB9F-A26747976932}.Debug|x86.ActiveCfg = Debug|Win32 + {204CD25F-AAEA-4CA1-AB9F-A26747976932}.Debug|x86.Build.0 = Debug|Win32 + {204CD25F-AAEA-4CA1-AB9F-A26747976932}.Release|x64.ActiveCfg = Release|x64 + {204CD25F-AAEA-4CA1-AB9F-A26747976932}.Release|x64.Build.0 = Release|x64 + {204CD25F-AAEA-4CA1-AB9F-A26747976932}.Release|x86.ActiveCfg = Release|Win32 + {204CD25F-AAEA-4CA1-AB9F-A26747976932}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {77ED5C57-8A8C-4D87-9818-ABFB99B8E9D2} + EndGlobalSection +EndGlobal diff --git a/tools/CorrelationTestbed/InstallAndCheckCorrelation/InstallAndCheckCorrelation/InstallAndCheckCorrelation.cpp b/tools/CorrelationTestbed/InstallAndCheckCorrelation/InstallAndCheckCorrelation/InstallAndCheckCorrelation.cpp new file mode 100644 index 0000000000..f8f047dbb2 --- /dev/null +++ b/tools/CorrelationTestbed/InstallAndCheckCorrelation/InstallAndCheckCorrelation/InstallAndCheckCorrelation.cpp @@ -0,0 +1,552 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include + +using namespace std::string_view_literals; +using namespace winrt::Microsoft::Management::Deployment; + +template +struct JSONPair +{ + JSONPair(std::string_view name, const T& value, bool comma = true) : + Name(name), Value(value), Comma(comma) + {} + + std::string_view Name; + const T& Value; + bool Comma; +}; + +template +struct JSONQuote +{ + constexpr static bool value = true; +}; + +template <> +struct JSONQuote +{ + constexpr static bool value = false; +}; + +template <> +struct JSONQuote +{ + constexpr static bool value = false; +}; + +template +std::ostream& operator<<(std::ostream& out, const JSONPair& pair) +{ + out << '"' << pair.Name << "\": "; + if (JSONQuote::value) + { + out << '"'; + } + out << pair.Value; + if (JSONQuote::value) + { + out << '"'; + } + if (pair.Comma) + { + out << ','; + } + return out << std::endl; +} + +std::string ConvertToUTF8(std::wstring_view input) +{ + if (input.empty()) + { + return {}; + } + + int utf8ByteCount = WideCharToMultiByte(CP_UTF8, 0, input.data(), wil::safe_cast(input.length()), nullptr, 0, nullptr, nullptr); + THROW_LAST_ERROR_IF(utf8ByteCount == 0); + + // Since the string view should not contain the null char, the result won't either. + // This allows us to use the resulting size value directly in the string constructor. + std::string result(wil::safe_cast(utf8ByteCount), '\0'); + + int utf8BytesWritten = WideCharToMultiByte(CP_UTF8, 0, input.data(), wil::safe_cast(input.length()), &result[0], wil::safe_cast(result.size()), nullptr, nullptr); + FAIL_FAST_HR_IF(E_UNEXPECTED, utf8ByteCount != utf8BytesWritten); + + return result; +} + +std::wstring ConvertToUTF16(std::string_view input, UINT codePage = CP_UTF8) +{ + if (input.empty()) + { + return {}; + } + + int utf16CharCount = MultiByteToWideChar(codePage, 0, input.data(), wil::safe_cast(input.length()), nullptr, 0); + THROW_LAST_ERROR_IF(utf16CharCount == 0); + + // Since the string view should not contain the null char, the result won't either. + // This allows us to use the resulting size value directly in the string constructor. + std::wstring result(wil::safe_cast(utf16CharCount), L'\0'); + + int utf16CharsWritten = MultiByteToWideChar(codePage, 0, input.data(), wil::safe_cast(input.length()), &result[0], wil::safe_cast(result.size())); + FAIL_FAST_HR_IF(E_UNEXPECTED, utf16CharCount != utf16CharsWritten); + + return result; +} + +// CLSIDs for WinGet package +const CLSID CLSID_PackageManager = { 0xC53A4F16, 0x787E, 0x42A4, 0xB3, 0x04, 0x29, 0xEF, 0xFB, 0x4B, 0xF5, 0x97 }; //C53A4F16-787E-42A4-B304-29EFFB4BF597 +const CLSID CLSID_InstallOptions = { 0x1095f097, 0xEB96, 0x453B, 0xB4, 0xE6, 0x16, 0x13, 0x63, 0x7F, 0x3B, 0x14 }; //1095F097-EB96-453B-B4E6-1613637F3B14 +const CLSID CLSID_FindPackagesOptions = { 0x572DED96, 0x9C60, 0x4526, { 0x8F, 0x92, 0xEE, 0x7D, 0x91, 0xD3, 0x8C, 0x1A } }; //572DED96-9C60-4526-8F92-EE7D91D38C1A +const CLSID CLSID_PackageMatchFilter = { 0xD02C9DAF, 0x99DC, 0x429C, { 0xB5, 0x03, 0x4E, 0x50, 0x4E, 0x4A, 0xB0, 0x00 } }; //D02C9DAF-99DC-429C-B503-4E504E4AB000 +const CLSID CLSID_CreateCompositePackageCatalogOptions = { 0x526534B8, 0x7E46, 0x47C8, { 0x84, 0x16, 0xB1, 0x68, 0x5C, 0x32, 0x7D, 0x37 } }; //526534B8-7E46-47C8-8416-B1685C327D37 + +// CLSIDs for WinGetDev package +const CLSID CLSID_PackageManager2 = { 0x74CB3139, 0xB7C5, 0x4B9E, { 0x93, 0x88, 0xE6, 0x61, 0x6D, 0xEA, 0x28, 0x8C } }; //74CB3139-B7C5-4B9E-9388-E6616DEA288C +const CLSID CLSID_InstallOptions2 = { 0x44FE0580, 0x62F7, 0x44D4, 0x9E, 0x91, 0xAA, 0x96, 0x14, 0xAB, 0x3E, 0x86 }; //44FE0580-62F7-44D4-9E91-AA9614AB3E86 +const CLSID CLSID_FindPackagesOptions2 = { 0x1BD8FF3A, 0xEC50, 0x4F69, { 0xAE, 0xEE, 0xDF, 0x4C, 0x9D, 0x3B, 0xAA, 0x96 } }; //1BD8FF3A-EC50-4F69-AEEE-DF4C9D3BAA96 +const CLSID CLSID_PackageMatchFilter2 = { 0x3F85B9F4, 0x487A, 0x4C48, { 0x90, 0x35, 0x29, 0x03, 0xF8, 0xA6, 0xD9, 0xE8 } }; //3F85B9F4-487A-4C48-9035-2903F8A6D9E8 +const CLSID CLSID_CreateCompositePackageCatalogOptions2 = { 0xEE160901, 0xB317, 0x4EA7, { 0x9C, 0xC6, 0x53, 0x55, 0xC6, 0xD7, 0xD8, 0xA7 } }; //EE160901-B317-4EA7-9CC6-5355C6D7D8A7 + +PackageManager CreatePackageManager(bool useDev) +{ + if (useDev) + { + return winrt::create_instance(CLSID_PackageManager2, CLSCTX_ALL); + } + return winrt::create_instance(CLSID_PackageManager, CLSCTX_ALL); +} + +InstallOptions CreateInstallOptions(bool useDev) +{ + if (useDev) + { + return winrt::create_instance(CLSID_InstallOptions2, CLSCTX_ALL); + } + return winrt::create_instance(CLSID_InstallOptions, CLSCTX_ALL); +} + +FindPackagesOptions CreateFindPackagesOptions(bool useDev) +{ + if (useDev) + { + return winrt::create_instance(CLSID_FindPackagesOptions2, CLSCTX_ALL); + } + return winrt::create_instance(CLSID_FindPackagesOptions, CLSCTX_ALL); +} + +CreateCompositePackageCatalogOptions CreateCreateCompositePackageCatalogOptions(bool useDev) +{ + if (useDev) + { + return winrt::create_instance(CLSID_CreateCompositePackageCatalogOptions2, CLSCTX_ALL); + } + return winrt::create_instance(CLSID_CreateCompositePackageCatalogOptions, CLSCTX_ALL); +} + +PackageMatchFilter CreatePackageMatchFilter(bool useDev) +{ + if (useDev) + { + return winrt::create_instance(CLSID_PackageMatchFilter2, CLSCTX_ALL); + } + return winrt::create_instance(CLSID_PackageMatchFilter, CLSCTX_ALL); +} + +int main(int argc, char** argv) try +{ + // Supports the following arguments: + // -id : [Required] The PackageIdentifier to install and check for correlation + std::string packageIdentifier; + + // -src : [Required] The source name for the package to install + std::string sourceName; + + // -out : [Required] The file to write results to + std::filesystem::path outputPath; + + // -dev : [Optional] Use the dev CLSIDs + bool useDevCLSIDs = false; + + for (int i = 1; i < argc; ++i) + { + if ("-id"sv == argv[i] && i + 1 < argc) + { + packageIdentifier = argv[++i]; + } + else if ("-src"sv == argv[i] && i + 1 < argc) + { + sourceName = argv[++i]; + } + else if ("-out"sv == argv[i] && i + 1 < argc) + { + outputPath = argv[++i]; + } + else if ("-dev"sv == argv[i]) + { + useDevCLSIDs = true; + } + } + + // Check inputs + if (outputPath.empty()) + { + std::cout << "No output file path specified, use -out" << std::endl; + return 2; + } + + if (!outputPath.has_stem()) + { + std::cout << "Output path is not a file" << std::endl; + return 3; + } + + std::filesystem::create_directories(outputPath.parent_path()); + + std::ofstream outputStream{ outputPath }; + + if (!outputStream) + { + std::cout << "Output file could not be created" << std::endl; + return 4; + } + + auto co_uninit = wil::CoInitializeEx(); + + // Result file outputs + HRESULT hr = S_OK; + std::string error; + std::string phase; + std::string action; + std::string packageName; + std::string packagePublisher; + bool correlatePackageKnown = false; + std::string packageKnownName; + std::string packageKnownPublisher; + bool correlateArchive = false; + std::string archiveName; + std::string archivePublisher; + + if (packageIdentifier.empty()) + { + hr = E_INVALIDARG; + error = "A package identifier must be supplied, use -id"; + goto output_result; + } + + if (sourceName.empty()) + { + hr = E_INVALIDARG; + error = "A source name must be supplied, use -src"; + goto output_result; + } + + // Execute the install step + phase = "Install"; + std::cout << "Connecting to PackageManager..." << std::endl; + try + { do { + action = "Create package manager"; + auto packageManager = CreatePackageManager(useDevCLSIDs); + + action = "Get source reference"; + auto sourceRef = packageManager.GetPackageCatalogByName(ConvertToUTF16(sourceName)); + + action = "Connecting to catalog"; + auto connectResult = sourceRef.Connect(); + + if (connectResult.Status() != ConnectResultStatus::Ok) + { + hr = E_FAIL; + error = "Error connecting to catalog"; + break; + } + + auto catalog = connectResult.PackageCatalog(); + + action = "Create find options"; + auto findOptions = CreateFindPackagesOptions(useDevCLSIDs); + + action = "Add package id filter"; + auto filter = CreatePackageMatchFilter(useDevCLSIDs); + filter.Field(PackageMatchField::Id); + filter.Option(PackageFieldMatchOption::Equals); + filter.Value(ConvertToUTF16(packageIdentifier)); + findOptions.Filters().Append(filter); + + action = "Find package"; + auto findResult = catalog.FindPackages(findOptions); + + if (findResult.Status() != FindPackagesResultStatus::Ok) + { + hr = E_FAIL; + error = "Error finding packages"; + break; + } + + action = "Get match"; + auto matches = findResult.Matches(); + + if (matches.Size() == 0) + { + hr = E_NOT_SET; + error = "Package not found"; + break; + } + + auto package = matches.GetAt(0).CatalogPackage(); + + action = "Inspect package"; + auto installVersion = package.DefaultInstallVersion(); + packageName = ConvertToUTF8(installVersion.DisplayName()); + if (useDevCLSIDs) + { + // Publisher is not yet available on the release version; make this unconditional when it is + packagePublisher = ConvertToUTF8(installVersion.Publisher()); + } + + action = "Create install options"; + auto installOptions = CreateInstallOptions(useDevCLSIDs); + + installOptions.PackageInstallScope(PackageInstallScope::Any); + installOptions.PackageInstallMode(PackageInstallMode::Silent); + + std::cout << "Beginning to install " << packageIdentifier << " (" << packageName << ") from " << sourceName << "..." << std::endl; + auto installResult = packageManager.InstallPackageAsync(package, installOptions).get(); + + if (installResult.Status() != InstallResultStatus::Ok) + { + hr = installResult.ExtendedErrorCode(); + error = "Error installing package"; + break; + } + + } while(0); } + catch (const winrt::hresult_error& hre) + { + hr = hre.code(); + error = ConvertToUTF8(hre.message()); + } + + if (FAILED(hr)) + { + goto output_result; + } + + // Check for the installed package being correlated when the remote package is known, + // as when trying to determine information about a single known package. + phase = "Correlate when package known"; + std::cout << "Correlating package when known..." << std::endl; + try + { do { + action = "Create package manager"; + auto packageManager = CreatePackageManager(useDevCLSIDs); + + action = "Get source reference"; + auto sourceRef = packageManager.GetPackageCatalogByName(ConvertToUTF16(sourceName)); + + action = "Create composite catalog options"; + auto compOptions = CreateCreateCompositePackageCatalogOptions(useDevCLSIDs); + + compOptions.Catalogs().Append(sourceRef); + compOptions.CompositeSearchBehavior(CompositeSearchBehavior::RemotePackagesFromAllCatalogs); + + action = "Create composite catalog reference"; + auto compRef = packageManager.CreateCompositePackageCatalog(compOptions); + + action = "Connecting to catalog"; + auto connectResult = compRef.Connect(); + + if (connectResult.Status() != ConnectResultStatus::Ok) + { + hr = E_FAIL; + error = "Error connecting to catalog"; + break; + } + + auto catalog = connectResult.PackageCatalog(); + + action = "Create find options"; + auto findOptions = CreateFindPackagesOptions(useDevCLSIDs); + + action = "Add package id filter"; + auto filter = CreatePackageMatchFilter(useDevCLSIDs); + filter.Field(PackageMatchField::Id); + filter.Option(PackageFieldMatchOption::Equals); + filter.Value(ConvertToUTF16(packageIdentifier)); + findOptions.Filters().Append(filter); + + action = "Find package"; + auto findResult = catalog.FindPackages(findOptions); + + if (findResult.Status() != FindPackagesResultStatus::Ok) + { + hr = E_FAIL; + error = "Error finding packages"; + break; + } + + action = "Get match"; + auto matches = findResult.Matches(); + + if (matches.Size() == 0) + { + hr = E_NOT_SET; + error = "Package not found"; + break; + } + + auto package = matches.GetAt(0).CatalogPackage(); + + action = "Inspect package for installed version"; + auto installed = package.InstalledVersion(); + + if (installed) + { + correlatePackageKnown = true; + packageKnownName = ConvertToUTF8(installed.DisplayName()); + if (useDevCLSIDs) + { + // Publisher is not yet available on the release version; make this unconditional when it is + packageKnownPublisher = ConvertToUTF8(installed.Publisher()); + } + } + + } while(0); } + catch (const winrt::hresult_error& hre) + { + hr = hre.code(); + error = ConvertToUTF8(hre.message()); + } + + // Check for the installed package being correlated when archiving local package information. + phase = "Correlate when archiving"; + std::cout << "Correlating package when archiving..." << std::endl; + try + { do { + action = "Create package manager"; + auto packageManager = CreatePackageManager(useDevCLSIDs); + + action = "Get source reference"; + auto sourceRef = packageManager.GetPackageCatalogByName(ConvertToUTF16(sourceName)); + + action = "Create composite catalog options"; + auto compOptions = CreateCreateCompositePackageCatalogOptions(useDevCLSIDs); + + compOptions.Catalogs().Append(sourceRef); + compOptions.CompositeSearchBehavior(CompositeSearchBehavior::LocalCatalogs); + + action = "Create composite catalog reference"; + auto compRef = packageManager.CreateCompositePackageCatalog(compOptions); + + action = "Connecting to catalog"; + auto connectResult = compRef.Connect(); + + if (connectResult.Status() != ConnectResultStatus::Ok) + { + hr = E_FAIL; + error = "Error connecting to catalog"; + break; + } + + auto catalog = connectResult.PackageCatalog(); + + action = "Create find options"; + auto findOptions = CreateFindPackagesOptions(useDevCLSIDs); + + action = "Find package"; + auto findResult = catalog.FindPackages(findOptions); + + if (findResult.Status() != FindPackagesResultStatus::Ok) + { + hr = E_FAIL; + error = "Error finding packages"; + break; + } + + action = "Get matches"; + auto matches = findResult.Matches(); + + action = "Get source info"; + auto sourceInfo = sourceRef.Info(); + auto sourceIdentifier = sourceInfo.Id(); + auto sourceType = sourceInfo.Type(); + + for (const auto& match : matches) + { + auto package = match.CatalogPackage(); + auto installed = package.InstalledVersion(); + + if (installed) + { + auto installedCatalogInfo = installed.PackageCatalog().Info(); + + if (installedCatalogInfo.Id() == sourceIdentifier && installedCatalogInfo.Type() == sourceType) + { + correlateArchive = true; + archiveName = ConvertToUTF8(installed.DisplayName()); + if (useDevCLSIDs) + { + // Publisher is not yet available on the release version; make this unconditional when it is + archivePublisher = ConvertToUTF8(installed.Publisher()); + } + break; + } + } + } + + } while(0); } + catch (const winrt::hresult_error& hre) + { + hr = hre.code(); + error = ConvertToUTF8(hre.message()); + } + + std::cout << "Done" << std::endl; + phase = "Completed"; + action.clear(); + +output_result: + outputStream << "{" << std::endl; + outputStream << JSONPair{ "PackageIdentifier", packageIdentifier }; + outputStream << JSONPair{ "Source", sourceName }; + outputStream << JSONPair{ "UseDev", useDevCLSIDs }; + outputStream << JSONPair{ "Error", error }; + outputStream << JSONPair{ "Phase", phase }; + outputStream << JSONPair{ "Action", action }; + outputStream << JSONPair{ "PackageName", packageName }; + outputStream << JSONPair{ "PackagePublisher", packagePublisher }; + outputStream << JSONPair{ "CorrelatePackageKnown", correlatePackageKnown }; + outputStream << JSONPair{ "PackageKnownName", packageKnownName }; + outputStream << JSONPair{ "PackageKnownPublisher", packageKnownPublisher }; + outputStream << JSONPair{ "CorrelateArchive", correlateArchive }; + outputStream << JSONPair{ "ArchiveName", archiveName }; + outputStream << JSONPair{ "ArchivePublisher", archivePublisher }; + // Keep at the end to prevent a dangling comma + outputStream << JSONPair{ "HRESULT", hr, false } << "}" << std::endl; + + return hr; +} +catch (const std::exception& e) +{ + std::cout << "Exception occurred: " << e.what() << std::endl; + return 1; +} +catch (...) +{ + std::cout << "Unknown exception occurred" << std::endl; + return 1; +} diff --git a/tools/CorrelationTestbed/InstallAndCheckCorrelation/InstallAndCheckCorrelation/InstallAndCheckCorrelation.vcxproj b/tools/CorrelationTestbed/InstallAndCheckCorrelation/InstallAndCheckCorrelation/InstallAndCheckCorrelation.vcxproj new file mode 100644 index 0000000000..82c1611dd9 --- /dev/null +++ b/tools/CorrelationTestbed/InstallAndCheckCorrelation/InstallAndCheckCorrelation/InstallAndCheckCorrelation.vcxproj @@ -0,0 +1,181 @@ + + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {204cd25f-aaea-4ca1-ab9f-a26747976932} + InstallAndCheckCorrelation + 10.0 + + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + false + + + true + + + false + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp17 + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp17 + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp17 + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp17 + MultiThreaded + + + Console + true + true + true + + + + + + + + true + Document + true + true + true + + + + + + + + Microsoft.Management.Deployment.winmd + true + + + + + + + + + + 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}. + + + + + + \ No newline at end of file diff --git a/tools/CorrelationTestbed/InstallAndCheckCorrelation/InstallAndCheckCorrelation/InstallAndCheckCorrelation.vcxproj.filters b/tools/CorrelationTestbed/InstallAndCheckCorrelation/InstallAndCheckCorrelation/InstallAndCheckCorrelation.vcxproj.filters new file mode 100644 index 0000000000..1adefe7af1 --- /dev/null +++ b/tools/CorrelationTestbed/InstallAndCheckCorrelation/InstallAndCheckCorrelation/InstallAndCheckCorrelation.vcxproj.filters @@ -0,0 +1,28 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + + + + + + + \ No newline at end of file diff --git a/tools/CorrelationTestbed/InstallAndCheckCorrelation/InstallAndCheckCorrelation/Microsoft.Management.Deployment.winmd b/tools/CorrelationTestbed/InstallAndCheckCorrelation/InstallAndCheckCorrelation/Microsoft.Management.Deployment.winmd new file mode 100644 index 0000000000000000000000000000000000000000..e544fa114925dffc1d9ab4f87337e9dd2f424f4c GIT binary patch literal 20480 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 NafQb{zCXn0zW}_L$1DH< literal 0 HcmV?d00001 diff --git a/tools/CorrelationTestbed/InstallAndCheckCorrelation/InstallAndCheckCorrelation/packages.config b/tools/CorrelationTestbed/InstallAndCheckCorrelation/InstallAndCheckCorrelation/packages.config new file mode 100644 index 0000000000..8d19b2fdd6 --- /dev/null +++ b/tools/CorrelationTestbed/InstallAndCheckCorrelation/InstallAndCheckCorrelation/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/tools/CorrelationTestbed/Process-CorrelationResults.ps1 b/tools/CorrelationTestbed/Process-CorrelationResults.ps1 new file mode 100644 index 0000000000..087cd72cf3 --- /dev/null +++ b/tools/CorrelationTestbed/Process-CorrelationResults.ps1 @@ -0,0 +1,37 @@ +Param( + [Parameter(Position = 0, HelpMessage = "The root location of the results.")] + [String] $ResultsPath +) + +$resultFile = Join-Path $ResultsPath "results.csv" +$failedFile = Join-Path $ResultsPath "failed.csv" + +if (Test-Path $resultFile) +{ + Remove-Item $resultFile -Force +} + +if (Test-Path $failedFile) +{ + Remove-Item $failedFile -Force +} + +foreach ($result in (Get-ChildItem $ResultsPath -Directory)) +{ + $resultJSON = Join-Path $result "install_and_correlate.json" + if (-not (Test-Path $resultJSON)) + { + continue + } + + $resultObj = (Get-Content -Path $resultJSON -Encoding utf8 | ConvertFrom-Json) + + if ($resultObj.HRESULT -eq 0) + { + Export-Csv -InputObject ($resultObj | Select-Object -Property * -ExcludeProperty @("Error", "Phase", "Action", "HRESULT") ) -Path $resultFile -Append + } + else + { + Export-Csv -InputObject $resultObj -Path $failedFile -Append + } +} diff --git a/tools/CorrelationTestbed/Test-CorrelationInSandbox.ps1 b/tools/CorrelationTestbed/Test-CorrelationInSandbox.ps1 index c1ed34925b..1713350900 100644 --- a/tools/CorrelationTestbed/Test-CorrelationInSandbox.ps1 +++ b/tools/CorrelationTestbed/Test-CorrelationInSandbox.ps1 @@ -5,11 +5,62 @@ Param( [Parameter(Position = 0, HelpMessage = "The package identifiers to test.")] [String[]] $PackageIdentifiers, [Parameter(Position = 1, HelpMessage = "The source name that the package identifiers are from.")] - [String] $Source + [String] $Source, + [Parameter(HelpMessage = "The directory where the correlation program is located.")] + [String] $ExePath, + [Parameter(HelpMessage = "Indicates that the local dev build should be used rather than the published package.")] + [Switch] $UseDev, + [Parameter(HelpMessage = "The directory where local dev build is located; only the release build works.")] + [String] $DevPackagePath, + [Parameter(HelpMessage = "The results output path.")] + [String] $ResultsPath, + [Parameter(HelpMessage = "The path to registry files that should be injected before the test.")] + [String] $RegFileDirectory ) $ErrorActionPreference = "Stop" +# Validate that the ExePath points to a reasonable location + +if (-not $ExePath) +{ + $ExePath = Join-Path $PSScriptRoot "InstallAndCheckCorrelation\x64\Release" +} + +if (-not (Test-Path (Join-Path $ExePath "InstallAndCheckCorrelation.exe"))) +{ + Write-Error -Category InvalidArgument -Message @" +InstallAndCheckCorrelation.exe does not exist in the path $ExePath +Either build it, or provide the location using -ExePath +"@ +} + +# Validate that the local dev manifest exists + +if ($UseDev) +{ + if (-not $DevPackagePath) + { + $DevPackagePath = Join-Path $PSScriptRoot "..\..\src\AppInstallerCLIPackage\bin\x64\Release\AppX" + } + + if ($DevPackagePath.ToLower().Contains("debug")) + { + Write-Error -Category InvalidArgument -Message @" +The Debug dev package does not work for unknown reasons. +Use the Release build or figure out how to make debug work and fix the scripts. +"@ + } + + if (-not (Test-Path (Join-Path $DevPackagePath "AppxManifest.xml"))) + { + Write-Error -Category InvalidArgument -Message @" +AppxManifest.xml does not exist in the path $DevPackagePath +Either build the local dev package, or provide the location using -DevPackagePath +"@ + } +} + # Check if Windows Sandbox is enabled if (-Not (Get-Command 'WindowsSandbox' -ErrorAction SilentlyContinue)) { @@ -22,6 +73,20 @@ $ Enable-WindowsOptionalFeature -Online -FeatureName 'Containers-DisposableClien '@ } +# Create output location for results + +if (-not $ResultsPath) +{ + $ResultsPath = Join-Path ([System.IO.Path]::GetTempPath()) (New-Guid) +} + +if (Test-Path $ResultsPath) +{ + Remove-Item -Recurse $ResultsPath -Force +} + +New-Item -ItemType Directory $ResultsPath > $nul + # Close Windows Sandbox function Close-WindowsSandbox { @@ -32,6 +97,8 @@ function Close-WindowsSandbox { $sandbox | Stop-Process $sandbox | Wait-Process -Timeout 30 + Start-Sleep 2 + Write-Host } Remove-Variable sandbox @@ -48,6 +115,8 @@ New-Item $tempFolder -ItemType Directory -ErrorAction SilentlyContinue | Out-Nul # Set dependencies +$desktopInSandbox = 'C:\Users\WDAGUtilityAccount\Desktop' + $apiLatestUrl = 'https://api.github.com/repos/microsoft/winget-cli/releases/latest' [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 @@ -89,6 +158,19 @@ $uiLibsUwp = @{ hash = "422FD24B231E87A842C4DAEABC6A335112E0D35B86FAC91F5CE7CF327E36A591" } +if ($UseDev) +{ + $devVCLibsFileName = "Microsoft.VCLibs.x64.14.00.Desktop.appx" + $devVCLibsPath = Join-Path ${env:ProgramFiles(x86)} "Microsoft SDKs\Windows Kits\10\ExtensionSDKs\Microsoft.VCLibs.Desktop\14.0\Appx\Retail\x64" + $devVCLibsPath = Join-Path $devVCLibsPath $devVCLibsFileName + + Copy-Item -Path $devVCLibsPath -Destination $tempFolder -Force + + $devVCLibsPathInSandbox = Join-Path -Path $desktopInSandbox -ChildPath (Join-Path -Path $tempFolderName -ChildPath $devVCLibsFileName) +} +else +{ + $dependencies = @($desktopAppInstaller, $vcLibsUwp, $uiLibsUwp) # Clean temp directory @@ -103,8 +185,6 @@ if (-Not [String]::IsNullOrWhiteSpace($Manifest)) { Write-Host '--> Checking dependencies' -$desktopInSandbox = 'C:\Users\WDAGUtilityAccount\Desktop' - foreach ($dependency in $dependencies) { $dependency.file = Join-Path -Path $tempFolder -ChildPath $dependency.fileName $dependency.pathInSandbox = Join-Path -Path $desktopInSandbox -ChildPath (Join-Path -Path $tempFolderName -ChildPath $dependency.fileName) @@ -139,6 +219,8 @@ $uiLibsUwp.file = (Join-Path -Path $tempFolder -ChildPath \Microsoft.UI.Xaml.2.7 $uiLibsUwp.pathInSandbox = Join-Path -Path $desktopInSandbox -ChildPath (Join-Path -Path $tempFolderName -ChildPath \Microsoft.UI.Xaml.2.7\tools\AppX\x64\Release\Microsoft.UI.Xaml.2.7.appx) Write-Host +} # !$UseDev + # Copy main script $mainPs1FileName = 'InSandboxScript.ps1' @@ -148,14 +230,24 @@ foreach ($packageIdentifier in $PackageIdentifiers) { # Create temporary location for output - $outPath = Join-Path ([System.IO.Path]::GetTempPath()) (New-Guid) + $outPath = Join-Path $ResultsPath $packageIdentifier New-Item -ItemType Directory $outPath > $nul $outPathInSandbox = Join-Path -Path $desktopInSandbox -ChildPath (Split-Path -Path $outPath -Leaf) - $bootstrapPs1Content = @" -.\$mainPs1FileName -DesktopAppInstallerPath '$($desktopAppInstaller.pathInSandbox)' -DesktopAppInstallerDependencyPath @('$($vcLibsUwp.pathInSandbox)', '$($uiLibsUwp.pathInSandbox)') -PackageIdentifier '$packageIdentifier' -SourceName '$Source'-OutputPath '$outPathInSandbox' + if ($UseDev) + { + $bootstrapPs1Content = @" +.\$mainPs1FileName -DesktopAppInstallerDependencyPath @('$devVCLibsPathInSandbox') -PackageIdentifier '$packageIdentifier' -SourceName '$Source' -OutputPath '$outPathInSandbox' -UseDev "@ + } + else + { + $bootstrapPs1Content = @" +.\$mainPs1FileName -DesktopAppInstallerPath '$($desktopAppInstaller.pathInSandbox)' -DesktopAppInstallerDependencyPath @('$($vcLibsUwp.pathInSandbox)', '$($uiLibsUwp.pathInSandbox)') -PackageIdentifier '$packageIdentifier' -SourceName '$Source' -OutputPath '$outPathInSandbox' +"@ + } + $bootstrapPs1FileName = 'Bootstrap.ps1' $bootstrapPs1Content | Out-File (Join-Path $tempFolder $bootstrapPs1FileName) -Force @@ -164,6 +256,34 @@ foreach ($packageIdentifier in $PackageIdentifiers) $bootstrapPs1InSandbox = Join-Path -Path $desktopInSandbox -ChildPath (Join-Path -Path $tempFolderName -ChildPath $bootstrapPs1FileName) $tempFolderInSandbox = Join-Path -Path $desktopInSandbox -ChildPath $tempFolderName + $exePathInSandbox = Join-Path -Path $desktopInSandbox -ChildPath "InstallAndCheckCorrelation" + + $devPackageInSandbox = Join-Path -Path $desktopInSandbox -ChildPath "DevPackage" + $devPackageXMLFragment = "" + + if ($UseDev) + { + $devPackageXMLFragment = @" + + $DevPackagePath + $devPackageInSandbox + +"@ + } + + $regFileDirInSandbox = Join-Path -Path $desktopInSandbox -ChildPath "RegFiles" + $regFileDirXMLFragment = "" + + if ($RegFileDirectory) + { + $regFileDirXMLFragment = @" + + $RegFileDirectory + $regFileDirInSandbox + true + +"@ + } $sandboxTestWsbContent = @" @@ -172,6 +292,13 @@ foreach ($packageIdentifier in $PackageIdentifiers) $tempFolder true + + $ExePath + $exePathInSandbox + true + + $devPackageXMLFragment + $regFileDirXMLFragment $outPath @@ -200,12 +327,16 @@ foreach ($packageIdentifier in $PackageIdentifiers) WindowsSandbox $SandboxTestWsbFile - $outputFileBlockerPath = Join-Path $outPath "test-out.txt" + $outputFileBlockerPath = Join-Path $outPath "done.txt" while (-not (Test-Path $outputFileBlockerPath)) { Start-Sleep 1 } - Close-WindowsSandbox + #Close-WindowsSandbox } + +Write-Host @" +--> Results are located at $ResultsPath +"@ \ No newline at end of file From 54403c4753cdfd36a2a5147fc3906ce72f24c391 Mon Sep 17 00:00:00 2001 From: JohnMcPMS Date: Fri, 8 Apr 2022 14:05:58 -0700 Subject: [PATCH 3/3] PR feedback --- .github/actions/spelling/allow.txt | 5 + .github/actions/spelling/excludes.txt | 2 + .github/actions/spelling/expect.txt | 2 + .../PackageManager.idl | 1 + .../InstallAndCheckCorrelation.cpp | 658 ++++++++++-------- tools/CorrelationTestbed/Readme.md | 42 ++ .../Test-CorrelationInSandbox.ps1 | 130 ++-- 7 files changed, 480 insertions(+), 360 deletions(-) create mode 100644 tools/CorrelationTestbed/Readme.md diff --git a/.github/actions/spelling/allow.txt b/.github/actions/spelling/allow.txt index 4e9ebad756..92f6af631d 100644 --- a/.github/actions/spelling/allow.txt +++ b/.github/actions/spelling/allow.txt @@ -89,6 +89,7 @@ createtables cref csproj CStr +csv CURSORPOSITON CUSTOMHEADER cwctype @@ -188,7 +189,9 @@ hfile HGLOBAL HIDECANCEL hinternet +HKCU HKEY +HKLM hmac HMODULE Homepage @@ -445,6 +448,7 @@ screenshots SCROLLER SCROLLVIEWER sdk +sdks seekg seinfo selectany @@ -538,6 +542,7 @@ TEXTFORMAT TEXTINCLUDE Threadpool Timeline +tls todo tokenizer tolower diff --git a/.github/actions/spelling/excludes.txt b/.github/actions/spelling/excludes.txt index 9be208676e..9eae271f13 100644 --- a/.github/actions/spelling/excludes.txt +++ b/.github/actions/spelling/excludes.txt @@ -37,4 +37,6 @@ ignore$ ^src/JsonCppLib/ ^src/Valijson/ ^src/YamlCppLib/ +# Because it doesn't handle argument -Words well +^tools/CorrelationTestbed/.*\.ps1$ ^\.github/ diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt index 47bae083be..302fb55b8d 100644 --- a/.github/actions/spelling/expect.txt +++ b/.github/actions/spelling/expect.txt @@ -407,6 +407,7 @@ vscode vstest vy wcslen +WDAG webpages Webserver website @@ -422,6 +423,7 @@ winreg withstarts wn Workflows +wsb wsl wsv wto diff --git a/src/Microsoft.Management.Deployment/PackageManager.idl b/src/Microsoft.Management.Deployment/PackageManager.idl index 3a1fb3c54a..e82c987d3e 100644 --- a/src/Microsoft.Management.Deployment/PackageManager.idl +++ b/src/Microsoft.Management.Deployment/PackageManager.idl @@ -271,6 +271,7 @@ namespace Microsoft.Management.Deployment /// Checks if this package version has at least one applicable installer. Boolean HasApplicableInstaller { get; }; + /// Gets the publisher string for this package version, if one is available. String Publisher { get; }; } diff --git a/tools/CorrelationTestbed/InstallAndCheckCorrelation/InstallAndCheckCorrelation/InstallAndCheckCorrelation.cpp b/tools/CorrelationTestbed/InstallAndCheckCorrelation/InstallAndCheckCorrelation/InstallAndCheckCorrelation.cpp index f8f047dbb2..a058c18ea8 100644 --- a/tools/CorrelationTestbed/InstallAndCheckCorrelation/InstallAndCheckCorrelation/InstallAndCheckCorrelation.cpp +++ b/tools/CorrelationTestbed/InstallAndCheckCorrelation/InstallAndCheckCorrelation/InstallAndCheckCorrelation.cpp @@ -121,110 +121,114 @@ const CLSID CLSID_FindPackagesOptions2 = { 0x1BD8FF3A, 0xEC50, 0x4F69, { 0xAE, 0 const CLSID CLSID_PackageMatchFilter2 = { 0x3F85B9F4, 0x487A, 0x4C48, { 0x90, 0x35, 0x29, 0x03, 0xF8, 0xA6, 0xD9, 0xE8 } }; //3F85B9F4-487A-4C48-9035-2903F8A6D9E8 const CLSID CLSID_CreateCompositePackageCatalogOptions2 = { 0xEE160901, 0xB317, 0x4EA7, { 0x9C, 0xC6, 0x53, 0x55, 0xC6, 0xD7, 0xD8, 0xA7 } }; //EE160901-B317-4EA7-9CC6-5355C6D7D8A7 -PackageManager CreatePackageManager(bool useDev) +// Helper object to make cleaner error handling +struct Main { - if (useDev) - { - return winrt::create_instance(CLSID_PackageManager2, CLSCTX_ALL); - } - return winrt::create_instance(CLSID_PackageManager, CLSCTX_ALL); -} - -InstallOptions CreateInstallOptions(bool useDev) -{ - if (useDev) - { - return winrt::create_instance(CLSID_InstallOptions2, CLSCTX_ALL); - } - return winrt::create_instance(CLSID_InstallOptions, CLSCTX_ALL); -} - -FindPackagesOptions CreateFindPackagesOptions(bool useDev) -{ - if (useDev) - { - return winrt::create_instance(CLSID_FindPackagesOptions2, CLSCTX_ALL); - } - return winrt::create_instance(CLSID_FindPackagesOptions, CLSCTX_ALL); -} - -CreateCompositePackageCatalogOptions CreateCreateCompositePackageCatalogOptions(bool useDev) -{ - if (useDev) - { - return winrt::create_instance(CLSID_CreateCompositePackageCatalogOptions2, CLSCTX_ALL); - } - return winrt::create_instance(CLSID_CreateCompositePackageCatalogOptions, CLSCTX_ALL); -} - -PackageMatchFilter CreatePackageMatchFilter(bool useDev) -{ - if (useDev) - { - return winrt::create_instance(CLSID_PackageMatchFilter2, CLSCTX_ALL); - } - return winrt::create_instance(CLSID_PackageMatchFilter, CLSCTX_ALL); -} - -int main(int argc, char** argv) try -{ - // Supports the following arguments: - // -id : [Required] The PackageIdentifier to install and check for correlation std::string packageIdentifier; - - // -src : [Required] The source name for the package to install std::string sourceName; - - // -out : [Required] The file to write results to std::filesystem::path outputPath; - - // -dev : [Optional] Use the dev CLSIDs bool useDevCLSIDs = false; - for (int i = 1; i < argc; ++i) + int ParseArgs(int argc, char** argv) { - if ("-id"sv == argv[i] && i + 1 < argc) + // Supports the following arguments: + // -id : [Required] The PackageIdentifier to install and check for correlation + // -src : [Required] The source name for the package to install + // -out : [Required] The file to write results to + // -dev : [Optional] Use the dev CLSIDs + + for (int i = 1; i < argc; ++i) { - packageIdentifier = argv[++i]; + if ("-id"sv == argv[i] && i + 1 < argc) + { + packageIdentifier = argv[++i]; + } + else if ("-src"sv == argv[i] && i + 1 < argc) + { + sourceName = argv[++i]; + } + else if ("-out"sv == argv[i] && i + 1 < argc) + { + outputPath = argv[++i]; + } + else if ("-dev"sv == argv[i]) + { + useDevCLSIDs = true; + } } - else if ("-src"sv == argv[i] && i + 1 < argc) + + // Check inputs + if (outputPath.empty()) { - sourceName = argv[++i]; + std::cout << "No output file path specified, use -out" << std::endl; + return 2; } - else if ("-out"sv == argv[i] && i + 1 < argc) + + if (!outputPath.has_stem()) { - outputPath = argv[++i]; + std::cout << "Output path is not a file" << std::endl; + return 3; } - else if ("-dev"sv == argv[i]) + + std::filesystem::create_directories(outputPath.parent_path()); + + outputStream.open(outputPath); + + if (!outputStream) { - useDevCLSIDs = true; + std::cout << "Output file could not be created" << std::endl; + return 4; } + + return 0; } - // Check inputs - if (outputPath.empty()) + PackageManager CreatePackageManager() { - std::cout << "No output file path specified, use -out" << std::endl; - return 2; + if (useDevCLSIDs) + { + return winrt::create_instance(CLSID_PackageManager2, CLSCTX_ALL); + } + return winrt::create_instance(CLSID_PackageManager, CLSCTX_ALL); } - if (!outputPath.has_stem()) + InstallOptions CreateInstallOptions() { - std::cout << "Output path is not a file" << std::endl; - return 3; + if (useDevCLSIDs) + { + return winrt::create_instance(CLSID_InstallOptions2, CLSCTX_ALL); + } + return winrt::create_instance(CLSID_InstallOptions, CLSCTX_ALL); } - std::filesystem::create_directories(outputPath.parent_path()); + FindPackagesOptions CreateFindPackagesOptions() + { + if (useDevCLSIDs) + { + return winrt::create_instance(CLSID_FindPackagesOptions2, CLSCTX_ALL); + } + return winrt::create_instance(CLSID_FindPackagesOptions, CLSCTX_ALL); + } - std::ofstream outputStream{ outputPath }; + CreateCompositePackageCatalogOptions CreateCreateCompositePackageCatalogOptions() + { + if (useDevCLSIDs) + { + return winrt::create_instance(CLSID_CreateCompositePackageCatalogOptions2, CLSCTX_ALL); + } + return winrt::create_instance(CLSID_CreateCompositePackageCatalogOptions, CLSCTX_ALL); + } - if (!outputStream) + PackageMatchFilter CreatePackageMatchFilter() { - std::cout << "Output file could not be created" << std::endl; - return 4; + if (useDevCLSIDs) + { + return winrt::create_instance(CLSID_PackageMatchFilter2, CLSCTX_ALL); + } + return winrt::create_instance(CLSID_PackageMatchFilter, CLSCTX_ALL); } - auto co_uninit = wil::CoInitializeEx(); + std::ofstream outputStream; // Result file outputs HRESULT hr = S_OK; @@ -240,305 +244,353 @@ int main(int argc, char** argv) try std::string archiveName; std::string archivePublisher; - if (packageIdentifier.empty()) + void ValidateArgs() { - hr = E_INVALIDARG; - error = "A package identifier must be supplied, use -id"; - goto output_result; - } + if (packageIdentifier.empty()) + { + hr = E_INVALIDARG; + error = "A package identifier must be supplied, use -id"; + return; + } - if (sourceName.empty()) - { - hr = E_INVALIDARG; - error = "A source name must be supplied, use -src"; - goto output_result; + if (sourceName.empty()) + { + hr = E_INVALIDARG; + error = "A source name must be supplied, use -src"; + return; + } } - // Execute the install step - phase = "Install"; - std::cout << "Connecting to PackageManager..." << std::endl; - try - { do { - action = "Create package manager"; - auto packageManager = CreatePackageManager(useDevCLSIDs); + void Install() + { + try + { + action = "Create package manager"; + auto packageManager = CreatePackageManager(); - action = "Get source reference"; - auto sourceRef = packageManager.GetPackageCatalogByName(ConvertToUTF16(sourceName)); + action = "Get source reference"; + auto sourceRef = packageManager.GetPackageCatalogByName(ConvertToUTF16(sourceName)); - action = "Connecting to catalog"; - auto connectResult = sourceRef.Connect(); + action = "Connecting to catalog"; + auto connectResult = sourceRef.Connect(); - if (connectResult.Status() != ConnectResultStatus::Ok) - { - hr = E_FAIL; - error = "Error connecting to catalog"; - break; - } + if (connectResult.Status() != ConnectResultStatus::Ok) + { + hr = E_FAIL; + error = "Error connecting to catalog"; + return; + } - auto catalog = connectResult.PackageCatalog(); + auto catalog = connectResult.PackageCatalog(); - action = "Create find options"; - auto findOptions = CreateFindPackagesOptions(useDevCLSIDs); + action = "Create find options"; + auto findOptions = CreateFindPackagesOptions(); - action = "Add package id filter"; - auto filter = CreatePackageMatchFilter(useDevCLSIDs); - filter.Field(PackageMatchField::Id); - filter.Option(PackageFieldMatchOption::Equals); - filter.Value(ConvertToUTF16(packageIdentifier)); - findOptions.Filters().Append(filter); + action = "Add package id filter"; + auto filter = CreatePackageMatchFilter(); + filter.Field(PackageMatchField::Id); + filter.Option(PackageFieldMatchOption::Equals); + filter.Value(ConvertToUTF16(packageIdentifier)); + findOptions.Filters().Append(filter); - action = "Find package"; - auto findResult = catalog.FindPackages(findOptions); + action = "Find package"; + auto findResult = catalog.FindPackages(findOptions); - if (findResult.Status() != FindPackagesResultStatus::Ok) - { - hr = E_FAIL; - error = "Error finding packages"; - break; - } + if (findResult.Status() != FindPackagesResultStatus::Ok) + { + hr = E_FAIL; + error = "Error finding packages"; + return; + } - action = "Get match"; - auto matches = findResult.Matches(); + action = "Get match"; + auto matches = findResult.Matches(); - if (matches.Size() == 0) - { - hr = E_NOT_SET; - error = "Package not found"; - break; - } + if (matches.Size() == 0) + { + hr = E_NOT_SET; + error = "Package not found"; + return; + } - auto package = matches.GetAt(0).CatalogPackage(); + auto package = matches.GetAt(0).CatalogPackage(); - action = "Inspect package"; - auto installVersion = package.DefaultInstallVersion(); - packageName = ConvertToUTF8(installVersion.DisplayName()); - if (useDevCLSIDs) - { - // Publisher is not yet available on the release version; make this unconditional when it is - packagePublisher = ConvertToUTF8(installVersion.Publisher()); - } + action = "Inspect package"; + auto installVersion = package.DefaultInstallVersion(); + packageName = ConvertToUTF8(installVersion.DisplayName()); + if (useDevCLSIDs) + { + // Publisher is not yet available on the release version; make this unconditional when it is + packagePublisher = ConvertToUTF8(installVersion.Publisher()); + } - action = "Create install options"; - auto installOptions = CreateInstallOptions(useDevCLSIDs); + action = "Create install options"; + auto installOptions = CreateInstallOptions(); - installOptions.PackageInstallScope(PackageInstallScope::Any); - installOptions.PackageInstallMode(PackageInstallMode::Silent); + installOptions.PackageInstallScope(PackageInstallScope::Any); + installOptions.PackageInstallMode(PackageInstallMode::Silent); - std::cout << "Beginning to install " << packageIdentifier << " (" << packageName << ") from " << sourceName << "..." << std::endl; - auto installResult = packageManager.InstallPackageAsync(package, installOptions).get(); + std::cout << "Beginning to install " << packageIdentifier << " (" << packageName << ") from " << sourceName << "..." << std::endl; + auto installResult = packageManager.InstallPackageAsync(package, installOptions).get(); - if (installResult.Status() != InstallResultStatus::Ok) + if (installResult.Status() != InstallResultStatus::Ok) + { + hr = installResult.ExtendedErrorCode(); + error = "Error installing package"; + return; + } + } + catch (const winrt::hresult_error& hre) { - hr = installResult.ExtendedErrorCode(); - error = "Error installing package"; - break; + hr = hre.code(); + error = ConvertToUTF8(hre.message()); } - - } while(0); } - catch (const winrt::hresult_error& hre) - { - hr = hre.code(); - error = ConvertToUTF8(hre.message()); } - if (FAILED(hr)) + void CorrelatePackageKnown() { - goto output_result; - } - - // Check for the installed package being correlated when the remote package is known, - // as when trying to determine information about a single known package. - phase = "Correlate when package known"; - std::cout << "Correlating package when known..." << std::endl; - try - { do { - action = "Create package manager"; - auto packageManager = CreatePackageManager(useDevCLSIDs); + try + { + action = "Create package manager"; + auto packageManager = CreatePackageManager(); - action = "Get source reference"; - auto sourceRef = packageManager.GetPackageCatalogByName(ConvertToUTF16(sourceName)); + action = "Get source reference"; + auto sourceRef = packageManager.GetPackageCatalogByName(ConvertToUTF16(sourceName)); - action = "Create composite catalog options"; - auto compOptions = CreateCreateCompositePackageCatalogOptions(useDevCLSIDs); + action = "Create composite catalog options"; + auto compOptions = CreateCreateCompositePackageCatalogOptions(); - compOptions.Catalogs().Append(sourceRef); - compOptions.CompositeSearchBehavior(CompositeSearchBehavior::RemotePackagesFromAllCatalogs); + compOptions.Catalogs().Append(sourceRef); + compOptions.CompositeSearchBehavior(CompositeSearchBehavior::RemotePackagesFromAllCatalogs); - action = "Create composite catalog reference"; - auto compRef = packageManager.CreateCompositePackageCatalog(compOptions); + action = "Create composite catalog reference"; + auto compRef = packageManager.CreateCompositePackageCatalog(compOptions); - action = "Connecting to catalog"; - auto connectResult = compRef.Connect(); + action = "Connecting to catalog"; + auto connectResult = compRef.Connect(); - if (connectResult.Status() != ConnectResultStatus::Ok) - { - hr = E_FAIL; - error = "Error connecting to catalog"; - break; - } + if (connectResult.Status() != ConnectResultStatus::Ok) + { + hr = E_FAIL; + error = "Error connecting to catalog"; + return; + } - auto catalog = connectResult.PackageCatalog(); + auto catalog = connectResult.PackageCatalog(); - action = "Create find options"; - auto findOptions = CreateFindPackagesOptions(useDevCLSIDs); + action = "Create find options"; + auto findOptions = CreateFindPackagesOptions(); - action = "Add package id filter"; - auto filter = CreatePackageMatchFilter(useDevCLSIDs); - filter.Field(PackageMatchField::Id); - filter.Option(PackageFieldMatchOption::Equals); - filter.Value(ConvertToUTF16(packageIdentifier)); - findOptions.Filters().Append(filter); + action = "Add package id filter"; + auto filter = CreatePackageMatchFilter(); + filter.Field(PackageMatchField::Id); + filter.Option(PackageFieldMatchOption::Equals); + filter.Value(ConvertToUTF16(packageIdentifier)); + findOptions.Filters().Append(filter); - action = "Find package"; - auto findResult = catalog.FindPackages(findOptions); + action = "Find package"; + auto findResult = catalog.FindPackages(findOptions); - if (findResult.Status() != FindPackagesResultStatus::Ok) - { - hr = E_FAIL; - error = "Error finding packages"; - break; - } + if (findResult.Status() != FindPackagesResultStatus::Ok) + { + hr = E_FAIL; + error = "Error finding packages"; + return; + } - action = "Get match"; - auto matches = findResult.Matches(); + action = "Get match"; + auto matches = findResult.Matches(); - if (matches.Size() == 0) - { - hr = E_NOT_SET; - error = "Package not found"; - break; - } + if (matches.Size() == 0) + { + hr = E_NOT_SET; + error = "Package not found"; + return; + } - auto package = matches.GetAt(0).CatalogPackage(); + auto package = matches.GetAt(0).CatalogPackage(); - action = "Inspect package for installed version"; - auto installed = package.InstalledVersion(); + action = "Inspect package for installed version"; + auto installed = package.InstalledVersion(); - if (installed) - { - correlatePackageKnown = true; - packageKnownName = ConvertToUTF8(installed.DisplayName()); - if (useDevCLSIDs) + if (installed) { - // Publisher is not yet available on the release version; make this unconditional when it is - packageKnownPublisher = ConvertToUTF8(installed.Publisher()); + correlatePackageKnown = true; + packageKnownName = ConvertToUTF8(installed.DisplayName()); + if (useDevCLSIDs) + { + // Publisher is not yet available on the release version; make this unconditional when it is + packageKnownPublisher = ConvertToUTF8(installed.Publisher()); + } } } + catch (const winrt::hresult_error& hre) + { + hr = hre.code(); + error = ConvertToUTF8(hre.message()); + } + } - } while(0); } - catch (const winrt::hresult_error& hre) + void CorrelateArchive() { - hr = hre.code(); - error = ConvertToUTF8(hre.message()); - } + try + { + action = "Create package manager"; + auto packageManager = CreatePackageManager(); - // Check for the installed package being correlated when archiving local package information. - phase = "Correlate when archiving"; - std::cout << "Correlating package when archiving..." << std::endl; - try - { do { - action = "Create package manager"; - auto packageManager = CreatePackageManager(useDevCLSIDs); + action = "Get source reference"; + auto sourceRef = packageManager.GetPackageCatalogByName(ConvertToUTF16(sourceName)); - action = "Get source reference"; - auto sourceRef = packageManager.GetPackageCatalogByName(ConvertToUTF16(sourceName)); + action = "Create composite catalog options"; + auto compOptions = CreateCreateCompositePackageCatalogOptions(); - action = "Create composite catalog options"; - auto compOptions = CreateCreateCompositePackageCatalogOptions(useDevCLSIDs); + compOptions.Catalogs().Append(sourceRef); + compOptions.CompositeSearchBehavior(CompositeSearchBehavior::LocalCatalogs); - compOptions.Catalogs().Append(sourceRef); - compOptions.CompositeSearchBehavior(CompositeSearchBehavior::LocalCatalogs); + action = "Create composite catalog reference"; + auto compRef = packageManager.CreateCompositePackageCatalog(compOptions); - action = "Create composite catalog reference"; - auto compRef = packageManager.CreateCompositePackageCatalog(compOptions); + action = "Connecting to catalog"; + auto connectResult = compRef.Connect(); - action = "Connecting to catalog"; - auto connectResult = compRef.Connect(); + if (connectResult.Status() != ConnectResultStatus::Ok) + { + hr = E_FAIL; + error = "Error connecting to catalog"; + return; + } - if (connectResult.Status() != ConnectResultStatus::Ok) - { - hr = E_FAIL; - error = "Error connecting to catalog"; - break; - } + auto catalog = connectResult.PackageCatalog(); - auto catalog = connectResult.PackageCatalog(); + action = "Create find options"; + auto findOptions = CreateFindPackagesOptions(); - action = "Create find options"; - auto findOptions = CreateFindPackagesOptions(useDevCLSIDs); + action = "Find package"; + auto findResult = catalog.FindPackages(findOptions); - action = "Find package"; - auto findResult = catalog.FindPackages(findOptions); + if (findResult.Status() != FindPackagesResultStatus::Ok) + { + hr = E_FAIL; + error = "Error finding packages"; + return; + } - if (findResult.Status() != FindPackagesResultStatus::Ok) - { - hr = E_FAIL; - error = "Error finding packages"; - break; - } + action = "Get matches"; + auto matches = findResult.Matches(); - action = "Get matches"; - auto matches = findResult.Matches(); + action = "Get source info"; + auto sourceInfo = sourceRef.Info(); + auto sourceIdentifier = sourceInfo.Id(); + auto sourceType = sourceInfo.Type(); - action = "Get source info"; - auto sourceInfo = sourceRef.Info(); - auto sourceIdentifier = sourceInfo.Id(); - auto sourceType = sourceInfo.Type(); + for (const auto& match : matches) + { + auto package = match.CatalogPackage(); - for (const auto& match : matches) - { - auto package = match.CatalogPackage(); - auto installed = package.InstalledVersion(); + if (ConvertToUTF8(package.Id()) != packageIdentifier) + { + continue; + } - if (installed) - { - auto installedCatalogInfo = installed.PackageCatalog().Info(); + auto installed = package.InstalledVersion(); - if (installedCatalogInfo.Id() == sourceIdentifier && installedCatalogInfo.Type() == sourceType) + if (installed) { - correlateArchive = true; - archiveName = ConvertToUTF8(installed.DisplayName()); - if (useDevCLSIDs) + auto installedCatalogInfo = installed.PackageCatalog().Info(); + + if (installedCatalogInfo.Id() == sourceIdentifier && installedCatalogInfo.Type() == sourceType) { - // Publisher is not yet available on the release version; make this unconditional when it is - archivePublisher = ConvertToUTF8(installed.Publisher()); + correlateArchive = true; + archiveName = ConvertToUTF8(installed.DisplayName()); + if (useDevCLSIDs) + { + // Publisher is not yet available on the release version; make this unconditional when it is + archivePublisher = ConvertToUTF8(installed.Publisher()); + } + break; } - break; } } } + catch (const winrt::hresult_error& hre) + { + hr = hre.code(); + error = ConvertToUTF8(hre.message()); + } + } + + void ReportResult() + { + if (outputStream) + { + outputStream << "{" << std::endl; + outputStream << JSONPair{ "PackageIdentifier", packageIdentifier }; + outputStream << JSONPair{ "Source", sourceName }; + outputStream << JSONPair{ "UseDev", useDevCLSIDs }; + outputStream << JSONPair{ "Error", error }; + outputStream << JSONPair{ "Phase", phase }; + outputStream << JSONPair{ "Action", action }; + outputStream << JSONPair{ "PackageName", packageName }; + outputStream << JSONPair{ "PackagePublisher", packagePublisher }; + outputStream << JSONPair{ "CorrelatePackageKnown", correlatePackageKnown }; + outputStream << JSONPair{ "PackageKnownName", packageKnownName }; + outputStream << JSONPair{ "PackageKnownPublisher", packageKnownPublisher }; + outputStream << JSONPair{ "CorrelateArchive", correlateArchive }; + outputStream << JSONPair{ "ArchiveName", archiveName }; + outputStream << JSONPair{ "ArchivePublisher", archivePublisher }; + // Keep at the end to prevent a dangling comma + outputStream << JSONPair{ "HRESULT", hr, false } << "}" << std::endl; + } + } - } while(0); } - catch (const winrt::hresult_error& hre) + void main(int argc, char** argv) { - hr = hre.code(); - error = ConvertToUTF8(hre.message()); + hr = ParseArgs(argc, argv); + if (hr != 0) + { + return; + } + + ValidateArgs(); + if (FAILED(hr)) + { + return; + } + + auto co_uninitialize = wil::CoInitializeEx(); + + // Execute the install step + phase = "Install"; + std::cout << "Connecting to PackageManager..." << std::endl; + Install(); + if (FAILED(hr)) + { + return; + } + + // Check for the installed package being correlated when the remote package is known, + // as when trying to determine information about a single known package. + phase = "Correlate when package known"; + std::cout << "Correlating package when known..." << std::endl; + CorrelatePackageKnown(); + + // Check for the installed package being correlated when archiving local package information. + phase = "Correlate when archiving"; + std::cout << "Correlating package when archiving..." << std::endl; + CorrelateArchive(); + + std::cout << "Done" << std::endl; + phase = "Completed"; + action.clear(); } +}; - std::cout << "Done" << std::endl; - phase = "Completed"; - action.clear(); - -output_result: - outputStream << "{" << std::endl; - outputStream << JSONPair{ "PackageIdentifier", packageIdentifier }; - outputStream << JSONPair{ "Source", sourceName }; - outputStream << JSONPair{ "UseDev", useDevCLSIDs }; - outputStream << JSONPair{ "Error", error }; - outputStream << JSONPair{ "Phase", phase }; - outputStream << JSONPair{ "Action", action }; - outputStream << JSONPair{ "PackageName", packageName }; - outputStream << JSONPair{ "PackagePublisher", packagePublisher }; - outputStream << JSONPair{ "CorrelatePackageKnown", correlatePackageKnown }; - outputStream << JSONPair{ "PackageKnownName", packageKnownName }; - outputStream << JSONPair{ "PackageKnownPublisher", packageKnownPublisher }; - outputStream << JSONPair{ "CorrelateArchive", correlateArchive }; - outputStream << JSONPair{ "ArchiveName", archiveName }; - outputStream << JSONPair{ "ArchivePublisher", archivePublisher }; - // Keep at the end to prevent a dangling comma - outputStream << JSONPair{ "HRESULT", hr, false } << "}" << std::endl; - - return hr; +int main(int argc, char** argv) try +{ + Main mainMain; + mainMain.main(argc, argv); + mainMain.ReportResult(); + return mainMain.hr; } catch (const std::exception& e) { diff --git a/tools/CorrelationTestbed/Readme.md b/tools/CorrelationTestbed/Readme.md new file mode 100644 index 0000000000..77fbbe3f9c --- /dev/null +++ b/tools/CorrelationTestbed/Readme.md @@ -0,0 +1,42 @@ +# E2E correlation testing +This directory holds a few scripts and a test project, all centered around enabling end-to-end validation of our correlation between system artifacts and packages in external sources. + +The test project uses the COM API to first install a package, then attempts to check for correlation in two ways (directions): +1. When the remote package identity is known, the caller will usually look it up via that remote identity. This path can enable additional information to be retrieved to make the correlation with. +2. When listing all of the local packages, the remote identity must be determined for each one. We can use only data known locally to make the correlation. + +The primary script, `Test-CorrelationInSandbox.ps1`, is based off of the sandbox test script in winget-pkgs (thanks to many people). It sets up the sandbox and initial script to run there for each package to test, then waits for a sentinel file to be created in the output location. The results of all of running the test project exe, as well as the ARP differences and winget logs are put in that output location, then the sandbox is destroyed for the next package to run. + +``` +Test-CorrelationInSandbox.ps1 +-- Required -- +[[-PackageIdentifiers] ] :: A set of package ids to test +[[-Source] ] :: The name of the source that the packages are from, ex. "winget" + +-- Optional -- +[-ExePath ] :: Path to the test exe; defaults to the Release output location +[-UseDev] :: Switch to use the local dev winget build +[-DevPackagePath ] :: Path to the local dev *Release* winget build; defaults to the normal location +[-ResultsPath ] :: Path to output the results to; defaults to a temp directory +[-RegFileDirectory ] :: Path to a directory containing .reg files to insert before the test, creating noise for correlation +``` + +Once testing is done, `Process-CorrelationResults.ps1` will take all of the results JSON files and put them into `results.csv` in the directory. If any tests failed to run, they will be in `failed.csv`. There are correlation columns in the CSV that can be averaged in Excel to get the correlation percentage. + +## Running a test +### Setup +First you must have built the test exe located in `InstallAndCheckCorrelation`. By default the Release x64 version is picked up by the script, so it is easiest to build that one. + +If you want to run against the local dev build of the winget COM server, the default is again to use Release x64. The sandbox will not run the debug build for unknown reasons currently. + +### Run the test pass +Run the `Test-CorrelationInSandbox.ps1` script, then wait for a while since it is going to download and install every package serially, with a little bit of overhead in between. + +A simple example call is: +``` +Test-CorrelationInSandbox.ps1 -PackageIdentifiers @("Microsoft.VisualStudioCode") -Source winget +``` +This will use the latest version of winget available on github. Adding `-UseDev` should be enough to use the local build instead if Release x64 is already deployed onto the host machine. + +### Collate the results +Running `Process-CorrelationResults.ps1` on the directory output at the end of `Test-CorrelationInSandbox.ps1` will place a CSV file with the results combined together. These results can be inspected for correctness and an overall correlation score determined from the different correlation paths. \ No newline at end of file diff --git a/tools/CorrelationTestbed/Test-CorrelationInSandbox.ps1 b/tools/CorrelationTestbed/Test-CorrelationInSandbox.ps1 index 1713350900..14fac2d48d 100644 --- a/tools/CorrelationTestbed/Test-CorrelationInSandbox.ps1 +++ b/tools/CorrelationTestbed/Test-CorrelationInSandbox.ps1 @@ -27,6 +27,8 @@ if (-not $ExePath) $ExePath = Join-Path $PSScriptRoot "InstallAndCheckCorrelation\x64\Release" } +$ExePath = [System.IO.Path]::GetFullPath($ExePath) + if (-not (Test-Path (Join-Path $ExePath "InstallAndCheckCorrelation.exe"))) { Write-Error -Category InvalidArgument -Message @" @@ -43,6 +45,8 @@ if ($UseDev) { $DevPackagePath = Join-Path $PSScriptRoot "..\..\src\AppInstallerCLIPackage\bin\x64\Release\AppX" } + + $DevPackagePath = [System.IO.Path]::GetFullPath($DevPackagePath) if ($DevPackagePath.ToLower().Contains("debug")) { @@ -73,20 +77,6 @@ $ Enable-WindowsOptionalFeature -Online -FeatureName 'Containers-DisposableClien '@ } -# Create output location for results - -if (-not $ResultsPath) -{ - $ResultsPath = Join-Path ([System.IO.Path]::GetTempPath()) (New-Guid) -} - -if (Test-Path $ResultsPath) -{ - Remove-Item -Recurse $ResultsPath -Force -} - -New-Item -ItemType Directory $ResultsPath > $nul - # Close Windows Sandbox function Close-WindowsSandbox { @@ -94,10 +84,16 @@ function Close-WindowsSandbox { if ($sandbox) { Write-Host '--> Closing Windows Sandbox' + $sandboxServer = Get-Process 'WindowsSandbox' -ErrorAction SilentlyContinue + $sandbox | Stop-Process $sandbox | Wait-Process -Timeout 30 - Start-Sleep 2 + # Also wait for the server to close + if ($sandboxServer) + { + $sandboxServer | Wait-Process -Timeout 30 + } Write-Host } @@ -106,6 +102,22 @@ function Close-WindowsSandbox { Close-WindowsSandbox +# Create output location for results + +if (-not $ResultsPath) +{ + $ResultsPath = Join-Path ([System.IO.Path]::GetTempPath()) (New-Guid) +} + +$ResultsPath = [System.IO.Path]::GetFullPath($ResultsPath) + +if (Test-Path $ResultsPath) +{ + Remove-Item -Recurse $ResultsPath -Force +} + +New-Item -ItemType Directory $ResultsPath | Out-Null + # Initialize Temp Folder $tempFolderName = 'CorrelationTestStaging' @@ -171,53 +183,53 @@ if ($UseDev) else { -$dependencies = @($desktopAppInstaller, $vcLibsUwp, $uiLibsUwp) + $dependencies = @($desktopAppInstaller, $vcLibsUwp, $uiLibsUwp) -# Clean temp directory + # Clean temp directory -Get-ChildItem $tempFolder -Recurse -Exclude $dependencies.fileName | Remove-Item -Force -Recurse + Get-ChildItem $tempFolder -Recurse -Exclude $dependencies.fileName | Remove-Item -Force -Recurse -if (-Not [String]::IsNullOrWhiteSpace($Manifest)) { - Copy-Item -Path $Manifest -Recurse -Destination $tempFolder -} + if (-Not [String]::IsNullOrWhiteSpace($Manifest)) { + Copy-Item -Path $Manifest -Recurse -Destination $tempFolder + } -# Download dependencies + # Download dependencies -Write-Host '--> Checking dependencies' + Write-Host '--> Checking dependencies' -foreach ($dependency in $dependencies) { - $dependency.file = Join-Path -Path $tempFolder -ChildPath $dependency.fileName - $dependency.pathInSandbox = Join-Path -Path $desktopInSandbox -ChildPath (Join-Path -Path $tempFolderName -ChildPath $dependency.fileName) + foreach ($dependency in $dependencies) { + $dependency.file = Join-Path -Path $tempFolder -ChildPath $dependency.fileName + $dependency.pathInSandbox = Join-Path -Path $desktopInSandbox -ChildPath (Join-Path -Path $tempFolderName -ChildPath $dependency.fileName) - # Only download if the file does not exist, or its hash does not match. - if (-Not ((Test-Path -Path $dependency.file -PathType Leaf) -And $dependency.hash -eq $(Get-FileHash $dependency.file).Hash)) { - Write-Host @" + # Only download if the file does not exist, or its hash does not match. + if (-Not ((Test-Path -Path $dependency.file -PathType Leaf) -And $dependency.hash -eq $(Get-FileHash $dependency.file).Hash)) { + Write-Host @" - Downloading: $($dependency.url) "@ - try { - $WebClient.DownloadFile($dependency.url, $dependency.file) - } - catch { - #Pass the exception as an inner exception - throw [System.Net.WebException]::new("Error downloading $($dependency.url).",$_.Exception) - } - if (-not ($dependency.hash -eq $(Get-FileHash $dependency.file).Hash)) { - throw [System.Activities.VersionMismatchException]::new('Dependency hash does not match the downloaded file') + try { + $WebClient.DownloadFile($dependency.url, $dependency.file) + } + catch { + #Pass the exception as an inner exception + throw [System.Net.WebException]::new("Error downloading $($dependency.url).",$_.Exception) + } + if (-not ($dependency.hash -eq $(Get-FileHash $dependency.file).Hash)) { + throw [System.Activities.VersionMismatchException]::new('Dependency hash does not match the downloaded file') + } } } -} -# Extract Microsoft.UI.Xaml from zip (if freshly downloaded). -# This is a workaround until https://github.com/microsoft/winget-cli/issues/1861 is resolved. + # Extract Microsoft.UI.Xaml from zip (if freshly downloaded). + # This is a workaround until https://github.com/microsoft/winget-cli/issues/1861 is resolved. -if (-Not (Test-Path (Join-Path -Path $tempFolder -ChildPath \Microsoft.UI.Xaml.2.7\tools\AppX\x64\Release\Microsoft.UI.Xaml.2.7.appx))){ - Expand-Archive -Path $uiLibsUwp.file -DestinationPath ($tempFolder + "\Microsoft.UI.Xaml.2.7") -Force -} -$uiLibsUwp.file = (Join-Path -Path $tempFolder -ChildPath \Microsoft.UI.Xaml.2.7\tools\AppX\x64\Release\Microsoft.UI.Xaml.2.7.appx) -$uiLibsUwp.pathInSandbox = Join-Path -Path $desktopInSandbox -ChildPath (Join-Path -Path $tempFolderName -ChildPath \Microsoft.UI.Xaml.2.7\tools\AppX\x64\Release\Microsoft.UI.Xaml.2.7.appx) -Write-Host + if (-Not (Test-Path (Join-Path -Path $tempFolder -ChildPath \Microsoft.UI.Xaml.2.7\tools\AppX\x64\Release\Microsoft.UI.Xaml.2.7.appx))){ + Expand-Archive -Path $uiLibsUwp.file -DestinationPath ($tempFolder + "\Microsoft.UI.Xaml.2.7") -Force + } + $uiLibsUwp.file = (Join-Path -Path $tempFolder -ChildPath \Microsoft.UI.Xaml.2.7\tools\AppX\x64\Release\Microsoft.UI.Xaml.2.7.appx) + $uiLibsUwp.pathInSandbox = Join-Path -Path $desktopInSandbox -ChildPath (Join-Path -Path $tempFolderName -ChildPath \Microsoft.UI.Xaml.2.7\tools\AppX\x64\Release\Microsoft.UI.Xaml.2.7.appx) + Write-Host } # !$UseDev @@ -231,7 +243,7 @@ foreach ($packageIdentifier in $PackageIdentifiers) # Create temporary location for output $outPath = Join-Path $ResultsPath $packageIdentifier - New-Item -ItemType Directory $outPath > $nul + New-Item -ItemType Directory $outPath | Out-Null $outPathInSandbox = Join-Path -Path $desktopInSandbox -ChildPath (Split-Path -Path $outPath -Leaf) @@ -252,7 +264,7 @@ foreach ($packageIdentifier in $PackageIdentifiers) $bootstrapPs1FileName = 'Bootstrap.ps1' $bootstrapPs1Content | Out-File (Join-Path $tempFolder $bootstrapPs1FileName) -Force - # Create Wsb file + # Create Windows Sandbox configuration file $bootstrapPs1InSandbox = Join-Path -Path $desktopInSandbox -ChildPath (Join-Path -Path $tempFolderName -ChildPath $bootstrapPs1FileName) $tempFolderInSandbox = Join-Path -Path $desktopInSandbox -ChildPath $tempFolderName @@ -314,13 +326,9 @@ foreach ($packageIdentifier in $PackageIdentifiers) $sandboxTestWsbContent | Out-File $sandboxTestWsbFile -Force Write-Host @" ---> Starting Windows Sandbox, and: - - Mounting the following directories: - - $tempFolder as read-only - - $outPath as read-write - - Installing WinGet - - Installing the package: $packageIdentifier - - Comparing ARP Entries +--> Starting Windows Sandbox: + - Package: $packageIdentifier + - Output directory: $outPath "@ Write-Host @@ -329,12 +337,20 @@ foreach ($packageIdentifier in $PackageIdentifiers) $outputFileBlockerPath = Join-Path $outPath "done.txt" + $waitTimeout = [System.TimeSpan]::new(0, 10, 0) + $startWaitTime = Get-Date + while (-not (Test-Path $outputFileBlockerPath)) { + $elapsedTime = (Get-Date) - $startWaitTime + if ($elapsedTime -gt $waitTimeout) + { + break + } Start-Sleep 1 } - #Close-WindowsSandbox + Close-WindowsSandbox } Write-Host @"