DebugCommand.cpp (18196B)
1 // Copyright (c) Microsoft Corporation. 2 // Licensed under the MIT License. 3 #include "pch.h" 4 5 #if _DEBUG 6 #include "DebugCommand.h" 7 #include <winrt/Microsoft.Management.Configuration.h> 8 #include <winrt/Microsoft.Management.Configuration.SetProcessorFactory.h> 9 #include "AppInstallerDownloader.h" 10 #include "Sixel.h" 11 12 using namespace AppInstaller::CLI::Execution; 13 14 namespace AppInstaller::CLI 15 { 16 namespace 17 { 18 std::string MakeInterfaceNameAttribute(std::wstring_view name) 19 { 20 std::string result = Utility::ConvertToUTF8(name); 21 Utility::FindAndReplace(result, "<", "<"); 22 Utility::FindAndReplace(result, ">", ">"); 23 return result; 24 } 25 26 std::string MakeIIDAttribute(const winrt::guid& guid) 27 { 28 std::string result; 29 wchar_t buffer[256]; 30 31 if (StringFromGUID2(guid, buffer, ARRAYSIZE(buffer))) 32 { 33 result = AppInstaller::Utility::ConvertToUTF8(buffer); 34 result = result.substr(1, result.length() - 2); 35 } 36 else 37 { 38 result = "error"; 39 } 40 41 return result; 42 } 43 44 template <typename Interface> 45 void OutputProxyStubInterfaceRegistration(Execution::Context& context) 46 { 47 context.Reporter.Info() << "<Interface Name=\"" << MakeInterfaceNameAttribute(winrt::name_of<Interface>()) << "\" InterfaceId=\"" << MakeIIDAttribute(winrt::guid_of<Interface>()) << "\" />" << std::endl; 48 } 49 50 template <typename Interface> 51 void OutputIIDMapping(Execution::Context& context) 52 { 53 context.Reporter.Info() << Utility::ConvertToUTF8(winrt::name_of<Interface>()) << " == " << winrt::guid_of<Interface>() << std::endl; 54 } 55 } 56 57 std::vector<std::unique_ptr<Command>> DebugCommand::GetCommands() const 58 { 59 return InitializeFromMoveOnly<std::vector<std::unique_ptr<Command>>>({ 60 std::make_unique<DumpProxyStubRegistrationsCommand>(FullName()), 61 std::make_unique<DumpInterestingIIDsCommand>(FullName()), 62 std::make_unique<DumpErrorResourceCommand>(FullName()), 63 std::make_unique<ShowSixelCommand>(FullName()), 64 std::make_unique<ProgressCommand>(FullName()), 65 }); 66 } 67 68 Resource::LocString DebugCommand::ShortDescription() const 69 { 70 return Utility::LocIndString("Debug only dev commands"sv); 71 } 72 73 Resource::LocString DebugCommand::LongDescription() const 74 { 75 return Utility::LocIndString("Commands that are useful in debugging and development."sv); 76 } 77 78 void DebugCommand::ExecuteInternal(Execution::Context& context) const 79 { 80 OutputHelp(context.Reporter); 81 } 82 83 Resource::LocString DumpProxyStubRegistrationsCommand::ShortDescription() const 84 { 85 return Utility::LocIndString("Dump proxy-stub registrations"sv); 86 } 87 88 Resource::LocString DumpProxyStubRegistrationsCommand::LongDescription() const 89 { 90 return Utility::LocIndString("Dump proxy-stub registrations for WinRT interfaces to be place in the manifest."sv); 91 } 92 93 void DumpProxyStubRegistrationsCommand::ExecuteInternal(Execution::Context& context) const 94 { 95 OutputProxyStubInterfaceRegistration<winrt::Windows::Foundation::Collections::IIterable<winrt::Microsoft::Management::Configuration::ConfigurationUnit>>(context); 96 OutputProxyStubInterfaceRegistration<winrt::Windows::Foundation::Collections::IIterable<winrt::Microsoft::Management::Configuration::ConfigurationSet>>(context); 97 OutputProxyStubInterfaceRegistration<winrt::Windows::Foundation::Collections::IIterable<winrt::Microsoft::Management::Configuration::ConfigurationConflict>>(context); 98 OutputProxyStubInterfaceRegistration<winrt::Windows::Foundation::Collections::IIterable<winrt::Microsoft::Management::Configuration::ConfigurationParameter>>(context); 99 OutputProxyStubInterfaceRegistration<winrt::Windows::Foundation::Collections::IIterable<winrt::Microsoft::Management::Configuration::IConfigurationUnitSettingDetails>>(context); 100 OutputProxyStubInterfaceRegistration<winrt::Windows::Foundation::Collections::IIterable<winrt::Microsoft::Management::Configuration::ConfigurationConflictSetting>>(context); 101 OutputProxyStubInterfaceRegistration<winrt::Windows::Foundation::Collections::IIterable<winrt::Microsoft::Management::Configuration::GetConfigurationUnitDetailsResult>>(context); 102 OutputProxyStubInterfaceRegistration<winrt::Windows::Foundation::Collections::IIterable<winrt::Microsoft::Management::Configuration::ApplyConfigurationUnitResult>>(context); 103 OutputProxyStubInterfaceRegistration<winrt::Windows::Foundation::Collections::IIterable<winrt::Microsoft::Management::Configuration::TestConfigurationUnitResult>>(context); 104 OutputProxyStubInterfaceRegistration<winrt::Windows::Foundation::Collections::IIterable<winrt::Microsoft::Management::Configuration::IApplyGroupMemberSettingsResult>>(context); 105 OutputProxyStubInterfaceRegistration<winrt::Windows::Foundation::Collections::IIterable<winrt::Microsoft::Management::Configuration::ITestSettingsResult>>(context); 106 OutputProxyStubInterfaceRegistration<winrt::Windows::Foundation::Collections::IIterable<winrt::Microsoft::Management::Configuration::ConfigurationEnvironment>>(context); 107 OutputProxyStubInterfaceRegistration<winrt::Windows::Foundation::Collections::IIterable<winrt::Microsoft::Management::Configuration::IConfigurationUnitProcessorDetails>>(context); 108 OutputProxyStubInterfaceRegistration<winrt::Microsoft::Management::Configuration::IConfigurationUnitProcessorDetails2>(context); 109 OutputProxyStubInterfaceRegistration<winrt::Microsoft::Management::Configuration::IConfigurationUnitProcessorDetails3>(context); 110 OutputProxyStubInterfaceRegistration<winrt::Microsoft::Management::Configuration::IGetAllSettingsConfigurationUnitProcessor>(context); 111 OutputProxyStubInterfaceRegistration<winrt::Microsoft::Management::Configuration::IGetAllUnitsConfigurationUnitProcessor>(context); 112 OutputProxyStubInterfaceRegistration<winrt::Microsoft::Management::Configuration::IFindUnitProcessorsSetProcessor>(context); 113 OutputProxyStubInterfaceRegistration<winrt::Microsoft::Management::Configuration::IConfigurationStatics2>(context); 114 OutputProxyStubInterfaceRegistration<winrt::Microsoft::Management::Configuration::IConfigurationStatics3>(context); 115 OutputProxyStubInterfaceRegistration<winrt::Microsoft::Management::Configuration::SetProcessorFactory::IPwshConfigurationSetProcessorFactoryProperties>(context); 116 117 // TODO: Fix the layering inversion created by the COM deployment API (probably in order to operate winget.exe against the COM server). 118 // Then this code can just have a CppWinRT reference to the deployment API and spit out the interface registrations just like for configuration. 119 HMODULE module = nullptr; 120 if (!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, reinterpret_cast<LPCWSTR>(&MakeInterfaceNameAttribute), &module)) 121 { 122 return; 123 } 124 125 // TODO: Have a PRIVATE export from WindowsPackageManager that returns a set of names and IIDs to include from the Deployment API surface 126 } 127 128 Resource::LocString DumpInterestingIIDsCommand::ShortDescription() const 129 { 130 return Utility::LocIndString("Dump some IIDs"sv); 131 } 132 133 Resource::LocString DumpInterestingIIDsCommand::LongDescription() const 134 { 135 return Utility::LocIndString("Dump some IIDs that might be useful."sv); 136 } 137 138 void DumpInterestingIIDsCommand::ExecuteInternal(Execution::Context& context) const 139 { 140 OutputIIDMapping<winrt::Microsoft::Management::Configuration::IConfigurationStatics>(context); 141 OutputIIDMapping<winrt::Microsoft::Management::Configuration::IConfigurationStatics2>(context); 142 OutputIIDMapping<winrt::Microsoft::Management::Configuration::IConfigurationStatics3>(context); 143 } 144 145 Resource::LocString DumpErrorResourceCommand::ShortDescription() const 146 { 147 return Utility::LocIndString("Dump error resources"sv); 148 } 149 150 Resource::LocString DumpErrorResourceCommand::LongDescription() const 151 { 152 return Utility::LocIndString("Dump the error information as resources."sv); 153 } 154 155 void DumpErrorResourceCommand::ExecuteInternal(Execution::Context& context) const 156 { 157 auto info = context.Reporter.Info(); 158 159 // <data name="InstallFlowReturnCodeInstallInProgress" xml:space="preserve"> 160 // <value>Another installation is already in progress. Try again later.</value> 161 // </data> 162 for (const auto& error : Errors::GetWinGetErrors()) 163 { 164 info << 165 " <data name=\"" << error->Symbol() << "\" xml:space=\"preserve\">\n" 166 " <value>" << error->GetDescription() << "</value>\n" 167 " </data>" << std::endl; 168 } 169 } 170 171 #define WINGET_DEBUG_SIXEL_FILE Args::Type::Manifest 172 #define WINGET_DEBUG_SIXEL_ASPECT_RATIO Args::Type::AcceptPackageAgreements 173 #define WINGET_DEBUG_SIXEL_TRANSPARENT Args::Type::AcceptSourceAgreements 174 #define WINGET_DEBUG_SIXEL_COLOR_COUNT Args::Type::ConfigurationAcceptWarning 175 #define WINGET_DEBUG_SIXEL_WIDTH Args::Type::AdminSettingEnable 176 #define WINGET_DEBUG_SIXEL_HEIGHT Args::Type::AllowReboot 177 #define WINGET_DEBUG_SIXEL_STRETCH Args::Type::AllVersions 178 #define WINGET_DEBUG_SIXEL_REPEAT Args::Type::Name 179 #define WINGET_DEBUG_SIXEL_OUT_FILE Args::Type::BlockingPin 180 181 std::vector<Argument> ShowSixelCommand::GetArguments() const 182 { 183 return { 184 Argument{ "file", 'f', WINGET_DEBUG_SIXEL_FILE, Resource::String::SourceListUpdatedNever, ArgumentType::Positional }, 185 Argument{ "aspect-ratio", 'a', WINGET_DEBUG_SIXEL_ASPECT_RATIO, Resource::String::SourceListUpdatedNever, ArgumentType::Standard }, 186 Argument{ "transparent", 't', WINGET_DEBUG_SIXEL_TRANSPARENT, Resource::String::SourceListUpdatedNever, ArgumentType::Flag }, 187 Argument{ "color-count", 'c', WINGET_DEBUG_SIXEL_COLOR_COUNT, Resource::String::SourceListUpdatedNever, ArgumentType::Standard }, 188 Argument{ "width", 'w', WINGET_DEBUG_SIXEL_WIDTH, Resource::String::SourceListUpdatedNever, ArgumentType::Standard }, 189 Argument{ "height", 'h', WINGET_DEBUG_SIXEL_HEIGHT, Resource::String::SourceListUpdatedNever, ArgumentType::Standard }, 190 Argument{ "stretch", 's', WINGET_DEBUG_SIXEL_STRETCH, Resource::String::SourceListUpdatedNever, ArgumentType::Flag }, 191 Argument{ "repeat", 'r', WINGET_DEBUG_SIXEL_REPEAT, Resource::String::SourceListUpdatedNever, ArgumentType::Flag }, 192 Argument{ "out-file", 'o', WINGET_DEBUG_SIXEL_OUT_FILE, Resource::String::SourceListUpdatedNever, ArgumentType::Standard }, 193 }; 194 } 195 196 Resource::LocString ShowSixelCommand::ShortDescription() const 197 { 198 return Utility::LocIndString("Output an image with sixels"sv); 199 } 200 201 Resource::LocString ShowSixelCommand::LongDescription() const 202 { 203 return Utility::LocIndString("Outputs an image from a file using sixel format."sv); 204 } 205 206 void ShowSixelCommand::ExecuteInternal(Execution::Context& context) const 207 { 208 using namespace VirtualTerminal; 209 std::unique_ptr<Sixel::Image> sixelImagePtr; 210 211 std::string imageUrl{ context.Args.GetArg(WINGET_DEBUG_SIXEL_FILE) }; 212 213 if (Utility::IsUrlRemote(imageUrl)) 214 { 215 auto imageStream = std::make_unique<std::stringstream>(); 216 ProgressCallback emptyCallback; 217 Utility::DownloadToStream(imageUrl, *imageStream, Utility::DownloadType::Manifest, emptyCallback); 218 219 sixelImagePtr = std::make_unique<Sixel::Image>(*imageStream, Manifest::IconFileTypeEnum::Unknown); 220 } 221 else 222 { 223 sixelImagePtr = std::make_unique<Sixel::Image>(Utility::ConvertToUTF16(imageUrl)); 224 } 225 226 Sixel::Image& sixelImage = *sixelImagePtr.get(); 227 228 if (context.Args.Contains(WINGET_DEBUG_SIXEL_ASPECT_RATIO)) 229 { 230 switch (context.Args.GetArg(WINGET_DEBUG_SIXEL_ASPECT_RATIO)[0]) 231 { 232 case '1': 233 sixelImage.AspectRatio(Sixel::AspectRatio::OneToOne); 234 break; 235 case '2': 236 sixelImage.AspectRatio(Sixel::AspectRatio::TwoToOne); 237 break; 238 case '3': 239 sixelImage.AspectRatio(Sixel::AspectRatio::ThreeToOne); 240 break; 241 case '5': 242 sixelImage.AspectRatio(Sixel::AspectRatio::FiveToOne); 243 break; 244 } 245 } 246 247 sixelImage.Transparency(context.Args.Contains(WINGET_DEBUG_SIXEL_TRANSPARENT)); 248 249 if (context.Args.Contains(WINGET_DEBUG_SIXEL_COLOR_COUNT)) 250 { 251 sixelImage.ColorCount(std::stoul(std::string{ context.Args.GetArg(WINGET_DEBUG_SIXEL_COLOR_COUNT) })); 252 } 253 254 if (context.Args.Contains(WINGET_DEBUG_SIXEL_WIDTH) && context.Args.Contains(WINGET_DEBUG_SIXEL_HEIGHT)) 255 { 256 sixelImage.RenderSizeInCells( 257 std::stoul(std::string{ context.Args.GetArg(WINGET_DEBUG_SIXEL_WIDTH) }), 258 std::stoul(std::string{ context.Args.GetArg(WINGET_DEBUG_SIXEL_HEIGHT) })); 259 } 260 261 sixelImage.StretchSourceToFill(context.Args.Contains(WINGET_DEBUG_SIXEL_STRETCH)); 262 263 sixelImage.UseRepeatSequence(context.Args.Contains(WINGET_DEBUG_SIXEL_REPEAT)); 264 265 if (context.Args.Contains(WINGET_DEBUG_SIXEL_OUT_FILE)) 266 { 267 std::ofstream stream{ Utility::ConvertToUTF16(context.Args.GetArg(WINGET_DEBUG_SIXEL_OUT_FILE)) }; 268 stream << sixelImage.Render().Get(); 269 } 270 else 271 { 272 OutputStream stream = context.Reporter.GetOutputStream(Reporter::Level::Info); 273 stream.ClearFormat(); 274 sixelImage.RenderTo(stream); 275 276 // Force a new line to show entire image 277 stream << std::endl; 278 } 279 } 280 281 #define WINGET_DEBUG_PROGRESS_SIXEL Args::Type::Manifest 282 #define WINGET_DEBUG_PROGRESS_DISABLED Args::Type::GatedVersion 283 #define WINGET_DEBUG_PROGRESS_HIDE Args::Type::AcceptPackageAgreements 284 #define WINGET_DEBUG_PROGRESS_TIME Args::Type::AcceptSourceAgreements 285 #define WINGET_DEBUG_PROGRESS_MESSAGE Args::Type::ConfigurationAcceptWarning 286 #define WINGET_DEBUG_PROGRESS_PERCENT Args::Type::AllowReboot 287 #define WINGET_DEBUG_PROGRESS_POST Args::Type::AllVersions 288 289 std::vector<Argument> ProgressCommand::GetArguments() const 290 { 291 return { 292 Argument{ "sixel", 's', WINGET_DEBUG_PROGRESS_SIXEL, Resource::String::SourceListUpdatedNever, ArgumentType::Flag }, 293 Argument{ "disabled", 'd', WINGET_DEBUG_PROGRESS_DISABLED, Resource::String::SourceListUpdatedNever, ArgumentType::Flag }, 294 Argument{ "hide", 'h', WINGET_DEBUG_PROGRESS_HIDE, Resource::String::SourceListUpdatedNever, ArgumentType::Flag }, 295 Argument{ "time", 't', WINGET_DEBUG_PROGRESS_TIME, Resource::String::SourceListUpdatedNever, ArgumentType::Standard }, 296 Argument{ "message", 'm', WINGET_DEBUG_PROGRESS_MESSAGE, Resource::String::SourceListUpdatedNever, ArgumentType::Standard }, 297 Argument{ "percent", 'p', WINGET_DEBUG_PROGRESS_PERCENT, Resource::String::SourceListUpdatedNever, ArgumentType::Flag }, 298 Argument{ "post", 0, WINGET_DEBUG_PROGRESS_POST, Resource::String::SourceListUpdatedNever, ArgumentType::Standard }, 299 }; 300 } 301 302 Resource::LocString ProgressCommand::ShortDescription() const 303 { 304 return Utility::LocIndString("Show progress"sv); 305 } 306 307 Resource::LocString ProgressCommand::LongDescription() const 308 { 309 return Utility::LocIndString("Show progress with various controls to emulate different behaviors."sv); 310 } 311 312 void ProgressCommand::ExecuteInternal(Execution::Context& context) const 313 { 314 if (context.Args.Contains(WINGET_DEBUG_PROGRESS_SIXEL)) 315 { 316 context.Reporter.SetStyle(Settings::VisualStyle::Sixel); 317 } 318 319 if (context.Args.Contains(WINGET_DEBUG_PROGRESS_DISABLED)) 320 { 321 context.Reporter.SetStyle(Settings::VisualStyle::Disabled); 322 } 323 324 auto progress = context.Reporter.BeginAsyncProgress(context.Args.Contains(WINGET_DEBUG_PROGRESS_HIDE)); 325 326 if (context.Args.Contains(WINGET_DEBUG_PROGRESS_MESSAGE)) 327 { 328 progress->Callback().SetProgressMessage(context.Args.GetArg(WINGET_DEBUG_PROGRESS_MESSAGE)); 329 } 330 331 bool sendProgress = context.Args.Contains(WINGET_DEBUG_PROGRESS_PERCENT); 332 333 UINT timeInSeconds = 3600; 334 if (context.Args.Contains(WINGET_DEBUG_PROGRESS_TIME)) 335 { 336 timeInSeconds = std::stoul(std::string{ context.Args.GetArg(WINGET_DEBUG_PROGRESS_TIME) }); 337 } 338 339 UINT ticks = timeInSeconds * 10; 340 for (UINT i = 0; i < ticks; ++i) 341 { 342 if (sendProgress) 343 { 344 progress->Callback().OnProgress(i, ticks, ProgressType::Bytes); 345 } 346 347 if (progress->Callback().IsCancelledBy(CancelReason::Any)) 348 { 349 sendProgress = false; 350 break; 351 } 352 353 std::this_thread::sleep_for(100ms); 354 } 355 356 if (sendProgress) 357 { 358 progress->Callback().OnProgress(ticks, ticks, ProgressType::Bytes); 359 } 360 361 progress.reset(); 362 363 if (context.Args.Contains(WINGET_DEBUG_PROGRESS_POST)) 364 { 365 context.Reporter.Info() << context.Args.GetArg(WINGET_DEBUG_PROGRESS_POST) << std::endl; 366 } 367 } 368 } 369 370 #endif