mirror of
https://github.com/microsoft/PowerToys
synced 2025-08-28 21:17:42 +00:00
* Peek (#22498) * Add peek dll project * add spacebar preview and launch on hotkey press * add todo * add process handle to handle continuous press of hotkey * add tool to stop all powertoys processes * Add a blank Peek page and update nav menu * Add some initial content to Peek page including a toggle * refactor settings parsing * rename spacebar peek to peek viewer * rename script to stop powertoys processes * remove tool * Adding FileUtils for retrieving selected file in File Explorer * Remove unnecessary SndPeekSettings * Add shortcut setting * Set the shortcut to ctrl+space * Launching viewer with selected FE file * Add PeekUI WinUI3 project with interop events * Moving FileTypeUtils into PeekFileUtils project * execute winui3 app on hotkey * Fix paths with spaces * remove winui3 project * Resolve comment * add wpf app with toggle visibility on hotkey * fix visibility on startup * remove window properties and add todos * Fixed hidden extension and system file handling * wip * Add working WPF app with FileExplorer querying * remove c++ projects * Move native awaiter * Working Image control with image files * Resize and move window based on explorer monitor * Image render, window positioning and sizing clean up * add window management logic and selection logic * add extension methods to add circular iterating capability to linkedlistnode * Add OnArrowKeyPresshandler * Added titlebar with file name and scaling with titlebar height * fix flashing window on startup and process kept alive when powertoys exits * remove wait for debugger loop in ui * Add KeyIsDown method * Fix KeyDown issue with Key handled and check for repeat * Add thumbnail logic * Add all folder items if only one item is selected * File type helper * Using hresult * Add cancellation and rotation handling * Use extension instead of path * fIX CONFLICTS * Fixing some file type checks * Add new icon for Peek * Update page with the new Peek icon * Initialize IsEnabled and hook ActivationShortcut to dllmain * add icon to taskbar and titlebar * Add theme sensitive backgrounds * rename event handlers * add settings image * Move window data into obserable object * Refactor viewmodel, interop and helpers * Clean up * Add loading spinner * Add todos * Fix conflicts * Move native code into its own folder * Add peek to installer * Fix building peek and peekui projects * Replace UWP namespaces to WinAppSDK * Working WASDK placeholder project * Add exit when powertoys runner exit * Working winui3 with image display * Add WIC project with <TreatWarningAsErros> false for now * Fit content to window * Use Size from Windows.Foundation * Change order * Add some todos * Refactored native/interop code and added helpers to imagepreviewer * Rename projects * Move some code * Remove using Co-authored-by: Michael Salmon <miksalmon@users.noreply.github.com> Co-authored-by: Michael Salmon 🐟 <michaelpsalmon@outlook.com> Co-authored-by: Alireza Ebadi Ghajari <alirezae@microsoft.com> Co-authored-by: Jessie Su <Jessie.Su@microsoft.com> Co-authored-by: sujessie <102062556+sujessie@users.noreply.github.com> * Bump Microsoft.Windows.SDK.BuildTools version * [Peek] Plugin pattern to enable any file type previewing (#22475) * [Peek] Fetching image size through PropertyStore (#22530) * Fetching metadata from PropertySTore * Releasing objects to fix crash * Creating new PropertyHelper Co-authored-by: Daniel Chau <dancha@microsoft.com> * Juliata/filetypes (#22538) * Using the same list of file extensions as Lightbox's AppxManifest, and ensuring we convert file extension to lowercase * Add IsFileTypeSupported to IPreviewer * respond to PR comments * Add scale awareness to window centering (#22541) * [Peek] Fix installer builds, project configs and update assets (#22540) * Update installer * Fix installer errors * Fix peek vcxproj * Add package signing * Add peek to arm64 * Add back ARM64 toMeasureToolUI * Add versions to project * Update assets and icons * Add correct icon * [Peek] Enable PropertyStore for offline files (#22567) * Enabling PropertyStore for offline files Co-authored-by: Daniel Chau <dancha@microsoft.com> * [Peek] Adding unsupported file previewer (#22598) * Unsupported file previewer * Fix file display info * Fix property store calls * Update TODO * [Peek] Add WebView2 integration (#22506) * First commit with WIP logic to support WV2 in Peek module * Minor code cleanup and try/catch block * Added control to wrap WebView2 logic * Cleanup * Added logic to handle HTML previewing Properly update FilePreview according to file type * Code cleanup Updated comments * Updated comment * Removed comment * Code cleanup * Improved opening of web browser preview to avoid "blank" or "seeing previous page" issue Removed unused method Added xaml fallback to guarantee default/starting state * Removed folder * Updated factory logic to match master * address code review * addressed PR review * address PR review * Address PR review * address PR review * Address PR review * [Peek] Add basic file querying and navigation (#22589) * Refactor to facilitate file data initialization * Extract file-related code to new FileManager class * Add temp basic version * Clean + add todo for cancellations * Fix various nav-related issues * Temp - start moving iteration-related code to bg thread * Minor tweaks * Add FEHelper todo * Rename FileManager + various tweaks * Add basic throttling * Improve bg thread synchronization * Clean * Clean * Rename based on feedback * Rename FileQuery * Rename properties * Rename remaining fields * Add todos for nav success/failures Co-authored-by: Esteban Margaron <emargaron@microsoft.com> * [Peek] Add customized title bar (#22600) * Add basic button UI * Add function to get default app name and to open file in default app * Correct error output * Add filename to titlebar * Remove titlebar text from Resw * Add basic button UI * Add function to get default app name and to open file in default app * Add filename to titlebar * Correct error output * Remove titlebar text from Resw * Add SetDragRectangles * Correct logic, update function name * Add localization * Cleanup and adaptive width * Add fileIndex/NumberOfFiles for multiple files activation * Refine titlebar styles * Update error message; Return HResult from native methods; Update variable initialisation and string null testing * Titlebar height and adaptive width refinement * Add fallback to launch app picker if fail to open default app * Temp change to hide AppTitle_FileCount * Update launch button to command; Add keyboard accelerator * Update titlebar inactive background color * Update tooltip to add keyboard accelerator * Add comments to resw file * Fix accidental deletion from previous merge Co-authored-by: Jojo Zhou <yizzho@microsoft.com> Co-authored-by: Yawen Hou <yawenhou@microsoft.com> * Fix crash * Fix wrong thread exception * Make CurrentItemIndex setter private * Update titlebar filecount text * Fix titlebar draggable region and interactive region (bump WinAppSdk to latest) * [Peek] Unsupported File Previewer - Formatting string from resources (#22609) * Moving to string resource usage * Moving ReadableStringHelper to common project * Fix comments * [Peek] Fix foregrounding (#22633) * Fixing foregrounding * Get window handle inside BringToForeground extension method Co-authored-by: Daniel Chau <dancha@microsoft.com> Co-authored-by: Samuel Chapleau <sachaple@microsoft.com> * [Peek] ImagePreviewer - Handle error states (#22637) * add better preview state handling * add error handling in imagepreviewer and better state handling * fix error handling so exception is not bubbled up * improve performance and hook up unsupported previewer on error * remove commented code * address pr comments * [Peek] add PDF viewing support (#22636) * [Peek] add PDF viewing support * Fixed issue which would redirect some HTML and PDF files to external browser * Fixed refactored interface name * [Peek] Refine titlebar adaptive width (#22642) * Adjust adaptive width of titlebar * Remove visualstate setters for AppTitle_FileCount Co-authored-by: Jojo Zhou <yizzho@microsoft.com> * [Peek] New File Explorer tabs break Shell API to get selected files (#22641) * fix FE tab bug * remove unnecessary unsafe keyword * [Peek] add extra logic to properly render PNG files with transparency (#22613) * [Peek] added extra logic to render PNG files with proper transparency * Moved logic to ThumbnailHelper Cleanup * Created a separated previewer for PNG to only load the preview image with thumbnail logic * removed unused code * Updated state loading change * [Peek] Unsupported File Previewer - Setting Window Size (#22645) * Adding setting for unsupported file window * Fix * [Peek] Add tooltip to File (#22640) * Add tooltip to File * Add placeholder text for no tooltip * Address comments * Use StringBuilder Co-authored-by: Jojo Zhou <yizzho@microsoft.com> * Add full image quality support (#22654) * [Peek] Window foregrounding simplification and fixes + keep window visible if FE single selection changed (#22657) * Use different apis to bring to foreground removing remote thread wait and work as well as library loading * Keep window open if single selected file in FE is different * Removed unused methods * [Peek] Add cancellation token OnFilePropertyChanged (#22643) * Cancel file loading before opening another file * Add omitted cancellation checks * Catch task cancelled exception; Add more cancellation checkpoints * Add cancellation checkpoint beofre GetBitmapFromHBitmapAsync * Correct typo * Update to pass cancellation token individually to each async methods * Add lost cancellationToken source * Add cancellation token to PngPreviewer Co-authored-by: Yawen Hou <yawenhou@microsoft.com> * [Peek] Unsupported File Previewer - Preserve Transparency For File Icons (#22650) * Preserving transparency or icons * Remove TODO Co-authored-by: Samuel Chapleau <sachaple@microsoft.com> * [Peek] Update some installer build steps + assets update (#22683) * Fix settings & peek.ui.wpf * Add back missing icon * Add missing files and actions to installer * Keep window open if the selected file in explorer is different (only works for single file selection) * Undo last * [Peek] Add copy keyboard accelerator (#22647) * add copy keyboard accelerator * Fix comments Co-authored-by: Samuel Chapleau <sachaple@microsoft.com> * [Peek] add WV2 improvements (behavior and UX) (#22685) * [Peek] added logic to get max monitor size for opening WebView2 * Removed ununsed dependency property * Added workaround for cases where the web page would not finish navigating in a quick timing, for example google.com. * Remove window extensions from common and use nullable size argument instead Co-authored-by: Samuel Chapleau <sachaple@microsoft.com> * [Peek] Merge main, self-contained .NET and fix WebView2 user data dir issue (#22899) * Merge remote-tracking branch 'origin/main' into peek * Test sc * Set WebView2 user data dir * spellcheck * Fix comment * Move check if higher quality image is already loaded to the exact line where we change the Preview bitmap (#23083) * Fix opening Peek when FE window is set to full name path (#23082) * Move check for png thubmnail loading priority * Remove Peek.UI.WPF project * Remove duplicated method in powertoys setup * [Peek] Fix selecting files from the correct focused opened File Explorer tab & from Desktop (#23489) * Get file based on active tab handle instead of window title * Refactor code to get active tab * Getting all items from the shell API working again, except for desktop * Refactor and cleanup com & native code * Add back removed peek xaml assets in Product.wxs * Remove some dependencies that do not seem necessary in Product.wxs * [Peek] Small images (#23554) * change stretch value * compare with actual window size * consider scaling factor * set max size * clean up * clean up * clean up previewers * scaling factor in bitmap previewer * max image size property * [Peek]Handle errors for HEIC/HEIF and fall back to default previewer if there is no thumbnail (#22684) * Handle errors when getting filesize by falling back to default previewer * Bringing back other file types that are fixed with these code changes --------- Co-authored-by: Samuel Chapleau <sachaple@microsoft.com> * [Peek] Add unsupported file icon fallback (#23735) * Refactor icon retrieval, refactor hbitmap to bitmap conversion, add icon fallback * Add svg to assets in installer * [Peek] Refactoring of file system models, removal of PngPreviewer, retrieving of folder size via Scripting com reference and other fixes (#23955) * Refactor icon retrieval, refactor hbitmap to bitmap conversion, add icon fallback * Add svg to assets in installer * - Refactor File class into IFileSystemItem, FileItem & FolderItem - Display size for folders using Scripting namespace - Remove default app buttons for files or folders not supporting it * Add better content type via storage apis * Add check for storagefile in PngPreviewer * Fix png stretching * Remove png previewer * Rename ThumbnailOptions.None to ThumbnailOptions.ResizeToFit * [Peek] Removed monitor percentage evaluation for the UnsupportedFilePreview control (#24002) * Remove settings for percentage of windows and keep default min size. * Fix margin on unsupported control * Use nullable Size for image size & open file on background thread (#24004) * [Peek] SVG support (#24237) * svg previewer * svg size * set scaling factor * set image size * changed image source type * non nullable image size * notify svg previewer changed * uncomment * rename BitmapPreviewer * move svg support * remove svg previewer * [Peek] Implementation of a performant and reliable Neighboring Files Query (#24943) * Use IShellItemArray as the backing array of item * Finalize and cleanup NFQ implementation * Cleanup remainder of the code * Remove unused using * [Peek] Pin the window position (#24927) * [Peek] Telemetry and logging (#25231) * text preview * scrolling * changed size * webview2 preview * common preview project * previewpane: use common project * peek: use common * previewpane: moved md * peek: md * previewpane: clean up * clean up * moved monaco files * moved formatters * rename * moved common monaco helper * dev files support * installer * removed versions * warnings: culture info * warnings: names * clean up * warnings: dispose * warnings: default values * warnings * warnings: charset * warnings: exceptions * suppress warning * installer: added peek * changed peek guid * monaco folders * peek deps * peek files * peek resources * removed additional monaco folder * set host name * Update installer * hardcode monaco path * leave single webview control * moved path to common * project * more meaningful todos * moved temp folder cleanup * todo * extension check * spell: monaco * spellcheck * spellcheck * fix id * fix spelling * key to spelling * id fix * Fix monaco resolution at install time * Fix user install. Add needed files * installer: remove peek localization files. It's a WinUI app * installer:fix signing * removed unused * settings: flyout enable/disable for Peek * simplify string * property changed handle * [Peek][Settings] Peek OOBE page (#25895) * [Peek] GPO (#25918) * Add Native methods file to exception * Fix merge issue on solution file * Adjust spellcheck * Remove boilerplate code * Add module interface telemetry * Remove change to README.md * Add entry to README * Clean up some non-changes * Fix order of Peek in Settings menu * [Settings] Make peek descriptions more descriptive --------- Co-authored-by: Michael Salmon <miksalmon@users.noreply.github.com> Co-authored-by: Michael Salmon 🐟 <michaelpsalmon@outlook.com> Co-authored-by: Alireza Ebadi Ghajari <alirezae@microsoft.com> Co-authored-by: Jessie Su <Jessie.Su@microsoft.com> Co-authored-by: sujessie <102062556+sujessie@users.noreply.github.com> Co-authored-by: Daniel Chau <d.chau@alumni.ubc.ca> Co-authored-by: Daniel Chau <dancha@microsoft.com> Co-authored-by: jth-ms <73617023+jth-ms@users.noreply.github.com> Co-authored-by: Robson <rp.pontin@gmail.com> Co-authored-by: estebanm123 <49930791+estebanm123@users.noreply.github.com> Co-authored-by: Esteban Margaron <emargaron@microsoft.com> Co-authored-by: Yawen Hou <Sytta@users.noreply.github.com> Co-authored-by: Jojo Zhou <yizzho@microsoft.com> Co-authored-by: Yawen Hou <yawenhou@microsoft.com> Co-authored-by: Jojo Zhou <39350350+Joanna-Zhou@users.noreply.github.com> Co-authored-by: Stefan Markovic <57057282+stefansjfw@users.noreply.github.com> Co-authored-by: Seraphima Zykova <zykovas91@gmail.com> Co-authored-by: Stefan Markovic <stefan@janeasystems.com> Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>
1488 lines
52 KiB
C++
1488 lines
52 KiB
C++
#include "stdafx.h"
|
|
#include "resource.h"
|
|
#include "RcResource.h"
|
|
#include <ProjectTelemetry.h>
|
|
|
|
#include <spdlog/sinks/base_sink.h>
|
|
|
|
#include "../../src/common/logger/logger.h"
|
|
#include "../../src/common/utils/gpo.h"
|
|
#include "../../src/common/utils/MsiUtils.h"
|
|
#include "../../src/common/utils/modulesRegistry.h"
|
|
#include "../../src/common/updating/installer.h"
|
|
#include "../../src/common/version/version.h"
|
|
|
|
#include <winrt/Windows.ApplicationModel.h>
|
|
#include <winrt/Windows.Foundation.h>
|
|
#include <winrt/Windows.Management.Deployment.h>
|
|
|
|
#include "DepsFilesLists.h"
|
|
|
|
using namespace std;
|
|
|
|
HINSTANCE DLL_HANDLE = nullptr;
|
|
|
|
TRACELOGGING_DEFINE_PROVIDER(
|
|
g_hProvider,
|
|
"Microsoft.PowerToysInstaller",
|
|
// {e1d8165d-5cb6-5c74-3b51-bdfbfe4f7a3b}
|
|
(0xe1d8165d, 0x5cb6, 0x5c74, 0x3b, 0x51, 0xbd, 0xfb, 0xfe, 0x4f, 0x7a, 0x3b),
|
|
TraceLoggingOptionProjectTelemetry());
|
|
|
|
const DWORD USERNAME_DOMAIN_LEN = DNLEN + UNLEN + 2; // Domain Name + '\' + User Name + '\0'
|
|
const DWORD USERNAME_LEN = UNLEN + 1; // User Name + '\0'
|
|
|
|
static const wchar_t* POWERTOYS_EXE_COMPONENT = L"{A2C66D91-3485-4D00-B04D-91844E6B345B}";
|
|
static const wchar_t* POWERTOYS_UPGRADE_CODE = L"{42B84BF7-5FBF-473B-9C8B-049DC16F7708}";
|
|
|
|
HRESULT getInstallFolder(MSIHANDLE hInstall, std::wstring& installationDir)
|
|
{
|
|
DWORD len = 0;
|
|
wchar_t _[1];
|
|
MsiGetPropertyW(hInstall, L"CustomActionData", _, &len);
|
|
len += 1;
|
|
installationDir.resize(len);
|
|
HRESULT hr = MsiGetPropertyW(hInstall, L"CustomActionData", installationDir.data(), &len);
|
|
if (installationDir.length())
|
|
{
|
|
installationDir.resize(installationDir.length() - 1);
|
|
}
|
|
ExitOnFailure(hr, "Failed to get INSTALLFOLDER property.");
|
|
LExit:
|
|
return hr;
|
|
}
|
|
|
|
UINT __stdcall CheckGPOCA(MSIHANDLE hInstall)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
hr = WcaInitialize(hInstall, "CheckGPOCA");
|
|
ExitOnFailure(hr, "Failed to initialize");
|
|
|
|
LPWSTR currentScope = nullptr;
|
|
hr = WcaGetProperty(L"InstallScope", ¤tScope);
|
|
|
|
if(std::wstring{ currentScope } == L"perUser")
|
|
{
|
|
if (powertoys_gpo::getDisablePerUserInstallationValue() == powertoys_gpo::gpo_rule_configured_enabled)
|
|
{
|
|
PMSIHANDLE hRecord = MsiCreateRecord(0);
|
|
MsiRecordSetString(hRecord, 0, TEXT("The system administrator has disabled per-user installation."));
|
|
MsiProcessMessage(hInstall, static_cast<INSTALLMESSAGE>(INSTALLMESSAGE_ERROR + MB_OK), hRecord);
|
|
hr = E_ABORT;
|
|
}
|
|
}
|
|
|
|
LExit:
|
|
UINT er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
|
|
return WcaFinalize(er);
|
|
}
|
|
|
|
UINT __stdcall ApplyModulesRegistryChangeSetsCA(MSIHANDLE hInstall)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
UINT er = ERROR_SUCCESS;
|
|
std::wstring installationFolder;
|
|
bool failedToApply = false;
|
|
|
|
hr = WcaInitialize(hInstall, "ApplyModulesRegistryChangeSets");
|
|
ExitOnFailure(hr, "Failed to initialize");
|
|
hr = getInstallFolder(hInstall, installationFolder);
|
|
ExitOnFailure(hr, "Failed to get installFolder.");
|
|
|
|
for (const auto& changeSet : getAllOnByDefaultModulesChangeSets(installationFolder))
|
|
{
|
|
if (!changeSet.apply())
|
|
{
|
|
Logger::error(L"Couldn't apply registry changeSet");
|
|
failedToApply = true;
|
|
}
|
|
}
|
|
|
|
if (!failedToApply)
|
|
{
|
|
Logger::info(L"All registry changeSets applied successfully");
|
|
}
|
|
LExit:
|
|
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
|
|
return WcaFinalize(er);
|
|
}
|
|
|
|
UINT __stdcall UnApplyModulesRegistryChangeSetsCA(MSIHANDLE hInstall)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
UINT er = ERROR_SUCCESS;
|
|
std::wstring installationFolder;
|
|
|
|
hr = WcaInitialize(hInstall, "UndoModulesRegistryChangeSets"); // original func name is too long
|
|
ExitOnFailure(hr, "Failed to initialize");
|
|
hr = getInstallFolder(hInstall, installationFolder);
|
|
ExitOnFailure(hr, "Failed to get installFolder.");
|
|
for (const auto& changeSet : getAllModulesChangeSets(installationFolder))
|
|
{
|
|
changeSet.unApply();
|
|
}
|
|
|
|
ExitOnFailure(hr, "Failed to extract msix");
|
|
|
|
LExit:
|
|
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
|
|
return WcaFinalize(er);
|
|
}
|
|
|
|
UINT __stdcall InstallEmbeddedMSIXCA(MSIHANDLE hInstall)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
UINT er = ERROR_SUCCESS;
|
|
hr = WcaInitialize(hInstall, "InstallEmbeddedMSIXCA");
|
|
ExitOnFailure(hr, "Failed to initialize");
|
|
|
|
if (auto msix = RcResource::create(IDR_BIN_MSIX_HELLO_PACKAGE, L"BIN", DLL_HANDLE))
|
|
{
|
|
Logger::info(L"Extracted MSIX");
|
|
// TODO: Use to activate embedded MSIX
|
|
const auto msix_path = std::filesystem::temp_directory_path() / "hello_package.msix";
|
|
if (!msix->saveAsFile(msix_path))
|
|
{
|
|
ExitOnFailure(hr, "Failed to save msix");
|
|
}
|
|
Logger::info(L"Saved MSIX");
|
|
using namespace winrt::Windows::Management::Deployment;
|
|
using namespace winrt::Windows::Foundation;
|
|
|
|
Uri msix_uri{ msix_path.wstring() };
|
|
PackageManager pm;
|
|
auto result = pm.AddPackageAsync(msix_uri, nullptr, DeploymentOptions::None).get();
|
|
if (!result)
|
|
{
|
|
ExitOnFailure(hr, "Failed to AddPackage");
|
|
}
|
|
|
|
Logger::info(L"MSIX[s] were installed!");
|
|
}
|
|
else
|
|
{
|
|
ExitOnFailure(hr, "Failed to extract msix");
|
|
}
|
|
|
|
LExit:
|
|
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
|
|
return WcaFinalize(er);
|
|
}
|
|
|
|
UINT __stdcall UninstallEmbeddedMSIXCA(MSIHANDLE hInstall)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
UINT er = ERROR_SUCCESS;
|
|
using namespace winrt::Windows::Management::Deployment;
|
|
using namespace winrt::Windows::Foundation;
|
|
// TODO: This must be replaced with the actual publisher and package name
|
|
const wchar_t package_name[] = L"46b35c25-b593-48d5-aeb1-d3e9c3b796e9";
|
|
const wchar_t publisher[] = L"CN=yuyoyuppe";
|
|
PackageManager pm;
|
|
|
|
hr = WcaInitialize(hInstall, "UninstallEmbeddedMSIXCA");
|
|
ExitOnFailure(hr, "Failed to initialize");
|
|
|
|
for (const auto& p : pm.FindPackagesForUser({}, package_name, publisher))
|
|
{
|
|
auto result = pm.RemovePackageAsync(p.Id().FullName()).get();
|
|
if (result)
|
|
{
|
|
Logger::info(L"MSIX was uninstalled!");
|
|
}
|
|
else
|
|
{
|
|
Logger::error(L"Couldn't uninstall MSIX!");
|
|
}
|
|
}
|
|
|
|
LExit:
|
|
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
|
|
return WcaFinalize(er);
|
|
}
|
|
|
|
// Creates a Scheduled Task to run at logon for the current user.
|
|
// The path of the executable to run should be passed as the CustomActionData (Value).
|
|
// Based on the Task Scheduler Logon Trigger Example:
|
|
// https://learn.microsoft.com/windows/win32/taskschd/logon-trigger-example--c---/
|
|
UINT __stdcall CreateScheduledTaskCA(MSIHANDLE hInstall)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
UINT er = ERROR_SUCCESS;
|
|
|
|
TCHAR username_domain[USERNAME_DOMAIN_LEN];
|
|
TCHAR username[USERNAME_LEN];
|
|
|
|
std::wstring wstrTaskName;
|
|
|
|
ITaskService* pService = nullptr;
|
|
ITaskFolder* pTaskFolder = nullptr;
|
|
ITaskDefinition* pTask = nullptr;
|
|
IRegistrationInfo* pRegInfo = nullptr;
|
|
ITaskSettings* pSettings = nullptr;
|
|
ITriggerCollection* pTriggerCollection = nullptr;
|
|
IRegisteredTask* pRegisteredTask = nullptr;
|
|
IPrincipal* pPrincipal = nullptr;
|
|
ITrigger* pTrigger = nullptr;
|
|
ILogonTrigger* pLogonTrigger = nullptr;
|
|
IAction* pAction = nullptr;
|
|
IActionCollection* pActionCollection = nullptr;
|
|
IExecAction* pExecAction = nullptr;
|
|
|
|
LPWSTR wszExecutablePath = nullptr;
|
|
|
|
hr = WcaInitialize(hInstall, "CreateScheduledTaskCA");
|
|
ExitOnFailure(hr, "Failed to initialize");
|
|
|
|
Logger::info(L"CreateScheduledTaskCA Initialized.");
|
|
|
|
// ------------------------------------------------------
|
|
// Get the Domain/Username for the trigger.
|
|
//
|
|
// This action needs to run as the system to get elevated privileges from the installation,
|
|
// so GetUserNameEx can't be used to get the current user details.
|
|
// The USERNAME and USERDOMAIN environment variables are used instead.
|
|
if (!GetEnvironmentVariable(L"USERNAME", username, USERNAME_LEN))
|
|
{
|
|
ExitWithLastError(hr, "Getting username failed: %x", hr);
|
|
}
|
|
if (!GetEnvironmentVariable(L"USERDOMAIN", username_domain, USERNAME_DOMAIN_LEN))
|
|
{
|
|
ExitWithLastError(hr, "Getting the user's domain failed: %x", hr);
|
|
}
|
|
wcscat_s(username_domain, L"\\");
|
|
wcscat_s(username_domain, username);
|
|
|
|
Logger::info(L"Current user detected: {}", username_domain);
|
|
|
|
// Task Name.
|
|
wstrTaskName = L"Autorun for ";
|
|
wstrTaskName += username;
|
|
|
|
// Get the executable path passed to the custom action.
|
|
hr = WcaGetProperty(L"CustomActionData", &wszExecutablePath);
|
|
ExitOnFailure(hr, "Failed to get the executable path from CustomActionData.");
|
|
|
|
// COM and Security Initialization is expected to have been done by the MSI.
|
|
// It couldn't be done in the DLL, anyway.
|
|
// ------------------------------------------------------
|
|
// Create an instance of the Task Service.
|
|
hr = CoCreateInstance(CLSID_TaskScheduler,
|
|
nullptr,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_ITaskService,
|
|
reinterpret_cast<void**>(&pService));
|
|
ExitOnFailure(hr, "Failed to create an instance of ITaskService: %x", hr);
|
|
|
|
// Connect to the task service.
|
|
hr = pService->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t());
|
|
ExitOnFailure(hr, "ITaskService::Connect failed: %x", hr);
|
|
|
|
// ------------------------------------------------------
|
|
// Get the PowerToys task folder. Creates it if it doesn't exist.
|
|
hr = pService->GetFolder(_bstr_t(L"\\PowerToys"), &pTaskFolder);
|
|
if (FAILED(hr))
|
|
{
|
|
// Folder doesn't exist. Get the Root folder and create the PowerToys subfolder.
|
|
ITaskFolder* pRootFolder = nullptr;
|
|
hr = pService->GetFolder(_bstr_t(L"\\"), &pRootFolder);
|
|
ExitOnFailure(hr, "Cannot get Root Folder pointer: %x", hr);
|
|
hr = pRootFolder->CreateFolder(_bstr_t(L"\\PowerToys"), _variant_t(L""), &pTaskFolder);
|
|
if (FAILED(hr))
|
|
{
|
|
pRootFolder->Release();
|
|
ExitOnFailure(hr, "Cannot create PowerToys task folder: %x", hr);
|
|
}
|
|
Logger::info(L"PowerToys task folder created.");
|
|
}
|
|
|
|
// If the same task exists, remove it.
|
|
pTaskFolder->DeleteTask(_bstr_t(wstrTaskName.c_str()), 0);
|
|
|
|
// Create the task builder object to create the task.
|
|
hr = pService->NewTask(0, &pTask);
|
|
ExitOnFailure(hr, "Failed to create a task definition: %x", hr);
|
|
|
|
// ------------------------------------------------------
|
|
// Get the registration info for setting the identification.
|
|
hr = pTask->get_RegistrationInfo(&pRegInfo);
|
|
ExitOnFailure(hr, "Cannot get identification pointer: %x", hr);
|
|
hr = pRegInfo->put_Author(_bstr_t(username_domain));
|
|
ExitOnFailure(hr, "Cannot put identification info: %x", hr);
|
|
|
|
// ------------------------------------------------------
|
|
// Create the settings for the task
|
|
hr = pTask->get_Settings(&pSettings);
|
|
ExitOnFailure(hr, "Cannot get settings pointer: %x", hr);
|
|
|
|
hr = pSettings->put_StartWhenAvailable(VARIANT_FALSE);
|
|
ExitOnFailure(hr, "Cannot put_StartWhenAvailable setting info: %x", hr);
|
|
hr = pSettings->put_StopIfGoingOnBatteries(VARIANT_FALSE);
|
|
ExitOnFailure(hr, "Cannot put_StopIfGoingOnBatteries setting info: %x", hr);
|
|
hr = pSettings->put_ExecutionTimeLimit(_bstr_t(L"PT0S")); //Unlimited
|
|
ExitOnFailure(hr, "Cannot put_ExecutionTimeLimit setting info: %x", hr);
|
|
hr = pSettings->put_DisallowStartIfOnBatteries(VARIANT_FALSE);
|
|
ExitOnFailure(hr, "Cannot put_DisallowStartIfOnBatteries setting info: %x", hr);
|
|
|
|
// ------------------------------------------------------
|
|
// Get the trigger collection to insert the logon trigger.
|
|
hr = pTask->get_Triggers(&pTriggerCollection);
|
|
ExitOnFailure(hr, "Cannot get trigger collection: %x", hr);
|
|
|
|
// Add the logon trigger to the task.
|
|
hr = pTriggerCollection->Create(TASK_TRIGGER_LOGON, &pTrigger);
|
|
ExitOnFailure(hr, "Cannot create the trigger: %x", hr);
|
|
|
|
hr = pTrigger->QueryInterface(
|
|
IID_ILogonTrigger, (void**)&pLogonTrigger);
|
|
pTrigger->Release();
|
|
ExitOnFailure(hr, "QueryInterface call failed for ILogonTrigger: %x", hr);
|
|
|
|
hr = pLogonTrigger->put_Id(_bstr_t(L"Trigger1"));
|
|
if (FAILED(hr))
|
|
{
|
|
Logger::error(L"Cannot put the trigger ID: {}", hr);
|
|
}
|
|
|
|
// Timing issues may make explorer not be started when the task runs.
|
|
// Add a little delay to mitigate this.
|
|
hr = pLogonTrigger->put_Delay(_bstr_t(L"PT03S"));
|
|
if (FAILED(hr))
|
|
{
|
|
Logger::error(L"Cannot put the trigger delay: {}", hr);
|
|
}
|
|
|
|
// Define the user. The task will execute when the user logs on.
|
|
// The specified user must be a user on this computer.
|
|
hr = pLogonTrigger->put_UserId(_bstr_t(username_domain));
|
|
pLogonTrigger->Release();
|
|
ExitOnFailure(hr, "Cannot add user ID to logon trigger: %x", hr);
|
|
|
|
// ------------------------------------------------------
|
|
// Add an Action to the task. This task will execute the path passed to this custom action.
|
|
|
|
// Get the task action collection pointer.
|
|
hr = pTask->get_Actions(&pActionCollection);
|
|
ExitOnFailure(hr, "Cannot get Task collection pointer: %x", hr);
|
|
|
|
// Create the action, specifying that it is an executable action.
|
|
hr = pActionCollection->Create(TASK_ACTION_EXEC, &pAction);
|
|
pActionCollection->Release();
|
|
ExitOnFailure(hr, "Cannot create the action: %x", hr);
|
|
|
|
// QI for the executable task pointer.
|
|
hr = pAction->QueryInterface(
|
|
IID_IExecAction, (void**)&pExecAction);
|
|
pAction->Release();
|
|
ExitOnFailure(hr, "QueryInterface call failed for IExecAction: %x", hr);
|
|
|
|
// Set the path of the executable to PowerToys (passed as CustomActionData).
|
|
hr = pExecAction->put_Path(_bstr_t(wszExecutablePath));
|
|
pExecAction->Release();
|
|
ExitOnFailure(hr, "Cannot set path of executable: %x", hr);
|
|
|
|
// ------------------------------------------------------
|
|
// Create the principal for the task
|
|
hr = pTask->get_Principal(&pPrincipal);
|
|
ExitOnFailure(hr, "Cannot get principal pointer: %x", hr);
|
|
|
|
// Set up principal information:
|
|
hr = pPrincipal->put_Id(_bstr_t(L"Principal1"));
|
|
if (FAILED(hr))
|
|
{
|
|
Logger::error(L"Cannot put the principal ID: {}", hr);
|
|
}
|
|
|
|
hr = pPrincipal->put_UserId(_bstr_t(username_domain));
|
|
if (FAILED(hr))
|
|
{
|
|
Logger::error(L"Cannot put principal user Id: {}", hr);
|
|
}
|
|
|
|
hr = pPrincipal->put_LogonType(TASK_LOGON_INTERACTIVE_TOKEN);
|
|
if (FAILED(hr))
|
|
{
|
|
Logger::error(L"Cannot put principal logon type: {}", hr);
|
|
}
|
|
|
|
// Run the task with the highest available privileges.
|
|
hr = pPrincipal->put_RunLevel(TASK_RUNLEVEL_LUA);
|
|
pPrincipal->Release();
|
|
ExitOnFailure(hr, "Cannot put principal run level: %x", hr);
|
|
|
|
// ------------------------------------------------------
|
|
// Save the task in the PowerToys folder.
|
|
{
|
|
_variant_t SDDL_FULL_ACCESS_FOR_EVERYONE = L"D:(A;;FA;;;WD)";
|
|
hr = pTaskFolder->RegisterTaskDefinition(
|
|
_bstr_t(wstrTaskName.c_str()),
|
|
pTask,
|
|
TASK_CREATE_OR_UPDATE,
|
|
_variant_t(username_domain),
|
|
_variant_t(),
|
|
TASK_LOGON_INTERACTIVE_TOKEN,
|
|
SDDL_FULL_ACCESS_FOR_EVERYONE,
|
|
&pRegisteredTask);
|
|
ExitOnFailure(hr, "Error saving the Task : %x", hr);
|
|
}
|
|
|
|
Logger::info(L"Scheduled task created for the current user.");
|
|
|
|
LExit:
|
|
ReleaseStr(wszExecutablePath);
|
|
if (pService)
|
|
{
|
|
pService->Release();
|
|
}
|
|
if (pTaskFolder)
|
|
{
|
|
pTaskFolder->Release();
|
|
}
|
|
if (pTask)
|
|
{
|
|
pTask->Release();
|
|
}
|
|
if (pRegInfo)
|
|
{
|
|
pRegInfo->Release();
|
|
}
|
|
if (pSettings)
|
|
{
|
|
pSettings->Release();
|
|
}
|
|
if (pTriggerCollection)
|
|
{
|
|
pTriggerCollection->Release();
|
|
}
|
|
if (pRegisteredTask)
|
|
{
|
|
pRegisteredTask->Release();
|
|
}
|
|
|
|
if (!SUCCEEDED(hr))
|
|
{
|
|
PMSIHANDLE hRecord = MsiCreateRecord(0);
|
|
MsiRecordSetString(hRecord, 0, TEXT("Failed to create a scheduled task to start PowerToys at user login. You can re-try to create the scheduled task using the PowerToys settings."));
|
|
MsiProcessMessage(hInstall, static_cast<INSTALLMESSAGE>(INSTALLMESSAGE_WARNING + MB_OK), hRecord);
|
|
}
|
|
|
|
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
|
|
return WcaFinalize(er);
|
|
}
|
|
|
|
// Removes all Scheduled Tasks in the PowerToys folder and deletes the folder afterwards.
|
|
// Based on the Task Scheduler Displaying Task Names and State example:
|
|
// https://learn.microsoft.com/windows/desktop/TaskSchd/displaying-task-names-and-state--c---/
|
|
UINT __stdcall RemoveScheduledTasksCA(MSIHANDLE hInstall)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
UINT er = ERROR_SUCCESS;
|
|
|
|
ITaskService* pService = nullptr;
|
|
ITaskFolder* pTaskFolder = nullptr;
|
|
IRegisteredTaskCollection* pTaskCollection = nullptr;
|
|
ITaskFolder* pRootFolder = nullptr;
|
|
LONG numTasks = 0;
|
|
|
|
hr = WcaInitialize(hInstall, "RemoveScheduledTasksCA");
|
|
ExitOnFailure(hr, "Failed to initialize");
|
|
|
|
Logger::info(L"RemoveScheduledTasksCA Initialized.");
|
|
|
|
// COM and Security Initialization is expected to have been done by the MSI.
|
|
// It couldn't be done in the DLL, anyway.
|
|
// ------------------------------------------------------
|
|
// Create an instance of the Task Service.
|
|
hr = CoCreateInstance(CLSID_TaskScheduler,
|
|
nullptr,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_ITaskService,
|
|
reinterpret_cast<void**>(&pService));
|
|
ExitOnFailure(hr, "Failed to create an instance of ITaskService: %x", hr);
|
|
|
|
// Connect to the task service.
|
|
hr = pService->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t());
|
|
ExitOnFailure(hr, "ITaskService::Connect failed: %x", hr);
|
|
|
|
// ------------------------------------------------------
|
|
// Get the PowerToys task folder.
|
|
hr = pService->GetFolder(_bstr_t(L"\\PowerToys"), &pTaskFolder);
|
|
if (FAILED(hr))
|
|
{
|
|
// Folder doesn't exist. No need to delete anything.
|
|
Logger::info(L"The PowerToys scheduled task folder wasn't found. Nothing to delete.");
|
|
hr = S_OK;
|
|
ExitFunction();
|
|
}
|
|
|
|
// -------------------------------------------------------
|
|
// Get the registered tasks in the folder.
|
|
hr = pTaskFolder->GetTasks(TASK_ENUM_HIDDEN, &pTaskCollection);
|
|
ExitOnFailure(hr, "Cannot get the registered tasks: %x", hr);
|
|
|
|
hr = pTaskCollection->get_Count(&numTasks);
|
|
for (LONG i = 0; i < numTasks; i++)
|
|
{
|
|
// Delete all the tasks found.
|
|
// If some tasks can't be deleted, the folder won't be deleted later and the user will still be notified.
|
|
IRegisteredTask* pRegisteredTask = nullptr;
|
|
hr = pTaskCollection->get_Item(_variant_t(i + 1), &pRegisteredTask);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
BSTR taskName = nullptr;
|
|
hr = pRegisteredTask->get_Name(&taskName);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pTaskFolder->DeleteTask(taskName, 0);
|
|
if (FAILED(hr))
|
|
{
|
|
Logger::error(L"Cannot delete the {} task: {}", taskName, hr);
|
|
}
|
|
SysFreeString(taskName);
|
|
}
|
|
else
|
|
{
|
|
Logger::error(L"Cannot get the registered task name: {}", hr);
|
|
}
|
|
pRegisteredTask->Release();
|
|
}
|
|
else
|
|
{
|
|
Logger::error(L"Cannot get the registered task item at index={}: {}", i + 1, hr);
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------
|
|
// Get the pointer to the root task folder and delete the PowerToys subfolder.
|
|
hr = pService->GetFolder(_bstr_t(L"\\"), &pRootFolder);
|
|
ExitOnFailure(hr, "Cannot get Root Folder pointer: %x", hr);
|
|
hr = pRootFolder->DeleteFolder(_bstr_t(L"PowerToys"), 0);
|
|
pRootFolder->Release();
|
|
ExitOnFailure(hr, "Cannot delete the PowerToys folder: %x", hr);
|
|
|
|
Logger::info(L"Deleted the PowerToys Task Scheduler folder.");
|
|
|
|
LExit:
|
|
if (pService)
|
|
{
|
|
pService->Release();
|
|
}
|
|
if (pTaskFolder)
|
|
{
|
|
pTaskFolder->Release();
|
|
}
|
|
if (pTaskCollection)
|
|
{
|
|
pTaskCollection->Release();
|
|
}
|
|
|
|
if (!SUCCEEDED(hr))
|
|
{
|
|
PMSIHANDLE hRecord = MsiCreateRecord(0);
|
|
MsiRecordSetString(hRecord, 0, TEXT("Failed to remove the PowerToys folder from the scheduled task. These can be removed manually later."));
|
|
MsiProcessMessage(hInstall, static_cast<INSTALLMESSAGE>(INSTALLMESSAGE_WARNING + MB_OK), hRecord);
|
|
}
|
|
|
|
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
|
|
return WcaFinalize(er);
|
|
}
|
|
|
|
UINT __stdcall TelemetryLogInstallSuccessCA(MSIHANDLE hInstall)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
UINT er = ERROR_SUCCESS;
|
|
|
|
hr = WcaInitialize(hInstall, "TelemetryLogInstallSuccessCA");
|
|
ExitOnFailure(hr, "Failed to initialize");
|
|
|
|
TraceLoggingWrite(
|
|
g_hProvider,
|
|
"Install_Success",
|
|
TraceLoggingWideString(get_product_version().c_str(), "Version"),
|
|
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
|
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
|
|
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
|
|
|
|
LExit:
|
|
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
|
|
return WcaFinalize(er);
|
|
}
|
|
|
|
UINT __stdcall TelemetryLogInstallCancelCA(MSIHANDLE hInstall)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
UINT er = ERROR_SUCCESS;
|
|
|
|
hr = WcaInitialize(hInstall, "TelemetryLogInstallCancelCA");
|
|
ExitOnFailure(hr, "Failed to initialize");
|
|
|
|
TraceLoggingWrite(
|
|
g_hProvider,
|
|
"Install_Cancel",
|
|
TraceLoggingWideString(get_product_version().c_str(), "Version"),
|
|
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
|
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
|
|
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
|
|
|
|
LExit:
|
|
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
|
|
return WcaFinalize(er);
|
|
}
|
|
|
|
UINT __stdcall TelemetryLogInstallFailCA(MSIHANDLE hInstall)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
UINT er = ERROR_SUCCESS;
|
|
|
|
hr = WcaInitialize(hInstall, "TelemetryLogInstallFailCA");
|
|
ExitOnFailure(hr, "Failed to initialize");
|
|
|
|
TraceLoggingWrite(
|
|
g_hProvider,
|
|
"Install_Fail",
|
|
TraceLoggingWideString(get_product_version().c_str(), "Version"),
|
|
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
|
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
|
|
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
|
|
|
|
LExit:
|
|
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
|
|
return WcaFinalize(er);
|
|
}
|
|
|
|
UINT __stdcall TelemetryLogUninstallSuccessCA(MSIHANDLE hInstall)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
UINT er = ERROR_SUCCESS;
|
|
|
|
hr = WcaInitialize(hInstall, "TelemetryLogUninstallSuccessCA");
|
|
ExitOnFailure(hr, "Failed to initialize");
|
|
|
|
TraceLoggingWrite(
|
|
g_hProvider,
|
|
"UnInstall_Success",
|
|
TraceLoggingWideString(get_product_version().c_str(), "Version"),
|
|
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
|
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
|
|
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
|
|
|
|
LExit:
|
|
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
|
|
return WcaFinalize(er);
|
|
}
|
|
|
|
UINT __stdcall TelemetryLogUninstallCancelCA(MSIHANDLE hInstall)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
UINT er = ERROR_SUCCESS;
|
|
|
|
hr = WcaInitialize(hInstall, "TelemetryLogUninstallCancelCA");
|
|
ExitOnFailure(hr, "Failed to initialize");
|
|
|
|
TraceLoggingWrite(
|
|
g_hProvider,
|
|
"UnInstall_Cancel",
|
|
TraceLoggingWideString(get_product_version().c_str(), "Version"),
|
|
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
|
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
|
|
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
|
|
|
|
LExit:
|
|
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
|
|
return WcaFinalize(er);
|
|
}
|
|
|
|
UINT __stdcall TelemetryLogUninstallFailCA(MSIHANDLE hInstall)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
UINT er = ERROR_SUCCESS;
|
|
|
|
hr = WcaInitialize(hInstall, "TelemetryLogUninstallFailCA");
|
|
ExitOnFailure(hr, "Failed to initialize");
|
|
|
|
TraceLoggingWrite(
|
|
g_hProvider,
|
|
"UnInstall_Fail",
|
|
TraceLoggingWideString(get_product_version().c_str(), "Version"),
|
|
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
|
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
|
|
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
|
|
|
|
LExit:
|
|
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
|
|
return WcaFinalize(er);
|
|
}
|
|
|
|
UINT __stdcall TelemetryLogRepairCancelCA(MSIHANDLE hInstall)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
UINT er = ERROR_SUCCESS;
|
|
|
|
hr = WcaInitialize(hInstall, "TelemetryLogRepairCancelCA");
|
|
ExitOnFailure(hr, "Failed to initialize");
|
|
|
|
TraceLoggingWrite(
|
|
g_hProvider,
|
|
"Repair_Cancel",
|
|
TraceLoggingWideString(get_product_version().c_str(), "Version"),
|
|
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
|
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
|
|
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
|
|
|
|
LExit:
|
|
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
|
|
return WcaFinalize(er);
|
|
}
|
|
|
|
UINT __stdcall TelemetryLogRepairFailCA(MSIHANDLE hInstall)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
UINT er = ERROR_SUCCESS;
|
|
|
|
hr = WcaInitialize(hInstall, "TelemetryLogRepairFailCA");
|
|
ExitOnFailure(hr, "Failed to initialize");
|
|
|
|
TraceLoggingWrite(
|
|
g_hProvider,
|
|
"Repair_Fail",
|
|
TraceLoggingWideString(get_product_version().c_str(), "Version"),
|
|
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
|
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
|
|
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
|
|
|
|
LExit:
|
|
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
|
|
return WcaFinalize(er);
|
|
}
|
|
|
|
UINT __stdcall DetectPrevInstallPathCA(MSIHANDLE hInstall)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
UINT er = ERROR_SUCCESS;
|
|
hr = WcaInitialize(hInstall, "DetectPrevInstallPathCA");
|
|
MsiSetPropertyW(hInstall, L"PREVIOUSINSTALLFOLDER", L"");
|
|
|
|
LPWSTR currentScope = nullptr;
|
|
hr = WcaGetProperty(L"InstallScope", ¤tScope);
|
|
|
|
try
|
|
{
|
|
if (auto install_path = GetMsiPackageInstalledPath(std::wstring{ currentScope } == L"perUser"))
|
|
{
|
|
MsiSetPropertyW(hInstall, L"PREVIOUSINSTALLFOLDER", install_path->data());
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
}
|
|
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
|
|
return WcaFinalize(er);
|
|
}
|
|
|
|
UINT __stdcall CertifyVirtualCameraDriverCA(MSIHANDLE hInstall)
|
|
{
|
|
#ifdef CIBuild // On pipeline we are using microsoft certification
|
|
WcaInitialize(hInstall, "CertifyVirtualCameraDriverCA");
|
|
return WcaFinalize(ERROR_SUCCESS);
|
|
#else
|
|
HRESULT hr = S_OK;
|
|
UINT er = ERROR_SUCCESS;
|
|
LPWSTR certificatePath = nullptr;
|
|
HCERTSTORE hCertStore = nullptr;
|
|
HANDLE hfile = nullptr;
|
|
DWORD size = INVALID_FILE_SIZE;
|
|
char* pFileContent = nullptr;
|
|
|
|
hr = WcaInitialize(hInstall, "CertifyVirtualCameraDriverCA");
|
|
ExitOnFailure(hr, "Failed to initialize", hr);
|
|
|
|
hr = WcaGetProperty(L"CustomActionData", &certificatePath);
|
|
ExitOnFailure(hr, "Failed to get install property", hr);
|
|
|
|
hCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, CERT_SYSTEM_STORE_LOCAL_MACHINE, L"AuthRoot");
|
|
if (!hCertStore)
|
|
{
|
|
hr = GetLastError();
|
|
ExitOnFailure(hr, "Cannot put principal run level: %x", hr);
|
|
}
|
|
|
|
hfile = CreateFile(certificatePath, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
|
|
if (hfile == INVALID_HANDLE_VALUE)
|
|
{
|
|
hr = GetLastError();
|
|
ExitOnFailure(hr, "Certificate file open failed", hr);
|
|
}
|
|
|
|
size = GetFileSize(hfile, nullptr);
|
|
if (size == INVALID_FILE_SIZE)
|
|
{
|
|
hr = GetLastError();
|
|
ExitOnFailure(hr, "Certificate file size not valid", hr);
|
|
}
|
|
|
|
pFileContent = static_cast<char*>(malloc(size));
|
|
|
|
DWORD sizeread;
|
|
if (!ReadFile(hfile, pFileContent, size, &sizeread, nullptr))
|
|
{
|
|
hr = GetLastError();
|
|
ExitOnFailure(hr, "Certificate file read failed", hr);
|
|
}
|
|
|
|
if (!CertAddEncodedCertificateToStore(hCertStore,
|
|
X509_ASN_ENCODING,
|
|
reinterpret_cast<const BYTE*>(pFileContent),
|
|
size,
|
|
CERT_STORE_ADD_ALWAYS,
|
|
nullptr))
|
|
{
|
|
hr = GetLastError();
|
|
ExitOnFailure(hr, "Adding certificate failed", hr);
|
|
}
|
|
|
|
free(pFileContent);
|
|
|
|
LExit:
|
|
ReleaseStr(certificatePath);
|
|
if (hCertStore)
|
|
{
|
|
CertCloseStore(hCertStore, 0);
|
|
}
|
|
if (hfile)
|
|
{
|
|
CloseHandle(hfile);
|
|
}
|
|
|
|
if (!SUCCEEDED(hr))
|
|
{
|
|
PMSIHANDLE hRecord = MsiCreateRecord(0);
|
|
MsiRecordSetString(hRecord, 0, TEXT("Failed to add certificate to store"));
|
|
MsiProcessMessage(hInstall, static_cast<INSTALLMESSAGE>(INSTALLMESSAGE_WARNING + MB_OK), hRecord);
|
|
}
|
|
|
|
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
|
|
return WcaFinalize(er);
|
|
#endif
|
|
}
|
|
|
|
UINT __stdcall InstallVirtualCameraDriverCA(MSIHANDLE hInstall)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
UINT er = ERROR_SUCCESS;
|
|
LPWSTR driverPath = nullptr;
|
|
|
|
hr = WcaInitialize(hInstall, "InstallVirtualCameraDriverCA");
|
|
ExitOnFailure(hr, "Failed to initialize");
|
|
|
|
hr = WcaGetProperty(L"CustomActionData", &driverPath);
|
|
ExitOnFailure(hr, "Failed to get install property");
|
|
|
|
BOOL requiresReboot;
|
|
DiInstallDriverW(GetConsoleWindow(), driverPath, DIIRFLAG_FORCE_INF, &requiresReboot);
|
|
|
|
hr = GetLastError();
|
|
ExitOnFailure(hr, "Failed to install driver");
|
|
|
|
LExit:
|
|
|
|
if (!SUCCEEDED(hr))
|
|
{
|
|
PMSIHANDLE hRecord = MsiCreateRecord(0);
|
|
MsiRecordSetString(hRecord, 0, TEXT("Failed to install virtual camera driver"));
|
|
MsiProcessMessage(hInstall, static_cast<INSTALLMESSAGE>(INSTALLMESSAGE_WARNING + MB_OK), hRecord);
|
|
}
|
|
|
|
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
|
|
return WcaFinalize(er);
|
|
}
|
|
|
|
UINT __stdcall UninstallVirtualCameraDriverCA(MSIHANDLE hInstall)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
UINT er = ERROR_SUCCESS;
|
|
LPWSTR driverPath = nullptr;
|
|
|
|
hr = WcaInitialize(hInstall, "UninstallVirtualCameraDriverCA");
|
|
ExitOnFailure(hr, "Failed to initialize");
|
|
|
|
hr = WcaGetProperty(L"CustomActionData", &driverPath);
|
|
ExitOnFailure(hr, "Failed to get uninstall property");
|
|
|
|
BOOL requiresReboot;
|
|
DiUninstallDriverW(GetConsoleWindow(), driverPath, 0, &requiresReboot);
|
|
|
|
switch (GetLastError())
|
|
{
|
|
case ERROR_ACCESS_DENIED:
|
|
case ERROR_FILE_NOT_FOUND:
|
|
case ERROR_INVALID_FLAGS:
|
|
case ERROR_IN_WOW64:
|
|
{
|
|
hr = GetLastError();
|
|
ExitOnFailure(hr, "Failed to uninstall driver");
|
|
break;
|
|
}
|
|
}
|
|
|
|
LExit:
|
|
|
|
if (!SUCCEEDED(hr))
|
|
{
|
|
PMSIHANDLE hRecord = MsiCreateRecord(0);
|
|
MsiRecordSetString(hRecord, 0, TEXT("Failed to uninstall virtual camera driver"));
|
|
MsiProcessMessage(hInstall, static_cast<INSTALLMESSAGE>(INSTALLMESSAGE_WARNING + MB_OK), hRecord);
|
|
}
|
|
|
|
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
|
|
return WcaFinalize(er);
|
|
}
|
|
|
|
UINT __stdcall UnRegisterContextMenuPackagesCA(MSIHANDLE hInstall)
|
|
{
|
|
using namespace winrt::Windows::Foundation;
|
|
using namespace winrt::Windows::Management::Deployment;
|
|
|
|
HRESULT hr = S_OK;
|
|
UINT er = ERROR_SUCCESS;
|
|
|
|
hr = WcaInitialize(hInstall, "UnRegisterContextMenuPackagesCA"); // original func name is too long
|
|
|
|
try
|
|
{
|
|
// Packages to unregister
|
|
const std::vector<std::wstring> packagesToRemoveDisplayName{ { L"PowerRenameContextMenu" }, { L"ImageResizerContextMenu" } };
|
|
|
|
PackageManager packageManager;
|
|
|
|
for (auto const& package : packageManager.FindPackages())
|
|
{
|
|
const auto& packageFullName = std::wstring{ package.Id().FullName() };
|
|
|
|
for (const auto& packageToRemove : packagesToRemoveDisplayName)
|
|
{
|
|
if (packageFullName.contains(packageToRemove))
|
|
{
|
|
auto deploymentOperation{ packageManager.RemovePackageAsync(packageFullName) };
|
|
deploymentOperation.get();
|
|
|
|
// Check the status of the operation
|
|
if (deploymentOperation.Status() == AsyncStatus::Error)
|
|
{
|
|
auto deploymentResult{ deploymentOperation.GetResults() };
|
|
auto errorCode = deploymentOperation.ErrorCode();
|
|
auto errorText = deploymentResult.ErrorText();
|
|
|
|
Logger::error(L"Unregister {} package failed. ErrorCode: {}, ErrorText: {}", packageFullName, std::to_wstring(errorCode), errorText);
|
|
|
|
er = ERROR_INSTALL_FAILURE;
|
|
}
|
|
else if (deploymentOperation.Status() == AsyncStatus::Canceled)
|
|
{
|
|
Logger::error(L"Unregister {} package canceled.", packageFullName);
|
|
|
|
er = ERROR_INSTALL_FAILURE;
|
|
}
|
|
else if (deploymentOperation.Status() == AsyncStatus::Completed)
|
|
{
|
|
Logger::info(L"Unregister {} package completed.", packageFullName);
|
|
}
|
|
else
|
|
{
|
|
Logger::debug(L"Unregister {} package started.", packageFullName);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
catch (std::exception& e)
|
|
{
|
|
std::string errorMessage{ "Exception thrown while trying to unregister sparse packages: " };
|
|
errorMessage += e.what();
|
|
Logger::error(errorMessage);
|
|
|
|
er = ERROR_INSTALL_FAILURE;
|
|
}
|
|
|
|
er = er == ERROR_SUCCESS ? (SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE) : er;
|
|
return WcaFinalize(er);
|
|
}
|
|
|
|
const std::wstring WinAppSDKConsumers[] =
|
|
{
|
|
L"Settings",
|
|
L"modules\\PowerRename",
|
|
L"modules\\MeasureTool",
|
|
L"modules\\FileLocksmith",
|
|
L"modules\\Hosts",
|
|
L"modules\\RegistryPreview",
|
|
L"modules\\Peek",
|
|
};
|
|
|
|
UINT __stdcall CreateWinAppSDKHardlinksCA(MSIHANDLE hInstall)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
UINT er = ERROR_SUCCESS;
|
|
std::wstring installationFolder, winAppSDKFilesSrcDir;
|
|
|
|
hr = WcaInitialize(hInstall, "CreateWinAppSDKHardlinksCA");
|
|
ExitOnFailure(hr, "Failed to initialize");
|
|
|
|
hr = getInstallFolder(hInstall, installationFolder);
|
|
ExitOnFailure(hr, "Failed to get installation folder");
|
|
|
|
winAppSDKFilesSrcDir = installationFolder + L"dll\\WinAppSDK\\";
|
|
|
|
for (auto file : winAppSdkFiles)
|
|
{
|
|
for (auto consumer : WinAppSDKConsumers)
|
|
{
|
|
std::error_code ec;
|
|
std::filesystem::create_hard_link((winAppSDKFilesSrcDir + file).c_str(), (installationFolder + consumer + L"\\" + file).c_str(), ec);
|
|
|
|
if (ec.value() != S_OK)
|
|
{
|
|
std::wstring errorMessage{ L"Error creating hard link for: " };
|
|
errorMessage += file;
|
|
errorMessage += L", error code: " + std::to_wstring(ec.value());
|
|
Logger::error(errorMessage);
|
|
}
|
|
}
|
|
}
|
|
|
|
LExit:
|
|
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
|
|
return WcaFinalize(er);
|
|
}
|
|
|
|
const std::wstring PTInteropConsumers[] =
|
|
{
|
|
L"modules\\ColorPicker",
|
|
L"modules\\PowerOCR",
|
|
L"modules\\launcher",
|
|
L"modules\\FancyZones",
|
|
L"modules\\ImageResizer",
|
|
L"Settings",
|
|
L"modules\\Awake",
|
|
L"modules\\MeasureTool",
|
|
L"modules\\PowerAccent",
|
|
L"modules\\FileLocksmith",
|
|
L"modules\\Hosts",
|
|
L"modules\\Peek",
|
|
L"modules\\FileExplorerPreview",
|
|
L"modules\\MouseUtils\\MouseJumpUI",
|
|
L"modules\\RegistryPreview",
|
|
};
|
|
|
|
UINT __stdcall CreatePTInteropHardlinksCA(MSIHANDLE hInstall)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
UINT er = ERROR_SUCCESS;
|
|
std::wstring installationFolder, interopFilesSrcDir;
|
|
|
|
hr = WcaInitialize(hInstall, "CreatePTInteropHardlinksCA");
|
|
ExitOnFailure(hr, "Failed to initialize");
|
|
|
|
hr = getInstallFolder(hInstall, installationFolder);
|
|
ExitOnFailure(hr, "Failed to get installation folder");
|
|
|
|
interopFilesSrcDir = installationFolder + L"dll\\Interop\\";
|
|
|
|
for (auto file : powerToysInteropFiles)
|
|
{
|
|
for (auto consumer : PTInteropConsumers)
|
|
{
|
|
std::error_code ec;
|
|
std::filesystem::create_hard_link((interopFilesSrcDir + file).c_str(), (installationFolder + consumer + L"\\" + file).c_str(), ec);
|
|
|
|
if (ec.value() != S_OK)
|
|
{
|
|
std::wstring errorMessage{ L"Error creating hard link for: " };
|
|
errorMessage += file;
|
|
errorMessage += L", error code: " + std::to_wstring(ec.value());
|
|
Logger::error(errorMessage);
|
|
}
|
|
}
|
|
}
|
|
|
|
LExit:
|
|
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
|
|
return WcaFinalize(er);
|
|
}
|
|
|
|
UINT __stdcall CreateDotnetRuntimeHardlinksCA(MSIHANDLE hInstall)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
UINT er = ERROR_SUCCESS;
|
|
std::wstring installationFolder, dotnetRuntimeFilesSrcDir, colorPickerDir, powerOCRDir, launcherDir, fancyZonesDir,
|
|
imageResizerDir, settingsDir, awakeDir, measureToolDir, powerAccentDir, fileExplorerAddOnsDir, hostsDir, fileLocksmithDir,
|
|
mouseJumpDir, registryPreviewDir, peekDir;
|
|
|
|
hr = WcaInitialize(hInstall, "CreateDotnetRuntimeHardlinksCA");
|
|
ExitOnFailure(hr, "Failed to initialize");
|
|
|
|
hr = getInstallFolder(hInstall, installationFolder);
|
|
ExitOnFailure(hr, "Failed to get installation folder");
|
|
|
|
dotnetRuntimeFilesSrcDir = installationFolder + L"dll\\dotnet\\";
|
|
colorPickerDir = installationFolder + L"modules\\ColorPicker\\";
|
|
powerOCRDir = installationFolder + L"modules\\PowerOCR\\";
|
|
launcherDir = installationFolder + L"modules\\launcher\\";
|
|
fancyZonesDir = installationFolder + L"modules\\FancyZones\\";
|
|
imageResizerDir = installationFolder + L"modules\\ImageResizer\\";
|
|
settingsDir = installationFolder + L"Settings\\";
|
|
awakeDir = installationFolder + L"modules\\Awake\\";
|
|
measureToolDir = installationFolder + L"modules\\MeasureTool\\";
|
|
powerAccentDir = installationFolder + L"modules\\PowerAccent\\";
|
|
fileExplorerAddOnsDir = installationFolder + L"modules\\FileExplorerPreview\\";
|
|
hostsDir = installationFolder + L"modules\\Hosts\\";
|
|
fileLocksmithDir = installationFolder + L"modules\\FileLocksmith\\";
|
|
mouseJumpDir = installationFolder + L"modules\\MouseUtils\\MouseJumpUI\\";
|
|
registryPreviewDir = installationFolder + L"modules\\RegistryPreview\\";
|
|
peekDir = installationFolder + L"modules\\Peek\\";
|
|
|
|
for (auto file : dotnetRuntimeFiles)
|
|
{
|
|
std::error_code ec;
|
|
std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (colorPickerDir + file).c_str(), ec);
|
|
std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (powerOCRDir + file).c_str(), ec);
|
|
std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (launcherDir + file).c_str(), ec);
|
|
std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (fancyZonesDir + file).c_str(), ec);
|
|
std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (imageResizerDir + file).c_str(), ec);
|
|
std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (settingsDir + file).c_str(), ec);
|
|
std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (awakeDir + file).c_str(), ec);
|
|
std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (measureToolDir + file).c_str(), ec);
|
|
std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (powerAccentDir + file).c_str(), ec);
|
|
std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (fileExplorerAddOnsDir + file).c_str(), ec);
|
|
std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (hostsDir + file).c_str(), ec);
|
|
std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (fileLocksmithDir + file).c_str(), ec);
|
|
std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (mouseJumpDir + file).c_str(), ec);
|
|
std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (registryPreviewDir + file).c_str(), ec);
|
|
std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (peekDir + file).c_str(), ec);
|
|
|
|
if (ec.value() != S_OK)
|
|
{
|
|
std::wstring errorMessage{ L"Error creating hard link for: " };
|
|
errorMessage += file;
|
|
errorMessage += L", error code: " + std::to_wstring(ec.value());
|
|
Logger::error(errorMessage);
|
|
er = ERROR_INSTALL_FAILURE;
|
|
}
|
|
}
|
|
|
|
for (auto file : dotnetRuntimeWPFFiles)
|
|
{
|
|
std::error_code ec;
|
|
std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (awakeDir + file).c_str(), ec);
|
|
std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (colorPickerDir + file).c_str(), ec);
|
|
std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (powerOCRDir + file).c_str(), ec);
|
|
std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (launcherDir + file).c_str(), ec);
|
|
std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (fancyZonesDir + file).c_str(), ec);
|
|
std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (imageResizerDir + file).c_str(), ec);
|
|
std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (powerAccentDir + file).c_str(), ec);
|
|
std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (fileExplorerAddOnsDir + file).c_str(), ec);
|
|
std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (hostsDir + file).c_str(), ec);
|
|
std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (mouseJumpDir + file).c_str(), ec);
|
|
std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (registryPreviewDir + file).c_str(), ec);
|
|
std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (peekDir + file).c_str(), ec);
|
|
|
|
if (ec.value() != S_OK)
|
|
{
|
|
std::wstring errorMessage{ L"Error creating hard link for: " };
|
|
errorMessage += file;
|
|
errorMessage += L", error code: " + std::to_wstring(ec.value());
|
|
Logger::error(errorMessage);
|
|
er = ERROR_INSTALL_FAILURE;
|
|
}
|
|
}
|
|
|
|
LExit:
|
|
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
|
|
return WcaFinalize(er);
|
|
}
|
|
|
|
UINT __stdcall DeleteWinAppSDKHardlinksCA(MSIHANDLE hInstall)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
UINT er = ERROR_SUCCESS;
|
|
std::wstring installationFolder;
|
|
|
|
hr = WcaInitialize(hInstall, "DeleteWinAppSDKHardlinksCA");
|
|
ExitOnFailure(hr, "Failed to initialize");
|
|
|
|
hr = getInstallFolder(hInstall, installationFolder);
|
|
ExitOnFailure(hr, "Failed to get installation folder");
|
|
|
|
try
|
|
{
|
|
for (auto file : winAppSdkFiles)
|
|
{
|
|
for (auto consumer : WinAppSDKConsumers)
|
|
{
|
|
DeleteFile((installationFolder + consumer + L"\\" + file).c_str());
|
|
}
|
|
}
|
|
}
|
|
catch (std::exception e)
|
|
{
|
|
std::string errorMessage{ "Exception thrown while trying to delete WAS hardlinks: " };
|
|
errorMessage += e.what();
|
|
Logger::error(errorMessage);
|
|
|
|
er = ERROR_INSTALL_FAILURE;
|
|
}
|
|
|
|
LExit:
|
|
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
|
|
return WcaFinalize(er);
|
|
}
|
|
|
|
UINT __stdcall DeletePTInteropHardlinksCA(MSIHANDLE hInstall)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
UINT er = ERROR_SUCCESS;
|
|
std::wstring installationFolder, interopFilesSrcDir;
|
|
|
|
hr = WcaInitialize(hInstall, "DeletePTInteropHardlinksCA");
|
|
ExitOnFailure(hr, "Failed to initialize");
|
|
|
|
hr = getInstallFolder(hInstall, installationFolder);
|
|
ExitOnFailure(hr, "Failed to get installation folder");
|
|
|
|
try
|
|
{
|
|
for (auto file : powerToysInteropFiles)
|
|
{
|
|
for (auto consumer : PTInteropConsumers)
|
|
{
|
|
DeleteFile((installationFolder + consumer + L"\\" + file).c_str());
|
|
}
|
|
}
|
|
}
|
|
catch (std::exception e)
|
|
{
|
|
std::string errorMessage{ "Exception thrown while trying to delete PowerToys Interop and VC Redist hardlinks: " };
|
|
errorMessage += e.what();
|
|
Logger::error(errorMessage);
|
|
|
|
er = ERROR_INSTALL_FAILURE;
|
|
}
|
|
|
|
LExit:
|
|
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
|
|
return WcaFinalize(er);
|
|
}
|
|
|
|
UINT __stdcall DeleteDotnetRuntimeHardlinksCA(MSIHANDLE hInstall)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
UINT er = ERROR_SUCCESS;
|
|
std::wstring installationFolder, colorPickerDir, powerOCRDir, launcherDir, fancyZonesDir,
|
|
imageResizerDir, settingsDir, awakeDir, measureToolDir, powerAccentDir, fileExplorerAddOnsDir,
|
|
hostsDir, fileLocksmithDir, mouseJumpDir, registryPreviewDir, peekDir;
|
|
|
|
hr = WcaInitialize(hInstall, "DeleteDotnetRuntimeHardlinksCA");
|
|
ExitOnFailure(hr, "Failed to initialize");
|
|
|
|
hr = getInstallFolder(hInstall, installationFolder);
|
|
ExitOnFailure(hr, "Failed to get installation folder");
|
|
|
|
colorPickerDir = installationFolder + L"modules\\ColorPicker\\";
|
|
powerOCRDir = installationFolder + L"modules\\PowerOCR\\";
|
|
launcherDir = installationFolder + L"modules\\launcher\\";
|
|
fancyZonesDir = installationFolder + L"modules\\FancyZones\\";
|
|
imageResizerDir = installationFolder + L"modules\\ImageResizer\\";
|
|
settingsDir = installationFolder + L"Settings\\";
|
|
awakeDir = installationFolder + L"modules\\Awake\\";
|
|
measureToolDir = installationFolder + L"modules\\MeasureTool\\";
|
|
powerAccentDir = installationFolder + L"modules\\PowerAccent\\";
|
|
fileExplorerAddOnsDir = installationFolder + L"modules\\FileExplorerPreview\\";
|
|
hostsDir = installationFolder + L"modules\\Hosts\\";
|
|
fileLocksmithDir = installationFolder + L"modules\\FileLocksmith\\";
|
|
mouseJumpDir = installationFolder + L"modules\\MouseUtils\\MouseJumpUI\\";
|
|
registryPreviewDir = installationFolder + L"modules\\RegistryPreview\\";
|
|
peekDir = installationFolder + L"modules\\Peek\\";
|
|
|
|
try
|
|
{
|
|
for (auto file : dotnetRuntimeFiles)
|
|
{
|
|
DeleteFile((colorPickerDir + file).c_str());
|
|
DeleteFile((powerOCRDir + file).c_str());
|
|
DeleteFile((launcherDir + file).c_str());
|
|
DeleteFile((fancyZonesDir + file).c_str());
|
|
DeleteFile((imageResizerDir + file).c_str());
|
|
DeleteFile((settingsDir + file).c_str());
|
|
DeleteFile((awakeDir + file).c_str());
|
|
DeleteFile((measureToolDir + file).c_str());
|
|
DeleteFile((powerAccentDir + file).c_str());
|
|
DeleteFile((fileExplorerAddOnsDir + file).c_str());
|
|
DeleteFile((hostsDir + file).c_str());
|
|
DeleteFile((fileLocksmithDir + file).c_str());
|
|
DeleteFile((mouseJumpDir + file).c_str());
|
|
DeleteFile((registryPreviewDir + file).c_str());
|
|
DeleteFile((peekDir + file).c_str());
|
|
}
|
|
|
|
for (auto file : dotnetRuntimeWPFFiles)
|
|
{
|
|
DeleteFile((awakeDir + file).c_str());
|
|
DeleteFile((colorPickerDir + file).c_str());
|
|
DeleteFile((powerOCRDir + file).c_str());
|
|
DeleteFile((launcherDir + file).c_str());
|
|
DeleteFile((fancyZonesDir + file).c_str());
|
|
DeleteFile((imageResizerDir + file).c_str());
|
|
DeleteFile((powerAccentDir + file).c_str());
|
|
DeleteFile((fileExplorerAddOnsDir + file).c_str());
|
|
DeleteFile((hostsDir + file).c_str());
|
|
DeleteFile((mouseJumpDir + file).c_str());
|
|
DeleteFile((registryPreviewDir + file).c_str());
|
|
DeleteFile((peekDir + file).c_str());
|
|
}
|
|
}
|
|
catch (std::exception e)
|
|
{
|
|
std::string errorMessage{ "Exception thrown while trying to delete dotnet runtime hardlinks: " };
|
|
errorMessage += e.what();
|
|
Logger::error(errorMessage);
|
|
|
|
er = ERROR_INSTALL_FAILURE;
|
|
}
|
|
|
|
LExit:
|
|
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
|
|
return WcaFinalize(er);
|
|
}
|
|
|
|
UINT __stdcall TerminateProcessesCA(MSIHANDLE hInstall)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
UINT er = ERROR_SUCCESS;
|
|
hr = WcaInitialize(hInstall, "TerminateProcessesCA");
|
|
|
|
std::vector<DWORD> processes;
|
|
const size_t maxProcesses = 4096;
|
|
DWORD bytes = maxProcesses * sizeof(processes[0]);
|
|
processes.resize(maxProcesses);
|
|
|
|
if (!EnumProcesses(processes.data(), bytes, &bytes))
|
|
{
|
|
return 1;
|
|
}
|
|
processes.resize(bytes / sizeof(processes[0]));
|
|
|
|
std::array<std::wstring_view, 24> processesToTerminate = {
|
|
L"PowerToys.PowerLauncher.exe",
|
|
L"PowerToys.Settings.exe",
|
|
L"PowerToys.Awake.exe",
|
|
L"PowerToys.FancyZones.exe",
|
|
L"PowerToys.FancyZonesEditor.exe",
|
|
L"PowerToys.FileLocksmithUI.exe",
|
|
L"PowerToys.MouseJumpUI.exe",
|
|
L"PowerToys.ColorPickerUI.exe",
|
|
L"PowerToys.AlwaysOnTop.exe",
|
|
L"PowerToys.RegistryPreview.exe",
|
|
L"PowerToys.Hosts.exe",
|
|
L"PowerToys.PowerRename.exe",
|
|
L"PowerToys.ImageResizer.exe",
|
|
L"PowerToys.GcodeThumbnailProvider.exe",
|
|
L"PowerToys.PdfThumbnailProvider.exe",
|
|
L"PowerToys.MonacoPreviewHandler.exe",
|
|
L"PowerToys.MarkdownPreviewHandler.exe",
|
|
L"PowerToys.StlThumbnailProvider.exe",
|
|
L"PowerToys.SvgThumbnailProvider.exe",
|
|
L"PowerToys.GcodePreviewHandler.exe",
|
|
L"PowerToys.PdfPreviewHandler.exe",
|
|
L"PowerToys.SvgPreviewHandler.exe",
|
|
L"PowerToys.Peek.UI.exe",
|
|
L"PowerToys.exe",
|
|
};
|
|
|
|
for (const auto procID : processes)
|
|
{
|
|
if (!procID)
|
|
{
|
|
continue;
|
|
}
|
|
wchar_t processName[MAX_PATH] = L"<unknown>";
|
|
|
|
HANDLE hProcess{ OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_TERMINATE, FALSE, procID) };
|
|
if (!hProcess)
|
|
{
|
|
continue;
|
|
}
|
|
HMODULE hMod;
|
|
DWORD cbNeeded;
|
|
|
|
if (!EnumProcessModules(hProcess, &hMod, sizeof(hMod), &cbNeeded))
|
|
{
|
|
CloseHandle(hProcess);
|
|
continue;
|
|
}
|
|
GetModuleBaseNameW(hProcess, hMod, processName, sizeof(processName) / sizeof(wchar_t));
|
|
|
|
for (const auto processToTerminate : processesToTerminate)
|
|
{
|
|
if (processName == processToTerminate)
|
|
{
|
|
const DWORD timeout = 500;
|
|
auto windowEnumerator = [](HWND hwnd, LPARAM procIDPtr) -> BOOL {
|
|
auto targetProcID = *reinterpret_cast<const DWORD*>(procIDPtr);
|
|
DWORD windowProcID = 0;
|
|
GetWindowThreadProcessId(hwnd, &windowProcID);
|
|
if (windowProcID == targetProcID)
|
|
{
|
|
DWORD_PTR _{};
|
|
SendMessageTimeoutA(hwnd, WM_CLOSE, 0, 0, SMTO_BLOCK, timeout, &_);
|
|
}
|
|
return TRUE;
|
|
};
|
|
EnumWindows(windowEnumerator, reinterpret_cast<LPARAM>(&procID));
|
|
Sleep(timeout);
|
|
TerminateProcess(hProcess, 0);
|
|
break;
|
|
}
|
|
}
|
|
CloseHandle(hProcess);
|
|
}
|
|
|
|
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
|
|
return WcaFinalize(er);
|
|
}
|
|
|
|
void initSystemLogger()
|
|
{
|
|
static std::once_flag initLoggerFlag;
|
|
std::call_once(initLoggerFlag, []() {
|
|
WCHAR temp_path[MAX_PATH];
|
|
auto ret = GetTempPath(MAX_PATH, temp_path);
|
|
|
|
if (ret)
|
|
{
|
|
Logger::init("PowerToysMSI", std::wstring{ temp_path } + L"\\PowerToysMSIInstaller", L"");
|
|
}
|
|
});
|
|
}
|
|
|
|
// DllMain - Initialize and cleanup WiX custom action utils.
|
|
extern "C" BOOL WINAPI DllMain(__in HINSTANCE hInst, __in ULONG ulReason, __in LPVOID)
|
|
{
|
|
switch (ulReason)
|
|
{
|
|
case DLL_PROCESS_ATTACH:
|
|
WcaGlobalInitialize(hInst);
|
|
initSystemLogger();
|
|
TraceLoggingRegister(g_hProvider);
|
|
DLL_HANDLE = hInst;
|
|
break;
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
TraceLoggingUnregister(g_hProvider);
|
|
WcaGlobalFinalize();
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|