winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 8e30e7b9d1933928642785d7ff4b07609fae0a13
parent 114ce1ed780cceedcd092a0da319fc37a1acae98
Author: Ryan Fu <69221034+ryfu-msft@users.noreply.github.com>
Date:   Fri,  2 Dec 2022 18:03:41 -0800

Support for PowerShell cmdlets in admin mode (#2642)


Diffstat:
M.github/actions/spelling/allow.txt | 20++++++++++++++++++++
M.gitignore | 6++++++
Mazure-pipelines.yml | 25+++++++++++++------------
Msrc/AppInstallerCLI.sln | 7+++++--
Msrc/AppInstallerCLIE2ETests/Constants.cs | 11+++++++++++
Asrc/AppInstallerCLIE2ETests/PowerShell/PowerShellModule.cs | 178+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/AppInstallerCLIE2ETests/README.md | 5++++-
Msrc/AppInstallerCLIE2ETests/SetUpFixture.cs | 6+++++-
Msrc/AppInstallerCLIE2ETests/Test.runsettings | 2++
Msrc/AppInstallerCLIE2ETests/TestCommon.cs | 7+++++++
Msrc/AppInstallerCLIPackage/Package.appxmanifest | 10++++++++++
Msrc/PowerShell/Microsoft.WinGet.Client/Commands/GetSourceCommand.cs | 1-
Msrc/PowerShell/Microsoft.WinGet.Client/Commands/InstallPackageCommand.cs | 7++-----
Msrc/PowerShell/Microsoft.WinGet.Client/Common/BaseClientCommand.cs | 4++--
Asrc/PowerShell/Microsoft.WinGet.Client/Common/ErrorCode.cs | 19+++++++++++++++++++
Msrc/PowerShell/Microsoft.WinGet.Client/Common/Utilities.cs | 13+++++++++++++
Msrc/PowerShell/Microsoft.WinGet.Client/Format.ps1xml | 4++--
Msrc/PowerShell/Microsoft.WinGet.Client/Helpers/ComObjectFactory.cs | 73++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Msrc/PowerShell/Microsoft.WinGet.Client/Microsoft.WinGet.Client.csproj | 31+++++++++++++++++++++++--------
Msrc/PowerShell/Microsoft.WinGet.Client/Properties/Resources.Designer.cs | 15++++++++++++---
Msrc/PowerShell/Microsoft.WinGet.Client/Properties/Resources.resx | 7+++++--
Asrc/WinGetServer/Utils.cpp | 36++++++++++++++++++++++++++++++++++++
Asrc/WinGetServer/Utils.h | 9+++++++++
Asrc/WinGetServer/WinGetServer.idl | 18++++++++++++++++++
Msrc/WinGetServer/WinGetServer.vcxproj | 32+++++++++++++++++++++++++++++---
Msrc/WinGetServer/WinGetServer.vcxproj.filters | 17+++++++++++++++++
Asrc/WinGetServer/WinGetServerManualActivation_Client.cpp | 158+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/WinGetServer/WinMain.cpp | 138+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Msrc/WinGetServer/packages.config | 1-
Msrc/WindowsPackageManager/Source.def | 2++
Msrc/WindowsPackageManager/WindowsPackageManager.h | 9+++++++++
Msrc/WindowsPackageManager/WindowsPackageManager.vcxproj | 16++++++++--------
Msrc/WindowsPackageManager/main.cpp | 18++++++++++++++++++
Msrc/Xlang/UndockedRegFreeWinRT/src/UndockedRegFreeWinRT/UndockedRegFreeWinRT/UndockedRegFreeWinRT.vcxproj | 12++++++++----
Msrc/Xlang/UndockedRegFreeWinRT/src/UndockedRegFreeWinRT/UndockedRegFreeWinRT/UndockedRegFreeWinRT.vcxproj.filters | 102++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Msrc/Xlang/UndockedRegFreeWinRT/src/UndockedRegFreeWinRT/UndockedRegFreeWinRT/winrtact.def | 5+++--
Mtemplates/e2e-test.template.yml | 6++++--
37 files changed, 904 insertions(+), 126 deletions(-)

diff --git a/.github/actions/spelling/allow.txt b/.github/actions/spelling/allow.txt @@ -37,6 +37,7 @@ aspnet authn authz autocomplete +AUTOLISTEN auxdata azureedge backend @@ -202,6 +203,7 @@ hcertstore HCRYPTMSG hfile HGLOBAL +HGlobal HIDECANCEL hinternet HKCU @@ -228,6 +230,7 @@ IDX IFACEMETHOD ifdef ifndef +ifspec ifstream IIS impl @@ -246,9 +249,11 @@ INSTALLPATH INSTALLUILEVEL interop INVALIDARG +INVALIDSID iomanip iostream IPortable +ipmo ISAPPROVEDFOROUTPUT isspace istream @@ -311,6 +316,8 @@ msixmgr msixsdk msixsdkx msixtest +MSHCTX +MSHLFLAGS msrc Multifile Multimatch @@ -318,6 +325,7 @@ mutex mutexes namespace namespaces +ncacn Nelon netcoreapp netstandard @@ -395,18 +403,23 @@ preindexed prepareforpackaging PRIMARYKEY prioritization +processthreadsapi PRODUCTNAME PRODUCTVERSION PROGRAMFILES PROGRESSONLY promptrestart PROPERTYDUMP +PROTSEQ +Protseq psz +PTOKEN ptr publiccontainer PUCHAR PVOID pwa +pwsh QCol RAII rclsid @@ -438,6 +451,7 @@ RESTSOURCE resw resx rethrowing +REQS roadmap robuffer rowcount @@ -473,7 +487,9 @@ SHELLEXEC SHELLEXECUTEINFO SHELLEXECUTEINFOA SHELLEXECUTEINFOW +shlobj Shlwapi +shtypes signtool silentwithprogress simplesave @@ -497,6 +513,7 @@ src srwlock sscanf sstream +STARTUPINFO STATEACTION STATFLAG STATSTG @@ -589,6 +606,7 @@ UIA UIF uint Uknown +ULARGE ulong ULONGLONG uncomment @@ -598,6 +616,7 @@ unicode UNICODESTRING uninstall uninstalling +Unmarshal Unregister Unregisters untimes @@ -669,6 +688,7 @@ WStr wstring wstringstream WTD +wtypesbase www xamarin xaml diff --git a/.gitignore b/.gitignore @@ -329,3 +329,8 @@ ASALocalRun/ # MFractors (Xamarin productivity tool) working folder .mfractor/ + +# Generated files from WinGetServer.idl +**/WinGetServer/WinGetServer.h +**/WinGetServer/WinGetServer_c.c +**/WinGetServer/WinGetServer_s.c+ \ No newline at end of file diff --git a/azure-pipelines.yml b/azure-pipelines.yml @@ -213,12 +213,6 @@ jobs: CleanTargetFolder: false OverWrite: true - - template: templates/e2e-test.template.yml - parameters: - title: "E2E Tests Packaged" - isPackaged: true - filter: "TestCategory!=InProcess&TestCategory!=OutOfProcess" - - task: PowerShell@2 displayName: 'Set program files directory' inputs: @@ -238,12 +232,6 @@ jobs: TargetFolder: '$(platformProgramFiles)\dotnet' Contents: resources.pri - - template: templates/e2e-test.template.yml - parameters: - title: "COM API E2E Tests (In-process)" - isPackaged: false - filter: "TestCategory=InProcess" - # Winmd accessed by test runner process (dotnet.exe) - task: CopyFiles@2 displayName: 'Copy winmd to dotnet directory' @@ -262,6 +250,18 @@ jobs: - template: templates/e2e-test.template.yml parameters: + title: "E2E Tests Packaged" + isPackaged: true + filter: "TestCategory!=InProcess&TestCategory!=OutOfProcess" + + - template: templates/e2e-test.template.yml + parameters: + title: "COM API E2E Tests (In-process)" + isPackaged: false + filter: "TestCategory=InProcess" + + - template: templates/e2e-test.template.yml + parameters: title: "COM API E2E Tests (Out-of-process)" isPackaged: true filter: "TestCategory=OutOfProcess" @@ -305,6 +305,7 @@ jobs: inputs: filePath: 'src\PowerShell\Microsoft.WinGet.Client\Copy-PlatformBinaries.ps1' arguments: '-Platform $(buildPlatform) -Configuration $(buildConfiguration) -OutDir $(artifactsDir)\PowerShell' + condition: always() - task: PublishPipelineArtifact@1 displayName: Publish Pipeline Artifacts diff --git a/src/AppInstallerCLI.sln b/src/AppInstallerCLI.sln @@ -134,6 +134,9 @@ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Management.Deployment.Projection", "Microsoft.Management.Deployment.Projection\Microsoft.Management.Deployment.Projection.csproj", "{0B104762-5CD8-47EE-A904-71C1C3F84DCD}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UndockedRegFreeWinRT", "Xlang\UndockedRegFreeWinRT\src\UndockedRegFreeWinRT\UndockedRegFreeWinRT\UndockedRegFreeWinRT.vcxproj", "{31ED69A8-5310-45A9-953F-56C351D2C3E1}" + ProjectSection(ProjectDependencies) = postProject + {2B00D362-AC92-41F3-A8D2-5B1599BDCA01} = {2B00D362-AC92-41F3-A8D2-5B1599BDCA01} + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Detours", "Xlang\UndockedRegFreeWinRT\src\UndockedRegFreeWinRT\detours\detours.vcxproj", "{787EC629-C0FB-4BA9-9746-4A82CD06B73E}" EndProject @@ -992,8 +995,8 @@ Global {31ED69A8-5310-45A9-953F-56C351D2C3E1}.Release|ARM64.ActiveCfg = Debug|Win32 {31ED69A8-5310-45A9-953F-56C351D2C3E1}.Release|x64.ActiveCfg = Release|x64 {31ED69A8-5310-45A9-953F-56C351D2C3E1}.Release|x64.Build.0 = Release|x64 - {31ED69A8-5310-45A9-953F-56C351D2C3E1}.Release|x86.ActiveCfg = Debug|Win32 - {31ED69A8-5310-45A9-953F-56C351D2C3E1}.Release|x86.Build.0 = Debug|Win32 + {31ED69A8-5310-45A9-953F-56C351D2C3E1}.Release|x86.ActiveCfg = Release|Win32 + {31ED69A8-5310-45A9-953F-56C351D2C3E1}.Release|x86.Build.0 = Release|Win32 {31ED69A8-5310-45A9-953F-56C351D2C3E1}.TestRelease|Any CPU.ActiveCfg = Debug|Win32 {31ED69A8-5310-45A9-953F-56C351D2C3E1}.TestRelease|Any CPU.Build.0 = Debug|Win32 {31ED69A8-5310-45A9-953F-56C351D2C3E1}.TestRelease|ARM.ActiveCfg = Debug|Win32 diff --git a/src/AppInstallerCLIE2ETests/Constants.cs b/src/AppInstallerCLIE2ETests/Constants.cs @@ -17,6 +17,7 @@ namespace AppInstallerCLIE2ETests public const string MsiInstallerPathParameter = "MsiTestInstallerPath"; public const string MsixInstallerPathParameter = "MsixTestInstallerPath"; public const string PackageCertificatePathParameter = "PackageCertificatePath"; + public const string PowerShellModulePathParameter = "PowerShellModulePath"; public const string AppInstallerTestCert = "AppInstallerTest.cer"; public const string AppInstallerTestCertThumbprint = "d03e7a688b388b1edde8476a627531c49db88017"; @@ -74,6 +75,16 @@ namespace AppInstallerCLIE2ETests public const string TestExeUninstallerFileName = "UninstallTestExe.bat"; public const string TestExeUninstalledFileName = "TestExeUninstalled.txt"; + // PowerShell Cmdlets + public const string FindCmdlet = "Find-WinGetPackage"; + public const string GetCmdlet = "Get-WinGetPackage"; + public const string GetSourceCmdlet = "Get-WinGetSource"; + public const string InstallCmdlet = "Install-WinGetPackage"; + public const string UninstallCmdlet = "Uninstall-WinGetPackage"; + public const string UpdateCmdlet = "Update-WinGetPackage"; + + public const string WindowsPackageManagerServer = "WindowsPackageManagerServer"; + // Locations public const string LocalAppData = "LocalAppData"; diff --git a/src/AppInstallerCLIE2ETests/PowerShell/PowerShellModule.cs b/src/AppInstallerCLIE2ETests/PowerShell/PowerShellModule.cs @@ -0,0 +1,177 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace AppInstallerCLIE2ETests.PowerShell +{ + using NUnit.Framework; + using System; + using System.Diagnostics; + using System.Linq; + + /// <summary> + /// Basic E2E smoke tests for verifying the behavior of the PowerShell module cmdlets. + /// Running the x86 PowerShell Module requires PowerShell Core (x86). These tests currently only target PowerShell Core (x64) + /// </summary> + [Category("PowerShell")] + public class PowerShellModule + { + // TODO: Consider using Pester framework for conducting more extensive PowerShell module tests. + + [OneTimeSetUp] + public void Setup() + { + TestCommon.RunAICLICommand("source add", $"-n {Constants.TestSourceName} {Constants.TestSourceUrl}"); + } + + [OneTimeTearDown] + public void TearDown() + { + // TODO: This is a workaround to an issue where the server takes longer than expected to terminate when + // running from the E2E tests. This can cause other E2E tests to fail when attempting to reset the test source. + if (IsRunning(Constants.WindowsPackageManagerServer)) + { + // There should only be one WinGetServer process running at a time. + Process serverProcess = Process.GetProcessesByName(Constants.WindowsPackageManagerServer).First(); + serverProcess.Kill(); + } + + TestCommon.RunAICLICommand("source remove", $"{Constants.TestSourceName}"); + } + + [Test] + public void AssertServerShutdownAfterExecution() + { + if (!Environment.Is64BitProcess) + { + return; + } + + var result = TestCommon.RunPowerShellCommandWithResult(Constants.GetSourceCmdlet, $"-Name {Constants.TestSourceName}"); + Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode, $"ExitCode: {result.ExitCode} Failed with the following output: {result.StdOut}, {result.StdErr}"); + + Assert.IsTrue(IsRunning(Constants.WindowsPackageManagerServer), $"{Constants.WindowsPackageManagerServer} is not running."); + Process serverProcess = Process.GetProcessesByName(Constants.WindowsPackageManagerServer).First(); + + // Wait a maximum of 30 seconds for the server process to exit. + bool serverProcessExit = serverProcess.WaitForExit(30000); + Assert.IsTrue(serverProcessExit, $"{Constants.WindowsPackageManagerServer} failed to terminate after creating COM object."); + } + + [Test] + public void GetWinGetSource() + { + if (!Environment.Is64BitProcess) + { + return; + } + + var getSourceResult = TestCommon.RunPowerShellCommandWithResult(Constants.GetSourceCmdlet, $"-Name {Constants.TestSourceName}"); + Assert.AreEqual(Constants.ErrorCode.S_OK, getSourceResult.ExitCode, $"ExitCode: {getSourceResult.ExitCode} Failed with the following output: {getSourceResult.StdOut}, {getSourceResult.StdErr}"); + Assert.IsTrue(getSourceResult.StdOut.Contains($"{Constants.TestSourceName}")); + } + + [Test] + public void FindWinGetPackage() + { + if (!Environment.Is64BitProcess) + { + return; + } + + var result = TestCommon.RunPowerShellCommandWithResult(Constants.FindCmdlet, $"-Id {Constants.ExeInstallerPackageId}"); + Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode, $"ExitCode: {result.ExitCode} Failed with the following output: {result.StdOut}; {result.StdErr}"); + Assert.IsTrue(result.StdOut.Contains("TestExeInstaller")); + } + + [Test] + public void GetWinGetPackage() + { + if (!Environment.Is64BitProcess) + { + return; + } + + var installResult = TestCommon.RunPowerShellCommandWithResult(Constants.InstallCmdlet, $"-Id {Constants.MsiInstallerPackageId}"); + var getResult = TestCommon.RunPowerShellCommandWithResult(Constants.GetCmdlet, $"-Id {Constants.MsiInstallerPackageId}"); + var uninstallResult = TestCommon.RunPowerShellCommandWithResult(Constants.UninstallCmdlet, $"-Id {Constants.MsiInstallerPackageId}"); + + Assert.AreEqual(Constants.ErrorCode.S_OK, installResult.ExitCode, $"ExitCode: {installResult.ExitCode}; Failed with the following output: {installResult.StdOut}; {installResult.StdErr}"); + Assert.AreEqual(Constants.ErrorCode.S_OK, getResult.ExitCode, $"Failed with the following output: {getResult.StdOut}"); + Assert.AreEqual(Constants.ErrorCode.S_OK, uninstallResult.ExitCode, $"Failed with the following output: {uninstallResult.StdOut}"); + + Assert.IsTrue(!string.IsNullOrEmpty(installResult.StdOut)); + Assert.IsTrue(getResult.StdOut.Contains("TestMsiInstaller")); + Assert.IsTrue(!string.IsNullOrEmpty(uninstallResult.StdOut)); + } + + [Test] + public void InstallWinGetPackage() + { + if (!Environment.Is64BitProcess) + { + return; + } + + var installResult = TestCommon.RunPowerShellCommandWithResult(Constants.InstallCmdlet, $"-Id {Constants.ExeInstallerPackageId}"); + var uninstallResult = TestCommon.RunPowerShellCommandWithResult(Constants.UninstallCmdlet, $"-Id {Constants.ExeInstallerPackageId}"); + + Assert.AreEqual(Constants.ErrorCode.S_OK, installResult.ExitCode, $"ExitCode: {installResult.ExitCode}; Failed with the following output: {installResult.StdOut}; {installResult.StdErr}"); + Assert.AreEqual(Constants.ErrorCode.S_OK, uninstallResult.ExitCode, $"Failed with the following output: {uninstallResult.StdOut}"); + + Assert.IsTrue(!string.IsNullOrEmpty(installResult.StdOut)); + Assert.IsTrue(!string.IsNullOrEmpty(uninstallResult.StdOut)); + } + + [Test] + public void UpdateWinGetPackage() + { + if (!Environment.Is64BitProcess) + { + return; + } + + var installResult = TestCommon.RunPowerShellCommandWithResult(Constants.InstallCmdlet, $"-Id {Constants.ExeInstallerPackageId} -Version 1.0.0.0"); + var updateResult = TestCommon.RunPowerShellCommandWithResult(Constants.UpdateCmdlet, $"-Id {Constants.ExeInstallerPackageId}"); + var getResult = TestCommon.RunPowerShellCommandWithResult(Constants.GetCmdlet, $"-Id {Constants.ExeInstallerPackageId}"); + var uninstallResult = TestCommon.RunPowerShellCommandWithResult(Constants.UninstallCmdlet, $"-Id {Constants.ExeInstallerPackageId}"); + + Assert.AreEqual(Constants.ErrorCode.S_OK, installResult.ExitCode, $"Failed with the following output: {installResult.StdOut}"); + Assert.AreEqual(Constants.ErrorCode.S_OK, updateResult.ExitCode, $"Failed with the following output: {updateResult.StdOut}"); + Assert.AreEqual(Constants.ErrorCode.S_OK, getResult.ExitCode, $"Failed with the following output: {getResult.StdOut}"); + Assert.AreEqual(Constants.ErrorCode.S_OK, uninstallResult.ExitCode, $"Failed with the following output: {uninstallResult.StdOut}"); + + Assert.IsTrue(!string.IsNullOrEmpty(installResult.StdOut)); + Assert.IsTrue(!string.IsNullOrEmpty(updateResult.StdOut)); + Assert.IsTrue(getResult.StdOut.Contains("2.0.0.0")); + Assert.IsTrue(!string.IsNullOrEmpty(uninstallResult.StdOut)); + } + + /// <summary> + /// There is a known issue where the server takes an abnormally long time to terminate after the E2E test pwsh processes finish execution. + /// This test verifies that the server does indeed terminate within 5 minutes after running all of the cmdlets. + /// Commented out to reduce the overall duration of the build pipeline. + /// </summary> + // [Test] + public void VerifyServerTermination() + { + TestCommon.RunPowerShellCommandWithResult(Constants.GetSourceCmdlet, $"-Name {Constants.TestSourceName}"); + TestCommon.RunPowerShellCommandWithResult(Constants.FindCmdlet, $"-Id {Constants.ExeInstallerPackageId}"); + TestCommon.RunPowerShellCommandWithResult(Constants.InstallCmdlet, $"-Id {Constants.ExeInstallerPackageId} -Version 1.0.0.0"); + TestCommon.RunPowerShellCommandWithResult(Constants.UpdateCmdlet, $"-Id {Constants.ExeInstallerPackageId}"); + TestCommon.RunPowerShellCommandWithResult(Constants.GetCmdlet, $"-Id {Constants.ExeInstallerPackageId}"); + TestCommon.RunPowerShellCommandWithResult(Constants.UninstallCmdlet, $"-Id {Constants.ExeInstallerPackageId}"); + + Assert.IsTrue(IsRunning(Constants.WindowsPackageManagerServer), $"{Constants.WindowsPackageManagerServer} is not running."); + Process serverProcess = Process.GetProcessesByName(Constants.WindowsPackageManagerServer).First(); + + // Wait a maximum of 5 minutes for the server process to exit. + bool serverProcessExit = serverProcess.WaitForExit(300000); + Assert.IsTrue(serverProcessExit, $"{Constants.WindowsPackageManagerServer} failed to terminate after creating COM object."); + } + + private bool IsRunning(string processName) + { + return Process.GetProcessesByName(processName).Length > 0; + } + } +}+ \ No newline at end of file diff --git a/src/AppInstallerCLIE2ETests/README.md b/src/AppInstallerCLIE2ETests/README.md @@ -56,7 +56,8 @@ Therefore to run the executable in the command line, simply change into the dire | StaticFileRootPath | Path to the set of static test files that will be served as the source for testing purposes. This path should be identical to the one provided to the LocalHostWebServer| | MsixTestInstallerPath | The MSIX (or APPX) Installer executable under test. | | ExeTestInstallerPath |The Exe Installer executable under test. | -| PackageCertificatePath |Signing Certificate Path used to certify test index source package| +| PackageCertificatePath | Signing Certificate Path used to certify test index source package | +| PowerShellModulePath | Path to the PowerShell module manifest file under test | #### Example of Test.runsettings format: @@ -72,6 +73,7 @@ Therefore to run the executable in the command line, simply change into the dire <Parameter name="MsixTestInstallerPath" value="MsixTestInstaller.msix" /> <Parameter name="ExeTestInstallerPath" value="ExeTestInstaller.exe" /> <Parameter name="PackageCertificatePath" value="certificate.pfx"/> + <Parameter name="PowerShellModulePath" value="TestPowerShellModule.psd1" /> </TestRunParameters> </RunSettings> @@ -90,6 +92,7 @@ Make sure to replace **MSFT** with your own user name. Modifying this example wi <Parameter name="MsixTestInstallerPath" value="C:\Users\MSFT\Temp\MsixTestInstaller.msix" /> <Parameter name="ExeTestInstallerPath" value="C:\Users\MSFT\source\repos\winget-cli\src\x64\Debug\AppInstallerTestExeInstaller\AppInstallerTestExeInstaller.exe" /> <Parameter name="PackageCertificatePath" value="C:\Users\MSFT\Temp\packageCertificate.pfx"/> + <Parameter name="PowerShellModulePath" value="C:\Users\MSFT\source\repos\winget-cli\src\x64\Debug\PowerShell\Microsoft.WinGet.Client.psd1" /> </TestRunParameters> </RunSettings> diff --git a/src/AppInstallerCLIE2ETests/SetUpFixture.cs b/src/AppInstallerCLIE2ETests/SetUpFixture.cs @@ -5,7 +5,6 @@ namespace AppInstallerCLIE2ETests { using Microsoft.Win32; using Newtonsoft.Json; - using Newtonsoft.Json.Linq; using NUnit.Framework; using System; using System.IO; @@ -96,6 +95,11 @@ namespace AppInstallerCLIE2ETests TestCommon.PackageCertificatePath = TestContext.Parameters.Get(Constants.PackageCertificatePathParameter); } + if (TestContext.Parameters.Exists(Constants.PowerShellModulePathParameter)) + { + TestCommon.PowerShellModulePath = TestContext.Parameters.Get(Constants.PowerShellModulePathParameter); + } + ReadTestInstallerPaths(); TestIndexSetup.GenerateTestDirectory(); diff --git a/src/AppInstallerCLIE2ETests/Test.runsettings b/src/AppInstallerCLIE2ETests/Test.runsettings @@ -19,6 +19,7 @@ MsixTestInstallerPath: The MSIX (or APPX) Installer executable under test. ExeTestInstallerPath: The Exe Installer executable under test. PackageCertificatePath: Signing Certificate Path used to certify Index Source Package + PowerShellModulePath: Path to the PowerShell module manifest file under test --> <TestRunParameters> <Parameter name="PackagedContext" value="true" /> @@ -32,5 +33,6 @@ <Parameter name="MsixTestInstallerPath" value="MsixTestInstaller.msix" /> <Parameter name="ExeTestInstallerPath" value="ExeTestInstaller.exe" /> <Parameter name="PackageCertificatePath" value="certificate.pfx"/> + <Parameter name="PowerShellModulePath" value="TestPowerShellModule.psd1" /> </TestRunParameters> </RunSettings> \ No newline at end of file diff --git a/src/AppInstallerCLIE2ETests/TestCommon.cs b/src/AppInstallerCLIE2ETests/TestCommon.cs @@ -36,6 +36,8 @@ namespace AppInstallerCLIE2ETests public static string PackageCertificatePath { get; set; } + public static string PowerShellModulePath { get; set; } + public static string SettingsJsonFilePath { get { @@ -252,6 +254,11 @@ namespace AppInstallerCLIE2ETests return result; } + public static RunCommandResult RunPowerShellCommandWithResult(string cmdlet, string args, int timeOut = 60000) + { + return RunCommandWithResult("pwsh.exe", $"-Command ipmo {PowerShellModulePath}; {cmdlet} {args}", timeOut); + } + public static string GetTestFile(string fileName) { return Path.Combine(TestContext.CurrentContext.TestDirectory, fileName); diff --git a/src/AppInstallerCLIPackage/Package.appxmanifest b/src/AppInstallerCLIPackage/Package.appxmanifest @@ -37,6 +37,16 @@ <uap5:ExecutionAlias Alias="WinGetDev.exe" /> </uap5:AppExecutionAlias> </uap5:Extension> + </Extensions> + </Application> + <Application Id="WinGetComServer" Executable="WinGetServer\WindowsPackageManagerServer.exe" EntryPoint="Windows.FullTrustApplication"> + <uap:VisualElements DisplayName="WinGet Dev COM Server" Square150x150Logo="Images\Square150x150Logo.png" Square44x44Logo="Images\Square44x44Logo.png" Description="The WinGet COM server." BackgroundColor="#0078d7" AppListEntry="none" /> + <Extensions> + <uap5:Extension Category="windows.appExecutionAlias"> + <uap5:AppExecutionAlias> + <uap5:ExecutionAlias Alias="WindowsPackageManagerServerDev.exe" /> + </uap5:AppExecutionAlias> + </uap5:Extension> <com:Extension Category="windows.comServer"> <com:ComServer> <com:ExeServer Executable="WinGetServer\WindowsPackageManagerServer.exe" DisplayName="Windows Package Manager Server" LaunchAndActivationPermission="O:SYG:SYD:(A;;11;;;WD)(A;;11;;;RC)(A;;11;;;AC)(A;;11;;;AN)S:P(ML;;NX;;;S-1-16-0)"> diff --git a/src/PowerShell/Microsoft.WinGet.Client/Commands/GetSourceCommand.cs b/src/PowerShell/Microsoft.WinGet.Client/Commands/GetSourceCommand.cs @@ -6,7 +6,6 @@ namespace Microsoft.WinGet.Client.Commands { - using System; using System.Management.Automation; using Microsoft.Management.Deployment; using Microsoft.WinGet.Client.Common; diff --git a/src/PowerShell/Microsoft.WinGet.Client/Commands/InstallPackageCommand.cs b/src/PowerShell/Microsoft.WinGet.Client/Commands/InstallPackageCommand.cs @@ -4,15 +4,12 @@ // </copyright> // ----------------------------------------------------------------------------- -#pragma warning disable SA1200 // Using directives should be placed correctly -using Windows.System; -#pragma warning restore SA1200 // Using directives should be placed correctly - namespace Microsoft.WinGet.Client.Commands -{ +{ using System.Management.Automation; using Microsoft.Management.Deployment; using Microsoft.WinGet.Client.Common; + using Windows.System; /// <summary> /// Installs a package from the pipeline or from a configured source. diff --git a/src/PowerShell/Microsoft.WinGet.Client/Common/BaseClientCommand.cs b/src/PowerShell/Microsoft.WinGet.Client/Common/BaseClientCommand.cs @@ -29,9 +29,9 @@ namespace Microsoft.WinGet.Client.Common public BaseClientCommand() : base() { - if (Utilities.ExecutingAsAdministrator) + if (Utilities.ExecutingAsSystem) { - throw new Exception(Utilities.ResourceManager.GetString("ExceptionAdministratorDisabled")); + throw new Exception(Utilities.ResourceManager.GetString("ExceptionSystemDisabled")); } } diff --git a/src/PowerShell/Microsoft.WinGet.Client/Common/ErrorCode.cs b/src/PowerShell/Microsoft.WinGet.Client/Common/ErrorCode.cs @@ -0,0 +1,19 @@ +// ----------------------------------------------------------------------------- +// <copyright file="ErrorCode.cs" company="Microsoft Corporation"> +// Copyright (c) Microsoft Corporation. Licensed under the MIT License. +// </copyright> +// ----------------------------------------------------------------------------- + +namespace Microsoft.WinGet.Client.Common +{ + /// <summary> + /// Error code constants. + /// </summary> + public class ErrorCode + { + /// <summary> + /// Error code for ERROR_FILE_NOT_FOUND. + /// </summary> + public const int FileNotFound = unchecked((int)0x80070002); + } +} diff --git a/src/PowerShell/Microsoft.WinGet.Client/Common/Utilities.cs b/src/PowerShell/Microsoft.WinGet.Client/Common/Utilities.cs @@ -37,5 +37,18 @@ namespace Microsoft.WinGet.Client.Common return principal.IsInRole(WindowsBuiltInRole.Administrator); } } + + /// <summary> + /// Gets a value indicating whether the current assembly is executing as a SYSTEM user. + /// </summary> + public static bool ExecutingAsSystem + { + get + { + WindowsIdentity identity = WindowsIdentity.GetCurrent(); + WindowsPrincipal principal = new (identity); + return principal.IsInRole(WindowsBuiltInRole.SystemOperator); + } + } } } diff --git a/src/PowerShell/Microsoft.WinGet.Client/Format.ps1xml b/src/PowerShell/Microsoft.WinGet.Client/Format.ps1xml @@ -74,10 +74,10 @@ <ScriptBlock>$_.Info.Name</ScriptBlock> </TableColumnItem> <TableColumnItem> - <ScriptBlock>$_.Info.Type</ScriptBlock> + <ScriptBlock>$_.Info.Argument</ScriptBlock> </TableColumnItem> <TableColumnItem> - <ScriptBlock>$_.Info.Argument</ScriptBlock> + <ScriptBlock>$_.Info.Type</ScriptBlock> </TableColumnItem> </TableColumnItems> </TableRowEntry> diff --git a/src/PowerShell/Microsoft.WinGet.Client/Helpers/ComObjectFactory.cs b/src/PowerShell/Microsoft.WinGet.Client/Helpers/ComObjectFactory.cs @@ -6,11 +6,12 @@ namespace Microsoft.WinGet.Client.Factories { - using System; - using Microsoft.Management.Deployment; - + using System; + using System.Runtime.InteropServices; + using Microsoft.Management.Deployment; + using Microsoft.WinGet.Client.Common; + #if NET - using System.Runtime.InteropServices; using WinRT; #endif @@ -19,14 +20,20 @@ namespace Microsoft.WinGet.Client.Factories /// </summary> public class ComObjectFactory { -#if USE_TEST_CLSIDS -#else +#if USE_PROD_CLSIDS private static readonly Guid PackageManagerClsid = Guid.Parse("C53A4F16-787E-42A4-B304-29EFFB4BF597"); private static readonly Guid FindPackagesOptionsClsid = Guid.Parse("572DED96-9C60-4526-8F92-EE7D91D38C1A"); private static readonly Guid CreateCompositePackageCatalogOptionsClsid = Guid.Parse("526534B8-7E46-47C8-8416-B1685C327D37"); private static readonly Guid InstallOptionsClsid = Guid.Parse("1095F097-EB96-453B-B4E6-1613637F3B14"); private static readonly Guid UninstallOptionsClsid = Guid.Parse("E1D9A11E-9F85-4D87-9C17-2B93143ADB8D"); private static readonly Guid PackageMatchFilterClsid = Guid.Parse("D02C9DAF-99DC-429C-B503-4E504E4AB000"); +#else + private static readonly Guid PackageManagerClsid = Guid.Parse("74CB3139-B7C5-4B9E-9388-E6616DEA288C"); + private static readonly Guid FindPackagesOptionsClsid = Guid.Parse("1BD8FF3A-EC50-4F69-AEEE-DF4C9D3BAA96"); + private static readonly Guid CreateCompositePackageCatalogOptionsClsid = Guid.Parse("EE160901-B317-4EA7-9CC6-5355C6D7D8A7"); + private static readonly Guid InstallOptionsClsid = Guid.Parse("44FE0580-62F7-44D4-9E91-AA9614AB3E86"); + private static readonly Guid UninstallOptionsClsid = Guid.Parse("AA2A5C04-1AD9-46C4-B74F-6B334AD7EB8C"); + private static readonly Guid PackageMatchFilterClsid = Guid.Parse("3F85B9F4-487A-4C48-9035-2903F8A6D9E8"); #endif private static readonly Type PackageManagerType = Type.GetTypeFromCLSID(PackageManagerClsid); @@ -34,7 +41,14 @@ namespace Microsoft.WinGet.Client.Factories private static readonly Type CreateCompositePackageCatalogOptionsType = Type.GetTypeFromCLSID(CreateCompositePackageCatalogOptionsClsid); private static readonly Type InstallOptionsType = Type.GetTypeFromCLSID(InstallOptionsClsid); private static readonly Type UninstallOptionsType = Type.GetTypeFromCLSID(UninstallOptionsClsid); - private static readonly Type PackageMatchFilterType = Type.GetTypeFromCLSID(PackageMatchFilterClsid); + private static readonly Type PackageMatchFilterType = Type.GetTypeFromCLSID(PackageMatchFilterClsid); + + private static readonly Guid PackageManagerIid = Guid.Parse("B375E3B9-F2E0-5C93-87A7-B67497F7E593"); + private static readonly Guid FindPackagesOptionsIid = Guid.Parse("A5270EDD-7DA7-57A3-BACE-F2593553561F"); + private static readonly Guid CreateCompositePackageCatalogOptionsIid = Guid.Parse("21ABAA76-089D-51C5-A745-C85EEFE70116"); + private static readonly Guid InstallOptionsIid = Guid.Parse("6EE9DB69-AB48-5E72-A474-33A924CD23B3"); + private static readonly Guid UninstallOptionsIid = Guid.Parse("3EBC67F0-8339-594B-8A42-F90B69D02BBE"); + private static readonly Guid PackageMatchFilterIid = Guid.Parse("D981ECA3-4DE5-5AD7-967A-698C7D60FC3B"); /// <summary> /// Creates an instance of the <see cref="PackageManager" /> class. @@ -42,7 +56,7 @@ namespace Microsoft.WinGet.Client.Factories /// <returns>A <see cref="PackageManager" /> instance.</returns> public virtual PackageManager CreatePackageManager() { - return Create<PackageManager>(PackageManagerType); + return Create<PackageManager>(PackageManagerType, PackageManagerIid); } /// <summary> @@ -51,7 +65,7 @@ namespace Microsoft.WinGet.Client.Factories /// <returns>A <see cref="FindPackagesOptions" /> instance.</returns> public virtual FindPackagesOptions CreateFindPackagesOptions() { - return Create<FindPackagesOptions>(FindPackagesOptionsType); + return Create<FindPackagesOptions>(FindPackagesOptionsType, FindPackagesOptionsIid); } /// <summary> @@ -60,7 +74,7 @@ namespace Microsoft.WinGet.Client.Factories /// <returns>A <see cref="CreateCompositePackageCatalogOptions" /> instance.</returns> public virtual CreateCompositePackageCatalogOptions CreateCreateCompositePackageCatalogOptions() { - return Create<CreateCompositePackageCatalogOptions>(CreateCompositePackageCatalogOptionsType); + return Create<CreateCompositePackageCatalogOptions>(CreateCompositePackageCatalogOptionsType, CreateCompositePackageCatalogOptionsIid); } /// <summary> @@ -69,7 +83,7 @@ namespace Microsoft.WinGet.Client.Factories /// <returns>An <see cref="InstallOptions" /> instance.</returns> public virtual InstallOptions CreateInstallOptions() { - return Create<InstallOptions>(InstallOptionsType); + return Create<InstallOptions>(InstallOptionsType, InstallOptionsIid); } /// <summary> @@ -78,7 +92,7 @@ namespace Microsoft.WinGet.Client.Factories /// <returns>A <see cref="UninstallOptions" /> instance.</returns> public virtual UninstallOptions CreateUninstallOptions() { - return Create<UninstallOptions>(UninstallOptionsType); + return Create<UninstallOptions>(UninstallOptionsType, UninstallOptionsIid); } /// <summary> @@ -87,12 +101,34 @@ namespace Microsoft.WinGet.Client.Factories /// <returns>A <see cref="PackageMatchFilter" /> instance.</returns> public virtual PackageMatchFilter CreatePackageMatchFilter() { - return Create<PackageMatchFilter>(PackageMatchFilterType); + return Create<PackageMatchFilter>(PackageMatchFilterType, PackageMatchFilterIid); } - private static T Create<T>(Type type) + private static T Create<T>(Type type, in Guid iid) { - object instance = Activator.CreateInstance(type); + object instance = null; + + if (Utilities.ExecutingAsAdministrator) + { + int hr = WinGetServerManualActivation_CreateInstance(type.GUID, iid, 0, out instance); + + if (hr < 0) + { + if (hr == ErrorCode.FileNotFound) + { + throw new Exception(Utilities.ResourceManager.GetString("WinGetPackageNotInstalled")); + } + else + { + throw new COMException($"Failed to create instance: {hr}", hr); + } + } + } + else + { + instance = Activator.CreateInstance(type); + } + #if NET IntPtr pointer = Marshal.GetIUnknownForObject(instance); return MarshalInterface<T>.FromAbi(pointer); @@ -100,5 +136,12 @@ namespace Microsoft.WinGet.Client.Factories return (T)instance; #endif } + + [DllImport("winrtact.dll", EntryPoint = "WinGetServerManualActivation_CreateInstance", ExactSpelling = true, PreserveSig = true)] + private static extern int WinGetServerManualActivation_CreateInstance( + [In, MarshalAs(UnmanagedType.LPStruct)] Guid clsid, + [In, MarshalAs(UnmanagedType.LPStruct)] Guid iid, + uint flags, + [Out, MarshalAs(UnmanagedType.IUnknown)] out object instance); } } diff --git a/src/PowerShell/Microsoft.WinGet.Client/Microsoft.WinGet.Client.csproj b/src/PowerShell/Microsoft.WinGet.Client/Microsoft.WinGet.Client.csproj @@ -16,6 +16,10 @@ <TargetFrameworks>$(CoreFramework);$(DesktopFramework)</TargetFrameworks> </PropertyGroup> + <PropertyGroup Condition="'$(WingetUseProdClsids)' == 'true'"> + <DefineConstants>USE_PROD_CLSIDS</DefineConstants> + </PropertyGroup> + <ItemGroup> <AdditionalFiles Include="stylecop.json" /> </ItemGroup> @@ -34,7 +38,7 @@ <OutputItemType>Content</OutputItemType> <CopyToOutputDirectory>Always</CopyToOutputDirectory> </ProjectReference> - <ProjectReference Include="..\..\UndockedRegFreeWinRT\UndockedRegFreeWinRT\UndockedRegFreeWinRT.vcxproj"> + <ProjectReference Include="..\..\Xlang\UndockedRegFreeWinRT\src\UndockedRegFreeWinRT\UndockedRegFreeWinRT\UndockedRegFreeWinRT.vcxproj"> <OutputItemType>Content</OutputItemType> <CopyToOutputDirectory>Always</CopyToOutputDirectory> </ProjectReference> @@ -48,7 +52,7 @@ <PackageReference Include="Microsoft.Windows.SDK.Contracts" Version="10.0.22000.196" PrivateAssets="all" /> </ItemGroup> - <ItemGroup> + <ItemGroup> <Compile Update="Properties\Resources.Designer.cs"> <DesignTime>True</DesignTime> <AutoGen>True</AutoGen> @@ -70,14 +74,25 @@ <TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion> </PropertyGroup> - <Target Name="CopyWinRTAct" AfterTargets="Build" Condition="'$(TargetFramework)' != ''"> + <Target Name="CopyWinRTAct-X64" AfterTargets="Build" Condition="'$(Platform)' == 'x64'"> <ItemGroup> - <!-- Ensure Referenced Project Output is in Output Directory --> - <WinRTActDll Include="$(SolutionDir)**\$(Platform)\$(Configuration)\**\winrtact.dll" /> + <WinRTActDll Include="$(SolutionDir)**\x64\$(Configuration)\UndockedRegFreeWinRT\winrtact.dll" /> </ItemGroup> - <Message Importance="high" Text="Copying '@(WinRTActDll)' to '$(OutputPath)'" /> - <Copy SourceFiles="@(WinRTActDll)" DestinationFolder="$(OutputPath)" /> - </Target> + <Message Importance="high" Text="Copying '@(WinRTActDll)'" /> + <Copy SourceFiles="@(WinRTActDll)" DestinationFolder="$(OutputPath)" /> + <Copy SourceFiles="@(WinRTActDll)" DestinationFolder="$(PowerShellModuleOutputDirectory)\$(Platform)\Core" /> + <Copy SourceFiles="@(WinRTActDll)" DestinationFolder="$(PowerShellModuleOutputDirectory)\$(Platform)\Desktop" /> + </Target> + + <Target Name="CopyWinRTAct-x86" AfterTargets="Build" Condition="'$(Platform)' == 'x86'"> + <ItemGroup> + <WinRTActDll Include="$(SolutionDir)**\Win32\$(Configuration)\UndockedRegFreeWinRT\winrtact.dll" /> + </ItemGroup> + <Message Importance="high" Text="Copying '@(WinRTActDll)'" /> + <Copy SourceFiles="@(WinRTActDll)" DestinationFolder="$(OutputPath)" /> + <Copy SourceFiles="@(WinRTActDll)" DestinationFolder="$(PowerShellModuleOutputDirectory)\$(Platform)\Core" /> + <Copy SourceFiles="@(WinRTActDll)" DestinationFolder="$(PowerShellModuleOutputDirectory)\$(Platform)\Desktop" /> + </Target> <!-- Build Microsoft.Winget.Client PowerShell Module --> <Target Name="CopyModuleFiles" AfterTargets="AfterBuild"> diff --git a/src/PowerShell/Microsoft.WinGet.Client/Properties/Resources.Designer.cs b/src/PowerShell/Microsoft.WinGet.Client/Properties/Resources.Designer.cs @@ -70,11 +70,11 @@ namespace Microsoft.WinGet.Client.Properties { } /// <summary> - /// Looks up a localized string similar to This cmdlet is currently disabled in an administrative context.. + /// Looks up a localized string similar to This cmdlet is currently disabled for SYSTEM.. /// </summary> - internal static string ExceptionAdministratorDisabled { + internal static string ExceptionSystemDisabled { get { - return ResourceManager.GetString("ExceptionAdministratorDisabled", resourceCulture); + return ResourceManager.GetString("ExceptionSystemDisabled", resourceCulture); } } @@ -149,5 +149,14 @@ namespace Microsoft.WinGet.Client.Properties { return ResourceManager.GetString("VagueCriteriaExceptionMessage", resourceCulture); } } + + /// <summary> + /// Looks up a localized string similar to Unable to execute command; WinGet package not installed.. + /// </summary> + internal static string WinGetPackageNotInstalled { + get { + return ResourceManager.GetString("WinGetPackageNotInstalled", resourceCulture); + } + } } } diff --git a/src/PowerShell/Microsoft.WinGet.Client/Properties/Resources.resx b/src/PowerShell/Microsoft.WinGet.Client/Properties/Resources.resx @@ -121,8 +121,8 @@ <value>No source matches the given value: {0}</value> <comment>{0} - The name of the source that was not found.</comment> </data> - <data name="ExceptionAdministratorDisabled" xml:space="preserve"> - <value>This cmdlet is currently disabled in an administrative context.</value> + <data name="ExceptionSystemDisabled" xml:space="preserve"> + <value>This cmdlet is currently disabled for SYSTEM.</value> </data> <data name="FindPackagesExceptionMessage" xml:space="preserve"> <value>An error occurred while searching for packages: {0}</value> @@ -154,4 +154,7 @@ <value>{0}, {1}, and {2} other packages matched the input criteria. Please refine the input.</value> <comment>{0} - The first conflicting package as a string. {1} - The second conflicting package. {2} - The number of other packages that also matched the input criteria.</comment> </data> + <data name="WinGetPackageNotInstalled" xml:space="preserve"> + <value>Unable to execute command; WinGet package not installed.</value> + </data> </root> \ No newline at end of file diff --git a/src/WinGetServer/Utils.cpp b/src/WinGetServer/Utils.cpp @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "Utils.h" +#pragma warning( push ) +#pragma warning ( disable : 6001 6388 6553) +#include <wil/resource.h> +#pragma warning( pop ) +#include <processthreadsapi.h> +#include <sddl.h> +#include <vector> + +unsigned char* GetUCharString(const std::string& str) +{ + return reinterpret_cast<unsigned char*>(const_cast<char*>(str.c_str())); +} + +std::string GetUserSID() +{ + HANDLE hToken = NULL; + THROW_LAST_ERROR_IF(!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)); + + DWORD dwBufferSize = 0; + THROW_LAST_ERROR_IF(!GetTokenInformation(hToken, TokenUser, NULL, 0, &dwBufferSize) && GetLastError() != ERROR_INSUFFICIENT_BUFFER); + + std::vector<BYTE> buffer; + buffer.resize(dwBufferSize); + PTOKEN_USER pTokenUser = reinterpret_cast<PTOKEN_USER>(&buffer[0]); + + THROW_LAST_ERROR_IF(!GetTokenInformation(hToken, TokenUser, pTokenUser, dwBufferSize, &dwBufferSize)); + THROW_HR_IF(CO_E_INVALIDSID, !IsValidSid(pTokenUser->User.Sid)); + + LPSTR pszSID = NULL; + THROW_LAST_ERROR_IF(!ConvertSidToStringSidA(pTokenUser->User.Sid, &pszSID)); + return std::string{ pszSID }; +}+ \ No newline at end of file diff --git a/src/WinGetServer/Utils.h b/src/WinGetServer/Utils.h @@ -0,0 +1,8 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include <string> + +unsigned char* GetUCharString(const std::string& str); + +std::string GetUserSID();+ \ No newline at end of file diff --git a/src/WinGetServer/WinGetServer.idl b/src/WinGetServer/WinGetServer.idl @@ -0,0 +1,17 @@ +import "wtypesbase.idl"; + +[ + uuid(0ca09dda-857f-479f-ba4b-da875d90051e), + version(1.0), + implicit_handle(handle_t WinGetServerManualActivation_IfHandle) +] +interface WinGetServerManualActivation +{ + HRESULT CreateInstance( + [in] GUID clsid, + [in] GUID iid, + [in] UINT32 flags, + [out, ref] UINT32* pcbBuffer, + [out, ref, size_is(, *pcbBuffer)] BYTE** ppBuffer + ); +}+ \ No newline at end of file diff --git a/src/WinGetServer/WinGetServer.vcxproj b/src/WinGetServer/WinGetServer.vcxproj @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <Import Project="$(SolutionDir)\packages\Microsoft.Windows.CppWinRT.2.0.210505.3\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('$(SolutionDir)\packages\Microsoft.Windows.CppWinRT.2.0.210505.3\build\native\Microsoft.Windows.CppWinRT.props')" /> <PropertyGroup Label="Globals"> <CppWinRTOptimized>true</CppWinRTOptimized> <CppWinRTRootNamespaceAutoMerge>true</CppWinRTRootNamespaceAutoMerge> @@ -107,18 +106,22 @@ <SubSystem>Windows</SubSystem> <GenerateWindowsMetadata>false</GenerateWindowsMetadata> <AdditionalLibraryDirectories>$(OutDir)..\Microsoft.Management.Deployment;$(OutDir)..\AppInstallerCLICore;$(OutDir)..\JsonCppLib;$(OutDir)..\AppInstallerRepositoryCore;$(OutDir)..\YamlCppLib;$(OutDir)..\AppInstallerCommonCore;$(OutDir)..\cpprestsdk;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> - <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> + <AdditionalDependencies>Rpcrt4.lib;Advapi32.lib;Shell32.lib;Ole32.lib;%(AdditionalDependencies)</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'"> <ClCompile> <Optimization>Disabled</Optimization> <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <LanguageStandard Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">stdcpp17</LanguageStandard> + <LanguageStandard Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">stdcpp17</LanguageStandard> + <LanguageStandard Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">stdcpp17</LanguageStandard> </ClCompile> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Platform)'=='Win32'"> <ClCompile> <PreprocessorDefinitions>WIN32;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <LanguageStandard Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">stdcpp17</LanguageStandard> </ClCompile> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)'=='Release'"> @@ -127,6 +130,10 @@ <FunctionLevelLinking>true</FunctionLevelLinking> <IntrinsicFunctions>true</IntrinsicFunctions> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <LanguageStandard Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">stdcpp17</LanguageStandard> + <LanguageStandard Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">stdcpp17</LanguageStandard> + <LanguageStandard Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">stdcpp17</LanguageStandard> + <LanguageStandard Condition="'$(Configuration)|$(Platform)'=='Release|x64'">stdcpp17</LanguageStandard> </ClCompile> <Link> <EnableCOMDATFolding>true</EnableCOMDATFolding> @@ -137,23 +144,42 @@ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">/debug:full /debugtype:cv,fixup /incremental:no %(AdditionalOptions)</AdditionalOptions> </Link> </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(WingetEnableReleaseBuild)'=='true'"> + <ClCompile> + <PreprocessorDefinitions>USE_PROD_WINGET_SERVER;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ClCompile> + </ItemDefinitionGroup> <ItemGroup> <ClInclude Include="resource.h" /> + <ClCompile Include="WinGetServerManualActivation_Client.cpp"> + <ExcludedFromBuild>true</ExcludedFromBuild> + </ClCompile> + <ClCompile Include="WinGetServer_s.c" /> <ClCompile Include="WinMain.cpp" /> <ResourceCompile Include="WinGetServer.rc" /> <Manifest Include="WinGetServer.exe.manifest" /> <None Include="packages.config" /> <None Include="PropertySheet.props" /> + <ClInclude Include="Utils.h" /> + <ClCompile Include="Utils.cpp" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\WindowsPackageManager\WindowsPackageManager.vcxproj"> <Project>{2046b5af-666d-4ce8-8d3e-c32c57908a56}</Project> </ProjectReference> </ItemGroup> + <ItemGroup> + <Midl Include="WinGetServer.idl"> + <EnableWindowsRuntime>false</EnableWindowsRuntime> + <GenerateClientFiles>Stub</GenerateClientFiles> + <GenerateServerFiles>Stub</GenerateServerFiles> + <ApplicationConfigurationMode>true</ApplicationConfigurationMode> + <HeaderFileName>WinGetServer.h</HeaderFileName> + </Midl> + </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> <Import Project="$(SolutionDir)\packages\Microsoft.Windows.ImplementationLibrary.1.0.210204.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('$(SolutionDir)\packages\Microsoft.Windows.ImplementationLibrary.1.0.210204.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" /> - <Import Project="$(SolutionDir)\packages\Microsoft.Windows.CppWinRT.2.0.210505.3\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('$(SolutionDir)\packages\Microsoft.Windows.CppWinRT.2.0.210505.3\build\native\Microsoft.Windows.CppWinRT.targets')" /> </ImportGroup> <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> <PropertyGroup> diff --git a/src/WinGetServer/WinGetServer.vcxproj.filters b/src/WinGetServer/WinGetServer.vcxproj.filters @@ -18,11 +18,23 @@ <ClInclude Include="resource.h"> <Filter>Header Files</Filter> </ClInclude> + <ClInclude Include="Utils.h"> + <Filter>Header Files</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <ClCompile Include="WinMain.cpp"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="WinGetServer_s.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="WinGetServerManualActivation_Client.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Utils.cpp"> + <Filter>Source Files</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <None Include="PropertySheet.props" /> @@ -36,4 +48,9 @@ <Filter>Resource Files</Filter> </ResourceCompile> </ItemGroup> + <ItemGroup> + <Midl Include="WinGetServer.idl"> + <Filter>Source Files</Filter> + </Midl> + </ItemGroup> </Project> \ No newline at end of file diff --git a/src/WinGetServer/WinGetServerManualActivation_Client.cpp b/src/WinGetServer/WinGetServerManualActivation_Client.cpp @@ -0,0 +1,157 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "WinGetServer.h" +#include "Utils.h" + +#include <wil/com.h> +#include <wil/result.h> +#include <wil/safecast.h> + +#include <memory> +#include <mutex> +#include <string> +#include <shtypes.h> +#include <filesystem> +#include <shlobj_core.h> + +#if USE_PROD_WINGET_SERVER +const std::wstring_view s_ServerExePath = L"Microsoft\\WindowsApps\\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe\\WindowsPackageManagerServer.exe"; +#else +const std::wstring_view s_ServerExePath = L"Microsoft\\WindowsApps\\WinGetDevCLI_8wekyb3d8bbwe\\WindowsPackageManagerServerDev.exe"; +#endif + +_Must_inspect_result_ +_Ret_maybenull_ _Post_writable_byte_size_(size) +void* __RPC_USER MIDL_user_allocate(_In_ size_t size) +{ + return malloc(size); +} + +void __RPC_USER MIDL_user_free(_Pre_maybenull_ _Post_invalid_ void* ptr) +{ + if (ptr) + { + free(ptr); + } +} + +std::filesystem::path GetKnownFolderPath(const KNOWNFOLDERID& id) +{ + wil::unique_cotaskmem_string knownFolder = nullptr; + THROW_IF_FAILED(SHGetKnownFolderPath(id, KF_FLAG_NO_ALIAS | KF_FLAG_DONT_VERIFY | KF_FLAG_NO_PACKAGE_REDIRECTION, NULL, &knownFolder)); + return knownFolder.get(); +} + +struct FreeWithRpcStringFree { void operator()(RPC_CSTR* in) { RpcStringFreeA(in); } }; +using UniqueRpcString = std::unique_ptr<RPC_CSTR, FreeWithRpcStringFree>; + +struct DeleteWithMidlFree { void operator()(void* m) { MIDL_user_free(m); } }; +using UniqueMidl = std::unique_ptr<BYTE, DeleteWithMidlFree>; + +void InitializeRpcBinding() +{ + std::string protocol = "ncacn_np"; + std::string endpoint = "\\pipe\\WinGetServerManualActivation_" + GetUserSID(); + + unsigned char* binding = nullptr; + UniqueRpcString bindingPtr; + + RPC_STATUS status = RpcStringBindingComposeA(nullptr, GetUCharString(protocol), nullptr, GetUCharString(endpoint), nullptr, &binding); + THROW_HR_IF(HRESULT_FROM_WIN32(status), status != RPC_S_OK); + bindingPtr.reset(&binding); + + status = RpcBindingFromStringBindingA(binding, &WinGetServerManualActivation_IfHandle); + THROW_HR_IF(HRESULT_FROM_WIN32(status), status != RPC_S_OK); +} + +HRESULT LaunchWinGetServerWithManualActivation() +{ + const std::filesystem::path& localAppDataPath = GetKnownFolderPath(FOLDERID_LocalAppData); + const std::filesystem::path& serverExePath = localAppDataPath / s_ServerExePath; + std::wstring commandLineInput = std::wstring{ serverExePath } + L" --manualActivation"; + + STARTUPINFO info = { sizeof(info) }; + wil::unique_process_information process; + + RETURN_LAST_ERROR_IF(!CreateProcessW(NULL, &commandLineInput[0], NULL, NULL, FALSE, 0, NULL, NULL, &info, &process)); + + // Wait for manual reset event from server before proceeding with COM activation. + wil::unique_event manualResetEvent; + if (manualResetEvent.try_open(L"WinGetServerStartEvent")) + { + manualResetEvent.wait(); + } + + return S_OK; +} + +HRESULT CallCreateInstance(REFCLSID rclsid, REFIID riid, UINT32 flags, UINT32* bufferByteCount, BYTE** buffer) +{ + RpcTryExcept + { + RETURN_IF_FAILED(CreateInstance(rclsid, riid, flags, bufferByteCount, buffer)); + } + RpcExcept(1) + { + return HRESULT_FROM_WIN32(RpcExceptionCode()); + } + RpcEndExcept; + + return S_OK; +} + +HRESULT CreateComInstance(REFCLSID rclsid, REFIID riid, UINT32 flags, void** out) +{ + UINT32 bufferByteCount = 0; + BYTE* buffer = nullptr; + UniqueMidl bufferPtr; + + RETURN_IF_FAILED(CallCreateInstance(rclsid, riid, flags, &bufferByteCount, &buffer)); + + bufferPtr.reset(buffer); + + wil::com_ptr<IStream> stream; + RETURN_IF_FAILED(CreateStreamOnHGlobal(nullptr, TRUE, &stream)); + RETURN_IF_FAILED(stream->Write(buffer, bufferByteCount, nullptr)); + RETURN_IF_FAILED(stream->Seek({}, STREAM_SEEK_SET, nullptr)); + + wil::com_ptr<IUnknown> output; + RETURN_IF_FAILED(CoUnmarshalInterface(stream.get(), riid, reinterpret_cast<void**>(&output))); + *out = output.detach(); + return S_OK; +} + +extern "C" HRESULT WinGetServerManualActivation_CreateInstance(REFCLSID rclsid, REFIID riid, UINT32 flags, void** out) +{ + RETURN_HR_IF_NULL(E_POINTER, out); + + static std::once_flag rpcBindingOnce; + try + { + std::call_once(rpcBindingOnce, InitializeRpcBinding); + } + CATCH_RETURN(); + + HRESULT result = CreateComInstance(rclsid, riid, flags, out); + if (FAILED(result)) + { + for (int i = 0; i < 3; i++) + { + result = LaunchWinGetServerWithManualActivation(); + if (result == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) + { + break; + } + + result = CreateComInstance(rclsid, riid, flags, out); + if (SUCCEEDED(result)) + { + break; + } + + Sleep(200); + } + } + + return result; +}+ \ No newline at end of file diff --git a/src/WinGetServer/WinMain.cpp b/src/WinGetServer/WinMain.cpp @@ -1,12 +1,21 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +#define NOMINMAX #pragma warning( push ) -#pragma warning ( disable : 6553) +#pragma warning ( disable : 6001 6388 6553) #include <wil/resource.h> +#include <wil/com.h> #pragma warning( pop ) -#include <winrt/base.h> #include <objidl.h> +#include <shellapi.h> +#include <sddl.h> #include <WindowsPackageManager.h> +#include "WinGetServer.h" +#include "Utils.h" + +#include <memory> +#include <string> +#include <string_view> // Holds the wwinmain open until COM tells us there are no more server connections wil::unique_event _comServerExitEvent; @@ -18,26 +27,141 @@ static void _releaseNotifier() noexcept _comServerExitEvent.SetEvent(); } -int __stdcall wWinMain(_In_ HINSTANCE, _In_opt_ HINSTANCE, _In_ LPWSTR, _In_ int) +HRESULT WindowsPackageManagerServerInitializeRPCServer() +{ + std::string userSID = GetUserSID(); + std::string endpoint = "\\pipe\\WinGetServerManualActivation_" + userSID; + RPC_STATUS status = RpcServerUseProtseqEpA(GetUCharString("ncacn_np"), RPC_C_PROTSEQ_MAX_REQS_DEFAULT, GetUCharString(endpoint), nullptr); + RETURN_HR_IF(HRESULT_FROM_WIN32(status), status != RPC_S_OK); + + // The goal of this security descriptor is to restrict RPC server access only to the user in admin mode. + // (ML;;NW;;;HI) specifies a high mandatory integrity level (requires admin). + // (A;;GA;;;UserSID) specifies access only for the user with the user SID (i.e. self). + wil::unique_hlocal_security_descriptor securityDescriptor; + std::string securityDescriptorString = "S:(ML;;NW;;;HI)D:(A;;GA;;;" + userSID + ")"; + RETURN_LAST_ERROR_IF(!ConvertStringSecurityDescriptorToSecurityDescriptorA(securityDescriptorString.c_str(), SDDL_REVISION_1, &securityDescriptor, nullptr)); + + status = RpcServerRegisterIf3(WinGetServerManualActivation_v1_0_s_ifspec, nullptr, nullptr, RPC_IF_ALLOW_LOCAL_ONLY | RPC_IF_AUTOLISTEN, RPC_C_LISTEN_MAX_CALLS_DEFAULT, 0, nullptr, securityDescriptor.get()); + RETURN_HR_IF(HRESULT_FROM_WIN32(status), status != RPC_S_OK); + + return S_OK; +} + +_Must_inspect_result_ +_Ret_maybenull_ _Post_writable_byte_size_(size) +void* __RPC_USER MIDL_user_allocate(_In_ size_t size) +{ + return malloc(size); +} + +void __RPC_USER MIDL_user_free(_Pre_maybenull_ _Post_invalid_ void* ptr) +{ + if (ptr) + { + free(ptr); + } +} + +extern "C" HRESULT CreateInstance( + /* [in] */ GUID clsid, + /* [in] */ GUID iid, + /* [in] */ UINT32, + /* [ref][out] */ UINT32 * pcbBuffer, + /* [size_is][size_is][ref][out] */ BYTE * *ppBuffer) +{ + RETURN_HR_IF_NULL(E_POINTER, pcbBuffer); + RETURN_HR_IF_NULL(E_POINTER, ppBuffer); + + wil::com_ptr<IStream> stream; + RETURN_IF_FAILED(CreateStreamOnHGlobal(nullptr, TRUE, &stream)); + + wil::com_ptr<IUnknown> instance; + RETURN_IF_FAILED(WindowsPackageManagerServerCreateInstance(clsid, iid, reinterpret_cast<void**>(&instance))); + + RETURN_IF_FAILED(CoMarshalInterface(stream.get(), iid, instance.get(), MSHCTX_LOCAL, nullptr, MSHLFLAGS_NORMAL)); + + ULARGE_INTEGER streamSize{}; + RETURN_IF_FAILED(stream->Seek({}, STREAM_SEEK_CUR, &streamSize)); + RETURN_HR_IF(E_NOT_SUFFICIENT_BUFFER, streamSize.QuadPart > std::numeric_limits<UINT32>::max()); + + UINT32 bufferSize = static_cast<UINT32>(streamSize.QuadPart); + + struct DeleteWithMidlFree { void operator()(void* m) { MIDL_user_free(m); } }; + std::unique_ptr<BYTE, DeleteWithMidlFree> buffer{ reinterpret_cast<BYTE*>(MIDL_user_allocate(bufferSize)) }; + + RETURN_IF_FAILED(stream->Seek({}, STREAM_SEEK_SET, nullptr)); + ULONG bytesRead = 0; + RETURN_IF_FAILED(stream->Read(buffer.get(), bufferSize, &bytesRead)); + RETURN_HR_IF(E_UNEXPECTED, bytesRead != bufferSize); + + *pcbBuffer = bufferSize; + *ppBuffer = buffer.release(); + + return S_OK; +} + +int __stdcall wWinMain(_In_ HINSTANCE, _In_opt_ HINSTANCE, _In_ LPWSTR cmdLine, _In_ int) { - winrt::init_apartment(); + wil::SetResultLoggingCallback(&WindowsPackageManagerServerWilResultLoggingCallback); + + RETURN_IF_FAILED(CoInitializeEx(nullptr, COINIT_MULTITHREADED)); // Enable fast rundown of objects so that the server exits faster when clients go away. { - winrt::com_ptr<IGlobalOptions> globalOptions; - winrt::check_hresult(CoCreateInstance(CLSID_GlobalOptions, nullptr, CLSCTX_INPROC, IID_PPV_ARGS(&globalOptions))); - winrt::check_hresult(globalOptions->Set(COMGLB_RO_SETTINGS, COMGLB_FAST_RUNDOWN)); + wil::com_ptr<IGlobalOptions> globalOptions; + RETURN_IF_FAILED(CoCreateInstance(CLSID_GlobalOptions, nullptr, CLSCTX_INPROC, IID_PPV_ARGS(&globalOptions))); + RETURN_IF_FAILED(globalOptions->Set(COMGLB_RO_SETTINGS, COMGLB_FAST_RUNDOWN)); } RETURN_IF_FAILED(WindowsPackageManagerServerInitialize()); + // Command line parsing + int argc = 0; + LPWSTR* argv = CommandLineToArgvW(cmdLine, &argc); + RETURN_LAST_ERROR_IF(!argv); + + bool manualActivation = false; + + // If command line gets more complicated, consider more complex parsing + if (argc == 1 && std::wstring_view{ L"--manualActivation" } == argv[0]) + { + manualActivation = true; + } + _comServerExitEvent.create(); RETURN_IF_FAILED(WindowsPackageManagerServerModuleCreate(&_releaseNotifier)); try { // Register all the CoCreatableClassWrlCreatorMapInclude classes RETURN_IF_FAILED(WindowsPackageManagerServerModuleRegister()); + + if (manualActivation) + { + HANDLE hMutex = NULL; + hMutex = CreateMutex(NULL, FALSE, TEXT("WinGetServerMutex")); + RETURN_LAST_ERROR_IF_NULL(hMutex); + + DWORD waitResult = WaitForSingleObject(hMutex, 0); + if (waitResult != WAIT_OBJECT_0 && waitResult != WAIT_ABANDONED) + { + return HRESULT_FROM_WIN32(ERROR_SERVICE_ALREADY_RUNNING); + } + + RETURN_IF_FAILED(WindowsPackageManagerServerInitializeRPCServer()); + } + + // Manual reset event to notify the client that the server is available. + wil::unique_event manualResetEvent; + if (!manualResetEvent.try_create(wil::EventOptions::ManualReset, L"WinGetServerStartEvent")) + { + manualResetEvent.open(L"WinGetServerStartEvent"); + } + + manualResetEvent.SetEvent(); + _comServerExitEvent.wait(); + + manualResetEvent.reset(); RETURN_IF_FAILED(WindowsPackageManagerServerModuleUnregister()); } CATCH_RETURN() diff --git a/src/WinGetServer/packages.config b/src/WinGetServer/packages.config @@ -1,5 +1,4 @@ <?xml version="1.0" encoding="utf-8"?> <packages> - <package id="Microsoft.Windows.CppWinRT" version="2.0.210505.3" targetFramework="native" /> <package id="Microsoft.Windows.ImplementationLibrary" version="1.0.210204.1" targetFramework="native" /> </packages> \ No newline at end of file diff --git a/src/WindowsPackageManager/Source.def b/src/WindowsPackageManager/Source.def @@ -5,6 +5,8 @@ EXPORTS WindowsPackageManagerServerModuleCreate WindowsPackageManagerServerModuleRegister WindowsPackageManagerServerModuleUnregister + WindowsPackageManagerServerWilResultLoggingCallback + WindowsPackageManagerServerCreateInstance WindowsPackageManagerInProcModuleInitialize WindowsPackageManagerInProcModuleTerminate WindowsPackageManagerInProcModuleGetClassObject diff --git a/src/WindowsPackageManager/WindowsPackageManager.h b/src/WindowsPackageManager/WindowsPackageManager.h @@ -3,6 +3,9 @@ #pragma once #include <hstring.h> +// Forward declaration +namespace wil { struct FailureInfo; } + extern "C" { #define WINDOWS_PACKAGE_MANAGER_API_CALLING_CONVENTION __stdcall @@ -25,6 +28,12 @@ extern "C" // Unregisters the server module class factories. WINDOWS_PACKAGE_MANAGER_API WindowsPackageManagerServerModuleUnregister(); + // Callback for logging the WIL result reported from the server. + void WINDOWS_PACKAGE_MANAGER_API_CALLING_CONVENTION WindowsPackageManagerServerWilResultLoggingCallback(const wil::FailureInfo& info) noexcept; + + // Creates an out-of-proc instance for manual activation scenarios. + WINDOWS_PACKAGE_MANAGER_API WindowsPackageManagerServerCreateInstance(REFCLSID rclsid, REFIID riid, void** out); + // Creates module for in-proc COM invocation. WINDOWS_PACKAGE_MANAGER_API WindowsPackageManagerInProcModuleInitialize(); diff --git a/src/WindowsPackageManager/WindowsPackageManager.vcxproj b/src/WindowsPackageManager/WindowsPackageManager.vcxproj @@ -166,9 +166,9 @@ <ClCompile> <Optimization>Disabled</Optimization> <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">$(ProjectDir);$(ProjectDir)..\AppInstallerCLICore\Public\;$(ProjectDir)..\AppInstallerCommonCore;$(ProjectDir)..\AppInstallerRepositoryCore;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\JsonCppLib\json;$(ProjectDir)..\Microsoft.Management.Deployment\Public;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">$(ProjectDir);$(ProjectDir)..\AppInstallerCLICore\Public\;$(ProjectDir)..\AppInstallerCommonCore;$(ProjectDir)..\AppInstallerRepositoryCore;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\JsonCppLib\json;$(ProjectDir)..\Microsoft.Management.Deployment\Public;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(ProjectDir);$(ProjectDir)..\AppInstallerCLICore\Public\;$(ProjectDir)..\AppInstallerCommonCore;$(ProjectDir)..\AppInstallerRepositoryCore;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\JsonCppLib\json;$(ProjectDir)..\Microsoft.Management.Deployment\Public;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">$(ProjectDir);$(ProjectDir)..\AppInstallerCLICore\Public\;$(ProjectDir)..\AppInstallerRepositoryCore;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\JsonCppLib\json;$(ProjectDir)..\Microsoft.Management.Deployment\Public;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">$(ProjectDir);$(ProjectDir)..\AppInstallerCLICore\Public\;$(ProjectDir)..\AppInstallerRepositoryCore;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\JsonCppLib\json;$(ProjectDir)..\Microsoft.Management.Deployment\Public;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(ProjectDir);$(ProjectDir)..\AppInstallerCLICore\Public\;$(ProjectDir)..\AppInstallerRepositoryCore;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\JsonCppLib\json;$(ProjectDir)..\Microsoft.Management.Deployment\Public;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <TreatWarningAsError Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">true</TreatWarningAsError> <TreatWarningAsError Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">true</TreatWarningAsError> <TreatWarningAsError Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</TreatWarningAsError> @@ -210,7 +210,7 @@ <ItemDefinitionGroup Condition="'$(Platform)'=='Win32'"> <ClCompile> <PreprocessorDefinitions>WIN32;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(ProjectDir);$(ProjectDir)..\AppInstallerCLICore\Public\;$(ProjectDir)..\AppInstallerCommonCore;$(ProjectDir)..\AppInstallerRepositoryCore;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\JsonCppLib\json;$(ProjectDir)..\Microsoft.Management.Deployment\Public;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(ProjectDir);$(ProjectDir)..\AppInstallerCLICore\Public\;$(ProjectDir)..\AppInstallerRepositoryCore;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\JsonCppLib\json;$(ProjectDir)..\Microsoft.Management.Deployment\Public;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <TreatWarningAsError Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</TreatWarningAsError> <ControlFlowGuard Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</ControlFlowGuard> <LanguageStandard Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">stdcpp17</LanguageStandard> @@ -227,10 +227,10 @@ <FunctionLevelLinking>true</FunctionLevelLinking> <IntrinsicFunctions>true</IntrinsicFunctions> <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">$(ProjectDir);$(ProjectDir)..\AppInstallerCLICore\Public\;$(ProjectDir)..\AppInstallerCommonCore;$(ProjectDir)..\AppInstallerRepositoryCore;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\Microsoft.Management.Deployment\Public;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">$(ProjectDir);$(ProjectDir)..\AppInstallerCLICore\Public\;$(ProjectDir)..\AppInstallerCommonCore;$(ProjectDir)..\AppInstallerRepositoryCore;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\Microsoft.Management.Deployment\Public;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(ProjectDir);$(ProjectDir)..\AppInstallerCLICore\Public\;$(ProjectDir)..\AppInstallerCommonCore;$(ProjectDir)..\AppInstallerRepositoryCore;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\Microsoft.Management.Deployment\Public;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(ProjectDir);$(ProjectDir)..\AppInstallerCLICore\Public\;$(ProjectDir)..\AppInstallerCommonCore;$(ProjectDir)..\AppInstallerRepositoryCore;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\Microsoft.Management.Deployment\Public;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">$(ProjectDir);$(ProjectDir)..\AppInstallerCLICore\Public\;$(ProjectDir)..\AppInstallerRepositoryCore;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\Microsoft.Management.Deployment\Public;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">$(ProjectDir);$(ProjectDir)..\AppInstallerCLICore\Public\;$(ProjectDir)..\AppInstallerRepositoryCore;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\Microsoft.Management.Deployment\Public;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(ProjectDir);$(ProjectDir)..\AppInstallerCLICore\Public\;$(ProjectDir)..\AppInstallerRepositoryCore;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\Microsoft.Management.Deployment\Public;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(ProjectDir);$(ProjectDir)..\AppInstallerCLICore\Public\;$(ProjectDir)..\AppInstallerRepositoryCore;$(ProjectDir)..\AppInstallerCommonCore\Public;$(ProjectDir)..\Microsoft.Management.Deployment\Public;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <TreatWarningAsError Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">true</TreatWarningAsError> <TreatWarningAsError Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">true</TreatWarningAsError> <TreatWarningAsError Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</TreatWarningAsError> diff --git a/src/WindowsPackageManager/main.cpp b/src/WindowsPackageManager/main.cpp @@ -10,6 +10,9 @@ #include "WindowsPackageManager.h" #include <AppInstallerCLICore.h> +#include <AppInstallerFileLogger.h> +#include <AppInstallerStrings.h> +#include <AppInstallerTelemetry.h> #include <ComClsids.h> using namespace winrt::Microsoft::Management::Deployment; @@ -58,6 +61,21 @@ extern "C" } CATCH_RETURN(); + void WINDOWS_PACKAGE_MANAGER_API_CALLING_CONVENTION WindowsPackageManagerServerWilResultLoggingCallback(const wil::FailureInfo& failure) noexcept try + { + AppInstaller::Logging::Telemetry().LogFailure(failure); + } + CATCH_LOG(); + + WINDOWS_PACKAGE_MANAGER_API WindowsPackageManagerServerCreateInstance(REFCLSID rclsid, REFIID riid, void** out) try + { + RETURN_HR_IF_NULL(E_POINTER, out); + ::Microsoft::WRL::ComPtr<IClassFactory> factory; + RETURN_IF_FAILED(::Microsoft::WRL::Module<::Microsoft::WRL::ModuleType::OutOfProc>::GetModule().GetClassObject(rclsid, IID_PPV_ARGS(&factory))); + RETURN_HR(factory->CreateInstance(nullptr, riid, out)); + } + CATCH_RETURN(); + WINDOWS_PACKAGE_MANAGER_API WindowsPackageManagerInProcModuleInitialize() try { ::Microsoft::WRL::Module<::Microsoft::WRL::ModuleType::InProc>::Create(); diff --git a/src/Xlang/UndockedRegFreeWinRT/src/UndockedRegFreeWinRT/UndockedRegFreeWinRT/UndockedRegFreeWinRT.vcxproj b/src/Xlang/UndockedRegFreeWinRT/src/UndockedRegFreeWinRT/UndockedRegFreeWinRT/UndockedRegFreeWinRT.vcxproj @@ -98,7 +98,7 @@ <SubSystem>Windows</SubSystem> <GenerateDebugInformation>true</GenerateDebugInformation> <EnableUAC>false</EnableUAC> - <AdditionalDependencies>comsuppw.lib;shlwapi.lib;xmllite.lib;runtimeobject.lib;Pathcch.lib;Rometadata.lib</AdditionalDependencies> + <AdditionalDependencies>comsuppw.lib;shlwapi.lib;xmllite.lib;runtimeobject.lib;Pathcch.lib;Rometadata.lib;Rpcrt4.lib;Shell32.lib;Advapi32.lib</AdditionalDependencies> <ModuleDefinitionFile>winrtact.def</ModuleDefinitionFile> </Link> </ItemDefinitionGroup> @@ -117,7 +117,7 @@ <SubSystem>Windows</SubSystem> <GenerateDebugInformation>true</GenerateDebugInformation> <EnableUAC>false</EnableUAC> - <AdditionalDependencies>comsuppw.lib;shlwapi.lib;xmllite.lib;runtimeobject.lib;Pathcch.lib;Rometadata.lib</AdditionalDependencies> + <AdditionalDependencies>comsuppw.lib;shlwapi.lib;xmllite.lib;runtimeobject.lib;Pathcch.lib;Rometadata.lib;Rpcrt4.lib;Shell32.lib;Advapi32.lib</AdditionalDependencies> <ModuleDefinitionFile>winrtact.def</ModuleDefinitionFile> </Link> </ItemDefinitionGroup> @@ -140,7 +140,7 @@ <OptimizeReferences>true</OptimizeReferences> <GenerateDebugInformation>true</GenerateDebugInformation> <EnableUAC>false</EnableUAC> - <AdditionalDependencies>comsuppw.lib;shlwapi.lib;xmllite.lib;runtimeobject.lib;Pathcch.lib;Rometadata.lib</AdditionalDependencies> + <AdditionalDependencies>comsuppw.lib;shlwapi.lib;xmllite.lib;runtimeobject.lib;Pathcch.lib;Rometadata.lib;Rpcrt4.lib;Shell32.lib;Advapi32.lib</AdditionalDependencies> <ModuleDefinitionFile>winrtact.def</ModuleDefinitionFile> </Link> </ItemDefinitionGroup> @@ -163,7 +163,7 @@ <OptimizeReferences>true</OptimizeReferences> <GenerateDebugInformation>true</GenerateDebugInformation> <EnableUAC>false</EnableUAC> - <AdditionalDependencies>comsuppw.lib;shlwapi.lib;xmllite.lib;runtimeobject.lib;Pathcch.lib;Rometadata.lib</AdditionalDependencies> + <AdditionalDependencies>comsuppw.lib;shlwapi.lib;xmllite.lib;runtimeobject.lib;Pathcch.lib;Rometadata.lib;Rpcrt4.lib;Shell32.lib;Advapi32.lib</AdditionalDependencies> <ModuleDefinitionFile>winrtact.def</ModuleDefinitionFile> </Link> </ItemDefinitionGroup> @@ -173,6 +173,10 @@ <None Include="winrtact.def" /> </ItemGroup> <ItemGroup> + <ClCompile Include="..\..\..\..\..\WinGetServer\WinGetServerManualActivation_Client.cpp" /> + <ClCompile Include="..\..\..\..\..\WinGetServer\WinGetServer_c.c" /> + <ClInclude Include="..\..\..\..\..\WinGetServer\Utils.h" /> + <ClCompile Include="..\..\..\..\..\WinGetServer\Utils.cpp" /> <ClCompile Include="catalog.cpp" /> <ClCompile Include="dllmain.cpp" /> <ClCompile Include="typeresolution.cpp" /> diff --git a/src/Xlang/UndockedRegFreeWinRT/src/UndockedRegFreeWinRT/UndockedRegFreeWinRT/UndockedRegFreeWinRT.vcxproj.filters b/src/Xlang/UndockedRegFreeWinRT/src/UndockedRegFreeWinRT/UndockedRegFreeWinRT/UndockedRegFreeWinRT.vcxproj.filters @@ -1,46 +1,58 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <ItemGroup> - <Filter Include="Source Files"> - <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> - <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> - </Filter> - <Filter Include="Header Files"> - <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> - <Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions> - </Filter> - <Filter Include="Resource Files"> - <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> - <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> - </Filter> - </ItemGroup> - <ItemGroup> - <None Include="cpp.hint" /> - <None Include="winrtact.def"> - <Filter>Source Files</Filter> - </None> - <None Include="packages.config" /> - </ItemGroup> - <ItemGroup> - <ClCompile Include="catalog.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="dllmain.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="typeresolution.h"> - <Filter>Header Files</Filter> - </ClCompile> - <ClCompile Include="typeresolution.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - </ItemGroup> - <ItemGroup> - <ClInclude Include="catalog.h"> - <Filter>Header Files</Filter> - </ClInclude> - <ClInclude Include="extwinrt.h"> - <Filter>Header Files</Filter> - </ClInclude> - </ItemGroup> +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <None Include="cpp.hint" /> + <None Include="winrtact.def"> + <Filter>Source Files</Filter> + </None> + <None Include="packages.config" /> + </ItemGroup> + <ItemGroup> + <ClCompile Include="catalog.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="dllmain.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="typeresolution.h"> + <Filter>Header Files</Filter> + </ClCompile> + <ClCompile Include="typeresolution.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\..\..\..\WinGetServer\WinGetServerManualActivation_Client.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\..\..\..\WinGetServer\WinGetServer_c.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\..\..\..\WinGetServer\Utils.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="catalog.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="extwinrt.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\..\..\..\WinGetServer\Utils.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> </Project> \ No newline at end of file diff --git a/src/Xlang/UndockedRegFreeWinRT/src/UndockedRegFreeWinRT/UndockedRegFreeWinRT/winrtact.def b/src/Xlang/UndockedRegFreeWinRT/src/UndockedRegFreeWinRT/UndockedRegFreeWinRT/winrtact.def @@ -1,4 +1,5 @@ LIBRARY winrtact EXPORTS - winrtact_Initialize- \ No newline at end of file + winrtact_Initialize + WinGetServerManualActivation_CreateInstance+ \ No newline at end of file diff --git a/templates/e2e-test.template.yml b/templates/e2e-test.template.yml @@ -25,7 +25,8 @@ steps: -MsiTestInstallerPath $(System.DefaultWorkingDirectory)\src\AppInstallerCLIE2ETests\TestData\AppInstallerTestMsiInstaller.msi -MsixTestInstallerPath $(Build.ArtifactStagingDirectory)\AppInstallerTestMsixInstaller.msix -ExeTestInstallerPath $(buildOutDir)\AppInstallerTestExeInstaller\AppInstallerTestExeInstaller.exe - -PackageCertificatePath $(AppInstallerTest.secureFilePath)' + -PackageCertificatePath $(AppInstallerTest.secureFilePath) + -PowerShellModulePath $(buildOutDir)\PowerShell\Microsoft.WinGet.Client.psd1' ${{ else }}: overrideTestrunParameters: '-PackagedContext false -AICLIPath $(System.DefaultWorkingDirectory)\src\AppInstallerCLIPackage\bin\$(buildPlatform)\$(buildConfiguration)\AppInstallerCLI\winget.exe @@ -34,4 +35,5 @@ steps: -MsiTestInstallerPath $(System.DefaultWorkingDirectory)\src\AppInstallerCLIE2ETests\TestData\AppInstallerTestMsiInstaller.msi -MsixTestInstallerPath $(Build.ArtifactStagingDirectory)\AppInstallerTestMsixInstaller.msix -ExeTestInstallerPath $(buildOutDir)\AppInstallerTestExeInstaller\AppInstallerTestExeInstaller.exe - -PackageCertificatePath $(AppInstallerTest.secureFilePath)' + -PackageCertificatePath $(AppInstallerTest.secureFilePath) + -PowerShellModulePath $(buildOutDir)\PowerShell\Microsoft.WinGet.Client.psd1'