mirror of
https://github.com/microsoft/PowerToys.git
synced 2024-12-26 18:58:29 +08:00
6f38cdec5a
* wix: add Notice.md #2442 * wix: close PowerToys which might be running during installation process #2887 * wix: update dotnet core to 3.1.4
729 lines
24 KiB
C++
729 lines
24 KiB
C++
#include "stdafx.h"
|
|
|
|
#include <ProjectTelemetry.h>
|
|
|
|
using namespace std;
|
|
|
|
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}";
|
|
|
|
// 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://docs.microsoft.com/en-us/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 = NULL;
|
|
ITaskFolder* pTaskFolder = NULL;
|
|
ITaskDefinition* pTask = NULL;
|
|
IRegistrationInfo* pRegInfo = NULL;
|
|
ITaskSettings* pSettings = NULL;
|
|
ITriggerCollection* pTriggerCollection = NULL;
|
|
IRegisteredTask* pRegisteredTask = NULL;
|
|
|
|
LPWSTR wszExecutablePath = NULL;
|
|
|
|
hr = WcaInitialize(hInstall, "CreateScheduledTaskCA");
|
|
ExitOnFailure(hr, "Failed to initialize");
|
|
|
|
WcaLog(LOGMSG_STANDARD, "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);
|
|
|
|
WcaLog(LOGMSG_STANDARD, "Current user detected: %ls", 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,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_ITaskService,
|
|
(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 = NULL;
|
|
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);
|
|
}
|
|
WcaLog(LOGMSG_STANDARD, "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.
|
|
ITrigger* pTrigger = NULL;
|
|
hr = pTriggerCollection->Create(TASK_TRIGGER_LOGON, &pTrigger);
|
|
ExitOnFailure(hr, "Cannot create the trigger: %x", hr);
|
|
|
|
ILogonTrigger* pLogonTrigger = NULL;
|
|
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))
|
|
{
|
|
WcaLogError(hr, "Cannot put the trigger ID: %x", 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))
|
|
{
|
|
WcaLogError(hr, "Cannot put the trigger delay: %x", 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.
|
|
IActionCollection* pActionCollection = NULL;
|
|
|
|
// 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.
|
|
IAction* pAction = NULL;
|
|
hr = pActionCollection->Create(TASK_ACTION_EXEC, &pAction);
|
|
pActionCollection->Release();
|
|
ExitOnFailure(hr, "Cannot create the action: %x", hr);
|
|
|
|
IExecAction* pExecAction = NULL;
|
|
// 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
|
|
IPrincipal* pPrincipal = NULL;
|
|
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))
|
|
{
|
|
WcaLogError(hr, "Cannot put the principal ID: %x", hr);
|
|
}
|
|
|
|
hr = pPrincipal->put_UserId(_bstr_t(username_domain));
|
|
if (FAILED(hr))
|
|
{
|
|
WcaLogError(hr, "Cannot put principal user Id: %x", hr);
|
|
}
|
|
|
|
hr = pPrincipal->put_LogonType(TASK_LOGON_INTERACTIVE_TOKEN);
|
|
if (FAILED(hr))
|
|
{
|
|
WcaLogError(hr, "Cannot put principal logon type: %x", 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);
|
|
}
|
|
|
|
WcaLog(LOGMSG_STANDARD, "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, 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://docs.microsoft.com/en-us/windows/desktop/TaskSchd/displaying-task-names-and-state--c---/
|
|
UINT __stdcall RemoveScheduledTasksCA(MSIHANDLE hInstall)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
UINT er = ERROR_SUCCESS;
|
|
|
|
ITaskService* pService = NULL;
|
|
ITaskFolder* pTaskFolder = NULL;
|
|
IRegisteredTaskCollection* pTaskCollection = NULL;
|
|
|
|
hr = WcaInitialize(hInstall, "RemoveScheduledTasksCA");
|
|
ExitOnFailure(hr, "Failed to initialize");
|
|
|
|
WcaLog(LOGMSG_STANDARD, "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,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_ITaskService,
|
|
(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.
|
|
WcaLog(LOGMSG_STANDARD, "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);
|
|
|
|
LONG numTasks = 0;
|
|
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 = NULL;
|
|
hr = pTaskCollection->get_Item(_variant_t(i + 1), &pRegisteredTask);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
BSTR taskName = NULL;
|
|
hr = pRegisteredTask->get_Name(&taskName);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pTaskFolder->DeleteTask(taskName, NULL);
|
|
if (FAILED(hr))
|
|
{
|
|
WcaLogError(hr, "Cannot delete the '%S' task: %x", taskName, hr);
|
|
}
|
|
SysFreeString(taskName);
|
|
}
|
|
else
|
|
{
|
|
WcaLogError(hr, "Cannot get the registered task name: %x", hr);
|
|
}
|
|
pRegisteredTask->Release();
|
|
}
|
|
else
|
|
{
|
|
WcaLogError(hr, "Cannot get the registered task item at index=%d: %x", i + 1, hr);
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------
|
|
// Get the pointer to the root task folder and delete the PowerToys subfolder.
|
|
ITaskFolder* pRootFolder = NULL;
|
|
hr = pService->GetFolder(_bstr_t(L"\\"), &pRootFolder);
|
|
ExitOnFailure(hr, "Cannot get Root Folder pointer: %x", hr);
|
|
hr = pRootFolder->DeleteFolder(_bstr_t(L"PowerToys"), NULL);
|
|
pRootFolder->Release();
|
|
ExitOnFailure(hr, "Cannot delete the PowerToys folder: %x", hr);
|
|
|
|
WcaLog(LOGMSG_STANDARD, "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, 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",
|
|
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",
|
|
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",
|
|
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",
|
|
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",
|
|
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",
|
|
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",
|
|
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",
|
|
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
|
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
|
|
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
|
|
|
|
LExit:
|
|
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
|
|
return WcaFinalize(er);
|
|
}
|
|
|
|
std::optional<std::wstring> getMsiPackageInstalledPath(const wchar_t* product_upgrade_code, const wchar_t* file_component)
|
|
{
|
|
constexpr size_t guid_length = 39;
|
|
wchar_t product_ID[guid_length];
|
|
if (const bool found = ERROR_SUCCESS == MsiEnumRelatedProductsW(product_upgrade_code, 0, 0, product_ID); !found)
|
|
{
|
|
return std::nullopt;
|
|
}
|
|
|
|
if (const bool installed = INSTALLSTATE_DEFAULT == MsiQueryProductStateW(product_ID); !installed)
|
|
{
|
|
return std::nullopt;
|
|
}
|
|
|
|
DWORD buf_size = MAX_PATH;
|
|
wchar_t buf[MAX_PATH];
|
|
if (ERROR_SUCCESS == MsiGetProductInfoW(product_ID, INSTALLPROPERTY_INSTALLLOCATION, buf, &buf_size) && buf_size)
|
|
{
|
|
return buf;
|
|
}
|
|
|
|
DWORD package_path_size = 0;
|
|
|
|
if (ERROR_SUCCESS != MsiGetProductInfoW(product_ID, INSTALLPROPERTY_LOCALPACKAGE, nullptr, &package_path_size))
|
|
{
|
|
return std::nullopt;
|
|
}
|
|
std::wstring package_path(++package_path_size, L'\0');
|
|
|
|
if (ERROR_SUCCESS != MsiGetProductInfoW(product_ID, INSTALLPROPERTY_LOCALPACKAGE, package_path.data(), &package_path_size))
|
|
{
|
|
return std::nullopt;
|
|
}
|
|
package_path.resize(size(package_path) - 1); // trim additional \0 which we got from MsiGetProductInfoW
|
|
|
|
wchar_t path[256];
|
|
DWORD path_size = 256;
|
|
MsiGetComponentPathW(product_ID, file_component, path, &path_size);
|
|
if (!path_size)
|
|
{
|
|
return std::nullopt;
|
|
}
|
|
PathCchRemoveFileSpec(path, path_size);
|
|
return path;
|
|
}
|
|
|
|
UINT __stdcall DetectPrevInstallPathCA(MSIHANDLE hInstall)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
UINT er = ERROR_SUCCESS;
|
|
hr = WcaInitialize(hInstall, "DetectPrevInstallPathCA");
|
|
|
|
try
|
|
{
|
|
if (auto install_path = getMsiPackageInstalledPath(POWERTOYS_UPGRADE_CODE, POWERTOYS_EXE_COMPONENT))
|
|
{
|
|
MsiSetPropertyW(hInstall, L"INSTALLFOLDER", install_path->data());
|
|
}
|
|
}
|
|
catch(...)
|
|
{
|
|
|
|
}
|
|
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, 4> processesToTerminate = {
|
|
L"PowerLauncher.exe",
|
|
L"Microsoft.PowerToys.Settings.UI.Runner.exe",
|
|
L"Microsoft.PowerToys.Settings.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);
|
|
}
|
|
|
|
|
|
// 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);
|
|
TraceLoggingRegister(g_hProvider);
|
|
break;
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
TraceLoggingUnregister(g_hProvider);
|
|
WcaGlobalFinalize();
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|