Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use WinGet API to improve Quick Fix results #17614

Merged
merged 19 commits into from
Aug 23, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝: weird to me that this isn't just in their nuget package

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are working on improving this facet.

<!-- Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT License. See LICENSE in the project root for license information. -->
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Native-Platform Condition="'$(Platform)' == 'Win32'">x86</Native-Platform>
<Native-Platform Condition="'$(Platform)' != 'Win32'">$(Platform)</Native-Platform>
</PropertyGroup>
<ItemGroup>
<Reference Include="$(WinGetPackageRoot)\lib\Microsoft.Management.Deployment.winmd">
<IsWinMDFile>true</IsWinMDFile>
<Implementation>$(WinGetPackageRoot)\runtimes\win10-$(Native-Platform)\native\winrtact.dll</Implementation>
Fixed Show fixed Hide fixed
</Reference>
</ItemGroup>
<Target Name="_FixWinGetWinmdPackaging" BeforeTargets="_ComputeAppxPackagePayload">
<ItemGroup>
<PackagingOutputs Include="$(WinGetPackageRoot)\lib\Microsoft.Management.Deployment.winmd">
<OutputGroup>CustomOutputGroupForPackaging</OutputGroup>
<ProjectName>$(ProjectName)</ProjectName>
<Implementation>winrtact.dll</Implementation>
Fixed Show fixed Hide fixed
<TargetPath>Microsoft.Management.Deployment.winmd</TargetPath>
</PackagingOutputs>
</ItemGroup>
</Target>
</Project>
1 change: 1 addition & 0 deletions dep/nuget/packages.config
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<package id="Microsoft.UI.Xaml" version="2.8.4" targetFramework="native" />
<package id="Microsoft.Web.WebView2" version="1.0.1661.34" targetFramework="native" />
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.240122.1" targetFramework="native" developmentDependency="true" />
<package id="Microsoft.WindowsPackageManager.ComInterop" version="1.8.1911" targetFramework="native" developmentDependency="true" />

<!-- Managed packages -->
<package id="Appium.WebDriver" version="3.0.0.2" targetFramework="net45" />
Expand Down
4 changes: 3 additions & 1 deletion src/cascadia/TerminalApp/TerminalAppLib.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
<PropertyGroup Label="NuGet Dependencies">
<TerminalCppWinrt>true</TerminalCppWinrt>
<TerminalMUX>true</TerminalMUX>
<TerminalWinGetInterop>true</TerminalWinGetInterop>
</PropertyGroup>
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
<Import Project="$(OpenConsoleDir)src\common.nugetversions.props" />
Expand Down Expand Up @@ -177,6 +178,7 @@
<ClInclude Include="SuggestionsControl.h">
<DependentUpon>SuggestionsControl.xaml</DependentUpon>
</ClInclude>
<ClInclude Include="WindowsPackageManagerFactory.h" />
</ItemGroup>
<!-- ========================= Cpp Files ======================== -->
<ItemGroup>
Expand Down Expand Up @@ -361,7 +363,7 @@
</Midl>
<Midl Include="FilteredCommand.idl" />
<Midl Include="IPaneContent.idl" />
<Midl Include="TerminalPaneContent.idl" >
<Midl Include="TerminalPaneContent.idl">
<DependentUpon>TaskPaneContent.xaml</DependentUpon>
<SubType>Code</SubType>
</Midl>
Expand Down
65 changes: 62 additions & 3 deletions src/cascadia/TerminalApp/TerminalPage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "LaunchPositionRequest.g.cpp"

using namespace winrt;
using namespace winrt::Microsoft::Management::Deployment;
using namespace winrt::Microsoft::Terminal::Control;
using namespace winrt::Microsoft::Terminal::Settings::Model;
using namespace winrt::Microsoft::Terminal::TerminalConnection;
Expand Down Expand Up @@ -3001,7 +3002,50 @@ namespace winrt::TerminalApp::implementation
ShowWindowChanged.raise(*this, args);
}

winrt::fire_and_forget TerminalPage::_SearchMissingCommandHandler(const IInspectable /*sender*/, const Microsoft::Terminal::Control::SearchMissingCommandEventArgs args)
Windows::Foundation::IAsyncOperation<IVectorView<MatchResult>> TerminalPage::_FindPackagesInCatalogAsync(PackageCatalog catalog, PackageMatchField field, PackageFieldMatchOption matchOption, hstring query)
{
FindPackagesOptions findPackagesOptions = WindowsPackageManagerFactory::Instance().CreateFindPackagesOptions();
PackageMatchFilter filter = WindowsPackageManagerFactory::Instance().CreatePackageMatchFilter();
filter.Field(field);
filter.Option(matchOption);
filter.Value(query);
findPackagesOptions.Filters().Append(filter);
findPackagesOptions.ResultLimit(20);
FindPackagesResult findPackagesResult{ co_await catalog.FindPackagesAsync(findPackagesOptions) };

co_return findPackagesResult.Matches();
}

Windows::Foundation::IAsyncOperation<IVectorView<MatchResult>> TerminalPage::_FindPackageAsync(hstring query)
{
co_await winrt::resume_background();
carlos-zamora marked this conversation as resolved.
Show resolved Hide resolved

PackageManager packageManager = WindowsPackageManagerFactory::Instance().CreatePackageManager();
PackageCatalogReference catalogRef{
packageManager.GetPredefinedPackageCatalog(PredefinedPackageCatalog::OpenWindowsCatalog)
};
ConnectResult connectResult = catalogRef.Connect();
carlos-zamora marked this conversation as resolved.
Show resolved Hide resolved
if (connectResult.Status() != ConnectResultStatus::Ok)
{
co_return nullptr;
}
PackageCatalog catalog = connectResult.PackageCatalog();

auto pkgList = co_await _FindPackagesInCatalogAsync(catalog, PackageMatchField::Command, PackageFieldMatchOption::StartsWithCaseInsensitive, query);
if (pkgList.Size() > 0)
{
co_return pkgList;
}
pkgList = co_await _FindPackagesInCatalogAsync(catalog, PackageMatchField::Name, PackageFieldMatchOption::ContainsCaseInsensitive, query);
if (pkgList.Size() > 0)
{
co_return pkgList;
}
pkgList = co_await _FindPackagesInCatalogAsync(catalog, PackageMatchField::Moniker, PackageFieldMatchOption::ContainsCaseInsensitive, query);
co_return pkgList;
}
carlos-zamora marked this conversation as resolved.
Show resolved Hide resolved

Windows::Foundation::IAsyncAction TerminalPage::_SearchMissingCommandHandler(const IInspectable /*sender*/, const Microsoft::Terminal::Control::SearchMissingCommandEventArgs args)
{
assert(!Dispatcher().HasThreadAccess());

Expand All @@ -3010,9 +3054,24 @@ namespace winrt::TerminalApp::implementation
co_return;
}

auto pkgList = co_await _FindPackageAsync(args.MissingCommand());
carlos-zamora marked this conversation as resolved.
Show resolved Hide resolved
for (int retries = 0; !pkgList && retries < 3; ++retries)
{
pkgList = co_await _FindPackageAsync(args.MissingCommand());
}

// no packages were found, nothing to suggest
if (pkgList.Size() == 0)
carlos-zamora marked this conversation as resolved.
Show resolved Hide resolved
{
co_return;
}

std::vector<hstring> suggestions;
suggestions.reserve(1);
suggestions.emplace_back(fmt::format(FMT_COMPILE(L"winget install {}"), args.MissingCommand()));
suggestions.reserve(pkgList.Size());
for (auto pkg : pkgList)
{
suggestions.emplace_back(fmt::format(FMT_COMPILE(L"winget install --id {}"), pkg.CatalogPackage().Id()));
carlos-zamora marked this conversation as resolved.
Show resolved Hide resolved
}

co_await wil::resume_foreground(Dispatcher());

Expand Down
6 changes: 5 additions & 1 deletion src/cascadia/TerminalApp/TerminalPage.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
#include "LaunchPositionRequest.g.h"
#include "Toast.h"

#include "WindowsPackageManagerFactory.h"

#define DECLARE_ACTION_HANDLER(action) void _Handle##action(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args);

namespace TerminalAppLocalTests
Expand Down Expand Up @@ -530,7 +532,9 @@ namespace winrt::TerminalApp::implementation
void _OpenSuggestions(const Microsoft::Terminal::Control::TermControl& sender, Windows::Foundation::Collections::IVector<winrt::Microsoft::Terminal::Settings::Model::Command> commandsCollection, winrt::TerminalApp::SuggestionsMode mode, winrt::hstring filterText);

void _ShowWindowChangedHandler(const IInspectable sender, const winrt::Microsoft::Terminal::Control::ShowWindowArgs args);
winrt::fire_and_forget _SearchMissingCommandHandler(const IInspectable sender, const winrt::Microsoft::Terminal::Control::SearchMissingCommandEventArgs args);
Windows::Foundation::IAsyncAction _SearchMissingCommandHandler(const IInspectable sender, const winrt::Microsoft::Terminal::Control::SearchMissingCommandEventArgs args);
Windows::Foundation::IAsyncOperation<Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Management::Deployment::MatchResult>> _FindPackagesInCatalogAsync(winrt::Microsoft::Management::Deployment::PackageCatalog catalog, winrt::Microsoft::Management::Deployment::PackageMatchField field, winrt::Microsoft::Management::Deployment::PackageFieldMatchOption matchOption, hstring query);
Windows::Foundation::IAsyncOperation<Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Management::Deployment::MatchResult>> _FindPackageAsync(hstring query);

winrt::fire_and_forget _windowPropertyChanged(const IInspectable& sender, const winrt::Windows::UI::Xaml::Data::PropertyChangedEventArgs& args);

Expand Down
140 changes: 140 additions & 0 deletions src/cascadia/TerminalApp/WindowsPackageManagerFactory.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
// Copyright (c) Microsoft Corporation.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝: it's so gross that we have to write this ourselves

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, which is why the nuget package is soon going to include a DLL that handles this part (exposes the activation factory so that one can just use WinRT language projections as normal).

// Licensed under the MIT license.
//
// Module Name:
// - WindowsPackageManagerFactory.h
//
// Abstract:
// - These set of factories are designed to create production-level instances of WinGet objects.
// Elevated sessions require manual activation of WinGet objects,
// while non-elevated sessions can use the standard WinRT activation system.
// Author:
// - Carlos Zamora (carlos-zamora) 23-Jul-2024

#pragma once

#include "../../types/inc/utils.hpp"
#include <combaseapi.h>
#include <wil/result_macros.h>
#include <wil/win32_helpers.h>
#include <type_traits>
carlos-zamora marked this conversation as resolved.
Show resolved Hide resolved

#include <winrt/Microsoft.Management.Deployment.h>

namespace winrt::TerminalApp::implementation
{
class WindowsPackageManagerFactory
{
public:
static WindowsPackageManagerFactory& Instance()
{
static WindowsPackageManagerFactory instance;
return instance;
}
carlos-zamora marked this conversation as resolved.
Show resolved Hide resolved

// Delete copy constructor and assignment operator
WindowsPackageManagerFactory(const WindowsPackageManagerFactory&) = delete;
WindowsPackageManagerFactory& operator=(const WindowsPackageManagerFactory&) = delete;

winrt::Microsoft::Management::Deployment::PackageManager CreatePackageManager()
{
return CreateInstance<winrt::Microsoft::Management::Deployment::PackageManager>();
}

winrt::Microsoft::Management::Deployment::FindPackagesOptions CreateFindPackagesOptions()
{
return CreateInstance<winrt::Microsoft::Management::Deployment::FindPackagesOptions>();
}

winrt::Microsoft::Management::Deployment::CreateCompositePackageCatalogOptions CreateCreateCompositePackageCatalogOptions()
{
return CreateInstance<winrt::Microsoft::Management::Deployment::CreateCompositePackageCatalogOptions>();
}

winrt::Microsoft::Management::Deployment::InstallOptions CreateInstallOptions()
{
return CreateInstance<winrt::Microsoft::Management::Deployment::InstallOptions>();
}

winrt::Microsoft::Management::Deployment::UninstallOptions CreateUninstallOptions()
{
return CreateInstance<winrt::Microsoft::Management::Deployment::UninstallOptions>();
}

winrt::Microsoft::Management::Deployment::PackageMatchFilter CreatePackageMatchFilter()
{
return CreateInstance<winrt::Microsoft::Management::Deployment::PackageMatchFilter>();
}

private:
wil::unique_hmodule _winrtactModule;
Fixed Show fixed Hide fixed

WindowsPackageManagerFactory()
{
if (::Microsoft::Console::Utils::IsRunningElevated())
{
_winrtactModule.reset(LoadLibraryExW(L"winrtact.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32));
Fixed Show fixed Hide fixed
Fixed Show fixed Hide fixed
}
}

template<typename T>
T CreateInstance(const guid& clsid, const guid& iid)
{
if (::Microsoft::Console::Utils::IsRunningElevated())
{
winrt::com_ptr<::IUnknown> result{};
try
{
extern HRESULT WinGetServerManualActivation_CreateInstance(REFCLSID rclsid, REFIID riid, UINT32 flags, void** out);

auto createFn = reinterpret_cast<HRESULT (*)(REFCLSID, REFIID, UINT32, void**)>(GetProcAddress(_winrtactModule.get(), "WinGetServerManualActivation_CreateInstance"));
THROW_LAST_ERROR_IF(!createFn);
THROW_IF_FAILED(createFn(clsid, iid, 0, result.put_void()));
return result.as<T>();
carlos-zamora marked this conversation as resolved.
Show resolved Hide resolved
}
catch (...)
{
}
}
return winrt::create_instance<T>(clsid, CLSCTX_ALL);
}

template<typename T>
T CreateInstance()
{
winrt::guid clsid, iid;
if (std::is_same<T, winrt::Microsoft::Management::Deployment::PackageManager>::value)
{
clsid = winrt::guid{ "C53A4F16-787E-42A4-B304-29EFFB4BF597" };
iid = winrt::guid_of<winrt::Microsoft::Management::Deployment::IPackageManager>();
}
else if (std::is_same<T, winrt::Microsoft::Management::Deployment::FindPackagesOptions>::value)
{
clsid = winrt::guid{ "572DED96-9C60-4526-8F92-EE7D91D38C1A" };
iid = winrt::guid_of<winrt::Microsoft::Management::Deployment::IFindPackagesOptions>();
}
else if (std::is_same<T, winrt::Microsoft::Management::Deployment::CreateCompositePackageCatalogOptions>::value)
{
clsid = winrt::guid{ "526534B8-7E46-47C8-8416-B1685C327D37" };
iid = winrt::guid_of<winrt::Microsoft::Management::Deployment::ICreateCompositePackageCatalogOptions>();
}
else if (std::is_same<T, winrt::Microsoft::Management::Deployment::InstallOptions>::value)
{
clsid = winrt::guid{ "1095F097-EB96-453B-B4E6-1613637F3B14" };
iid = winrt::guid_of<winrt::Microsoft::Management::Deployment::IInstallOptions>();
}
else if (std::is_same<T, winrt::Microsoft::Management::Deployment::UninstallOptions>::value)
{
clsid = winrt::guid{ "E1D9A11E-9F85-4D87-9C17-2B93143ADB8D" };
iid = winrt::guid_of<winrt::Microsoft::Management::Deployment::IUninstallOptions>();
}
else if (std::is_same<T, winrt::Microsoft::Management::Deployment::PackageMatchFilter>::value)
{
clsid = winrt::guid{ "D02C9DAF-99DC-429C-B503-4E504E4AB000" };
iid = winrt::guid_of<winrt::Microsoft::Management::Deployment::IPackageMatchFilter>();
}

return CreateInstance<T>(clsid, iid);
}
};
}
1 change: 1 addition & 0 deletions src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
<TerminalCppWinrt>true</TerminalCppWinrt>
<TerminalThemeHelpers>true</TerminalThemeHelpers>
<TerminalMUX>true</TerminalMUX>
<TerminalWinGetInterop>true</TerminalWinGetInterop>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need this for WindowsTerminal.exe in addition to TerminalApp? Is this cause the dependency doesn't get rolled up?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct. Just tested it without it. The QF menu won't appear.

</PropertyGroup>

<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
Expand Down
5 changes: 5 additions & 0 deletions src/common.nugetversions.props
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,9 @@
<!-- WinUI (which depends on WebView2 as of 2.8.0) -->
<Import Project="$(MSBuildThisFileDirectory)..\packages\Microsoft.UI.Xaml.2.8.4\build\native\Microsoft.UI.Xaml.props" Condition="'$(TerminalMUX)' == 'true' and Exists('$(MSBuildThisFileDirectory)..\packages\Microsoft.UI.Xaml.2.8.4\build\native\Microsoft.UI.Xaml.props')" />

<!-- WinGet Interop -->
<PropertyGroup>
<WinGetPackageRoot>$(MSBuildThisFileDirectory)..\packages\Microsoft.WindowsPackageManager.ComInterop.1.8.1911\</WinGetPackageRoot>
</PropertyGroup>

</Project>
2 changes: 2 additions & 0 deletions src/common.nugetversions.targets
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@
<!-- WIL (so widely used that this one does not have a TerminalWIL opt-in property; it is automatic) -->
<Import Project="$(MSBuildThisFileDirectory)..\packages\Microsoft.Windows.ImplementationLibrary.1.0.240122.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('$(MSBuildThisFileDirectory)..\packages\Microsoft.Windows.ImplementationLibrary.1.0.240122.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />

<!-- WinGet Interop -->
<Import Project="$(MSBuildThisFileDirectory)..\build\rules\Microsoft.WindowsPackageManager.ComInterop.Additional.targets" Condition="'$(TerminalWinGetInterop)' == 'true'" />
</ImportGroup>

<Target Name="Terminal_EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
Expand Down
Loading