mirror of
https://github.com/Relintai/pandemonium_engine.git
synced 2025-01-03 01:19:38 +01:00
Implemented SubProcessWindows. It will likely have issues / not compile.
This commit is contained in:
parent
4a6868b79d
commit
25533eed23
@ -45,6 +45,7 @@
|
||||
#include "servers/rendering/rendering_server_raster.h"
|
||||
#include "servers/rendering/rendering_server_wrap_mt.h"
|
||||
#include "windows_terminal_logger.h"
|
||||
#include "sub_process_windows.h"
|
||||
|
||||
#include <avrt.h>
|
||||
#include <direct.h>
|
||||
@ -197,6 +198,8 @@ void OS_Windows::initialize_core() {
|
||||
DirAccess::make_default<DirAccessWindows>(DirAccess::ACCESS_USERDATA);
|
||||
DirAccess::make_default<DirAccessWindows>(DirAccess::ACCESS_FILESYSTEM);
|
||||
|
||||
SubProcessWindows::make_default();
|
||||
|
||||
NetSocketPosix::make_default();
|
||||
|
||||
// We need to know how often the clock is updated
|
||||
|
330
platform/windows/sub_process_windows.cpp
Normal file
330
platform/windows/sub_process_windows.cpp
Normal file
@ -0,0 +1,330 @@
|
||||
/*************************************************************************/
|
||||
/* dir_access_unix.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "sub_process_windows.h"
|
||||
|
||||
#include <process.h>
|
||||
|
||||
Error SubProcessWindows::start() {
|
||||
if (_executable_path.empty()) {
|
||||
return ERR_FILE_BAD_PATH;
|
||||
}
|
||||
|
||||
if (is_process_running()) {
|
||||
return ERR_BUSY;
|
||||
}
|
||||
|
||||
String path = p_path.replace("/", "\\");
|
||||
|
||||
String cmdline = _quote_command_line_argument(path);
|
||||
const List<String>::Element *I = p_arguments.front();
|
||||
while (I) {
|
||||
cmdline += " " + _quote_command_line_argument(I->get());
|
||||
I = I->next();
|
||||
}
|
||||
|
||||
ZeroMemory(&_process_info.si, sizeof(_process_info.si));
|
||||
_process_info.si.cb = sizeof(_process_info.si);
|
||||
ZeroMemory(&_process_info.pi, sizeof(_process_info.pi));
|
||||
LPSTARTUPINFOW si_w = (LPSTARTUPINFOW)&_process_info.si;
|
||||
|
||||
Char16String modstr = cmdline.utf16(); // Windows wants to change this no idea why.
|
||||
|
||||
bool inherit_handles = false;
|
||||
|
||||
if (_read_output) {
|
||||
// Create pipe for StdOut and StdErr.
|
||||
SECURITY_ATTRIBUTES sa;
|
||||
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||
sa.bInheritHandle = true;
|
||||
sa.lpSecurityDescriptor = NULL;
|
||||
|
||||
ERR_FAIL_COND_V(!CreatePipe(&_pipe_handles[0], &_pipe_handles[1], &sa, 0), ERR_CANT_FORK);
|
||||
ERR_FAIL_COND_V(!SetHandleInformation(_pipe_handles[0], HANDLE_FLAG_INHERIT, 0), ERR_CANT_FORK); // Read handle is for host process only and should not be inherited.
|
||||
|
||||
_process_info.si.dwFlags |= STARTF_USESTDHANDLES;
|
||||
_process_info.si.hStdOutput = _pipe_handles[1];
|
||||
if (_read_std_err) {
|
||||
_process_info.si.hStdError = _pipe_handles[1];
|
||||
}
|
||||
inherit_handles = true;
|
||||
}
|
||||
|
||||
DWORD creaton_flags = NORMAL_PRIORITY_CLASS;
|
||||
if (_open_console) {
|
||||
creaton_flags |= CREATE_NEW_CONSOLE;
|
||||
} else {
|
||||
creaton_flags |= CREATE_NO_WINDOW;
|
||||
}
|
||||
|
||||
int ret = CreateProcessW(nullptr, (LPWSTR)(modstr.ptrw()), nullptr, nullptr, inherit_handles, creaton_flags, nullptr, nullptr, si_w, &_process_info.pi);
|
||||
if (!ret && _read_output) {
|
||||
CloseHandle(_pipe_handles[0]); // Cleanup pipe handles.
|
||||
CloseHandle(_pipe_handles[1]);
|
||||
|
||||
_pipe_handles[0] = NULL;
|
||||
_pipe_handles[1] = NULL;
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_V(ret == 0, ERR_CANT_FORK);
|
||||
|
||||
if (_blocking) {
|
||||
if (_read_output) {
|
||||
CloseHandle(_pipe_handles[1]); // Close pipe write handle (only child process is writing).
|
||||
|
||||
int bytes_in_buffer = 0;
|
||||
|
||||
const int CHUNK_SIZE = 4096;
|
||||
DWORD read = 0;
|
||||
for (;;) { // Read StdOut and StdErr from pipe.
|
||||
_bytes.resize(bytes_in_buffer + CHUNK_SIZE);
|
||||
const bool success = ReadFile(_pipe_handles[0], _bytes.ptr() + bytes_in_buffer, CHUNK_SIZE, &read, NULL);
|
||||
if (!success || read == 0) {
|
||||
break;
|
||||
}
|
||||
// Assume that all possible encodings are ASCII-compatible.
|
||||
// Break at newline to allow receiving long output in portions.
|
||||
int newline_index = -1;
|
||||
for (int i = read - 1; i >= 0; i--) {
|
||||
if (_bytes[bytes_in_buffer + i] == '\n') {
|
||||
newline_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (newline_index == -1) {
|
||||
bytes_in_buffer += read;
|
||||
continue;
|
||||
}
|
||||
|
||||
const int bytes_to_convert = bytes_in_buffer + (newline_index + 1);
|
||||
_append_to_pipe(_bytes.ptr(), bytes_to_convert);
|
||||
|
||||
bytes_in_buffer = read - (newline_index + 1);
|
||||
memmove(_bytes.ptr(), _bytes.ptr() + bytes_to_convert, bytes_in_buffer);
|
||||
}
|
||||
|
||||
if (bytes_in_buffer > 0) {
|
||||
_append_to_pipe(_bytes.ptr(), bytes_in_buffer);
|
||||
}
|
||||
|
||||
CloseHandle(_pipe_handles[0]); // Close pipe read handle.
|
||||
}
|
||||
|
||||
WaitForSingleObject(_process_info.pi.hProcess, INFINITE);
|
||||
|
||||
if (r_exitcode) {
|
||||
DWORD ret2;
|
||||
GetExitCodeProcess(_process_info.pi.hProcess, &ret2);
|
||||
_exitcode = ret2;
|
||||
}
|
||||
|
||||
CloseHandle(_process_info.pi.hProcess);
|
||||
CloseHandle(_process_info.pi.hThread);
|
||||
} else {
|
||||
if (_read_output) {
|
||||
//eventually we will need to keep this
|
||||
CloseHandle(_pipe_handles[1]); // Close pipe write handle (only child process is writing).
|
||||
_pipe_handles[1] = NULL;
|
||||
}
|
||||
|
||||
_process_started = true;
|
||||
|
||||
ProcessID pid = _process_info.pi.dwProcessId;
|
||||
_process_id = pid;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error SubProcessWindows::stop() {
|
||||
if (!_process_started) {
|
||||
return OK;
|
||||
}
|
||||
|
||||
if (_pipe_handles[0]) {
|
||||
CloseHandle(_pipe_handles[0]); // Cleanup pipe handles.
|
||||
_pipe_handles[0] = NULL;
|
||||
}
|
||||
|
||||
if (_pipe_handles[1]) {
|
||||
CloseHandle(_pipe_handles[1]);
|
||||
_pipe_handles[1] = NULL;
|
||||
}
|
||||
|
||||
const int ret = TerminateProcess(_process_info.pi.hProcess, 0);
|
||||
|
||||
CloseHandle(_process_info.pi.hProcess);
|
||||
CloseHandle(_process_info.pi.hThread);
|
||||
|
||||
ZeroMemory(&_process_info.si, sizeof(_process_info.si));
|
||||
_process_info.si.cb = sizeof(_process_info.si);
|
||||
ZeroMemory(&_process_info.pi, sizeof(_process_info.pi));
|
||||
|
||||
_process_started = false;
|
||||
|
||||
return ret != 0 ? OK : FAILED;
|
||||
}
|
||||
|
||||
Error SubProcessWindows::poll() {
|
||||
if (!_process_started) {
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
if (!_pipe_handles[0]) {
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
_pipe.clear();
|
||||
|
||||
int bytes_in_buffer = 0;
|
||||
|
||||
const int CHUNK_SIZE = 4096;
|
||||
DWORD read = 0;
|
||||
|
||||
_bytes.resize(bytes_in_buffer + CHUNK_SIZE);
|
||||
const bool success = ReadFile(_pipe_handles[0], _bytes.ptr() + bytes_in_buffer, CHUNK_SIZE, &read, NULL);
|
||||
|
||||
if (!success) {
|
||||
stop();
|
||||
return ERR_FILE_EOF;
|
||||
}
|
||||
|
||||
if (read == 0) {
|
||||
return OK;
|
||||
}
|
||||
|
||||
// Assume that all possible encodings are ASCII-compatible.
|
||||
// Break at newline to allow receiving long output in portions.
|
||||
int newline_index = -1;
|
||||
for (int i = read - 1; i >= 0; i--) {
|
||||
if (_bytes[bytes_in_buffer + i] == '\n') {
|
||||
newline_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (newline_index == -1) {
|
||||
bytes_in_buffer += read;
|
||||
return OK;
|
||||
}
|
||||
|
||||
const int bytes_to_convert = bytes_in_buffer + (newline_index + 1);
|
||||
_append_to_pipe(_bytes.ptr(), bytes_to_convert);
|
||||
|
||||
bytes_in_buffer = read - (newline_index + 1);
|
||||
memmove(_bytes.ptr(), _bytes.ptr() + bytes_to_convert, bytes_in_buffer);
|
||||
|
||||
if (bytes_in_buffer > 0) {
|
||||
_append_to_pipe(_bytes.ptr(), bytes_in_buffer);
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error SubProcessWindows::send_signal(const int p_signal) {
|
||||
//Not Yet Impl
|
||||
ERR_FAIL_V(ERR_BUG);
|
||||
}
|
||||
|
||||
Error SubProcessWindows::send_data(const String &p_data) {
|
||||
//Not Yet Impl
|
||||
ERR_FAIL_V(ERR_BUG);
|
||||
}
|
||||
|
||||
bool SubProcessWindows::is_process_running() const {
|
||||
if (_process_id == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_process_started) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD dw_exit_code = 0;
|
||||
if (!GetExitCodeProcess(_process_info.pi.hProcess, &dw_exit_code)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dw_exit_code != STILL_ACTIVE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
String SubProcessWindows::_quote_command_line_argument(const String &p_text) const {
|
||||
for (int i = 0; i < p_text.size(); i++) {
|
||||
CharType c = p_text[i];
|
||||
if (c == ' ' || c == '&' || c == '(' || c == ')' || c == '[' || c == ']' || c == '{' || c == '}' || c == '^' || c == '=' || c == ';' || c == '!' || c == '\'' || c == '+' || c == ',' || c == '`' || c == '~') {
|
||||
return "\"" + p_text + "\"";
|
||||
}
|
||||
}
|
||||
return p_text;
|
||||
}
|
||||
|
||||
void SubProcessWindows::_append_to_pipe(char *p_bytes, int p_size) {
|
||||
// Try to convert from default ANSI code page to Unicode.
|
||||
LocalVector<wchar_t> wchars;
|
||||
int total_wchars = MultiByteToWideChar(CP_ACP, 0, p_bytes, p_size, nullptr, 0);
|
||||
if (total_wchars > 0) {
|
||||
wchars.resize(total_wchars);
|
||||
if (MultiByteToWideChar(CP_ACP, 0, p_bytes, p_size, wchars.ptr(), total_wchars) == 0) {
|
||||
wchars.clear();
|
||||
}
|
||||
}
|
||||
|
||||
if (_pipe_mutex) {
|
||||
_pipe_mutex->lock();
|
||||
}
|
||||
if (wchars.empty()) {
|
||||
// Let's hope it's compatible with UTF-8.
|
||||
_pipe += String::utf8(p_bytes, p_size);
|
||||
} else {
|
||||
_pipe += String(wchars.ptr(), total_wchars);
|
||||
}
|
||||
if (_pipe_mutex) {
|
||||
_pipe_mutex->unlock();
|
||||
}
|
||||
}
|
||||
|
||||
SubProcessWindows::SubProcessWindows() :
|
||||
SubProcess() {
|
||||
_pipe_handles = { NULL, NULL };
|
||||
|
||||
_process_started = false;
|
||||
|
||||
ZeroMemory(&_process_info.si, sizeof(_process_info.si));
|
||||
_process_info.si.cb = sizeof(_process_info.si);
|
||||
ZeroMemory(&_process_info.pi, sizeof(_process_info.pi));
|
||||
}
|
||||
SubProcessWindows::~SubProcessWindows() {
|
||||
stop();
|
||||
}
|
67
platform/windows/sub_process_windows.h
Normal file
67
platform/windows/sub_process_windows.h
Normal file
@ -0,0 +1,67 @@
|
||||
#ifndef SUB_PROCESS_WINDOWS_H
|
||||
#define SUB_PROCESS_WINDOWS_H
|
||||
|
||||
/*************************************************************************/
|
||||
/* dir_access_unix.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "core/os/sub_process.h"
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <windowsx.h>
|
||||
|
||||
class SubProcessWindows : public SubProcess {
|
||||
public:
|
||||
virtual Error start();
|
||||
virtual Error stop();
|
||||
virtual Error poll();
|
||||
virtual Error send_signal(const int p_signal);
|
||||
virtual Error send_data(const String &p_data);
|
||||
virtual bool is_process_running() const;
|
||||
|
||||
SubProcessWindows();
|
||||
~SubProcessWindows();
|
||||
|
||||
protected:
|
||||
String _quote_command_line_argument(const String &p_text) const;
|
||||
void _append_to_pipe(char *p_bytes, int p_size);
|
||||
|
||||
struct ProcessInfo {
|
||||
STARTUPINFO si;
|
||||
PROCESS_INFORMATION pi;
|
||||
};
|
||||
|
||||
bool _process_started;
|
||||
HANDLE _pipe_handles[2];
|
||||
ProcessInfo _process_info;
|
||||
LocalVector<char> _bytes;
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user