Skip to content

Commit

Permalink
✨ Can now pass command-line args to the exe we spawn
Browse files Browse the repository at this point in the history
  • Loading branch information
JulesFouchy committed Dec 6, 2024
1 parent cd1c083 commit a94f8a1
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 8 deletions.
5 changes: 4 additions & 1 deletion include/Cool/spawn_process.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@
#include <filesystem>
#include <optional>
#include <string>
#include <vector>

namespace Cool {

/// Returns a non-empty optional containing an error message if something went wrong
auto spawn_process(std::filesystem::path const& executable_absolute_path) -> std::optional<std::string>;
/// You can pass command line args (argc / argv) as a vector of strings
/// NB: you don't need to add quotes if your args contain spaces (e.g. `spawn_process(my_exe, {"Hello", "i have many words"})` will send two args: Hello and "i have many words")
auto spawn_process(std::filesystem::path const& executable_absolute_path, std::vector<std::string> const& args = {}) -> std::optional<std::string>;

} // namespace Cool
29 changes: 23 additions & 6 deletions src/spawn_process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,22 @@ std::string GetLastErrorAsString()
return message;
}

auto spawn_process_impl(std::filesystem::path const& executable_absolute_path) -> std::optional<std::string>
auto spawn_process_impl(std::filesystem::path const& executable_absolute_path, std::vector<std::string> const& args) -> std::optional<std::string>
{
STARTUPINFO si;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
PROCESS_INFORMATION pi;
ZeroMemory(&pi, sizeof(pi));
if (!CreateProcess(executable_absolute_path.string().c_str(), nullptr, nullptr, nullptr, false, 0, nullptr, nullptr, &si, &pi))

// Combine the exe path and arguments into a single command line
std::string command_line = '\"' + executable_absolute_path.string() + '\"'; // we need to add quotes in case exe path contains spaces
for (auto const& arg : args)
command_line += " \"" + arg + '\"';
auto cmd = std::vector<char>(command_line.begin(), command_line.end()); // The API needs a char* so we can't use std::string::c_str()
cmd.push_back('\0');

if (!CreateProcess(nullptr, cmd.data(), nullptr, nullptr, false, 0, nullptr, nullptr, &si, &pi))
return GetLastErrorAsString();

CloseHandle(pi.hProcess);
Expand All @@ -52,15 +60,24 @@ auto spawn_process_impl(std::filesystem::path const& executable_absolute_path) -
#include <cstring>

namespace {
auto spawn_process_impl(std::filesystem::path const& executable_absolute_path) -> std::optional<std::string>
auto spawn_process_impl(std::filesystem::path const& executable_absolute_path, std::vector<std::string> const& args) -> std::optional<std::string>
{
pid_t pid = fork();

if (pid < 0)
return std::string{"Fork failed: "} + strerror(errno);

if (pid == 0) // We are in the child process:
execl(executable_absolute_path.string().c_str(), executable_absolute_path.string().c_str(), nullptr);
{
auto const exe_path = executable_absolute_path.string(); // Store the string so that the c_str() is kept alive
auto args_array = std::vector<const char*>{};
args_array.push_back(exe_path.c_str());
for (auto const& arg : args)
args_array.push_back(arg.c_str());
args_array.push_back(nullptr);

execv(exe_path.c_str(), args_array.data());

Check failure on line 79 in src/spawn_process.cpp

View workflow job for this annotation

GitHub Actions / Linux Clang Debug

no matching function for call to 'execv'

Check failure on line 79 in src/spawn_process.cpp

View workflow job for this annotation

GitHub Actions / Linux Clang Release

no matching function for call to 'execv'

Check failure on line 79 in src/spawn_process.cpp

View workflow job for this annotation

GitHub Actions / Linux GCC Debug

invalid conversion from ‘const char**’ to ‘char* const*’ [-fpermissive]

Check failure on line 79 in src/spawn_process.cpp

View workflow job for this annotation

GitHub Actions / Linux GCC Release

invalid conversion from ‘const char**’ to ‘char* const*’ [-fpermissive]

Check failure on line 79 in src/spawn_process.cpp

View workflow job for this annotation

GitHub Actions / MacOS Clang Debug

no matching function for call to 'execv'

Check failure on line 79 in src/spawn_process.cpp

View workflow job for this annotation

GitHub Actions / MacOS Clang Release

no matching function for call to 'execv'
}

return std::nullopt;
}
Expand All @@ -72,10 +89,10 @@ auto spawn_process_impl(std::filesystem::path const& executable_absolute_path) -

namespace Cool {

auto spawn_process(std::filesystem::path const& executable_absolute_path) -> std::optional<std::string>
auto spawn_process(std::filesystem::path const& executable_absolute_path, std::vector<std::string> const& args) -> std::optional<std::string>
{
assert(std::filesystem::weakly_canonical(executable_absolute_path) == executable_absolute_path);
return spawn_process_impl(executable_absolute_path);
return spawn_process_impl(executable_absolute_path, args);
}

} // namespace Cool
5 changes: 4 additions & 1 deletion tests/dummy_exe.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@
#include <iomanip>
#include <iostream>

int main()
int main(int argc, char** argv)
{
for (int i = 1; i < argc; ++i)
std::cout << "Command-line arg: '" << argv[i] << "'\n";

int prev_sec{};
int count{0};
while (count < 5)
Expand Down
29 changes: 29 additions & 0 deletions tests/tests.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,38 @@
#include <iostream>
#include <thread>
#include "Cool/spawn_process.hpp"

int main()
{
using namespace std::literals;

std::cout << "Start spawning process\n";
Cool::spawn_process(EXECUTABLE_PATH);
std::cout << "Done spawning process\n";

std::this_thread::sleep_for(6s);

std::cout << "Start spawning process\n";
Cool::spawn_process(EXECUTABLE_PATH, {"Hello"});
std::cout << "Done spawning process\n";

std::this_thread::sleep_for(6s);

std::cout << "Start spawning process\n";
Cool::spawn_process(EXECUTABLE_PATH, {"Hello", "World"});
std::cout << "Done spawning process\n";

std::this_thread::sleep_for(6s);

std::cout << "Start spawning process\n";
Cool::spawn_process(EXECUTABLE_PATH, {"Hello", "World", "two words"});
std::cout << "Done spawning process\n";

std::this_thread::sleep_for(6s);

std::cout << "Start spawning process\n";
Cool::spawn_process(EXECUTABLE_PATH, {"Hello", "World", "\\\"two words\\\""});
std::cout << "Done spawning process\n";

std::this_thread::sleep_for(6s);
}

0 comments on commit a94f8a1

Please sign in to comment.