diff --git a/ExecThread.gd b/ExecThread.gd index 1ee731e..a3667c8 100644 --- a/ExecThread.gd +++ b/ExecThread.gd @@ -3,22 +3,24 @@ extends Reference const NativeExec = preload('./godot-native-exec.gdns') signal _completed(instance) +func _native_exec_new(): + return NativeExec.new() + func exec(cmd: String, args: PoolStringArray, stdout := [], stderr := [], timeout := 10 * 60 * 1000) -> int: var thread := Thread.new() var stdout_ = [] var stderr_ = [] - var instance = {} var err = thread.start(self, '_thread_exec', { cmd=cmd, args=args, stdout=stdout_, stderr=stderr_, timeout=timeout, - instance=instance + instance=thread }) assert(err == OK) var yielded = yield(self, '_completed') - while yielded != instance: + while yielded != thread: yielded = yield(self, '_completed') var result = thread.wait_to_finish() for s in stdout_: @@ -29,8 +31,8 @@ func exec(cmd: String, args: PoolStringArray, stdout := [], stderr := [], timeou func _thread_exec(a): var result := 0 - if OS.get_name() == 'Windows': - var ne := NativeExec.new() + var ne := _native_exec_new() as NativeExec if OS.get_name() == 'Windows' else null + if ne: result = ne.exec(a.cmd, a.args, a.stdout, a.stderr, a.timeout) else: # unfortunately stderr would be combined here... so disable it @@ -38,5 +40,5 @@ func _thread_exec(a): call_deferred('_emit_completed', a.instance) return result -func _emit_completed(instance: Dictionary): +func _emit_completed(instance: Thread): emit_signal('_completed', instance) diff --git a/src/exec.cpp b/src/exec.cpp index dcf7c00..eafa8dc 100644 --- a/src/exec.cpp +++ b/src/exec.cpp @@ -9,8 +9,9 @@ using namespace godot; -HANDLE CreateChildProcess(String cmdline, PoolStringArray args, HANDLE childStdIn, HANDLE childStdOut, HANDLE childStdErr); +HANDLE CreateChildProcess(String cmdline, PoolStringArray args, HANDLE &childStdIn, HANDLE &childStdOut, HANDLE &childStdErr); +// #define DEBUG #ifdef DEBUG #define debug_print(msg) Godot::print(msg) #else @@ -52,7 +53,7 @@ static size_t readToArray(HANDLE handle, Array output) { buf[read_bytes] = 0; success = ReadFile(handle, buf, read_bytes, &read_bytes, NULL); if (success && read_bytes > 0) { - String str = buf; + Variant str = buf; debug_print(str); output.append(str); } @@ -141,26 +142,33 @@ godot_int NativeExec::exec(String cmd, PoolStringArray args, Array stdout_, Arra sAttr.lpSecurityDescriptor = nullptr; if (!CreatePipe(&childStdOutRd, &childStdOutWr, &sAttr, 0)) { + debug_print("exec::CreatePipe() failed "); goto exit; } if (!SetHandleInformation(childStdOutRd, HANDLE_FLAG_INHERIT, 0)) { + debug_print("exec::SetHandleInformation(childStdOutRd) failed "); goto exit; } if (!CreatePipe(&childStdErrRd, &childStdErrWr, &sAttr, 0)) { + debug_print("exec::createPipe(childStdErrRd) failed "); goto exit; } if (!SetHandleInformation(childStdErrRd, HANDLE_FLAG_INHERIT, 0)) { + debug_print("exec::SetHandleInformation(childStdErrRd) failed "); goto exit; } if (!CreatePipe(&childStdInRd, &childStdInWr, &sAttr, 0)) { + debug_print("exec::CreatePipe(childStdInRd) failed "); goto exit; } if (!SetHandleInformation(childStdInWr, HANDLE_FLAG_INHERIT, 0)) { + debug_print("exec::SetHandleInformation(childStdInRd) failed "); goto exit; } processHandle = CreateChildProcess(cmd, args, childStdInRd, childStdOutWr, childStdErrWr); if (processHandle == INVALID_HANDLE_VALUE) { + debug_print("exec::CreateChildProcess(...) failed "); exitCode = 0xFFFF; goto exit; } @@ -194,16 +202,20 @@ godot_int NativeExec::exec(String cmd, PoolStringArray args, Array stdout_, Arra childStdInRd, childStdInWr }; + int i = 0; for (const auto& h: handles) { if (h != INVALID_HANDLE_VALUE) { + debug_print("exec::Close Handle " + String::num_int64(i)); CloseHandle(h); } + ++i; } + debug_print("~NativeExec::exec()"); return exitCode; } -HANDLE CreateChildProcess(String cmd, PoolStringArray args, HANDLE childStdIn, HANDLE childStdOut, HANDLE childStdErr) { +HANDLE CreateChildProcess(String cmd, PoolStringArray args, HANDLE &childStdIn, HANDLE &childStdOut, HANDLE &childStdErr) { // Create a child process that uses the previously created pipes for STDIN and STDOUT. PROCESS_INFORMATION procInfo = {0}; STARTUPINFOW startInfo = {0}; @@ -225,13 +237,21 @@ HANDLE CreateChildProcess(String cmd, PoolStringArray args, HANDLE childStdIn, H quotedArgs += " "; quotedArgs += args[i].find(" ") > -1 ? "\"" + args[i] + "\"" : args[i]; } + const wchar_t *wArgs = quotedArgs.unicode_str(); + size_t size = sizeof(wchar_t) * (quotedArgs.length() + 1); + wchar_t *wArgsBuff = (wchar_t *)godot::api->godot_alloc(size); + memcpy(wArgsBuff, wArgs, size); + debug_print("CreateProcesW"); debug_print(quotedArgs); + debug_print(String::num_int64(size)); + debug_print("wArgsBuff"); + debug_print(wArgsBuff); // Create the child process. bSuccess = CreateProcessW( // Don't use application name because it won't run from $PATH NULL, - // I think it's ok if this buffer gets modified.. - (wchar_t *)quotedArgs.unicode_str(), // command line + // This buffer may be modified (null character added after the process filename) + wArgsBuff, // command line NULL, // process security attributes NULL, // primary thread security attributes TRUE, // handles are inherited @@ -240,20 +260,29 @@ HANDLE CreateChildProcess(String cmd, PoolStringArray args, HANDLE childStdIn, H NULL, // use parent's current directory &startInfo, // STARTUPINFO pointer &procInfo); // receives PROCESS_INFORMATION + debug_print("wArgsBuff2"); + debug_print(wArgsBuff); + godot::api->godot_free(wArgsBuff); // If an error occurs, exit the application. if (bSuccess) { // Close handles to the child process primary thread. // Some applications might keep these handles to monitor the status // of the child process, for example. + debug_print("exec::Close Handle procInfo.hThread"); CloseHandle(procInfo.hThread); // Close handles to the stdin and stdout pipes no longer needed by the child process. // If they are not explicitly closed, there is no way to recognize that the child process has ended. - + debug_print("exec::Close Handle childStdErr"); CloseHandle(childStdErr); + childStdErr = INVALID_HANDLE_VALUE; + debug_print("exec::Close Handle childStdOut"); CloseHandle(childStdOut); + childStdOut = INVALID_HANDLE_VALUE; + debug_print("exec::Close Handle childStdIn"); CloseHandle(childStdIn); + childStdIn = INVALID_HANDLE_VALUE; return procInfo.hProcess; } else { PRINT_ERROR("CreateProcess failed", GetLastError());