authorgravatar for johnnymarler@gmail.comJonathan Marler <johnnymarler@gmail.com> 2025-03-02 20:27:17-07:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2025-03-25 23:48:27+01:00
log1408288b95952b486c8bdce3b1e8eb6910de4cb7
treec4887006fd1f266639aebe177e960002b9701108
parent8a8b3019f356d91f304a06f5302653ee0c3cee54

support more process creation options on Windows

Adds a CreateProcessFlags packed struct for all the possible flags to CreateProcessW on windows. In addition, propagates the existing `start_suspended` option in std.process.Child which was previously only used on Darwin. Also adds a `create_no_window` option to std.process.Child which is a commonly used flag for launching console executables on windows without causing a new console window to "pop up".

4 files changed, 56 insertions(+), 9 deletions(-)

lib/std/os/windows.zig+36-1
...@@ -1918,13 +1918,48 @@ pub const CreateProcessError = error{...@@ -1918,13 +1918,48 @@ pub const CreateProcessError = error{
1918 Unexpected,1918 Unexpected,
1919};1919};
19201920
1921pub const CreateProcessFlags = packed struct(u32) {
1922 debug_process: bool = false,
1923 debug_only_this_process: bool = false,
1924 create_suspended: bool = false,
1925 detached_process: bool = false,
1926 create_new_console: bool = false,
1927 normal_priority_class: bool = false,
1928 idle_priority_class: bool = false,
1929 high_priority_class: bool = false,
1930 realtime_priority_class: bool = false,
1931 create_new_process_group: bool = false,
1932 create_unicode_environment: bool = false,
1933 create_separate_wow_vdm: bool = false,
1934 create_shared_wow_vdm: bool = false,
1935 create_forcedos: bool = false,
1936 below_normal_priority_class: bool = false,
1937 above_normal_priority_class: bool = false,
1938 inherit_parent_affinity: bool = false,
1939 inherit_caller_priority: bool = false,
1940 create_protected_process: bool = false,
1941 extended_startupinfo_present: bool = false,
1942 process_mode_background_begin: bool = false,
1943 process_mode_background_end: bool = false,
1944 create_secure_process: bool = false,
1945 _reserved: bool = false,
1946 create_breakaway_from_job: bool = false,
1947 create_preserve_code_authz_level: bool = false,
1948 create_default_error_mode: bool = false,
1949 create_no_window: bool = false,
1950 profile_user: bool = false,
1951 profile_kernel: bool = false,
1952 profile_server: bool = false,
1953 create_ignore_system_default: bool = false,
1954};
1955
1921pub fn CreateProcessW(1956pub fn CreateProcessW(
1922 lpApplicationName: ?LPCWSTR,1957 lpApplicationName: ?LPCWSTR,
1923 lpCommandLine: ?LPWSTR,1958 lpCommandLine: ?LPWSTR,
1924 lpProcessAttributes: ?*SECURITY_ATTRIBUTES,1959 lpProcessAttributes: ?*SECURITY_ATTRIBUTES,
1925 lpThreadAttributes: ?*SECURITY_ATTRIBUTES,1960 lpThreadAttributes: ?*SECURITY_ATTRIBUTES,
1926 bInheritHandles: BOOL,1961 bInheritHandles: BOOL,
1927 dwCreationFlags: DWORD,1962 dwCreationFlags: CreateProcessFlags,
1928 lpEnvironment: ?*anyopaque,1963 lpEnvironment: ?*anyopaque,
1929 lpCurrentDirectory: ?LPCWSTR,1964 lpCurrentDirectory: ?LPCWSTR,
1930 lpStartupInfo: *STARTUPINFOW,1965 lpStartupInfo: *STARTUPINFOW,
lib/std/os/windows/kernel32.zig+1-1
...@@ -314,7 +314,7 @@ pub extern "kernel32" fn CreateProcessW(...@@ -314,7 +314,7 @@ pub extern "kernel32" fn CreateProcessW(
314 lpProcessAttributes: ?*SECURITY_ATTRIBUTES,314 lpProcessAttributes: ?*SECURITY_ATTRIBUTES,
315 lpThreadAttributes: ?*SECURITY_ATTRIBUTES,315 lpThreadAttributes: ?*SECURITY_ATTRIBUTES,
316 bInheritHandles: BOOL,316 bInheritHandles: BOOL,
317 dwCreationFlags: DWORD,317 dwCreationFlags: windows.CreateProcessFlags,
318 lpEnvironment: ?LPVOID,318 lpEnvironment: ?LPVOID,
319 lpCurrentDirectory: ?LPCWSTR,319 lpCurrentDirectory: ?LPCWSTR,
320 lpStartupInfo: *STARTUPINFOW,320 lpStartupInfo: *STARTUPINFOW,
lib/std/process/Child.zig+18-6
...@@ -80,9 +80,13 @@ expand_arg0: Arg0Expand,...@@ -80,9 +80,13 @@ expand_arg0: Arg0Expand,
80/// Darwin-only. Disable ASLR for the child process.80/// Darwin-only. Disable ASLR for the child process.
81disable_aslr: bool = false,81disable_aslr: bool = false,
8282
83/// Darwin-only. Start child process in suspended state as if SIGSTOP was sent.83/// Darwin and Windows only. Start child process in suspended state. For Darwin it's started
84/// as if SIGSTOP was sent.
84start_suspended: bool = false,85start_suspended: bool = false,
8586
87/// Windows-only. Sets the CREATE_NO_WINDOW flag in CreateProcess.
88create_no_window: bool = false,
89
86/// Set to true to obtain rusage information for the child process.90/// Set to true to obtain rusage information for the child process.
87/// Depending on the target platform and implementation status, the91/// Depending on the target platform and implementation status, the
88/// requested statistics may or may not be available. If they are92/// requested statistics may or may not be available. If they are
...@@ -854,6 +858,12 @@ fn spawnWindows(self: *ChildProcess) SpawnError!void {...@@ -854,6 +858,12 @@ fn spawnWindows(self: *ChildProcess) SpawnError!void {
854 const app_name_w = try unicode.wtf8ToWtf16LeAllocZ(self.allocator, app_basename_wtf8);858 const app_name_w = try unicode.wtf8ToWtf16LeAllocZ(self.allocator, app_basename_wtf8);
855 defer self.allocator.free(app_name_w);859 defer self.allocator.free(app_name_w);
856860
861 const flags: windows.CreateProcessFlags = .{
862 .create_suspended = self.start_suspended,
863 .create_unicode_environment = true,
864 .create_no_window = self.create_no_window,
865 };
866
857 run: {867 run: {
858 const PATH: [:0]const u16 = process.getenvW(unicode.utf8ToUtf16LeStringLiteral("PATH")) orelse &[_:0]u16{};868 const PATH: [:0]const u16 = process.getenvW(unicode.utf8ToUtf16LeStringLiteral("PATH")) orelse &[_:0]u16{};
859 const PATHEXT: [:0]const u16 = process.getenvW(unicode.utf8ToUtf16LeStringLiteral("PATHEXT")) orelse &[_:0]u16{};869 const PATHEXT: [:0]const u16 = process.getenvW(unicode.utf8ToUtf16LeStringLiteral("PATHEXT")) orelse &[_:0]u16{};
...@@ -889,7 +899,7 @@ fn spawnWindows(self: *ChildProcess) SpawnError!void {...@@ -889,7 +899,7 @@ fn spawnWindows(self: *ChildProcess) SpawnError!void {
889 dir_buf.shrinkRetainingCapacity(normalized_len);899 dir_buf.shrinkRetainingCapacity(normalized_len);
890 }900 }
891901
892 windowsCreateProcessPathExt(self.allocator, &dir_buf, &app_buf, PATHEXT, &cmd_line_cache, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo) catch |no_path_err| {902 windowsCreateProcessPathExt(self.allocator, &dir_buf, &app_buf, PATHEXT, &cmd_line_cache, envp_ptr, cwd_w_ptr, flags, &siStartInfo, &piProcInfo) catch |no_path_err| {
893 const original_err = switch (no_path_err) {903 const original_err = switch (no_path_err) {
894 // argv[0] contains unsupported characters that will never resolve to a valid exe.904 // argv[0] contains unsupported characters that will never resolve to a valid exe.
895 error.InvalidArg0 => return error.FileNotFound,905 error.InvalidArg0 => return error.FileNotFound,
...@@ -917,7 +927,7 @@ fn spawnWindows(self: *ChildProcess) SpawnError!void {...@@ -917,7 +927,7 @@ fn spawnWindows(self: *ChildProcess) SpawnError!void {
917 const normalized_len = windows.normalizePath(u16, dir_buf.items) catch continue;927 const normalized_len = windows.normalizePath(u16, dir_buf.items) catch continue;
918 dir_buf.shrinkRetainingCapacity(normalized_len);928 dir_buf.shrinkRetainingCapacity(normalized_len);
919929
920 if (windowsCreateProcessPathExt(self.allocator, &dir_buf, &app_buf, PATHEXT, &cmd_line_cache, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo)) {930 if (windowsCreateProcessPathExt(self.allocator, &dir_buf, &app_buf, PATHEXT, &cmd_line_cache, envp_ptr, cwd_w_ptr, flags, &siStartInfo, &piProcInfo)) {
921 break :run;931 break :run;
922 } else |err| switch (err) {932 } else |err| switch (err) {
923 // argv[0] contains unsupported characters that will never resolve to a valid exe.933 // argv[0] contains unsupported characters that will never resolve to a valid exe.
...@@ -1016,6 +1026,7 @@ fn windowsCreateProcessPathExt(...@@ -1016,6 +1026,7 @@ fn windowsCreateProcessPathExt(
1016 cmd_line_cache: *WindowsCommandLineCache,1026 cmd_line_cache: *WindowsCommandLineCache,
1017 envp_ptr: ?[*]u16,1027 envp_ptr: ?[*]u16,
1018 cwd_ptr: ?[*:0]u16,1028 cwd_ptr: ?[*:0]u16,
1029 flags: windows.CreateProcessFlags,
1019 lpStartupInfo: *windows.STARTUPINFOW,1030 lpStartupInfo: *windows.STARTUPINFOW,
1020 lpProcessInformation: *windows.PROCESS_INFORMATION,1031 lpProcessInformation: *windows.PROCESS_INFORMATION,
1021) !void {1032) !void {
...@@ -1166,7 +1177,7 @@ fn windowsCreateProcessPathExt(...@@ -1166,7 +1177,7 @@ fn windowsCreateProcessPathExt(
1166 else1177 else
1167 full_app_name;1178 full_app_name;
11681179
1169 if (windowsCreateProcess(app_name_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_ptr, lpStartupInfo, lpProcessInformation)) |_| {1180 if (windowsCreateProcess(app_name_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_ptr, flags, lpStartupInfo, lpProcessInformation)) |_| {
1170 return;1181 return;
1171 } else |err| switch (err) {1182 } else |err| switch (err) {
1172 error.FileNotFound,1183 error.FileNotFound,
...@@ -1221,7 +1232,7 @@ fn windowsCreateProcessPathExt(...@@ -1221,7 +1232,7 @@ fn windowsCreateProcessPathExt(
1221 else1232 else
1222 full_app_name;1233 full_app_name;
12231234
1224 if (windowsCreateProcess(app_name_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_ptr, lpStartupInfo, lpProcessInformation)) |_| {1235 if (windowsCreateProcess(app_name_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_ptr, flags, lpStartupInfo, lpProcessInformation)) |_| {
1225 return;1236 return;
1226 } else |err| switch (err) {1237 } else |err| switch (err) {
1227 error.FileNotFound => continue,1238 error.FileNotFound => continue,
...@@ -1247,6 +1258,7 @@ fn windowsCreateProcess(...@@ -1247,6 +1258,7 @@ fn windowsCreateProcess(
1247 cmd_line: [*:0]u16,1258 cmd_line: [*:0]u16,
1248 envp_ptr: ?[*]u16,1259 envp_ptr: ?[*]u16,
1249 cwd_ptr: ?[*:0]u16,1260 cwd_ptr: ?[*:0]u16,
1261 flags: windows.CreateProcessFlags,
1250 lpStartupInfo: *windows.STARTUPINFOW,1262 lpStartupInfo: *windows.STARTUPINFOW,
1251 lpProcessInformation: *windows.PROCESS_INFORMATION,1263 lpProcessInformation: *windows.PROCESS_INFORMATION,
1252) !void {1264) !void {
...@@ -1273,7 +1285,7 @@ fn windowsCreateProcess(...@@ -1273,7 +1285,7 @@ fn windowsCreateProcess(
1273 null,1285 null,
1274 null,1286 null,
1275 windows.TRUE,1287 windows.TRUE,
1276 windows.CREATE_UNICODE_ENVIRONMENT,1288 flags,
1277 @as(?*anyopaque, @ptrCast(envp_ptr)),1289 @as(?*anyopaque, @ptrCast(envp_ptr)),
1278 cwd_ptr,1290 cwd_ptr,
1279 lpStartupInfo,1291 lpStartupInfo,
test/standalone/windows_argv/fuzz.zig+1-1
...@@ -138,7 +138,7 @@ fn spawnVerify(verify_path: [:0]const u16, cmd_line: [:0]const u16) !windows.DWO...@@ -138,7 +138,7 @@ fn spawnVerify(verify_path: [:0]const u16, cmd_line: [:0]const u16) !windows.DWO
138 null,138 null,
139 null,139 null,
140 windows.TRUE,140 windows.TRUE,
141 0,141 .{},
142 null,142 null,
143 null,143 null,
144 &startup_info,144 &startup_info,