#ifdef _WIN32 #define NOMINMAX #include #include "platform.hpp" #include #include #include 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(value.size()), nullptr, 0); std::wstring result(size, L'\0'); MultiByteToWideChar(CP_UTF8, 0, value.data(), static_cast(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& 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(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(exit_code); CloseHandle(process.hThread); CloseHandle(process.hProcess); CloseHandle(read_pipe); return result; } } // namespace connector #endif