mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-06-07 17:42:45 +08:00
354 lines
10 KiB
C++
354 lines
10 KiB
C++
// dllmain.cpp : Defines the entry point for the DLL application.
|
|
#include "pch.h"
|
|
|
|
#include <interface/powertoy_module_interface.h>
|
|
|
|
#include <atomic>
|
|
#include <common/logger/logger.h>
|
|
#include <common/utils/logger_helper.h>
|
|
#include <common/SettingsAPI/settings_helpers.h>
|
|
#include <common/SettingsAPI/settings_objects.h>
|
|
#include <common/utils/resources.h>
|
|
#include <common/utils/package.h>
|
|
#include <common/utils/process_path.h>
|
|
#include <common/utils/winapi_error.h>
|
|
#include <common/interop/shared_constants.h>
|
|
#include <Psapi.h>
|
|
#include <TlHelp32.h>
|
|
#include <thread>
|
|
|
|
HINSTANCE g_hInst_cmdPal = 0;
|
|
|
|
BOOL APIENTRY DllMain(HMODULE hInstance,
|
|
DWORD ul_reason_for_call,
|
|
LPVOID)
|
|
{
|
|
switch (ul_reason_for_call)
|
|
{
|
|
case DLL_PROCESS_ATTACH:
|
|
g_hInst_cmdPal = hInstance;
|
|
break;
|
|
case DLL_THREAD_ATTACH:
|
|
case DLL_THREAD_DETACH:
|
|
case DLL_PROCESS_DETACH:
|
|
break;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
class CmdPal : public PowertoyModuleIface
|
|
{
|
|
private:
|
|
std::wstring app_name;
|
|
|
|
//contains the non localized key of the powertoy
|
|
std::wstring app_key;
|
|
|
|
HANDLE m_hTerminateEvent;
|
|
|
|
// Track if this is the first call to enable
|
|
bool firstEnableCall = true;
|
|
|
|
static bool LaunchApp(const std::wstring& appPath, const std::wstring& commandLineArgs, bool elevated, bool silentFail)
|
|
{
|
|
std::wstring dir = std::filesystem::path(appPath).parent_path();
|
|
|
|
SHELLEXECUTEINFO sei = { 0 };
|
|
sei.cbSize = sizeof(SHELLEXECUTEINFO);
|
|
sei.hwnd = nullptr;
|
|
sei.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE;
|
|
if (silentFail)
|
|
{
|
|
sei.fMask = sei.fMask | SEE_MASK_FLAG_NO_UI;
|
|
}
|
|
sei.lpVerb = elevated ? L"runas" : L"open";
|
|
sei.lpFile = appPath.c_str();
|
|
sei.lpParameters = commandLineArgs.c_str();
|
|
sei.lpDirectory = dir.c_str();
|
|
sei.nShow = SW_SHOWNORMAL;
|
|
|
|
if (!ShellExecuteEx(&sei))
|
|
{
|
|
std::wstring error = get_last_error_or_default(GetLastError());
|
|
Logger::error(L"Failed to launch process. {}", error);
|
|
return false;
|
|
}
|
|
|
|
m_launched.store(true);
|
|
return true;
|
|
}
|
|
|
|
std::vector<DWORD> GetProcessesIdByName(const std::wstring& processName)
|
|
{
|
|
std::vector<DWORD> processIds;
|
|
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
|
|
|
if (snapshot != INVALID_HANDLE_VALUE)
|
|
{
|
|
PROCESSENTRY32 processEntry;
|
|
processEntry.dwSize = sizeof(PROCESSENTRY32);
|
|
|
|
if (Process32First(snapshot, &processEntry))
|
|
{
|
|
do
|
|
{
|
|
if (_wcsicmp(processEntry.szExeFile, processName.c_str()) == 0)
|
|
{
|
|
processIds.push_back(processEntry.th32ProcessID);
|
|
}
|
|
} while (Process32Next(snapshot, &processEntry));
|
|
}
|
|
|
|
CloseHandle(snapshot);
|
|
}
|
|
|
|
return processIds;
|
|
}
|
|
|
|
void TerminateCmdPal()
|
|
{
|
|
auto processIds = GetProcessesIdByName(L"Microsoft.CmdPal.UI.exe");
|
|
|
|
if (processIds.size() == 0)
|
|
{
|
|
Logger::trace(L"Nothing To PROCESS_TERMINATE");
|
|
return;
|
|
}
|
|
|
|
for (DWORD pid : processIds)
|
|
{
|
|
HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
|
|
|
|
if (hProcess != NULL)
|
|
{
|
|
SetEvent(m_hTerminateEvent);
|
|
|
|
// Wait for 1.5 seconds for the process to end correctly and stop etw tracer
|
|
WaitForSingleObject(hProcess, 1500);
|
|
|
|
TerminateProcess(hProcess, 0);
|
|
CloseHandle(hProcess);
|
|
}
|
|
}
|
|
}
|
|
|
|
public:
|
|
static std::atomic<bool> m_enabled;
|
|
static std::atomic<bool> m_launched;
|
|
|
|
CmdPal()
|
|
{
|
|
app_name = L"CmdPal";
|
|
app_key = L"CmdPal";
|
|
LoggerHelpers::init_logger(app_key, L"ModuleInterface", "CmdPal");
|
|
|
|
m_hTerminateEvent = CreateDefaultEvent(CommonSharedConstants::CMDPAL_EXIT_EVENT);
|
|
}
|
|
|
|
~CmdPal()
|
|
{
|
|
CmdPal::m_enabled.store(false);
|
|
}
|
|
|
|
// Destroy the powertoy and free memory
|
|
virtual void destroy() override
|
|
{
|
|
Logger::trace("CmdPal::destroy()");
|
|
TerminateCmdPal();
|
|
delete this;
|
|
}
|
|
|
|
// Return the localized display name of the powertoy
|
|
virtual const wchar_t* get_name() override
|
|
{
|
|
return app_name.c_str();
|
|
}
|
|
|
|
// Return the non localized key of the powertoy, this will be cached by the runner
|
|
virtual const wchar_t* get_key() override
|
|
{
|
|
return app_key.c_str();
|
|
}
|
|
|
|
// Return the configured status for the gpo policy for the module
|
|
virtual powertoys_gpo::gpo_rule_configured_t gpo_policy_enabled_configuration() override
|
|
{
|
|
return powertoys_gpo::getConfiguredCmdPalEnabledValue();
|
|
}
|
|
|
|
virtual bool get_config(wchar_t* buffer, int* buffer_size) override
|
|
{
|
|
HINSTANCE hinstance = reinterpret_cast<HINSTANCE>(&__ImageBase);
|
|
|
|
// Create a Settings object.
|
|
PowerToysSettings::Settings settings(hinstance, get_name());
|
|
|
|
return settings.serialize_to_buffer(buffer, buffer_size);
|
|
}
|
|
|
|
virtual void call_custom_action(const wchar_t* /*action*/) override
|
|
{
|
|
}
|
|
|
|
virtual void set_config(const wchar_t* config) override
|
|
{
|
|
try
|
|
{
|
|
// Parse the input JSON string.
|
|
PowerToysSettings::PowerToyValues values =
|
|
PowerToysSettings::PowerToyValues::from_json_string(config, get_key());
|
|
|
|
// If you don't need to do any custom processing of the settings, proceed
|
|
// to persists the values calling:
|
|
values.save_to_settings_file();
|
|
// Otherwise call a custom function to process the settings before saving them to disk:
|
|
// save_settings();
|
|
}
|
|
catch (std::exception&)
|
|
{
|
|
// Improper JSON.
|
|
}
|
|
}
|
|
|
|
virtual void enable()
|
|
{
|
|
Logger::trace("CmdPal::enable()");
|
|
|
|
CmdPal::m_enabled.store(true);
|
|
|
|
std::wstring packageName = L"Microsoft.CommandPalette";
|
|
// Launch CmdPal as normal user using explorer
|
|
std::wstring launchPath = L"explorer.exe";
|
|
std::wstring launchArgs = L"x-cmdpal://background";
|
|
#ifdef IS_DEV_BRANDING
|
|
packageName = L"Microsoft.CommandPalette.Dev";
|
|
#endif
|
|
|
|
if (!package::GetRegisteredPackage(packageName, false).has_value())
|
|
{
|
|
try
|
|
{
|
|
Logger::info(L"CmdPal not installed. Installing...");
|
|
|
|
std::wstring installationFolder = get_module_folderpath();
|
|
#ifdef _DEBUG
|
|
std::wstring archSubdir = L"x64";
|
|
#ifdef _M_ARM64
|
|
archSubdir = L"ARM64";
|
|
#endif
|
|
auto msix = package::FindMsixFile(installationFolder + L"\\WinUI3Apps\\CmdPal\\AppPackages\\Microsoft.CmdPal.UI_0.0.1.0_Debug_Test\\", false);
|
|
auto dependencies = package::FindMsixFile(installationFolder + L"\\WinUI3Apps\\CmdPal\\AppPackages\\Microsoft.CmdPal.UI_0.0.1.0_Debug_Test\\Dependencies\\" + archSubdir + L"\\", true);
|
|
#else
|
|
auto msix = package::FindMsixFile(installationFolder + L"\\WinUI3Apps\\CmdPal\\", false);
|
|
auto dependencies = package::FindMsixFile(installationFolder + L"\\WinUI3Apps\\CmdPal\\Dependencies\\", true);
|
|
#endif
|
|
|
|
if (!msix.empty())
|
|
{
|
|
auto msixPath = msix[0];
|
|
|
|
if (!package::RegisterPackage(msixPath, dependencies))
|
|
{
|
|
Logger::error(L"Failed to install CmdPal package");
|
|
}
|
|
}
|
|
}
|
|
catch (std::exception& e)
|
|
{
|
|
std::string errorMessage{ "Exception thrown while trying to install CmdPal package: " };
|
|
errorMessage += e.what();
|
|
Logger::error(errorMessage);
|
|
}
|
|
}
|
|
|
|
if (!package::GetRegisteredPackage(packageName, false).has_value())
|
|
{
|
|
Logger::error("Cmdpal is not registered, quit..");
|
|
return;
|
|
}
|
|
|
|
if (!firstEnableCall)
|
|
{
|
|
Logger::trace("Not first attempt, try to launch");
|
|
LaunchApp(launchPath, launchArgs, false /*no elevated*/, false /*error pop up*/);
|
|
}
|
|
else
|
|
{
|
|
// If first time enable, do retry launch.
|
|
Logger::trace("First attempt, try to launch");
|
|
std::thread launchThread(&CmdPal::RetryLaunch, launchPath, launchArgs);
|
|
launchThread.detach();
|
|
}
|
|
|
|
firstEnableCall = false;
|
|
}
|
|
|
|
virtual void disable()
|
|
{
|
|
Logger::trace("CmdPal::disable()");
|
|
TerminateCmdPal();
|
|
|
|
CmdPal::m_enabled.store(false);
|
|
}
|
|
|
|
static void RetryLaunch(std::wstring path, std::wstring cmdArgs)
|
|
{
|
|
const int base_delay_milliseconds = 1000;
|
|
int max_retry = 9; // 2**9 - 1 seconds. Control total wait time within 10 min.
|
|
int retry = 0;
|
|
do
|
|
{
|
|
auto launch_result = LaunchApp(path, cmdArgs, false, retry < max_retry);
|
|
if (launch_result)
|
|
{
|
|
Logger::info(L"CmdPal launched successfully after {} retries.", retry);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
Logger::error(L"Retry {} launch CmdPal launch failed.", retry);
|
|
}
|
|
|
|
// When we got max retry, we don't need to wait for the next retry.
|
|
if (retry < max_retry)
|
|
{
|
|
int delay = base_delay_milliseconds * (1 << (retry));
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(delay));
|
|
}
|
|
++retry;
|
|
} while (retry <= max_retry && m_enabled.load() && !m_launched.load());
|
|
|
|
if (!m_enabled.load() || m_launched.load())
|
|
{
|
|
Logger::error(L"Retry cancelled. CmdPal is disabled or already launched.");
|
|
}
|
|
else
|
|
{
|
|
Logger::error(L"CmdPal launch failed after {} attempts.", retry);
|
|
}
|
|
}
|
|
|
|
virtual bool on_hotkey(size_t) override
|
|
{
|
|
return false;
|
|
}
|
|
|
|
virtual size_t get_hotkeys(Hotkey*, size_t) override
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
virtual bool is_enabled() override
|
|
{
|
|
return CmdPal::m_enabled.load();
|
|
}
|
|
};
|
|
|
|
std::atomic<bool> CmdPal::m_enabled{ false };
|
|
std::atomic<bool> CmdPal::m_launched{ false };
|
|
|
|
extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create()
|
|
{
|
|
return new CmdPal();
|
|
}
|