winget-cli

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

Exports.cpp (24075B)


      1 // Copyright (c) Microsoft Corporation.
      2 // Licensed under the MIT License.
      3 #include "pch.h"
      4 #include "WinGetUtil.h"
      5 
      6 #include <AppInstallerDownloader.h>
      7 #include <AppInstallerFileLogger.h>
      8 #include <AppInstallerLogging.h>
      9 #include <AppInstallerStrings.h>
     10 #include <AppInstallerTelemetry.h>
     11 #include <Microsoft/SQLiteIndex.h>
     12 #include <winget/ManifestYamlParser.h>
     13 #include <winget/ThreadGlobals.h>
     14 #include <winget/InstallerMetadataCollectionContext.h>
     15 #include <PackageDependenciesValidation.h>
     16 #include <public/winget/PackageDependenciesValidationUtil.h>
     17 #include <ArpVersionValidation.h>
     18 
     19 using namespace AppInstaller::Utility;
     20 using namespace AppInstaller::Manifest;
     21 using namespace AppInstaller::Repository;
     22 using namespace AppInstaller::Repository::Metadata;
     23 using namespace AppInstaller::Repository::Microsoft;
     24 
     25 namespace
     26 {
     27     std::filesystem::path GetPathOrEmpty(WINGET_STRING potentiallyNullPath)
     28     {
     29         return potentiallyNullPath ? std::filesystem::path{ potentiallyNullPath } : std::filesystem::path{};
     30     }
     31 
     32     SQLiteIndex::Property GetSQLiteIndexProperty(WinGetSQLiteIndexProperty property)
     33     {
     34         switch (property)
     35         {
     36         case WinGetSQLiteIndexProperty_PackageUpdateTrackingBaseTime: return SQLiteIndex::Property::PackageUpdateTrackingBaseTime;
     37         case WinGetSQLiteIndexProperty_IntermediateFileOutputPath: return SQLiteIndex::Property::IntermediateFileOutputPath;
     38         }
     39 
     40         THROW_HR(E_INVALIDARG);
     41     }
     42 }
     43 
     44 extern "C"
     45 {
     46     WINGET_UTIL_API WinGetLoggingInit(WINGET_STRING logPath) try
     47     {
     48         THROW_HR_IF(E_INVALIDARG, !logPath);
     49 
     50         thread_local AppInstaller::ThreadLocalStorage::WingetThreadGlobals threadGlobals;
     51         thread_local std::once_flag initLogging;
     52 
     53         std::call_once(initLogging, []() {
     54             std::unique_ptr<AppInstaller::ThreadLocalStorage::PreviousThreadGlobals> previous = threadGlobals.SetForCurrentThread();
     55             // Intentionally release to leave the local ThreadGlobals.
     56             previous.release();
     57             // Enable all logs for now.
     58             AppInstaller::Logging::Log().SetEnabledChannels(AppInstaller::Logging::Channel::All);
     59             AppInstaller::Logging::Log().SetLevel(AppInstaller::Logging::Level::Verbose);
     60             AppInstaller::Logging::EnableWilFailureTelemetry();
     61             });
     62 
     63         std::filesystem::path pathAsPath = logPath;
     64         std::string loggerName = AppInstaller::Logging::FileLogger::GetNameForPath(pathAsPath);
     65 
     66         if (!AppInstaller::Logging::Log().ContainsLogger(loggerName))
     67         {
     68             // Let FileLogger use default file prefix
     69             AppInstaller::Logging::FileLogger::Add(pathAsPath);
     70         }
     71 
     72         return S_OK;
     73     }
     74     CATCH_RETURN()
     75 
     76     WINGET_UTIL_API WinGetLoggingTerm(WINGET_STRING logPath) try
     77     {
     78         if (logPath)
     79         {
     80             std::string loggerName = AppInstaller::Logging::FileLogger::GetNameForPath(logPath);
     81             (void)AppInstaller::Logging::Log().RemoveLogger(loggerName);
     82         }
     83         else
     84         {
     85             AppInstaller::Logging::Log().RemoveAllLoggers();
     86         }
     87 
     88         return S_OK;
     89     }
     90     CATCH_RETURN()
     91 
     92     WINGET_UTIL_API WinGetSQLiteIndexCreate(WINGET_STRING filePath, UINT32 majorVersion, UINT32 minorVersion, WINGET_SQLITE_INDEX_HANDLE* index) try
     93     {
     94         THROW_HR_IF(E_INVALIDARG, !filePath);
     95         THROW_HR_IF(E_INVALIDARG, !index);
     96         THROW_HR_IF(E_INVALIDARG, !!*index);
     97 
     98         std::string filePathUtf8 = ConvertToUTF8(filePath);
     99         AppInstaller::SQLite::Version internalVersion{ majorVersion, minorVersion };
    100 
    101         std::unique_ptr<SQLiteIndex> result = std::make_unique<SQLiteIndex>(SQLiteIndex::CreateNew(filePathUtf8, internalVersion));
    102 
    103         *index = static_cast<WINGET_SQLITE_INDEX_HANDLE>(result.release());
    104 
    105         return S_OK;
    106     }
    107     CATCH_RETURN()
    108 
    109     WINGET_UTIL_API WinGetSQLiteIndexOpen(WINGET_STRING filePath, WINGET_SQLITE_INDEX_HANDLE* index) try
    110     {
    111         THROW_HR_IF(E_INVALIDARG, !filePath);
    112         THROW_HR_IF(E_INVALIDARG, !index);
    113         THROW_HR_IF(E_INVALIDARG, !!*index);
    114 
    115         std::string filePathUtf8 = ConvertToUTF8(filePath);
    116 
    117         std::unique_ptr<SQLiteIndex> result = std::make_unique<SQLiteIndex>(SQLiteIndex::Open(filePathUtf8, SQLiteIndex::OpenDisposition::ReadWrite));
    118 
    119         *index = static_cast<WINGET_SQLITE_INDEX_HANDLE>(result.release());
    120 
    121         return S_OK;
    122     }
    123     CATCH_RETURN()
    124 
    125     WINGET_UTIL_API WinGetSQLiteIndexClose(WINGET_SQLITE_INDEX_HANDLE index) try
    126     {
    127         std::unique_ptr<SQLiteIndex> toClose(reinterpret_cast<SQLiteIndex*>(index));
    128 
    129         return S_OK;
    130     }
    131     CATCH_RETURN()
    132 
    133     WINGET_UTIL_API WinGetSQLiteIndexMigrate(
    134         WINGET_SQLITE_INDEX_HANDLE index,
    135         UINT32 majorVersion,
    136         UINT32 minorVersion) try
    137     {
    138         THROW_HR_IF(E_INVALIDARG, !index);
    139 
    140         return reinterpret_cast<SQLiteIndex*>(index)->MigrateTo({ majorVersion, minorVersion }) ? S_OK : HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
    141     }
    142     CATCH_RETURN()
    143 
    144 
    145     WINGET_UTIL_API WinGetSQLiteIndexSetProperty(
    146         WINGET_SQLITE_INDEX_HANDLE index,
    147         WinGetSQLiteIndexProperty property,
    148         WINGET_STRING value) try
    149     {
    150         THROW_HR_IF(E_INVALIDARG, !index);
    151         THROW_HR_IF(E_INVALIDARG, !value);
    152 
    153         std::string valueUtf8 = ConvertToUTF8(value);
    154 
    155         reinterpret_cast<SQLiteIndex*>(index)->SetProperty(GetSQLiteIndexProperty(property), valueUtf8);
    156 
    157         return S_OK;
    158     }
    159     CATCH_RETURN()
    160 
    161     WINGET_UTIL_API WinGetSQLiteIndexAddManifest(
    162         WINGET_SQLITE_INDEX_HANDLE index, 
    163         WINGET_STRING manifestPath, WINGET_STRING relativePath) try
    164     {
    165         THROW_HR_IF(E_INVALIDARG, !index);
    166         THROW_HR_IF(E_INVALIDARG, !manifestPath);
    167         THROW_HR_IF(E_INVALIDARG, !relativePath);
    168 
    169         reinterpret_cast<SQLiteIndex*>(index)->AddManifest(manifestPath, relativePath);
    170 
    171         return S_OK;
    172     }
    173     CATCH_RETURN()
    174 
    175     WINGET_UTIL_API WinGetSQLiteIndexUpdateManifest(
    176         WINGET_SQLITE_INDEX_HANDLE index,
    177         WINGET_STRING manifestPath,
    178         WINGET_STRING relativePath,
    179         BOOL* indexModified) try
    180     {
    181         THROW_HR_IF(E_INVALIDARG, !index);
    182         THROW_HR_IF(E_INVALIDARG, !manifestPath);
    183         THROW_HR_IF(E_INVALIDARG, !relativePath);
    184 
    185         bool result = reinterpret_cast<SQLiteIndex*>(index)->UpdateManifest(manifestPath, relativePath);
    186         if (indexModified)
    187         {
    188             *indexModified = (result ? TRUE : FALSE);
    189         }
    190 
    191         return S_OK;
    192     }
    193     CATCH_RETURN()
    194 
    195     WINGET_UTIL_API WinGetSQLiteIndexAddOrUpdateManifest(
    196         WINGET_SQLITE_INDEX_HANDLE index,
    197         WINGET_STRING manifestPath,
    198         WINGET_STRING relativePath,
    199         BOOL* indexModified) try
    200     {
    201         THROW_HR_IF(E_INVALIDARG, !index);
    202         THROW_HR_IF(E_INVALIDARG, !manifestPath);
    203         THROW_HR_IF(E_INVALIDARG, !relativePath);
    204 
    205         bool result = reinterpret_cast<SQLiteIndex*>(index)->AddOrUpdateManifest(manifestPath, relativePath);
    206         if (indexModified)
    207         {
    208             *indexModified = (result ? TRUE : FALSE);
    209         }
    210 
    211         return S_OK;
    212     }
    213     CATCH_RETURN()
    214 
    215     WINGET_UTIL_API WinGetSQLiteIndexRemoveManifest(
    216         WINGET_SQLITE_INDEX_HANDLE index, 
    217         WINGET_STRING manifestPath,
    218         WINGET_STRING relativePath) try
    219     {
    220         THROW_HR_IF(E_INVALIDARG, !index);
    221         THROW_HR_IF(E_INVALIDARG, !manifestPath);
    222         THROW_HR_IF(E_INVALIDARG, !relativePath);
    223 
    224         reinterpret_cast<SQLiteIndex*>(index)->RemoveManifest(manifestPath, relativePath);
    225 
    226         return S_OK;
    227     }
    228     CATCH_RETURN()
    229 
    230     WINGET_UTIL_API WinGetSQLiteIndexPrepareForPackaging(
    231         WINGET_SQLITE_INDEX_HANDLE index) try
    232     {
    233         THROW_HR_IF(E_INVALIDARG, !index);
    234 
    235         reinterpret_cast<SQLiteIndex*>(index)->PrepareForPackaging();
    236 
    237         return S_OK;
    238     }
    239     CATCH_RETURN()
    240 
    241     WINGET_UTIL_API WinGetSQLiteIndexCheckConsistency(
    242         WINGET_SQLITE_INDEX_HANDLE index,
    243         BOOL* succeeded) try
    244     {
    245         THROW_HR_IF(E_INVALIDARG, !index);
    246         THROW_HR_IF(E_INVALIDARG, !succeeded);
    247 
    248         auto sqliteIndex = reinterpret_cast<SQLiteIndex*>(index);
    249         bool result = sqliteIndex->CheckConsistency(true);
    250 
    251         *succeeded = (result ? TRUE : FALSE);
    252 
    253         return S_OK;
    254     }
    255     CATCH_RETURN()
    256 
    257     WINGET_UTIL_API WinGetValidateManifest(
    258         WINGET_STRING manifestPath,
    259         BOOL* succeeded,
    260         WINGET_STRING_OUT* message) try
    261     {
    262         THROW_HR_IF(E_INVALIDARG, !manifestPath);
    263         THROW_HR_IF(E_INVALIDARG, !succeeded);
    264 
    265         try
    266         {
    267             ManifestValidateOption validateOption;
    268             validateOption.FullValidation = true;
    269             validateOption.ThrowOnWarning = true;
    270 
    271             (void)YamlParser::CreateFromPath(manifestPath, validateOption);
    272 
    273             *succeeded = TRUE;
    274         }
    275         catch (const ManifestException& e)
    276         {
    277             *succeeded = e.IsWarningOnly();
    278             if (message)
    279             {
    280                 *message = ::SysAllocString(ConvertToUTF16(e.GetManifestErrorMessage()).c_str());
    281             }
    282         }
    283 
    284         return S_OK;
    285     }
    286     CATCH_RETURN()
    287 
    288     WINGET_UTIL_API WinGetValidateManifestV2(
    289         WINGET_STRING inputPath,
    290         BOOL* succeeded,
    291         WINGET_STRING_OUT* message,
    292         WINGET_STRING mergedManifestPath,
    293         WinGetValidateManifestOption option) try
    294     {
    295         THROW_HR_IF(E_INVALIDARG, !inputPath);
    296         THROW_HR_IF(E_INVALIDARG, !succeeded);
    297 
    298         try
    299         {
    300             ManifestValidateOption validateOption;
    301             validateOption.FullValidation = true;
    302             validateOption.ThrowOnWarning = true;
    303             validateOption.SchemaValidationOnly = WI_IsFlagSet(option, WinGetValidateManifestOption::SchemaValidationOnly);
    304             validateOption.ErrorOnVerifiedPublisherFields = WI_IsFlagSet(option, WinGetValidateManifestOption::ErrorOnVerifiedPublisherFields);
    305             validateOption.InstallerValidation = WI_IsFlagSet(option, WinGetValidateManifestOption::InstallerValidations);
    306 
    307             (void)YamlParser::CreateFromPath(inputPath, validateOption, mergedManifestPath ? mergedManifestPath : L"");
    308 
    309             *succeeded = TRUE;
    310         }
    311         catch (const ManifestException& e)
    312         {
    313             *succeeded = e.IsWarningOnly();
    314             if (message)
    315             {
    316                 *message = ::SysAllocString(ConvertToUTF16(e.GetManifestErrorMessage()).c_str());
    317             }
    318         }
    319 
    320         return S_OK;
    321     }
    322     CATCH_RETURN()
    323 
    324     WINGET_UTIL_API WinGetCreateManifest(
    325         WINGET_STRING inputPath,
    326         BOOL* succeeded,
    327         WINGET_MANIFEST_HANDLE* manifest,
    328         WINGET_STRING_OUT* message,
    329         WINGET_STRING mergedManifestPath,
    330         WinGetCreateManifestOption option) try
    331     {
    332         THROW_HR_IF(E_INVALIDARG, !inputPath);
    333         THROW_HR_IF(E_INVALIDARG, !succeeded);
    334         THROW_HR_IF(E_INVALIDARG, !!*manifest);
    335         // ErrorOnVerifiedPublisherFields can only be used with SchemaAndSemanticValidation
    336         THROW_HR_IF(E_INVALIDARG, (WI_IsFlagSet(option, WinGetCreateManifestOption::ReturnErrorOnVerifiedPublisherFields) && WI_IsFlagClear(option, WinGetCreateManifestOption::SchemaAndSemanticValidation)));
    337 
    338         *succeeded = false;
    339         *manifest = nullptr;
    340 
    341         try
    342         {
    343             ManifestValidateOption validateOption;
    344 
    345             if (WI_IsFlagSet(option, WinGetCreateManifestOption::SchemaValidation) || WI_IsFlagSet(option, WinGetCreateManifestOption::SchemaAndSemanticValidation))
    346             {
    347                 validateOption.FullValidation = true;
    348                 validateOption.ThrowOnWarning = true;
    349                 validateOption.SchemaValidationOnly = WI_IsFlagClear(option, WinGetCreateManifestOption::SchemaAndSemanticValidation);
    350                 validateOption.ErrorOnVerifiedPublisherFields = WI_IsFlagSet(option, WinGetCreateManifestOption::ReturnErrorOnVerifiedPublisherFields);
    351             }
    352 
    353             if (WI_IsFlagSet(option, WinGetCreateManifestOption::AllowShadowManifest))
    354             {
    355                 validateOption.AllowShadowManifest = true;
    356             }
    357 
    358             std::unique_ptr<Manifest> result = std::make_unique<Manifest>(YamlParser::CreateFromPath(inputPath, validateOption, mergedManifestPath ? mergedManifestPath : L""));
    359 
    360             *manifest = static_cast<WINGET_MANIFEST_HANDLE>(result.release());
    361             *succeeded = true;
    362         }
    363         catch (const ManifestException& e)
    364         {
    365             *succeeded = e.IsWarningOnly();
    366             if (*succeeded)
    367             {
    368                 ManifestValidateOption validateOption;
    369                 if (WI_IsFlagSet(option, WinGetCreateManifestOption::AllowShadowManifest))
    370                 {
    371                     validateOption.AllowShadowManifest = true;
    372                 }
    373 
    374                 std::unique_ptr<Manifest> result = std::make_unique<Manifest>(YamlParser::CreateFromPath(inputPath, validateOption));
    375                 *manifest = static_cast<WINGET_MANIFEST_HANDLE>(result.release());
    376             }
    377             if (message)
    378             {
    379                 *message = ::SysAllocString(ConvertToUTF16(e.GetManifestErrorMessage()).c_str());
    380             }
    381         }
    382 
    383         return S_OK;
    384     }
    385     CATCH_RETURN()
    386 
    387     WINGET_UTIL_API WinGetCloseManifest(
    388         WINGET_MANIFEST_HANDLE manifest) try
    389     {
    390         THROW_HR_IF(E_INVALIDARG, !manifest);
    391 
    392         std::unique_ptr<Manifest> toClose{ reinterpret_cast<Manifest*>(manifest) };
    393 
    394         return S_OK;
    395     }
    396     CATCH_RETURN()
    397 
    398     WINGET_UTIL_API WinGetValidateManifestV3(
    399         WINGET_MANIFEST_HANDLE manifest,
    400         WINGET_SQLITE_INDEX_HANDLE index,
    401         WinGetValidateManifestResult* result,
    402         WINGET_STRING_OUT* message,
    403         WinGetValidateManifestOptionV2 option,
    404         WinGetValidateManifestOperationType operationType) try
    405     {
    406         THROW_HR_IF(E_INVALIDARG, !manifest);
    407         THROW_HR_IF(E_INVALIDARG, !result);
    408         // Index should be provided if DependenciesValidation or ArpVersionValidation is to be performed
    409         THROW_HR_IF(E_INVALIDARG, !index && (WI_IsFlagSet(option, WinGetValidateManifestOptionV2::DependenciesValidation) || WI_IsFlagSet(option, WinGetValidateManifestOptionV2::ArpVersionValidation)));
    410         THROW_HR_IF(E_INVALIDARG, option == WinGetValidateManifestOptionV2::None);
    411         
    412         *result = WinGetValidateManifestResult::InternalError;
    413 
    414         std::string validationMessage;
    415         auto validationResult = WinGetValidateManifestResult::Success;
    416 
    417         Manifest* manifestPtr = reinterpret_cast<Manifest*>(manifest);
    418         SQLiteIndex* sqliteIndex = reinterpret_cast<SQLiteIndex*>(index);
    419 
    420         if (WI_IsFlagSet(option, WinGetValidateManifestOptionV2::DependenciesValidation))
    421         {
    422             try
    423             {
    424                 if (operationType == WinGetValidateManifestOperationType::OperationTypeDelete)
    425                 {
    426                     PackageDependenciesValidation::VerifyDependenciesStructureForManifestDelete(sqliteIndex, *manifestPtr);
    427                 }
    428                 else
    429                 {
    430                     PackageDependenciesValidation::ValidateManifestDependencies(sqliteIndex, *manifestPtr);
    431                 }
    432             }
    433             catch (const ManifestException& e)
    434             {
    435                 if (!e.IsWarningOnly())
    436                 {
    437                     validationResult |= WinGetValidateManifestResult::DependenciesValidationFailure;
    438                 }
    439                 
    440                 validationResult |= static_cast<WinGetValidateManifestResult>( AppInstaller::Manifest::GetDependenciesValidationResultFromException(e) );
    441               
    442                 if (message)
    443                 {
    444                     validationMessage += e.GetManifestErrorMessage();
    445                 }
    446             }
    447         }
    448         
    449         if (WI_IsFlagSet(option, WinGetValidateManifestOptionV2::ArpVersionValidation))
    450         {
    451             try
    452             {
    453                 ValidateManifestArpVersion(sqliteIndex, *manifestPtr);
    454             }
    455             catch (const ManifestException& e)
    456             {
    457                 WI_SetFlagIf(validationResult, WinGetValidateManifestResult::ArpVersionValidationFailure, !e.IsWarningOnly());
    458                 if (message)
    459                 {
    460                     validationMessage += e.GetManifestErrorMessage();
    461                 }
    462             }
    463         }
    464 
    465         if (WI_IsFlagSet(option, WinGetValidateManifestOptionV2::InstallerValidation))
    466         {
    467             try
    468             {
    469                 auto errors = ValidateManifestInstallers(*manifestPtr);
    470                 if (errors.size() > 0)
    471                 {
    472                     // Throw the errors as ManifestExceptions to get processed errors and message.
    473                     THROW_EXCEPTION(ManifestException({ std::move(errors) }));
    474                 }
    475             }
    476             catch (const ManifestException& e)
    477             {
    478                 WI_SetFlagIf(validationResult, WinGetValidateManifestResult::InstallerValidationFailure, !e.IsWarningOnly());
    479                 if (message)
    480                 {
    481                     validationMessage += e.GetManifestErrorMessage();
    482                 }
    483             }
    484         }
    485 
    486         *result = validationResult;
    487         if (message)
    488         {
    489             *message = ::SysAllocString(ConvertToUTF16(validationMessage).c_str());
    490         }
    491 
    492         return S_OK;
    493     }
    494     CATCH_RETURN()
    495 
    496     WINGET_UTIL_API WinGetValidateManifestDependencies(
    497         WINGET_STRING inputPath,
    498         BOOL* succeeded,
    499         WINGET_STRING_OUT* message,
    500         WINGET_SQLITE_INDEX_HANDLE index,
    501         WinGetValidateManifestDependenciesOption validationOption) try
    502     {
    503         THROW_HR_IF(E_INVALIDARG, !inputPath);
    504         THROW_HR_IF(E_INVALIDARG, !succeeded);
    505 
    506         try
    507         {
    508             Manifest manifest = YamlParser::CreateFromPath(inputPath);
    509             SQLiteIndex* sqliteIndex(reinterpret_cast<SQLiteIndex*>(index));
    510             
    511             switch (validationOption)
    512             {
    513                 case WinGetValidateManifestDependenciesOption::DefaultValidation:
    514                     PackageDependenciesValidation::ValidateManifestDependencies(sqliteIndex, manifest);
    515                     break;
    516                 case WinGetValidateManifestDependenciesOption::ForDelete:
    517                     PackageDependenciesValidation::VerifyDependenciesStructureForManifestDelete(sqliteIndex, manifest);
    518                     break;
    519                 default:
    520                     THROW_HR(E_INVALIDARG);
    521             }
    522 
    523             *succeeded = TRUE;
    524         }
    525         catch (const ManifestException& e)
    526         {
    527             *succeeded = e.IsWarningOnly();
    528             if (message)
    529             {
    530                 *message = ::SysAllocString(ConvertToUTF16(e.GetManifestErrorMessage()).c_str());
    531             }
    532         }
    533 
    534         return S_OK;
    535     }
    536     CATCH_RETURN()
    537 
    538     WINGET_UTIL_API WinGetDownload(
    539         WINGET_STRING url,
    540         WINGET_STRING filePath,
    541         BYTE* sha256Hash,
    542         UINT32 sha256HashLength) try
    543     {
    544         THROW_HR_IF(E_INVALIDARG, !url);
    545         THROW_HR_IF(E_INVALIDARG, !filePath);
    546 
    547         bool computeHash = sha256Hash != nullptr && sha256HashLength != 0;
    548         THROW_HR_IF(E_INVALIDARG, !computeHash && (sha256Hash != nullptr || sha256HashLength != 0));
    549         THROW_HR_IF(E_INVALIDARG, computeHash && sha256HashLength != 32);
    550 
    551         AppInstaller::ProgressCallback callback;
    552         auto downloadResult = Download(ConvertToUTF8(url), filePath, DownloadType::WinGetUtil, callback);
    553 
    554         // At this point, if computeHash is set we have verified that the buffer is valid and 32 bytes.
    555         if (computeHash)
    556         {
    557             const auto& hash = downloadResult.Sha256Hash;
    558 
    559             // The SHA 256 hash length should always be 32 bytes.
    560             THROW_HR_IF(E_UNEXPECTED, hash.size() != sha256HashLength);
    561             std::copy(hash.begin(), hash.end(), sha256Hash);
    562         }
    563 
    564         return S_OK;
    565     }
    566     CATCH_RETURN()
    567 
    568     WINGET_UTIL_API WinGetCompareVersions(
    569         WINGET_STRING versionA,
    570         WINGET_STRING versionB,
    571         INT* comparisonResult) try
    572     {
    573         THROW_HR_IF(E_INVALIDARG, !versionA);
    574         THROW_HR_IF(E_INVALIDARG, !versionB);
    575 
    576         Version vA{ ConvertToUTF8(versionA) };
    577         Version vB{ ConvertToUTF8(versionB) };
    578 
    579         *comparisonResult = vA < vB ? -1 : (vA == vB ? 0 : 1);
    580 
    581         return S_OK;
    582     }
    583     CATCH_RETURN()
    584 
    585     WINGET_UTIL_API WinGetBeginInstallerMetadataCollection(
    586         WINGET_STRING inputJSON,
    587         WINGET_STRING logFilePath,
    588         WinGetBeginInstallerMetadataCollectionOptions options,
    589         WINGET_INSTALLER_METADATA_COLLECTION_HANDLE* collectionHandle) try
    590     {
    591         THROW_HR_IF(E_INVALIDARG, !inputJSON);
    592         THROW_HR_IF(E_INVALIDARG, !collectionHandle);
    593         THROW_HR_IF(E_INVALIDARG, !!*collectionHandle);
    594         // Flags specifying what inputJSON means are mutually exclusive
    595         THROW_HR_IF(E_INVALIDARG, !WI_IsClearOrSingleFlagSetInMask(options,
    596             WinGetBeginInstallerMetadataCollectionOption_InputIsFilePath | WinGetBeginInstallerMetadataCollectionOption_InputIsURI));
    597 
    598         std::unique_ptr<InstallerMetadataCollectionContext> result;
    599 
    600         if (WI_IsFlagSet(options, WinGetBeginInstallerMetadataCollectionOption_InputIsFilePath))
    601         {
    602             result = InstallerMetadataCollectionContext::FromFile(inputJSON, GetPathOrEmpty(logFilePath));
    603         }
    604         else if (WI_IsFlagSet(options, WinGetBeginInstallerMetadataCollectionOption_InputIsURI))
    605         {
    606             result = InstallerMetadataCollectionContext::FromURI(inputJSON, GetPathOrEmpty(logFilePath));
    607         }
    608         else
    609         {
    610             result = InstallerMetadataCollectionContext::FromJSON(inputJSON, GetPathOrEmpty(logFilePath));
    611         }
    612 
    613         *collectionHandle = static_cast<WINGET_INSTALLER_METADATA_COLLECTION_HANDLE>(result.release());
    614 
    615         return S_OK;
    616     }
    617     CATCH_RETURN()
    618 
    619     WINGET_UTIL_API WinGetCompleteInstallerMetadataCollection(
    620         WINGET_INSTALLER_METADATA_COLLECTION_HANDLE collectionHandle,
    621         WINGET_STRING outputFilePath,
    622         WinGetCompleteInstallerMetadataCollectionOptions options) try
    623     {
    624         THROW_HR_IF(E_INVALIDARG, !collectionHandle);
    625 
    626         // Since we always free the handle from calling this function, we can just store it in a unique_ptr from the start
    627         std::unique_ptr<InstallerMetadataCollectionContext> context{ reinterpret_cast<InstallerMetadataCollectionContext*>(collectionHandle) };
    628 
    629         if (WI_IsFlagSet(options, WinGetCompleteInstallerMetadataCollectionOption_Abandon))
    630         {
    631             return S_OK;
    632         }
    633 
    634         THROW_HR_IF(E_INVALIDARG, !outputFilePath);
    635 
    636         context->Complete(outputFilePath);
    637 
    638         return S_OK;
    639     }
    640     CATCH_RETURN()
    641 
    642     WINGET_UTIL_API WinGetMergeInstallerMetadata(
    643         WINGET_STRING inputJSON,
    644         WINGET_STRING_OUT* outputJSON,
    645         UINT32 maximumOutputSizeInBytes,
    646         WINGET_STRING logFilePath,
    647         WinGetMergeInstallerMetadataOptions) try
    648     {
    649         THROW_HR_IF(E_INVALIDARG, !inputJSON);
    650         THROW_HR_IF(E_INVALIDARG, !outputJSON);
    651 
    652         std::wstring merged = InstallerMetadataCollectionContext::Merge(inputJSON, maximumOutputSizeInBytes, GetPathOrEmpty(logFilePath));
    653         *outputJSON = ::SysAllocString(merged.c_str());
    654 
    655         return S_OK;
    656     }
    657     CATCH_RETURN()
    658 }