diff options
-rw-r--r-- | osdep/subprocess-win.c | 425 | ||||
-rw-r--r-- | osdep/terminal-win.c | 28 | ||||
-rw-r--r-- | osdep/windows_utils.c | 69 | ||||
-rw-r--r-- | osdep/windows_utils.h | 16 | ||||
-rw-r--r-- | wscript_build.py | 2 |
5 files changed, 399 insertions, 141 deletions
diff --git a/osdep/subprocess-win.c b/osdep/subprocess-win.c index b5a9e1a24d..bb3527eaa6 100644 --- a/osdep/subprocess-win.c +++ b/osdep/subprocess-win.c @@ -21,7 +21,7 @@ #include "osdep/subprocess.h" #include "osdep/io.h" -#include "osdep/atomic.h" +#include "osdep/windows_utils.h" #include "mpv_talloc.h" #include "common/common.h" @@ -29,6 +29,11 @@ #include "misc/bstr.h" #include "misc/thread_tools.h" +// Internal CRT FD flags +#define FOPEN (0x01) +#define FPIPE (0x08) +#define FDEV (0x40) + static void write_arg(bstr *cmdline, char *arg) { // Empty args must be represented as an empty quoted string @@ -88,16 +93,18 @@ static void write_arg(bstr *cmdline, char *arg) } // Convert an array of arguments to a properly escaped command-line string -static wchar_t *write_cmdline(void *ctx, char **argv) +static wchar_t *write_cmdline(void *ctx, char *argv0, char **args) { - // argv[0] should always be quoted. Otherwise, arguments may be interpreted - // as part of the program name. Also, it can't contain escape sequences. + // argv0 should always be quoted. Otherwise, arguments may be interpreted as + // part of the program name. Also, it can't contain escape sequences. bstr cmdline = {0}; - bstr_xappend_asprintf(NULL, &cmdline, "\"%s\"", argv[0]); + bstr_xappend_asprintf(NULL, &cmdline, "\"%s\"", argv0); - for (int i = 1; argv[i]; i++) { - bstr_xappend(NULL, &cmdline, bstr0(" ")); - write_arg(&cmdline, argv[i]); + if (args) { + for (int i = 0; args[i]; i++) { + bstr_xappend(NULL, &cmdline, bstr0(" ")); + write_arg(&cmdline, args[i]); + } } wchar_t *wcmdline = mp_from_utf8(ctx, cmdline.start); @@ -105,40 +112,6 @@ static wchar_t *write_cmdline(void *ctx, char **argv) return wcmdline; } -static int create_overlapped_pipe(HANDLE *read, HANDLE *write) -{ - static atomic_ulong counter = ATOMIC_VAR_INIT(0); - - // Generate pipe name - unsigned long id = atomic_fetch_add(&counter, 1); - unsigned pid = GetCurrentProcessId(); - wchar_t buf[36]; - swprintf(buf, MP_ARRAY_SIZE(buf), L"\\\\.\\pipe\\mpv-anon-%08x-%08lx", - pid, id); - - // The function for creating anonymous pipes (CreatePipe) can't create - // overlapped pipes, so instead, use a named pipe with a unique name - *read = CreateNamedPipeW(buf, PIPE_ACCESS_INBOUND | - FILE_FLAG_FIRST_PIPE_INSTANCE | FILE_FLAG_OVERLAPPED, - PIPE_TYPE_BYTE | PIPE_WAIT | PIPE_REJECT_REMOTE_CLIENTS, - 1, 0, 4096, 0, NULL); - if (*read == INVALID_HANDLE_VALUE) - goto error; - - // Open the write end of the pipe as a synchronous handle - *write = CreateFileW(buf, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, NULL); - if (*write == INVALID_HANDLE_VALUE) { - CloseHandle(*read); - goto error; - } - - return 0; -error: - *read = *write = INVALID_HANDLE_VALUE; - return -1; -} - static void delete_handle_list(void *p) { LPPROC_THREAD_ATTRIBUTE_LIST list = p; @@ -177,8 +150,8 @@ error: static int sparse_wait(HANDLE *handles, unsigned num_handles) { unsigned w_num_handles = 0; - HANDLE w_handles[10]; - int map[10]; + HANDLE w_handles[MP_SUBPROCESS_MAX_FDS + 2]; + int map[MP_SUBPROCESS_MAX_FDS + 2]; if (num_handles > MP_ARRAY_SIZE(w_handles)) return -1; @@ -209,62 +182,227 @@ static int async_read(HANDLE file, void *buf, unsigned size, OVERLAPPED* ol) return 0; } -static void write_none(void *ctx, char *data, size_t size) +static bool is_valid_handle(HANDLE h) { + // _get_osfhandle can return -2 "when the file descriptor is not associated + // with a stream" + return h && h != INVALID_HANDLE_VALUE && (intptr_t)h != -2; } -int mp_subprocess(char **args, struct mp_cancel *cancel, void *ctx, - subprocess_read_cb on_stdout, subprocess_read_cb on_stderr, - char **error) +static wchar_t *convert_environ(void *ctx, char **env) +{ + // Environment size in wchar_ts, including the trailing NUL + size_t env_size = 1; + + for (int i = 0; env[i]; i++) { + int count = MultiByteToWideChar(CP_UTF8, 0, env[i], -1, NULL, 0); + if (count <= 0) + abort(); + env_size += count; + } + + wchar_t *ret = talloc_array(ctx, wchar_t, env_size); + size_t pos = 0; + + for (int i = 0; env[i]; i++) { + int count = MultiByteToWideChar(CP_UTF8, 0, env[i], -1, + ret + pos, env_size - pos); + if (count <= 0) + abort(); + pos += count; + } + + return ret; +} + +void mp_subprocess2(struct mp_subprocess_opts *opts, + struct mp_subprocess_result *res) { wchar_t *tmp = talloc_new(NULL); - int status = -1; + DWORD r; + + HANDLE share_hndls[MP_SUBPROCESS_MAX_FDS] = {0}; + int share_hndl_count = 0; + HANDLE wait_hndls[MP_SUBPROCESS_MAX_FDS + 2] = {0}; + int wait_hndl_count = 0; + struct { + HANDLE handle; + bool handle_close; + char crt_flags; + HANDLE read; - HANDLE write; - OVERLAPPED ol; - char buf[4096]; - subprocess_read_cb read_cb; - } pipes[2] = { - { .read_cb = on_stdout ? on_stdout : write_none }, - { .read_cb = on_stderr ? on_stderr : write_none }, - }; + OVERLAPPED read_ol; + char *read_buf; + } fd_data[MP_SUBPROCESS_MAX_FDS] = {0}; + + // The maximum target FD is limited because FDs have to fit in two sparse + // arrays in STARTUPINFO.lpReserved2, which has a maximum size of 65535 + // bytes. The first four bytes are the handle count, followed by one byte + // per handle for flags, and an intptr_t per handle for the HANDLE itself. + static const int crt_fd_max = (65535 - sizeof(int)) / (1 + sizeof(intptr_t)); + int crt_fd_count = 0; // If the function exits before CreateProcess, there was an init error - *error = "init"; + *res = (struct mp_subprocess_result){ .error = MP_SUBPROCESS_EINIT }; - for (int i = 0; i < 2; i++) { - pipes[i].ol.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL); - if (!pipes[i].ol.hEvent) - goto done; - if (create_overlapped_pipe(&pipes[i].read, &pipes[i].write)) - goto done; - if (!SetHandleInformation(pipes[i].write, HANDLE_FLAG_INHERIT, - HANDLE_FLAG_INHERIT)) + STARTUPINFOEXW si = { + .StartupInfo = { + .cb = sizeof si, + .dwFlags = STARTF_USESTDHANDLES | STARTF_FORCEOFFFEEDBACK, + }, + }; + + for (int n = 0; n < opts->num_fds; n++) { + if (opts->fds[n].fd >= crt_fd_max) { + // Target FD is too big to fit in the CRT FD array + res->error = MP_SUBPROCESS_EUNSUPPORTED; goto done; + } + + if (opts->fds[n].fd >= crt_fd_count) + crt_fd_count = opts->fds[n].fd + 1; + + if (opts->fds[n].src_fd >= 0) { + HANDLE src_handle = (HANDLE)_get_osfhandle(opts->fds[n].src_fd); + + // Invalid handles are just ignored. This is because sometimes the + // standard handles are invalid in Windows, like in GUI processes. + // In this case mp_subprocess2 callers should still be able to + // blindly forward the standard FDs. + if (!is_valid_handle(src_handle)) + continue; + + DWORD type = GetFileType(src_handle); + bool is_console_handle = false; + switch (type & 0xff) { + case FILE_TYPE_DISK: + fd_data[n].crt_flags = FOPEN; + break; + case FILE_TYPE_CHAR: + fd_data[n].crt_flags = FOPEN | FDEV; + is_console_handle = GetConsoleMode(src_handle, &(DWORD){0}); + break; + case FILE_TYPE_PIPE: + fd_data[n].crt_flags = FOPEN | FPIPE; + break; + case FILE_TYPE_UNKNOWN: + continue; + } + + if (is_console_handle) { + // Some Windows versions have bugs when duplicating console + // handles, or when adding console handles to the CreateProcess + // handle list, so just use the handle directly for now. Console + // handles treat inheritance weirdly, so this should still work. + fd_data[n].handle = src_handle; + } else { + // Instead of making the source handle inheritable, just + // duplicate it to an inheritable handle + if (!DuplicateHandle(GetCurrentProcess(), src_handle, + GetCurrentProcess(), &fd_data[n].handle, 0, + TRUE, DUPLICATE_SAME_ACCESS)) + goto done; + fd_data[n].handle_close = true; + + share_hndls[share_hndl_count++] = fd_data[n].handle; + } + + } else if (opts->fds[n].on_read && !opts->detach) { + fd_data[n].read_ol.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL); + if (!fd_data[n].read_ol.hEvent) + goto done; + + struct w32_create_anon_pipe_opts o = { + .server_flags = PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, + .client_inheritable = true, + }; + if (!mp_w32_create_anon_pipe(&fd_data[n].read, &fd_data[n].handle, &o)) + goto done; + fd_data[n].handle_close = true; + + wait_hndls[n] = fd_data[n].read_ol.hEvent; + wait_hndl_count++; + + fd_data[n].crt_flags = FOPEN | FPIPE; + fd_data[n].read_buf = talloc_size(tmp, 4096); + + share_hndls[share_hndl_count++] = fd_data[n].handle; + + } else { + DWORD access; + if (opts->fds[n].fd == 0) { + access = FILE_GENERIC_READ; + } else if (opts->fds[n].fd <= 2) { + access = FILE_GENERIC_WRITE | FILE_READ_ATTRIBUTES; + } else { + access = FILE_GENERIC_READ | FILE_GENERIC_WRITE; + } + + SECURITY_ATTRIBUTES sa = { + .nLength = sizeof sa, + .bInheritHandle = TRUE, + }; + fd_data[n].crt_flags = FOPEN | FDEV; + fd_data[n].handle = CreateFileW(L"NUL", access, + FILE_SHARE_READ | FILE_SHARE_WRITE, + &sa, OPEN_EXISTING, 0, NULL); + fd_data[n].handle_close = true; + } + + switch (opts->fds[n].fd) { + case 0: + si.StartupInfo.hStdInput = fd_data[n].handle; + break; + case 1: + si.StartupInfo.hStdOutput = fd_data[n].handle; + break; + case 2: + si.StartupInfo.hStdError = fd_data[n].handle; + break; + } } + // Convert the UTF-8 environment into a UTF-16 Windows environment block + wchar_t *env = NULL; + if (opts->env) + env = convert_environ(tmp, opts->env); + // Convert the args array to a UTF-16 Windows command-line string - wchar_t *cmdline = write_cmdline(tmp, args); + char **args = opts->args && opts->args[0] ? &opts->args[1] : 0; + wchar_t *cmdline = write_cmdline(tmp, opts->exe, args); + + // Get pointers to the arrays in lpReserved2. This is an undocumented data + // structure used by MSVCRT (and other frameworks and runtimes) to emulate + // FD inheritance. The format is unofficially documented here: + // https://www.catch22.net/tuts/undocumented-createprocess + si.StartupInfo.cbReserved2 = sizeof(int) + crt_fd_count * (1 + sizeof(intptr_t)); + si.StartupInfo.lpReserved2 = talloc_size(tmp, si.StartupInfo.cbReserved2); + char *crt_buf_flags = si.StartupInfo.lpReserved2 + sizeof(int); + char *crt_buf_hndls = crt_buf_flags + crt_fd_count; + + memcpy(si.StartupInfo.lpReserved2, &crt_fd_count, sizeof(int)); + + // Fill the handle array with INVALID_HANDLE_VALUE, for unassigned handles + for (int n = 0; n < crt_fd_count; n++) { + HANDLE h = INVALID_HANDLE_VALUE; + memcpy(crt_buf_hndls + n * sizeof(intptr_t), &h, sizeof(intptr_t)); + } + + for (int n = 0; n < opts->num_fds; n++) { + crt_buf_flags[opts->fds[n].fd] = fd_data[n].crt_flags; + memcpy(crt_buf_hndls + opts->fds[n].fd * sizeof(intptr_t), + &fd_data[n].handle, sizeof(intptr_t)); + } DWORD flags = CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT; PROCESS_INFORMATION pi = {0}; - STARTUPINFOEXW si = { - .StartupInfo = { - .cb = sizeof(si), - .dwFlags = STARTF_USESTDHANDLES | STARTF_FORCEOFFFEEDBACK, - .hStdInput = NULL, - .hStdOutput = pipes[0].write, - .hStdError = pipes[1].write, - }, - // Specify which handles are inherited by the subprocess. If this isn't - // specified, the subprocess inherits all inheritable handles, which - // could include handles created by other threads. See: - // http://blogs.msdn.com/b/oldnewthing/archive/2011/12/16/10248328.aspx - .lpAttributeList = create_handle_list(tmp, - (HANDLE[]){ pipes[0].write, pipes[1].write }, 2), - }; + // Specify which handles are inherited by the subprocess. If this isn't + // specified, the subprocess inherits all inheritable handles, which could + // include handles created by other threads. See: + // http://blogs.msdn.com/b/oldnewthing/archive/2011/12/16/10248328.aspx + si.lpAttributeList = create_handle_list(tmp, share_hndls, share_hndl_count); // If we have a console, the subprocess will automatically attach to it so // it can receive Ctrl+C events. If we don't have a console, prevent the @@ -274,87 +412,104 @@ int mp_subprocess(char **args, struct mp_cancel *cancel, void *ctx, if (!GetConsoleCP()) flags |= CREATE_NO_WINDOW; - if (!CreateProcessW(NULL, cmdline, NULL, NULL, TRUE, flags, NULL, NULL, + if (!CreateProcessW(NULL, cmdline, NULL, NULL, TRUE, flags, env, NULL, &si.StartupInfo, &pi)) goto done; talloc_free(cmdline); + talloc_free(env); + talloc_free(si.StartupInfo.lpReserved2); talloc_free(si.lpAttributeList); CloseHandle(pi.hThread); - // Init is finished - *error = NULL; + for (int n = 0; n < opts->num_fds; n++) { + if (fd_data[n].handle_close && is_valid_handle(fd_data[n].handle)) + CloseHandle(fd_data[n].handle); + fd_data[n].handle = NULL; + + if (fd_data[n].read) { + // Do the first read operation on each pipe + if (async_read(fd_data[n].read, fd_data[n].read_buf, 4096, + &fd_data[n].read_ol)) + { + CloseHandle(fd_data[n].read); + wait_hndls[n] = fd_data[n].read = NULL; + wait_hndl_count--; + } + } + } - // List of handles to watch with sparse_wait - HANDLE handles[] = { pipes[0].ol.hEvent, pipes[1].ol.hEvent, pi.hProcess, - cancel ? mp_cancel_get_event(cancel) : NULL }; + if (opts->detach) { + res->error = MP_SUBPROCESS_OK; + goto done; + } - for (int i = 0; i < 2; i++) { - // Close our copy of the write end of the pipes - CloseHandle(pipes[i].write); - pipes[i].write = NULL; + res->error = MP_SUBPROCESS_EGENERIC; - // Do the first read operation on each pipe - if (async_read(pipes[i].read, pipes[i].buf, 4096, &pipes[i].ol)) { - CloseHandle(pipes[i].read); - handles[i] = pipes[i].read = NULL; - } - } + wait_hndls[MP_SUBPROCESS_MAX_FDS] = pi.hProcess; + wait_hndl_count++; - DWORD r; - DWORD exit_code; - while (pipes[0].read || pipes[1].read || pi.hProcess) { - int i = sparse_wait(handles, MP_ARRAY_SIZE(handles)); - switch (i) { - case 0: - case 1: - // Complete the read operation on the pipe - if (!GetOverlappedResult(pipes[i].read, &pipes[i].ol, &r, TRUE)) { - CloseHandle(pipes[i].read); - handles[i] = pipes[i].read = NULL; - break; - } + if (opts->cancel) + wait_hndls[MP_SUBPROCESS_MAX_FDS + 1] = mp_cancel_get_event(opts->cancel); - pipes[i].read_cb(ctx, pipes[i].buf, r); + DWORD exit_code; + while (wait_hndl_count) { + int n = sparse_wait(wait_hndls, MP_ARRAY_SIZE(wait_hndls)); - // Begin the next read operation on the pipe - if (async_read(pipes[i].read, pipes[i].buf, 4096, &pipes[i].ol)) { - CloseHandle(pipes[i].read); - handles[i] = pipes[i].read = NULL; + if (n >= 0 && n < MP_SUBPROCESS_MAX_FDS) { + // Complete the read operation on the pipe + if (!GetOverlappedResult(fd_data[n].read, &fd_data[n].read_ol, &r, TRUE)) { + CloseHandle(fd_data[n].read); + wait_hndls[n] = fd_data[n].read = NULL; + wait_hndl_count--; + } else { + opts->fds[n].on_read(opts->fds[n].on_read_ctx, + fd_data[n].read_buf, r); + + // Begin the next read operation on the pipe + if (async_read(fd_data[n].read, fd_data[n].read_buf, 4096, + &fd_data[n].read_ol)) + { + CloseHandle(fd_data[n].read); + wait_hndls[n] = fd_data[n].read = NULL; + wait_hndl_count--; + } } - break; - case 2: + } else if (n == MP_SUBPROCESS_MAX_FDS) { // pi.hProcess GetExitCodeProcess(pi.hProcess, &exit_code); - status = exit_code; + res->exit_status = exit_code; CloseHandle(pi.hProcess); - handles[i] = pi.hProcess = NULL; - break; - case 3: + wait_hndls[n] = pi.hProcess = NULL; + wait_hndl_count--; + + } else if (n == MP_SUBPROCESS_MAX_FDS + 1) { // opts.cancel if (pi.hProcess) { TerminateProcess(pi.hProcess, 1); - *error = "killed"; - status = MP_SUBPROCESS_EKILLED_BY_US; + res->error = MP_SUBPROCESS_EKILLED_BY_US; goto done; } - break; - default: + } else { goto done; } } + res->error = MP_SUBPROCESS_OK; + done: - for (int i = 0; i < 2; i++) { - if (pipes[i].read) { + for (int n = 0; n < opts->num_fds; n++) { + if (is_valid_handle(fd_data[n].read)) { // Cancel any pending I/O (if the process was killed) - CancelIo(pipes[i].read); - GetOverlappedResult(pipes[i].read, &pipes[i].ol, &r, TRUE); - CloseHandle(pipes[i].read); + CancelIo(fd_data[n].read); + GetOverlappedResult(fd_data[n].read, &fd_data[n].read_ol, &r, TRUE); + CloseHandle(fd_data[n].read); } - if (pipes[i].write) CloseHandle(pipes[i].write); - if (pipes[i].ol.hEvent) CloseHandle(pipes[i].ol.hEvent); + if (fd_data[n].handle_close && is_valid_handle(fd_data[n].handle)) + CloseHandle(fd_data[n].handle); + if (fd_data[n].read_ol.hEvent) + CloseHandle(fd_data[n].read_ol.hEvent); } - if (pi.hProcess) CloseHandle(pi.hProcess); + if (pi.hProcess) + CloseHandle(pi.hProcess); talloc_free(tmp); - return status; } diff --git a/osdep/terminal-win.c b/osdep/terminal-win.c index ad7631f9da..3672f3a763 100644 --- a/osdep/terminal-win.c +++ b/osdep/terminal-win.c @@ -360,10 +360,27 @@ static bool is_a_console(HANDLE h) static void reopen_console_handle(DWORD std, int fd, FILE *stream) { - if (is_a_console(GetStdHandle(std))) { - freopen("CONOUT$", "wt", stream); - dup2(fileno(stream), fd); + HANDLE handle = GetStdHandle(std); + if (is_a_console(handle)) { + if (fd == 0) { + freopen("CONIN$", "rt", stream); + } else { + freopen("CONOUT$", "wt", stream); + } setvbuf(stream, NULL, _IONBF, 0); + + // Set the low-level FD to the new handle value, since mp_subprocess2 + // callers might rely on low-level FDs being set. Note, with this + // method, fileno(stdin) != STDIN_FILENO, but that shouldn't matter. + int unbound_fd = -1; + if (fd == 0) { + unbound_fd = _open_osfhandle((intptr_t)handle, _O_RDONLY); + } else { + unbound_fd = _open_osfhandle((intptr_t)handle, _O_WRONLY); + } + // dup2 will duplicate the underlying handle. Don't close unbound_fd, + // since that will close the original handle. + dup2(unbound_fd, fd); } } @@ -384,8 +401,9 @@ bool terminal_try_attach(void) if (!AttachConsole(ATTACH_PARENT_PROCESS)) return false; - // We have a console window. Redirect output streams to that console's - // low-level handles, so things that use printf directly work later on. + // We have a console window. Redirect input/output streams to that console's + // low-level handles, so things that use stdio work later on. + reopen_console_handle(STD_INPUT_HANDLE, STDIN_FILENO, stdin); reopen_console_handle(STD_OUTPUT_HANDLE, STDOUT_FILENO, stdout); reopen_console_handle(STD_ERROR_HANDLE, STDERR_FILENO, stderr); diff --git a/osdep/windows_utils.c b/osdep/windows_utils.c index a60eba3d26..a647e9f00d 100644 --- a/osdep/windows_utils.c +++ b/osdep/windows_utils.c @@ -24,6 +24,8 @@ #include <d3d9.h> #include <dxgi1_2.h> +#include "common/common.h" +#include "osdep/atomic.h" #include "windows_utils.h" char *mp_GUID_to_str_buf(char *buf, size_t buf_size, const GUID *guid) @@ -158,3 +160,70 @@ char *mp_HRESULT_to_str_buf(char *buf, size_t buf_size, HRESULT hr) snprintf(buf, buf_size, "%s (0x%"PRIx32")", msg, (uint32_t)hr); return buf; } + +bool mp_w32_create_anon_pipe(HANDLE *server, HANDLE *client, + struct w32_create_anon_pipe_opts *opts) +{ + static atomic_ulong counter = ATOMIC_VAR_INIT(0); + + // Generate pipe name + unsigned long id = atomic_fetch_add(&counter, 1); + unsigned pid = GetCurrentProcessId(); + wchar_t buf[36]; + swprintf(buf, MP_ARRAY_SIZE(buf), L"\\\\.\\pipe\\mpv-anon-%08x-%08lx", + pid, id); + + DWORD client_access = 0; + DWORD out_buffer = opts->out_buf_size; + DWORD in_buffer = opts->in_buf_size; + + if (opts->server_flags & PIPE_ACCESS_INBOUND) { + client_access |= FILE_GENERIC_WRITE | FILE_READ_ATTRIBUTES; + if (!in_buffer) + in_buffer = 4096; + } + if (opts->server_flags & PIPE_ACCESS_OUTBOUND) { + client_access |= FILE_GENERIC_READ | FILE_WRITE_ATTRIBUTES; + if (!out_buffer) + out_buffer = 4096; + } + + SECURITY_ATTRIBUTES inherit_sa = { + .nLength = sizeof inherit_sa, + .bInheritHandle = TRUE, + }; + + // The function for creating anonymous pipes (CreatePipe) can't create + // overlapped pipes, so instead, use a named pipe with a unique name + *server = CreateNamedPipeW(buf, + opts->server_flags | FILE_FLAG_FIRST_PIPE_INSTANCE, + opts->server_mode | PIPE_REJECT_REMOTE_CLIENTS, + 1, out_buffer, in_buffer, 0, + opts->server_inheritable ? &inherit_sa : NULL); + if (*server == INVALID_HANDLE_VALUE) + goto error; + + // Open the write end of the pipe as a synchronous handle + *client = CreateFileW(buf, client_access, 0, + opts->client_inheritable ? &inherit_sa : NULL, + OPEN_EXISTING, + opts->client_flags | SECURITY_SQOS_PRESENT | + SECURITY_ANONYMOUS, NULL); + if (*client == INVALID_HANDLE_VALUE) { + CloseHandle(*server); + goto error; + } + + if (opts->client_mode) { + if (!SetNamedPipeHandleState(*client, &opts->client_mode, NULL, NULL)) { + CloseHandle(*server); + CloseHandle(*client); + goto error; + } + } + + return true; +error: + *server = *client = INVALID_HANDLE_VALUE; + return false; +} diff --git a/osdep/windows_utils.h b/osdep/windows_utils.h index a20a558709..a8a5e947b8 100644 --- a/osdep/windows_utils.h +++ b/osdep/windows_utils.h @@ -19,6 +19,7 @@ #define MP_WINDOWS_UTILS_H_ #include <windows.h> +#include <stdbool.h> // Conditionally release a COM interface and set the pointer to NULL #define SAFE_RELEASE(u) \ @@ -30,4 +31,19 @@ char *mp_HRESULT_to_str_buf(char *buf, size_t buf_size, HRESULT hr); #define mp_HRESULT_to_str(hr) mp_HRESULT_to_str_buf((char[256]){0}, 256, (hr)) #define mp_LastError_to_str() mp_HRESULT_to_str(HRESULT_FROM_WIN32(GetLastError())) +struct w32_create_anon_pipe_opts { + DWORD server_flags; + DWORD server_mode; + bool server_inheritable; + DWORD out_buf_size; + DWORD in_buf_size; + + DWORD client_flags; + DWORD client_mode; + bool client_inheritable; +}; + +bool mp_w32_create_anon_pipe(HANDLE *server, HANDLE *client, + struct w32_create_anon_pipe_opts *opts); + #endif diff --git a/wscript_build.py b/wscript_build.py index 746cb16261..7cf5d5a784 100644 --- a/wscript_build.py +++ b/wscript_build.py @@ -208,7 +208,7 @@ def build(ctx): subprocess_c = ctx.pick_first_matching_dep([ ( "osdep/subprocess-posix.c", "posix" ), - # broken ( "osdep/subprocess-win.c", "win32-desktop" ), + ( "osdep/subprocess-win.c", "win32-desktop" ), ( "osdep/subprocess-dummy.c" ), ]) |