连接器客户端
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
#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
|
||||
Reference in New Issue
Block a user