diff --git a/.pipelines/versionAndSignCheck.ps1 b/.pipelines/versionAndSignCheck.ps1 index 1bb271300d..44295b84ff 100644 --- a/.pipelines/versionAndSignCheck.ps1 +++ b/.pipelines/versionAndSignCheck.ps1 @@ -53,6 +53,10 @@ $nullVersionExceptions = @( "Microsoft.Windows.Widgets.dll", "AdaptiveCards.ObjectModel.WinUI3.dll", "AdaptiveCards.Rendering.WinUI3.dll") -join '|'; +$signatureExceptions = @( + "Namotion.Reflection.dll", + "NJsonSchema.Annotations.dll", + "NJsonSchema.dll") -join '|'; $totalFailure = 0; Write-Host $DirPath; @@ -86,7 +90,7 @@ $items | ForEach-Object { } else { $auth = Get-AuthenticodeSignature $_.FullName - if ($auth.SignerCertificate -eq $null) { + if ($auth.SignerCertificate -eq $null -and $_.Name -notmatch $signatureExceptions) { Write-Host "Not Signed: " + $_.FullName $totalFailure++; } diff --git a/NOTICE.md b/NOTICE.md index bedc11379d..03d8434c93 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -1522,6 +1522,7 @@ SOFTWARE. - ModernWpfUI - Moq - MSTest +- NJsonSchema - NLog - NLog.Extensions.Logging - NLog.Schema diff --git a/installer/PowerToysSetup/Product.wxs b/installer/PowerToysSetup/Product.wxs index 77ffad8483..ffdb60ae54 100644 --- a/installer/PowerToysSetup/Product.wxs +++ b/installer/PowerToysSetup/Product.wxs @@ -191,6 +191,11 @@ Installed AND (NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL") + + + + Installed AND (REMOVE="ALL") + Installed AND (NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL") @@ -252,6 +257,11 @@ Property="UnApplyModulesRegistryChangeSets" Value="[INSTALLFOLDER]" /> + + + @@ -325,6 +335,15 @@ DllEntry="UninstallDSCModuleCA" /> + + + = 9 && _wcsicmp(name.c_str() + (name.size() - 9), L".dsc.json") == 0) + { + std::error_code ec; + std::filesystem::remove(p, ec); + if (ec) + { + Logger::warn(L"DeleteDscJsonFilesCA: failed to delete {}", p.c_str()); + } + } + } + } + } + } + catch (...) + { + Logger::warn(L"DeleteDscJsonFilesCA: exception while deleting files"); + } + +LExit: + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} + const wchar_t *DSC_CONFIGURE_PSD1_NAME = L"Microsoft.PowerToys.Configure.psd1"; const wchar_t *DSC_CONFIGURE_PSM1_NAME = L"Microsoft.PowerToys.Configure.psm1"; diff --git a/installer/PowerToysSetupCustomActions/CustomAction.def b/installer/PowerToysSetupCustomActions/CustomAction.def index 9467ca2204..3fb518c28a 100644 --- a/installer/PowerToysSetupCustomActions/CustomAction.def +++ b/installer/PowerToysSetupCustomActions/CustomAction.def @@ -20,6 +20,7 @@ EXPORTS InstallDSCModuleCA InstallCmdPalPackageCA UnApplyModulesRegistryChangeSetsCA + DeleteDscJsonFilesCA UnRegisterCmdPalPackageCA UnRegisterContextMenuPackagesCA UninstallEmbeddedMSIXCA diff --git a/installer/PowerToysSetupCustomActionsVNext/CustomAction.cpp b/installer/PowerToysSetupCustomActionsVNext/CustomAction.cpp index 8c3ad76448..7e9e3ff358 100644 --- a/installer/PowerToysSetupCustomActionsVNext/CustomAction.cpp +++ b/installer/PowerToysSetupCustomActionsVNext/CustomAction.cpp @@ -3,6 +3,7 @@ #include "RcResource.h" #include #include +#include #include "../../src/common/logger/logger.h" #include "../../src/common/utils/gpo.h" @@ -232,7 +233,9 @@ UINT __stdcall LaunchPowerToysCA(MSIHANDLE hInstall) auto action = [&commandLine](HANDLE userToken) { - STARTUPINFO startupInfo{.cb = sizeof(STARTUPINFO), .wShowWindow = SW_SHOWNORMAL}; + STARTUPINFO startupInfo = { 0 }; + startupInfo.cb = sizeof(STARTUPINFO); + startupInfo.wShowWindow = SW_SHOWNORMAL; PROCESS_INFORMATION processInformation; PVOID lpEnvironment = NULL; @@ -271,7 +274,9 @@ UINT __stdcall LaunchPowerToysCA(MSIHANDLE hInstall) } else { - STARTUPINFO startupInfo{.cb = sizeof(STARTUPINFO), .wShowWindow = SW_SHOWNORMAL}; + STARTUPINFO startupInfo = { 0 }; + startupInfo.cb = sizeof(STARTUPINFO); + startupInfo.wShowWindow = SW_SHOWNORMAL; PROCESS_INFORMATION processInformation; @@ -398,6 +403,130 @@ LExit: return WcaFinalize(er); } +// Generate DSC manifests during install by invoking PowerToys.DSC.exe +UINT __stdcall GenerateDscManifestCA(MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + std::wstring installationFolder; + std::wstring exePath; + std::wstring args; + std::wstring commandLine; + STARTUPINFO startupInfo = { 0 }; + PROCESS_INFORMATION processInformation = { 0 }; + DWORD exitCode = 0; + + hr = WcaInitialize(hInstall, "GenerateDscManifest"); + ExitOnFailure(hr, "Failed to initialize"); + + hr = getInstallFolder(hInstall, installationFolder); + ExitOnFailure(hr, "Failed to get installFolder."); + + // Build executable path: \PowerToys.DSC.exe + exePath = installationFolder + L"\\PowerToys.DSC.exe"; + + // Build args: manifest --resource settings --outputDir "" + args = L"manifest --resource settings --outputDir \"" + installationFolder + L"\""; + commandLine = L"\"" + exePath + L"\" " + args; + + // Try to start the process and wait for it to finish; ignore failures (best-effort) + startupInfo.cb = sizeof(STARTUPINFO); + startupInfo.wShowWindow = SW_HIDE; + + if (!CreateProcess( + nullptr, + commandLine.data(), + nullptr, + nullptr, + FALSE, + CREATE_DEFAULT_ERROR_MODE, + nullptr, + nullptr, + &startupInfo, + &processInformation)) + { + Logger::error(L"GenerateDscManifestCA: failed to launch DSC manifest generator"); + // Don't fail installation on error + goto LExit; + } + + // Wait for completion + WaitForSingleObject(processInformation.hProcess, INFINITE); + + if (GetExitCodeProcess(processInformation.hProcess, &exitCode)) + { + if (exitCode != 0) + { + Logger::warn(L"GenerateDscManifestCA: manifest generator exited with non-zero code"); + } + else + { + Logger::info(L"GenerateDscManifestCA: manifests generated successfully"); + } + } + + if (!CloseHandle(processInformation.hProcess)) + { + Logger::warn(L"GenerateDscManifestCA: failed to close process handle"); + } + if (!CloseHandle(processInformation.hThread)) + { + Logger::warn(L"GenerateDscManifestCA: failed to close thread handle"); + } + +LExit: + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} + +// Delete any *.dsc.json files under the install folder on uninstall +UINT __stdcall DeleteDscJsonFilesCA(MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + std::wstring installationFolder; + + hr = WcaInitialize(hInstall, "DeleteDscJsonFiles"); + ExitOnFailure(hr, "Failed to initialize"); + + hr = getInstallFolder(hInstall, installationFolder); + ExitOnFailure(hr, "Failed to get installFolder."); + + try + { + std::filesystem::path root{ installationFolder }; + if (std::filesystem::exists(root) && std::filesystem::is_directory(root)) + { + for (auto const &entry : std::filesystem::recursive_directory_iterator(root, std::filesystem::directory_options::skip_permission_denied)) + { + if (!entry.is_regular_file()) continue; + const auto &p = entry.path(); + if (p.has_extension() && _wcsicmp(p.extension().c_str(), L".json") == 0) + { + auto name = p.filename().wstring(); + if (name.size() >= 9 && _wcsicmp(name.c_str() + (name.size() - 9), L".dsc.json") == 0) + { + std::error_code ec; + std::filesystem::remove(p, ec); + if (ec) + { + Logger::warn(L"DeleteDscJsonFilesCA: failed to delete %s", p.c_str()); + } + } + } + } + } + } + catch (...) + { + Logger::warn(L"DeleteDscJsonFilesCA: exception while deleting files"); + } + +LExit: + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} + const wchar_t *DSC_CONFIGURE_PSD1_NAME = L"Microsoft.PowerToys.Configure.psd1"; const wchar_t *DSC_CONFIGURE_PSM1_NAME = L"Microsoft.PowerToys.Configure.psm1"; @@ -424,7 +553,7 @@ UINT __stdcall InstallDSCModuleCA(MSIHANDLE hInstall) const auto modulesPath = baseModulesPath / L"Microsoft.PowerToys.Configure" / (get_product_version(false) + L".0"); std::error_code errorCode; - fs::create_directories(modulesPath, errorCode); + std::filesystem::create_directories(modulesPath, errorCode); if (errorCode) { hr = E_FAIL; @@ -433,7 +562,7 @@ UINT __stdcall InstallDSCModuleCA(MSIHANDLE hInstall) for (const auto *filename : {DSC_CONFIGURE_PSD1_NAME, DSC_CONFIGURE_PSM1_NAME}) { - fs::copy_file(fs::path(installationFolder) / "DSCModules" / filename, modulesPath / filename, fs::copy_options::overwrite_existing, errorCode); + std::filesystem::copy_file(std::filesystem::path(installationFolder) / "DSCModules" / filename, modulesPath / filename, std::filesystem::copy_options::overwrite_existing, errorCode); if (errorCode) { @@ -481,7 +610,7 @@ UINT __stdcall UninstallDSCModuleCA(MSIHANDLE hInstall) for (const auto *filename : {DSC_CONFIGURE_PSD1_NAME, DSC_CONFIGURE_PSM1_NAME}) { - fs::remove(versionedModulePath / filename, errorCode); + std::filesystem::remove(versionedModulePath / filename, errorCode); if (errorCode) { @@ -492,7 +621,7 @@ UINT __stdcall UninstallDSCModuleCA(MSIHANDLE hInstall) for (const auto *modulePath : {&versionedModulePath, &powerToysModulePath}) { - fs::remove(*modulePath, errorCode); + std::filesystem::remove(*modulePath, errorCode); if (errorCode) { diff --git a/installer/PowerToysSetupCustomActionsVNext/CustomAction.def b/installer/PowerToysSetupCustomActionsVNext/CustomAction.def index 39efc9ff70..721298c6fd 100644 --- a/installer/PowerToysSetupCustomActionsVNext/CustomAction.def +++ b/installer/PowerToysSetupCustomActionsVNext/CustomAction.def @@ -32,4 +32,5 @@ EXPORTS CleanFileLocksmithRuntimeRegistryCA CleanPowerRenameRuntimeRegistryCA CleanNewPlusRuntimeRegistryCA - \ No newline at end of file + GenerateDscManifestCA + DeleteDscJsonFilesCA \ No newline at end of file diff --git a/installer/PowerToysSetupVNext/Product.wxs b/installer/PowerToysSetupVNext/Product.wxs index e343897d5d..ce652ce208 100644 --- a/installer/PowerToysSetupVNext/Product.wxs +++ b/installer/PowerToysSetupVNext/Product.wxs @@ -119,6 +119,8 @@ + + @@ -132,6 +134,8 @@ + + @@ -244,6 +248,14 @@ + + + + + + + + diff --git a/src/dsc/v3/PowerToys.DSC/PowerToys.DSC.csproj b/src/dsc/v3/PowerToys.DSC/PowerToys.DSC.csproj index abfcc23200..230cd4556b 100644 --- a/src/dsc/v3/PowerToys.DSC/PowerToys.DSC.csproj +++ b/src/dsc/v3/PowerToys.DSC/PowerToys.DSC.csproj @@ -12,6 +12,8 @@ PowerToys DSC PowerToys.DSC enable + + true @@ -39,7 +41,7 @@ - +