Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add launch on startup and exit functionality with first run detection (#115) #117

Merged
merged 6 commits into from
Nov 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified src/chrome++.ini
Binary file not shown.
12 changes: 12 additions & 0 deletions src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,18 @@ std::wstring GetCrCommandLine() {
}
return GetIniString(L"General", L"CommandLine", L""); // Deprecated
}
std::wstring GetLaunchOnStartup() {
return GetIniString(L"general", L"launch_on_startup", L"");
}

bool IsKillLaunchOnExit() {
return ::GetPrivateProfileIntW(L"general", L"kill_launch_on_exit", 0,
kIniPath.c_str()) != 0;
}

std::wstring GetLaunchOnExit() {
return GetIniString(L"general", L"launch_on_exit", L"");
}

std::wstring GetDirPath(const std::wstring& dir_type) {
std::wstring path = CanonicalizePath(GetAppDir() + L"\\..\\" + dir_type);
Expand Down
84 changes: 54 additions & 30 deletions src/portable.h
Original file line number Diff line number Diff line change
@@ -1,34 +1,6 @@
#ifndef PORTABLE_H_
#define PORTABLE_H_

std::wstring QuoteSpaceIfNeeded(const std::wstring& str) {
if (str.find(L' ') == std::wstring::npos)
return std::move(str);

std::wstring escaped(L"\"");
for (auto c : str) {
if (c == L'"')
escaped += L'"';
escaped += c;
}
escaped += L'"';
return std::move(escaped);
}

std::wstring JoinArgsString(std::vector<std::wstring> lines,
const std::wstring& delimiter) {
std::wstring text;
bool first = true;
for (auto& line : lines) {
if (!first)
text += delimiter;
else
first = false;
text += QuoteSpaceIfNeeded(line);
}
return text;
}

// Construct new command line with portable mode.
std::wstring GetCommand(LPWSTR param) {
std::vector<std::wstring> args;
Expand Down Expand Up @@ -97,10 +69,56 @@ std::wstring GetCommand(LPWSTR param) {
return JoinArgsString(args, L" ");
}

bool IsFirstRun() {
HANDLE hMutex =
CreateMutexW(nullptr, TRUE, L"Global\\ChromePlusFirstRunMutex");
if (hMutex == nullptr || GetLastError() == ERROR_ALREADY_EXISTS) {
if (hMutex) {
CloseHandle(hMutex);
}
return false;
}
return true;
}

void LaunchCommands(const std::wstring& get_commands,
int show_command,
std::vector<HANDLE>* program_handles) {
auto commands = StringSplit(
get_commands, L';',
L""); // Quotes should not be used as they can cause errors with paths
// that contain spaces. Since semicolons rarely appear in names and
// commands, they are used as delimiters.
if (commands.empty()) {
return;
}
for (const auto& command : commands) {
std::wstring expanded_path = ExpandEnvironmentPath(command);
ReplaceStringIni(expanded_path, L"%app%", GetAppDir());
HANDLE handle = RunExecute(expanded_path.c_str(), show_command);
if (program_handles != nullptr && handle != nullptr) {
program_handles->push_back(handle);
}
}
}

void KillLaunchOnExit(std::vector<HANDLE>* program_handles) {
if (IsKillLaunchOnExit() && program_handles != nullptr) {
for (auto handle : *program_handles) {
TerminateProcess(handle, 0);
}
}
}

void Portable(LPWSTR param) {
std::vector<HANDLE> program_handles = {nullptr};
bool first_run = IsFirstRun();
if (first_run) {
LaunchCommands(GetLaunchOnStartup(), SW_SHOW, &program_handles);
}

wchar_t path[MAX_PATH];
::GetModuleFileName(nullptr, path, MAX_PATH);

std::wstring args = GetCommand(param);

SHELLEXECUTEINFO sei = {0};
Expand All @@ -109,9 +127,15 @@ void Portable(LPWSTR param) {
sei.lpVerb = L"open";
sei.lpFile = path;
sei.nShow = SW_SHOWNORMAL;

sei.lpParameters = args.c_str();

if (ShellExecuteEx(&sei)) {
if (first_run) {
WaitForSingleObject(sei.hProcess, INFINITE);
CloseHandle(sei.hProcess);
KillLaunchOnExit(&program_handles);
LaunchCommands(GetLaunchOnExit(), SW_HIDE, nullptr);
}
ExitProcess(0);
}
}
Expand Down
57 changes: 57 additions & 0 deletions src/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,34 @@ bool ReplaceStringInPlace(std::string& subject,
return find;
}

std::wstring QuoteSpaceIfNeeded(const std::wstring& str) {
if (str.find(L' ') == std::wstring::npos)
return std::move(str);

std::wstring escaped(L"\"");
for (auto c : str) {
if (c == L'"')
escaped += L'"';
escaped += c;
}
escaped += L'"';
return std::move(escaped);
}

std::wstring JoinArgsString(std::vector<std::wstring> lines,
const std::wstring& delimiter) {
std::wstring text;
bool first = true;
for (auto& line : lines) {
if (!first)
text += delimiter;
else
first = false;
text += QuoteSpaceIfNeeded(line);
}
return text;
}

// Memory and module search functions.
// Search memory.
uint8_t* memmem(uint8_t* src, int n, const uint8_t* sub, int m) {
Expand Down Expand Up @@ -334,6 +362,35 @@ void ExecuteCommand(int id, HWND hwnd = 0) {
::SendMessageTimeoutW(hwnd, WM_SYSCOMMAND, id, 0, 0, 1000, 0);
}

HANDLE RunExecute(const wchar_t* command, WORD show = SW_SHOW) {
int nArgs = 0;
std::vector<std::wstring> command_line;
LPWSTR* szArglist = CommandLineToArgvW(command, &nArgs);
for (int i = 0; i < nArgs; ++i) {
command_line.push_back(QuoteSpaceIfNeeded(szArglist[i]));
}
LocalFree(szArglist);

SHELLEXECUTEINFO ShExecInfo = {0};
ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
ShExecInfo.lpFile = command_line[0].c_str();
ShExecInfo.nShow = show;

std::wstring parameter;
for (size_t i = 1; i < command_line.size(); ++i) {
parameter += command_line[i];
parameter += L" ";
}
if (command_line.size() > 1) {
ShExecInfo.lpParameters = parameter.c_str();
}
if (ShellExecuteEx(&ShExecInfo)) {
return ShExecInfo.hProcess;
}
return nullptr;
}

bool IsFullScreen(HWND hwnd) {
RECT windowRect;
return (GetWindowRect(hwnd, &windowRect) &&
Expand Down