implement std.os.ChildProcess for windows

This commit is contained in:
Andrew Kelley 2017-10-13 09:31:03 -04:00
parent 7f9dc4ebc1
commit 8d3eaab871
8 changed files with 533 additions and 69 deletions

View file

@ -18,7 +18,7 @@ clarity.
writing buggy code.
* Debug mode optimizes for fast compilation time and crashing with a stack trace
when undefined behavior *would* happen.
* Release mode produces heavily optimized code. What other projects call
* ReleaseFast mode produces heavily optimized code. What other projects call
"Link Time Optimization" Zig does automatically.
* Compatible with C libraries with no wrapper necessary. Directly include
C .h files and get access to the functions and symbols therein.
@ -36,16 +36,13 @@ clarity.
a preprocessor or macros.
* The binaries produced by Zig have complete debugging information so you can,
for example, use GDB to debug your software.
* Mark functions as tests and automatically run them with `zig test`.
* Built-in unit tests with `zig test`.
* Friendly toward package maintainers. Reproducible build, bootstrapping
process carefully documented. Issues filed by package maintainers are
considered especially important.
* Cross-compiling is a primary use case.
* In addition to creating executables, creating a C library is a primary use
case. You can export an auto-generated .h file.
* For OS development, Zig supports all architectures that LLVM does. All the
standard library that does not depend on an OS is available to you in
freestanding mode.
### Support Table

View file

@ -7,9 +7,9 @@ pub const BufSet = struct {
const BufSetHashMap = HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8);
pub fn init(allocator: &Allocator) -> BufSet {
pub fn init(a: &Allocator) -> BufSet {
var self = BufSet {
.hash_map = BufSetHashMap.init(allocator),
.hash_map = BufSetHashMap.init(a),
};
return self;
}
@ -45,6 +45,10 @@ pub const BufSet = struct {
return self.hash_map.iterator();
}
pub fn allocator(self: &const BufSet) -> &Allocator {
return self.hash_map.allocator;
}
fn free(self: &BufSet, value: []const u8) {
// remove the const
const mut_value = @ptrCast(&u8, value.ptr)[0..value.len];

View file

@ -91,8 +91,17 @@ pub const Buffer = struct {
}
pub fn appendByte(self: &Buffer, byte: u8) -> %void {
%return self.resize(self.len() + 1);
self.list.items[self.len() - 1] = byte;
return self.appendByteNTimes(byte, 1);
}
pub fn appendByteNTimes(self: &Buffer, byte: u8, count: usize) -> %void {
var prev_size: usize = self.len();
%return self.resize(prev_size + count);
var i: usize = 0;
while (i < count) : (i += 1) {
self.list.items[prev_size + i] = byte;
}
}
pub fn eql(self: &const Buffer, m: []const u8) -> bool {

View file

@ -1,4 +1,5 @@
const debug = @import("debug.zig");
const mem = @import("mem.zig");
const assert = debug.assert;
pub fn len(ptr: &const u8) -> usize {
@ -36,3 +37,13 @@ fn testCStrFnsImpl() {
assert(cmp(c"aoeu", c"aoez") == -1);
assert(len(c"123456789") == 9);
}
/// Returns a mutable slice with exactly the same size which is guaranteed to
/// have a null byte after it.
/// Caller owns the returned memory.
pub fn addNullByte(allocator: &mem.Allocator, slice: []const u8) -> %[]u8 {
const result = %return allocator.alloc(u8, slice.len + 1);
mem.copy(u8, result, slice);
result[slice.len] = 0;
return result[0..slice.len];
}

View file

@ -254,6 +254,21 @@ pub fn indexOfScalarPos(comptime T: type, slice: []const T, start_index: usize,
return null;
}
pub fn indexOfAny(comptime T: type, slice: []const T, values: []const T) -> ?usize {
return indexOfAnyPos(T, slice, 0, values);
}
pub fn indexOfAnyPos(comptime T: type, slice: []const T, start_index: usize, values: []const T) -> ?usize {
var i: usize = start_index;
while (i < slice.len) : (i += 1) {
for (values) |value| {
if (slice[i] == value)
return i;
}
}
return null;
}
pub fn indexOf(comptime T: type, haystack: []const T, needle: []const T) -> ?usize {
return indexOfPos(T, haystack, 0, needle);
}

View file

@ -1,22 +1,30 @@
const io = @import("../io.zig");
const os = @import("index.zig");
const std = @import("../index.zig");
const cstr = std.cstr;
const io = std.io;
const os = std.os;
const posix = os.posix;
const mem = @import("../mem.zig");
const windows = os.windows;
const mem = std.mem;
const Allocator = mem.Allocator;
const debug = @import("../debug.zig");
const debug = std.debug;
const assert = debug.assert;
const BufMap = @import("../buf_map.zig").BufMap;
const BufMap = std.BufMap;
const Buffer = std.Buffer;
const builtin = @import("builtin");
const Os = builtin.Os;
const LinkedList = @import("../linked_list.zig").LinkedList;
const LinkedList = std.LinkedList;
error PermissionDenied;
error ProcessNotFound;
var children_nodes = LinkedList(&ChildProcess).init();
const is_windows = builtin.os == Os.windows;
pub const ChildProcess = struct {
pub pid: i32,
pub pid: if (is_windows) void else i32,
pub handle: if (is_windows) windows.HANDLE else void,
pub allocator: &mem.Allocator,
pub stdin: ?&io.OutStream,
@ -38,16 +46,16 @@ pub const ChildProcess = struct {
pub stderr_behavior: StdIo,
/// Set to change the user id when spawning the child process.
pub uid: ?u32,
pub uid: if (is_windows) void else ?u32,
/// Set to change the group id when spawning the child process.
pub gid: ?u32,
pub gid: if (is_windows) void else ?u32,
/// Set to change the current working directory when spawning the child process.
pub cwd: ?[]const u8,
err_pipe: [2]i32,
llnode: LinkedList(&ChildProcess).Node,
err_pipe: if (is_windows) void else [2]i32,
llnode: if (is_windows) void else LinkedList(&ChildProcess).Node,
pub const Term = enum {
Exited: i32,
@ -73,14 +81,15 @@ pub const ChildProcess = struct {
.allocator = allocator,
.argv = argv,
.pid = undefined,
.handle = undefined,
.err_pipe = undefined,
.llnode = undefined,
.term = null,
.onTerm = null,
.env_map = null,
.cwd = null,
.uid = null,
.gid = null,
.uid = if (is_windows) {} else null,
.gid = if (is_windows) {} else null,
.stdin = null,
.stdout = null,
.stderr = null,
@ -101,9 +110,10 @@ pub const ChildProcess = struct {
/// onTerm can be called before `spawn` returns.
/// On success must call `kill` or `wait`.
pub fn spawn(self: &ChildProcess) -> %void {
return switch (builtin.os) {
Os.linux, Os.macosx, Os.ios, Os.darwin => self.spawnPosix(),
else => @compileError("Unsupported OS"),
if (is_windows) {
return self.spawnWindows();
} else {
return self.spawnPosix();
};
}
@ -114,6 +124,30 @@ pub const ChildProcess = struct {
/// Forcibly terminates child process and then cleans up all resources.
pub fn kill(self: &ChildProcess) -> %Term {
if (is_windows) {
return self.killWindows(1);
} else {
return self.killPosix();
}
}
pub fn killWindows(self: &ChildProcess, exit_code: windows.UINT) -> %Term {
if (self.term) |term| {
self.cleanupStreams();
return term;
}
if (!windows.TerminateProcess(self.handle, exit_code)) {
const err = windows.GetLastError();
return switch (err) {
else => error.Unexpected,
};
}
self.waitUnwrappedWindows();
return ??self.term;
}
pub fn killPosix(self: &ChildProcess) -> %Term {
block_SIGCHLD();
defer restore_SIGCHLD();
@ -137,6 +171,24 @@ pub const ChildProcess = struct {
/// Blocks until child process terminates and then cleans up all resources.
pub fn wait(self: &ChildProcess) -> %Term {
if (is_windows) {
return self.waitWindows();
} else {
return self.waitPosix();
}
}
fn waitWindows(self: &ChildProcess) -> %Term {
if (self.term) |term| {
self.cleanupStreams();
return term;
}
%return self.waitUnwrappedWindows();
return ??self.term;
}
fn waitPosix(self: &ChildProcess) -> %Term {
block_SIGCHLD();
defer restore_SIGCHLD();
@ -153,6 +205,23 @@ pub const ChildProcess = struct {
self.allocator.destroy(self);
}
fn waitUnwrappedWindows(self: &ChildProcess) -> %void {
const result = os.windowsWaitSingle(self.handle, windows.INFINITE);
self.term = (%Term)({
var exit_code: windows.DWORD = undefined;
if (!windows.GetExitCodeProcess(self.handle, &exit_code)) {
Term.Unknown{0}
} else {
Term.Exited {@bitCast(i32, exit_code)}
}
});
os.windowsClose(self.handle);
self.cleanupStreams();
return result;
}
fn waitUnwrapped(self: &ChildProcess) {
var status: i32 = undefined;
while (true) {
@ -262,16 +331,21 @@ pub const ChildProcess = struct {
} else {
null
};
%defer if (stdin_ptr) |ptr| self.allocator.destroy(ptr);
const stdout_ptr = if (self.stdout_behavior == StdIo.Pipe) {
%return self.allocator.create(io.InStream)
} else {
null
};
%defer if (stdout_ptr) |ptr| self.allocator.destroy(ptr);
const stderr_ptr = if (self.stderr_behavior == StdIo.Pipe) {
%return self.allocator.create(io.InStream)
} else {
null
};
%defer if (stderr_ptr) |ptr| self.allocator.destroy(ptr);
block_SIGCHLD();
const pid_result = posix.fork();
@ -355,6 +429,195 @@ pub const ChildProcess = struct {
if (self.stderr_behavior == StdIo.Pipe) { os.posixClose(stderr_pipe[1]); }
}
fn spawnWindows(self: &ChildProcess) -> %void {
var saAttr: windows.SECURITY_ATTRIBUTES = undefined;
saAttr.nLength = @sizeOf(windows.SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = true;
saAttr.lpSecurityDescriptor = null;
const any_ignore = (self.stdin_behavior == StdIo.Ignore or
self.stdout_behavior == StdIo.Ignore or
self.stderr_behavior == StdIo.Ignore);
const nul_handle = if (any_ignore) {
%return os.windowsOpen("NUL", windows.GENERIC_READ, windows.FILE_SHARE_READ,
windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL, null)
} else {
undefined
};
defer { if (any_ignore) os.windowsClose(nul_handle); };
if (any_ignore) {
%return windowsSetHandleInfo(nul_handle, windows.HANDLE_FLAG_INHERIT, 0);
}
var g_hChildStd_IN_Rd: ?windows.HANDLE = null;
var g_hChildStd_IN_Wr: ?windows.HANDLE = null;
switch (self.stdin_behavior) {
StdIo.Pipe => {
%return windowsMakePipeIn(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr);
},
StdIo.Ignore => {
g_hChildStd_IN_Rd = nul_handle;
},
StdIo.Inherit => {
g_hChildStd_IN_Rd = windows.GetStdHandle(windows.STD_INPUT_HANDLE);
},
StdIo.Close => {
g_hChildStd_IN_Rd = null;
},
}
%defer if (self.stdin_behavior == StdIo.Pipe) { windowsDestroyPipe(g_hChildStd_IN_Rd, g_hChildStd_IN_Wr); };
var g_hChildStd_OUT_Rd: ?windows.HANDLE = null;
var g_hChildStd_OUT_Wr: ?windows.HANDLE = null;
switch (self.stdout_behavior) {
StdIo.Pipe => {
%return windowsMakePipeOut(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr);
},
StdIo.Ignore => {
g_hChildStd_OUT_Wr = nul_handle;
},
StdIo.Inherit => {
g_hChildStd_OUT_Wr = windows.GetStdHandle(windows.STD_OUTPUT_HANDLE);
},
StdIo.Close => {
g_hChildStd_OUT_Wr = null;
},
}
%defer if (self.stdin_behavior == StdIo.Pipe) { windowsDestroyPipe(g_hChildStd_OUT_Rd, g_hChildStd_OUT_Wr); };
var g_hChildStd_ERR_Rd: ?windows.HANDLE = null;
var g_hChildStd_ERR_Wr: ?windows.HANDLE = null;
switch (self.stderr_behavior) {
StdIo.Pipe => {
%return windowsMakePipeOut(&g_hChildStd_ERR_Rd, &g_hChildStd_ERR_Wr, &saAttr);
},
StdIo.Ignore => {
g_hChildStd_ERR_Wr = nul_handle;
},
StdIo.Inherit => {
g_hChildStd_ERR_Wr = windows.GetStdHandle(windows.STD_ERROR_HANDLE);
},
StdIo.Close => {
g_hChildStd_ERR_Wr = null;
},
}
%defer if (self.stdin_behavior == StdIo.Pipe) { windowsDestroyPipe(g_hChildStd_ERR_Rd, g_hChildStd_ERR_Wr); };
const stdin_ptr = if (self.stdin_behavior == StdIo.Pipe) {
%return self.allocator.create(io.OutStream)
} else {
null
};
%defer if (stdin_ptr) |ptr| self.allocator.destroy(ptr);
const stdout_ptr = if (self.stdout_behavior == StdIo.Pipe) {
%return self.allocator.create(io.InStream)
} else {
null
};
%defer if (stdout_ptr) |ptr| self.allocator.destroy(ptr);
const stderr_ptr = if (self.stderr_behavior == StdIo.Pipe) {
%return self.allocator.create(io.InStream)
} else {
null
};
%defer if (stderr_ptr) |ptr| self.allocator.destroy(ptr);
const cmd_line = %return windowsCreateCommandLine(self.allocator, self.argv);
defer self.allocator.free(cmd_line);
var siStartInfo = windows.STARTUPINFOA {
.cb = @sizeOf(windows.STARTUPINFOA),
.hStdError = g_hChildStd_ERR_Wr,
.hStdOutput = g_hChildStd_OUT_Wr,
.hStdInput = g_hChildStd_IN_Rd,
.dwFlags = windows.STARTF_USESTDHANDLES,
.lpReserved = null,
.lpDesktop = null,
.lpTitle = null,
.dwX = 0,
.dwY = 0,
.dwXSize = 0,
.dwYSize = 0,
.dwXCountChars = 0,
.dwYCountChars = 0,
.dwFillAttribute = 0,
.wShowWindow = 0,
.cbReserved2 = 0,
.lpReserved2 = null,
};
var piProcInfo: windows.PROCESS_INFORMATION = undefined;
const app_name = %return cstr.addNullByte(self.allocator, self.argv[0]);
defer self.allocator.free(app_name);
const cwd_slice = if (self.cwd) |cwd| {
%return cstr.addNullByte(self.allocator, cwd)
} else {
null
};
defer if (cwd_slice) |cwd| self.allocator.free(cwd);
const cwd_ptr = if (cwd_slice) |cwd| cwd.ptr else null;
const maybe_envp_buf = if (self.env_map) |env_map| {
%return os.createNullDelimitedEnvMap(self.allocator, env_map)
} else {
null
};
defer if (maybe_envp_buf) |envp_buf| self.allocator.free(envp_buf);
const envp_ptr = if (maybe_envp_buf) |envp_buf| envp_buf.ptr else null;
if (!windows.CreateProcessA(app_name.ptr, cmd_line.ptr, null, null, true, 0,
@ptrCast(?&c_void, envp_ptr),
cwd_ptr, &siStartInfo, &piProcInfo))
{
const err = windows.GetLastError();
return switch (err) {
windows.ERROR.FILE_NOT_FOUND => error.FileNotFound,
else => error.Unexpected,
};
}
os.windowsClose(piProcInfo.hThread);
if (stdin_ptr) |outstream| {
*outstream = io.OutStream {
.fd = {},
.handle = g_hChildStd_IN_Wr,
.handle_id = undefined,
.buffer = undefined,
.index = 0,
};
}
if (stdout_ptr) |instream| {
*instream = io.InStream {
.fd = {},
.handle = g_hChildStd_OUT_Rd,
.handle_id = undefined,
};
}
if (stderr_ptr) |instream| {
*instream = io.InStream {
.fd = {},
.handle = g_hChildStd_ERR_Rd,
.handle_id = undefined,
};
}
self.handle = piProcInfo.hProcess;
self.term = null;
self.stdin = stdin_ptr;
self.stdout = stdout_ptr;
self.stderr = stderr_ptr;
if (self.stdin_behavior == StdIo.Pipe) { os.windowsClose(??g_hChildStd_IN_Rd); }
if (self.stderr_behavior == StdIo.Pipe) { os.windowsClose(??g_hChildStd_ERR_Wr); }
if (self.stdout_behavior == StdIo.Pipe) { os.windowsClose(??g_hChildStd_OUT_Wr); }
}
fn setUpChildIo(stdio: StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) -> %void {
switch (stdio) {
StdIo.Pipe => %return os.posixDup2(pipe_fd, std_fileno),
@ -365,6 +628,84 @@ pub const ChildProcess = struct {
}
};
/// Caller must dealloc.
/// Guarantees a null byte at result[result.len].
fn windowsCreateCommandLine(allocator: &Allocator, argv: []const []const u8) -> %[]u8 {
var buf = %return Buffer.initSize(allocator, 0);
defer buf.deinit();
for (argv) |arg, arg_i| {
if (arg_i != 0)
%return buf.appendByte(' ');
if (mem.indexOfAny(u8, arg, " \t\n\"") == null) {
%return buf.append(arg);
continue;
}
%return buf.appendByte('"');
var backslash_count: usize = 0;
for (arg) |byte| {
switch (byte) {
'\\' => backslash_count += 1,
'"' => {
%return buf.appendByteNTimes('\\', backslash_count * 2 + 1);
%return buf.appendByte('"');
backslash_count = 0;
},
else => {
%return buf.appendByteNTimes('\\', backslash_count);
%return buf.appendByte(byte);
backslash_count = 0;
},
}
}
%return buf.appendByteNTimes('\\', backslash_count * 2);
%return buf.appendByte('"');
}
return buf.toOwnedSlice();
}
fn windowsDestroyPipe(rd: ?windows.HANDLE, wr: ?windows.HANDLE) {
if (rd) |h| os.windowsClose(h);
if (wr) |h| os.windowsClose(h);
}
fn windowsMakePipe(rd: &windows.HANDLE, wr: &windows.HANDLE, sattr: &windows.SECURITY_ATTRIBUTES) -> %void {
if (!windows.CreatePipe(rd, wr, sattr, 0)) {
const err = windows.GetLastError();
return switch (err) {
else => error.Unexpected,
};
}
}
fn windowsSetHandleInfo(h: windows.HANDLE, mask: windows.DWORD, flags: windows.DWORD) -> %void {
if (!windows.SetHandleInformation(h, mask, flags)) {
const err = windows.GetLastError();
return switch (err) {
else => error.Unexpected,
};
}
}
fn windowsMakePipeIn(rd: &?windows.HANDLE, wr: &?windows.HANDLE, sattr: &windows.SECURITY_ATTRIBUTES) -> %void {
var rd_h: windows.HANDLE = undefined;
var wr_h: windows.HANDLE = undefined;
%return windowsMakePipe(&rd_h, &wr_h, sattr);
%return windowsSetHandleInfo(wr_h, windows.HANDLE_FLAG_INHERIT, 0);
*rd = rd_h;
*wr = wr_h;
}
fn windowsMakePipeOut(rd: &?windows.HANDLE, wr: &?windows.HANDLE, sattr: &windows.SECURITY_ATTRIBUTES) -> %void {
var rd_h: windows.HANDLE = undefined;
var wr_h: windows.HANDLE = undefined;
%return windowsMakePipe(&rd_h, &wr_h, sattr);
%return windowsSetHandleInfo(rd_h, windows.HANDLE_FLAG_INHERIT, 0);
*rd = rd_h;
*wr = wr_h;
}
fn makePipe() -> %[2]i32 {
var fds: [2]i32 = undefined;
const err = posix.getErrno(posix.pipe(&fds));

View file

@ -382,6 +382,37 @@ pub fn posixDup2(old_fd: i32, new_fd: i32) -> %void {
}
}
pub fn createNullDelimitedEnvMap(allocator: &Allocator, env_map: &const BufMap) -> %[]?&u8 {
const envp_count = env_map.count();
const envp_buf = %return allocator.alloc(?&u8, envp_count + 1);
mem.set(?&u8, envp_buf, null);
%defer freeNullDelimitedEnvMap(allocator, envp_buf);
{
var it = env_map.iterator();
var i: usize = 0;
while (it.next()) |pair| : (i += 1) {
const env_buf = %return allocator.alloc(u8, pair.key.len + pair.value.len + 2);
@memcpy(&env_buf[0], pair.key.ptr, pair.key.len);
env_buf[pair.key.len] = '=';
@memcpy(&env_buf[pair.key.len + 1], pair.value.ptr, pair.value.len);
env_buf[env_buf.len - 1] = 0;
envp_buf[i] = env_buf.ptr;
}
assert(i == envp_count);
}
assert(envp_buf[envp_count] == null);
return envp_buf;
}
pub fn freeNullDelimitedEnvMap(allocator: &Allocator, envp_buf: []?&u8) {
for (envp_buf) |env| {
const env_buf = if (env) |ptr| cstr.toSlice(ptr) else break;
allocator.free(env_buf);
}
allocator.free(envp_buf);
}
/// This function must allocate memory to add a null terminating bytes on path and each arg.
/// It must also convert to KEY=VALUE\0 format for environment variables, and include null
/// pointers after the args and after the environment variables.
@ -408,31 +439,8 @@ pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap,
}
argv_buf[argv.len] = null;
const envp_count = env_map.count();
const envp_buf = %return allocator.alloc(?&u8, envp_count + 1);
mem.set(?&u8, envp_buf, null);
defer {
for (envp_buf) |env| {
const env_buf = if (env) |ptr| cstr.toSlice(ptr) else break;
allocator.free(env_buf);
}
allocator.free(envp_buf);
}
{
var it = env_map.iterator();
var i: usize = 0;
while (it.next()) |pair| : (i += 1) {
const env_buf = %return allocator.alloc(u8, pair.key.len + pair.value.len + 2);
@memcpy(&env_buf[0], pair.key.ptr, pair.key.len);
env_buf[pair.key.len] = '=';
@memcpy(&env_buf[pair.key.len + 1], pair.value.ptr, pair.value.len);
env_buf[env_buf.len - 1] = 0;
envp_buf[i] = env_buf.ptr;
}
assert(i == envp_count);
}
envp_buf[envp_count] = null;
const envp_buf = %return createNullDelimitedEnvMap(allocator, env_map);
defer freeNullDelimitedEnvMap(allocator, envp_buf);
const exe_path = argv[0];
if (mem.indexOfScalar(u8, exe_path, '/') != null) {
@ -1367,6 +1375,22 @@ fn testWindowsCmdLine(input_cmd_line: &const u8, expected_args: []const []const
assert(it.next(&debug.global_allocator) == null);
}
error WaitAbandoned;
error WaitTimeOut;
pub fn windowsWaitSingle(handle: windows.HANDLE, milliseconds: windows.DWORD) -> %void {
const result = windows.WaitForSingleObject(handle, milliseconds);
return switch (result) {
windows.WAIT_ABANDONED => error.WaitAbandoned,
windows.WAIT_OBJECT_0 => {},
windows.WAIT_TIMEOUT => error.WaitTimeOut,
windows.WAIT_FAILED => switch (windows.GetLastError()) {
else => error.Unexpected,
},
else => error.Unexpected,
};
}
test "std.os" {
_ = @import("child_process.zig");
_ = @import("darwin_errno.zig");

View file

@ -14,6 +14,14 @@ pub extern "kernel32" stdcallcc fn CreateFileA(lpFileName: LPCSTR, dwDesiredAcce
dwShareMode: DWORD, lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES, dwCreationDisposition: DWORD,
dwFlagsAndAttributes: DWORD, hTemplateFile: ?HANDLE) -> HANDLE;
pub extern "kernel32" stdcallcc fn CreatePipe(hReadPipe: &HANDLE, hWritePipe: &HANDLE,
lpPipeAttributes: &SECURITY_ATTRIBUTES, nSize: DWORD) -> BOOL;
pub extern "kernel32" stdcallcc fn CreateProcessA(lpApplicationName: ?LPCSTR, lpCommandLine: LPSTR,
lpProcessAttributes: ?&SECURITY_ATTRIBUTES, lpThreadAttributes: ?&SECURITY_ATTRIBUTES, bInheritHandles: BOOL,
dwCreationFlags: DWORD, lpEnvironment: ?LPVOID, lpCurrentDirectory: ?LPCSTR, lpStartupInfo: &STARTUPINFOA,
lpProcessInformation: &PROCESS_INFORMATION) -> BOOL;
pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) -> bool;
pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) -> noreturn;
@ -24,11 +32,10 @@ pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out
pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: WORD, lpBuffer: ?LPSTR) -> DWORD;
/// Retrieves the calling thread's last-error code value. The last-error code is maintained on a per-thread basis.
/// Multiple threads do not overwrite each other's last-error code.
pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCode: &DWORD) -> BOOL;
pub extern "kernel32" stdcallcc fn GetLastError() -> DWORD;
/// Retrieves file information for the specified file.
pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx(in_hFile: HANDLE,
in_FileInformationClass: FILE_INFO_BY_HANDLE_CLASS, out_lpFileInformation: &c_void,
in_dwBufferSize: DWORD) -> bool;
@ -36,26 +43,29 @@ pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx(in_hFile: HANDLE
pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(hFile: HANDLE, lpszFilePath: LPSTR,
cchFilePath: DWORD, dwFlags: DWORD) -> DWORD;
/// Retrieves a handle to the specified standard device (standard input, standard output, or standard error).
pub extern "kernel32" stdcallcc fn GetProcessHeap() -> HANDLE;
pub extern "kernel32" stdcallcc fn GetStdHandle(in_nStdHandle: DWORD) -> ?HANDLE;
pub extern "kernel32" stdcallcc fn ReadFile(in_hFile: HANDLE, out_lpBuffer: LPVOID,
in_nNumberOfBytesToRead: DWORD, out_lpNumberOfBytesRead: &DWORD,
in_out_lpOverlapped: ?&OVERLAPPED) -> BOOL;
/// Writes data to the specified file or input/output (I/O) device.
/// This function is designed for both synchronous and asynchronous operation. For a similar function designed solely for asynchronous operation, see WriteFileEx.
pub extern "kernel32" stdcallcc fn WriteFile(in_hFile: HANDLE, in_lpBuffer: &const c_void,
in_nNumberOfBytesToWrite: DWORD, out_lpNumberOfBytesWritten: ?&DWORD,
in_out_lpOverlapped: ?&OVERLAPPED) -> BOOL;
pub extern "kernel32" stdcallcc fn Sleep(dwMilliseconds: DWORD);
pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) -> LPVOID;
pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID) -> BOOL;
pub extern "kernel32" stdcallcc fn GetProcessHeap() -> HANDLE;
pub extern "kernel32" stdcallcc fn ReadFile(in_hFile: HANDLE, out_lpBuffer: LPVOID,
in_nNumberOfBytesToRead: DWORD, out_lpNumberOfBytesRead: &DWORD,
in_out_lpOverlapped: ?&OVERLAPPED) -> BOOL;
pub extern "kernel32" stdcallcc fn SetHandleInformation(hObject: HANDLE, dwMask: DWORD, dwFlags: DWORD) -> BOOL;
pub extern "kernel32" stdcallcc fn Sleep(dwMilliseconds: DWORD);
pub extern "kernel32" stdcallcc fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) -> BOOL;
pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) -> DWORD;
pub extern "kernel32" stdcallcc fn WriteFile(in_hFile: HANDLE, in_lpBuffer: &const c_void,
in_nNumberOfBytesToWrite: DWORD, out_lpNumberOfBytesWritten: ?&DWORD,
in_out_lpOverlapped: ?&OVERLAPPED) -> BOOL;
pub extern "user32" stdcallcc fn MessageBoxA(hWnd: ?HANDLE, lpText: ?LPCTSTR, lpCaption: ?LPCTSTR, uType: UINT) -> c_int;
@ -88,7 +98,7 @@ pub const INT = c_int;
pub const ULONG_PTR = usize;
pub const WCHAR = u16;
pub const LPCVOID = &const c_void;
pub const LPBYTE = &BYTE;
/// The standard input device. Initially, this is the console input buffer, CONIN$.
pub const STD_INPUT_HANDLE = @maxValue(DWORD) - 10 + 1;
@ -158,7 +168,7 @@ pub const VOLUME_NAME_NT = 0x2;
pub const SECURITY_ATTRIBUTES = extern struct {
nLength: DWORD,
lpSecurityDescriptor: LPVOID,
lpSecurityDescriptor: ?LPVOID,
bInheritHandle: BOOL,
};
pub const PSECURITY_ATTRIBUTES = &SECURITY_ATTRIBUTES;
@ -189,3 +199,56 @@ pub const FILE_ATTRIBUTE_OFFLINE = 0x1000;
pub const FILE_ATTRIBUTE_READONLY = 0x1;
pub const FILE_ATTRIBUTE_SYSTEM = 0x4;
pub const FILE_ATTRIBUTE_TEMPORARY = 0x100;
pub const PROCESS_INFORMATION = extern struct {
hProcess: HANDLE,
hThread: HANDLE,
dwProcessId: DWORD,
dwThreadId: DWORD,
};
pub const STARTUPINFOA = extern struct {
cb: DWORD,
lpReserved: ?LPSTR,
lpDesktop: ?LPSTR,
lpTitle: ?LPSTR,
dwX: DWORD,
dwY: DWORD,
dwXSize: DWORD,
dwYSize: DWORD,
dwXCountChars: DWORD,
dwYCountChars: DWORD,
dwFillAttribute: DWORD,
dwFlags: DWORD,
wShowWindow: WORD,
cbReserved2: WORD,
lpReserved2: ?LPBYTE,
hStdInput: ?HANDLE,
hStdOutput: ?HANDLE,
hStdError: ?HANDLE,
};
pub const STARTF_FORCEONFEEDBACK = 0x00000040;
pub const STARTF_FORCEOFFFEEDBACK = 0x00000080;
pub const STARTF_PREVENTPINNING = 0x00002000;
pub const STARTF_RUNFULLSCREEN = 0x00000020;
pub const STARTF_TITLEISAPPID = 0x00001000;
pub const STARTF_TITLEISLINKNAME = 0x00000800;
pub const STARTF_UNTRUSTEDSOURCE = 0x00008000;
pub const STARTF_USECOUNTCHARS = 0x00000008;
pub const STARTF_USEFILLATTRIBUTE = 0x00000010;
pub const STARTF_USEHOTKEY = 0x00000200;
pub const STARTF_USEPOSITION = 0x00000004;
pub const STARTF_USESHOWWINDOW = 0x00000001;
pub const STARTF_USESIZE = 0x00000002;
pub const STARTF_USESTDHANDLES = 0x00000100;
pub const INFINITE = 4294967295;
pub const WAIT_ABANDONED = 0x00000080;
pub const WAIT_OBJECT_0 = 0x00000000;
pub const WAIT_TIMEOUT = 0x00000102;
pub const WAIT_FAILED = 0xFFFFFFFF;
pub const HANDLE_FLAG_INHERIT = 0x00000001;
pub const HANDLE_FLAG_PROTECT_FROM_CLOSE = 0x00000002;