Files
SpineParticlesWeb/connector/native/process_windows.cpp
T
2026-09-05 23:35:45 +08:00

99 lines
3.5 KiB
C++

#ifdef _WIN32
#define NOMINMAX
#include <windows.h>
#include "platform.hpp"
#include <chrono>
#include <stdexcept>
#include <thread>
namespace connector {
namespace {
std::wstring wide(const std::string& value) {
if (value.empty()) return {};
const int size = MultiByteToWideChar(CP_UTF8, 0, value.data(), static_cast<int>(value.size()), nullptr, 0);
std::wstring result(size, L'\0');
MultiByteToWideChar(CP_UTF8, 0, value.data(), static_cast<int>(value.size()), result.data(), size);
return result;
}
std::wstring quote(const std::string& value) {
std::wstring source = wide(value);
std::wstring result = L"\"";
unsigned backslashes = 0;
for (wchar_t character : source) {
if (character == L'\\') { ++backslashes; continue; }
if (character == L'\"') result.append(backslashes * 2 + 1, L'\\');
else result.append(backslashes, L'\\');
backslashes = 0;
result.push_back(character);
}
result.append(backslashes * 2, L'\\');
return result + L'\"';
}
} // namespace
ProcessResult run_process(const std::string& executable,
const std::vector<std::string>& arguments,
const std::filesystem::path& working_directory,
int timeout_seconds) {
SECURITY_ATTRIBUTES attributes{sizeof(SECURITY_ATTRIBUTES), nullptr, TRUE};
HANDLE read_pipe = nullptr;
HANDLE write_pipe = nullptr;
if (!CreatePipe(&read_pipe, &write_pipe, &attributes, 0)) throw std::runtime_error("无法创建 Spine 输出管道");
SetHandleInformation(read_pipe, HANDLE_FLAG_INHERIT, 0);
std::wstring command = quote(executable);
for (const std::string& argument : arguments) command += L" " + quote(argument);
STARTUPINFOW startup{};
startup.cb = sizeof(startup);
startup.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
startup.wShowWindow = SW_HIDE;
startup.hStdOutput = write_pipe;
startup.hStdError = write_pipe;
startup.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
PROCESS_INFORMATION process{};
const std::wstring cwd = working_directory.wstring();
const BOOL started = CreateProcessW(nullptr, command.data(), nullptr, nullptr, TRUE, CREATE_NO_WINDOW,
nullptr, cwd.empty() ? nullptr : cwd.c_str(), &startup, &process);
CloseHandle(write_pipe);
if (!started) {
CloseHandle(read_pipe);
throw std::runtime_error("无法启动 Spine.com,请检查路径和权限");
}
ProcessResult result;
const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(timeout_seconds);
while (true) {
DWORD available = 0;
if (PeekNamedPipe(read_pipe, nullptr, 0, nullptr, &available, nullptr) && available) {
char buffer[8192];
DWORD count = 0;
if (ReadFile(read_pipe, buffer, (std::min)(available, static_cast<DWORD>(sizeof(buffer))), &count, nullptr) && count && result.output.size() < 4 * 1024 * 1024) {
result.output.append(buffer, count);
}
}
const DWORD state = WaitForSingleObject(process.hProcess, 20);
if (state == WAIT_OBJECT_0) break;
if (std::chrono::steady_clock::now() >= deadline) {
result.timed_out = true;
TerminateProcess(process.hProcess, 1);
WaitForSingleObject(process.hProcess, INFINITE);
break;
}
}
DWORD exit_code = 1;
GetExitCodeProcess(process.hProcess, &exit_code);
result.exit_code = static_cast<int>(exit_code);
CloseHandle(process.hThread);
CloseHandle(process.hProcess);
CloseHandle(read_pipe);
return result;
}
} // namespace connector
#endif