aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgingerBill <bill@gingerbill.org>2024-07-16 11:56:31 +0100
committergingerBill <bill@gingerbill.org>2024-07-16 11:56:31 +0100
commit169fc4d3be48d16d9d6fdb41a8b7312ce76d0ecd (patch)
treef3a63ba3259b797b69dfd84f4fb63bddbba36605
parentaffe8f7144203c5e4c09c3d5369826a7a5fc144e (diff)
General clean up of the os2/process_windows.odin code
-rw-r--r--core/os/os2/process.odin8
-rw-r--r--core/os/os2/process_windows.odin760
-rw-r--r--core/sys/windows/ntdll.odin106
-rw-r--r--core/sys/windows/types.odin6
4 files changed, 383 insertions, 497 deletions
diff --git a/core/os/os2/process.odin b/core/os/os2/process.odin
index d407ffb18..6dfb8d9d9 100644
--- a/core/os/os2/process.odin
+++ b/core/os/os2/process.odin
@@ -1,9 +1,7 @@
package os2
-import "core:sync"
-import "core:time"
import "base:runtime"
-import "core:strings"
+import "core:time"
/*
In procedures that explicitly state this as one of the allowed values,
@@ -20,9 +18,9 @@ args := get_args()
get_args :: proc() -> []string {
result := make([]string, len(runtime.args__), heap_allocator())
for rt_arg, i in runtime.args__ {
- result[i] = cast(string) rt_arg
+ result[i] = string(rt_arg)
}
- return result[:]
+ return result
}
/*
diff --git a/core/os/os2/process_windows.odin b/core/os/os2/process_windows.odin
index e16cedd9f..2ffee66e8 100644
--- a/core/os/os2/process_windows.odin
+++ b/core/os/os2/process_windows.odin
@@ -1,86 +1,104 @@
//+build windows
+//+private file
package os2
-import "core:sys/windows"
-import "core:strings"
-import "core:time"
-
import "base:runtime"
-_Process_Handle :: windows.HANDLE
+import "core:strings"
+import win32 "core:sys/windows"
+import "core:time"
+@(private="package")
_exit :: proc "contextless" (code: int) -> ! {
- windows.ExitProcess(u32(code))
+ win32.ExitProcess(u32(code))
}
+@(private="package")
_get_uid :: proc() -> int {
return -1
}
+@(private="package")
_get_euid :: proc() -> int {
return -1
}
+@(private="package")
_get_gid :: proc() -> int {
return -1
}
+@(private="package")
_get_egid :: proc() -> int {
return -1
}
+@(private="package")
_get_pid :: proc() -> int {
- return cast(int) windows.GetCurrentProcessId()
+ return int(win32.GetCurrentProcessId())
}
+@(private="package")
_get_ppid :: proc() -> int {
- our_pid := windows.GetCurrentProcessId()
- snap := windows.CreateToolhelp32Snapshot(windows.TH32CS_SNAPPROCESS, 0)
- if snap == windows.INVALID_HANDLE_VALUE {
+ our_pid := win32.GetCurrentProcessId()
+ snap := win32.CreateToolhelp32Snapshot(win32.TH32CS_SNAPPROCESS, 0)
+ if snap == win32.INVALID_HANDLE_VALUE {
return -1
}
- defer windows.CloseHandle(snap)
- entry := windows.PROCESSENTRY32W { dwSize = size_of(windows.PROCESSENTRY32W) }
- status := windows.Process32FirstW(snap, &entry)
- for status {
+ defer win32.CloseHandle(snap)
+ entry := win32.PROCESSENTRY32W { dwSize = size_of(win32.PROCESSENTRY32W) }
+ for status := win32.Process32FirstW(snap, &entry); status; /**/ {
if entry.th32ProcessID == our_pid {
- return cast(int) entry.th32ParentProcessID
+ return int(entry.th32ParentProcessID)
}
- status = windows.Process32NextW(snap, &entry)
+ status = win32.Process32NextW(snap, &entry)
}
return -1
}
+@(private="package")
_process_list :: proc(allocator: runtime.Allocator) -> ([]int, Error) {
pid_list := make([dynamic]int, allocator)
- snap := windows.CreateToolhelp32Snapshot(windows.TH32CS_SNAPPROCESS, 0)
- if snap == windows.INVALID_HANDLE_VALUE {
+ snap := win32.CreateToolhelp32Snapshot(win32.TH32CS_SNAPPROCESS, 0)
+ if snap == win32.INVALID_HANDLE_VALUE {
return pid_list[:], _get_platform_error()
}
- entry := windows.PROCESSENTRY32W { dwSize = size_of(windows.PROCESSENTRY32W) }
- status := windows.Process32FirstW(snap, &entry)
+ entry := win32.PROCESSENTRY32W { dwSize = size_of(win32.PROCESSENTRY32W) }
+ status := win32.Process32FirstW(snap, &entry)
for status {
- append(&pid_list, cast(int) entry.th32ProcessID)
- status = windows.Process32NextW(snap, &entry)
+ append(&pid_list, int(entry.th32ProcessID))
+ status = win32.Process32NextW(snap, &entry)
}
return pid_list[:], nil
}
+@(require_results)
+read_memory_as_struct :: proc(h: win32.HANDLE, addr: rawptr, dest: ^$T) -> (bytes_read: uint, err: Error) {
+ if !win32.ReadProcessMemory(h, addr, dest, size_of(T), &bytes_read) {
+ err = _get_platform_error()
+ }
+ return
+}
+@(require_results)
+read_memory_as_slice :: proc(h: win32.HANDLE, addr: rawptr, dest: []$T) -> (bytes_read: uint, err: Error) {
+ if !win32.ReadProcessMemory(h, addr, raw_data(dest), len(dest)*size_of(T), &bytes_read) {
+ err = _get_platform_error()
+ }
+ return
+}
+
+@(private="package")
_process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) {
info.pid = pid
defer if err != nil {
free_process_info(info, allocator)
}
- need_snapprocess := \
- .PPid in selection ||
- .Priority in selection
- need_snapmodule := \
- .Executable_Path in selection
- need_peb := \
- .Command_Line in selection ||
- .Environment in selection ||
- .Working_Dir in selection
+
+ need_snapprocess := selection >= {.PPid, .Priority}
+ need_snapmodule := .Executable_Path in selection
+ need_peb := selection >= {.Command_Line, .Environment, .Working_Dir}
need_process_handle := need_peb || .Username in selection
+
// Data obtained from process snapshots
if need_snapprocess {
entry, entry_err := _process_entry_by_pid(info.pid)
@@ -89,55 +107,39 @@ _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator
return
}
if .PPid in selection {
- info.fields |= {.PPid}
+ info.fields += {.PPid}
info.ppid = int(entry.th32ParentProcessID)
}
if .Priority in selection {
- info.fields |= {.Priority}
+ info.fields += {.Priority}
info.priority = int(entry.pcPriClassBase)
}
}
if need_snapmodule {
- exe_path, exe_path_err := _process_exe_by_pid(pid, allocator)
- if exe_path_err != nil {
- err = exe_path_err
- return
- }
- info.fields |= {.Executable_Path}
+ exe_path := _process_exe_by_pid(pid, allocator) or_return
+ info.fields += {.Executable_Path}
info.executable_path = exe_path
}
- ph := windows.INVALID_HANDLE_VALUE
+ ph := win32.INVALID_HANDLE_VALUE
if need_process_handle {
- ph = windows.OpenProcess(
- windows.PROCESS_QUERY_LIMITED_INFORMATION | windows.PROCESS_VM_READ,
+ ph = win32.OpenProcess(
+ win32.PROCESS_QUERY_LIMITED_INFORMATION | win32.PROCESS_VM_READ,
false,
u32(pid),
)
- if ph == windows.INVALID_HANDLE_VALUE {
+ if ph == win32.INVALID_HANDLE_VALUE {
err = _get_platform_error()
return
}
}
- defer if ph != windows.INVALID_HANDLE_VALUE {
- windows.CloseHandle(ph)
+ defer if ph != win32.INVALID_HANDLE_VALUE {
+ win32.CloseHandle(ph)
}
+
if need_peb {
- // TODO(flysand): This was not tested with WOW64 or 32-bit processes,
- // might need to be revised later when issues occur.
- ntdll_lib := windows.LoadLibraryW(windows.L("ntdll.dll"))
- if ntdll_lib == nil {
- err = _get_platform_error()
- return
- }
- defer windows.FreeLibrary(ntdll_lib)
- NtQueryInformationProcess := cast(NtQueryInformationProcess_T) windows.GetProcAddress(ntdll_lib, "NtQueryInformationProcess")
- if NtQueryInformationProcess == nil {
- err = _get_platform_error()
- return
- }
- process_info_size: u32 = ---
- process_info: PROCESS_BASIC_INFORMATION = ---
- status := NtQueryInformationProcess(ph, .ProcessBasicInformation, &process_info, size_of(process_info), &process_info_size)
+ process_info_size: u32
+ process_info: win32.PROCESS_BASIC_INFORMATION
+ status := win32.NtQueryInformationProcess(ph, .ProcessBasicInformation, &process_info, size_of(process_info), &process_info_size)
if status != 0 {
// TODO(flysand): There's probably a mismatch between NTSTATUS and
// windows userland error codes, I haven't checked.
@@ -149,45 +151,25 @@ _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator
err = General_Error.Unsupported
return
}
- process_peb: PEB = ---
- bytes_read: uint = ---
- read_struct :: proc(h: windows.HANDLE, addr: rawptr, dest: ^$T, br: ^uint) -> windows.BOOL {
- return windows.ReadProcessMemory(h, addr, dest, size_of(T), br)
- }
- read_slice :: proc(h: windows.HANDLE, addr: rawptr, dest: []$T, br: ^uint) -> windows.BOOL {
- return windows.ReadProcessMemory(h, addr, raw_data(dest), len(dest)*size_of(T), br)
- }
- if !read_struct(ph, process_info.PebBaseAddress, &process_peb, &bytes_read) {
- err = _get_platform_error()
- return
- }
- process_params: RTL_USER_PROCESS_PARAMETERS = ---
- if !read_struct(ph, process_peb.ProcessParameters, &process_params, &bytes_read) {
- err = _get_platform_error()
- return
- }
+ process_peb: win32.PEB
+
+ _ = read_memory_as_struct(ph, process_info.PebBaseAddress, &process_peb) or_return
+
+ process_params: win32.RTL_USER_PROCESS_PARAMETERS
+ _ = read_memory_as_struct(ph, process_peb.ProcessParameters, &process_params) or_return
+
if .Command_Line in selection || .Command_Args in selection {
TEMP_ALLOCATOR_GUARD()
cmdline_w := make([]u16, process_params.CommandLine.Length, temp_allocator())
- if !read_slice(ph, process_params.CommandLine.Buffer, cmdline_w, &bytes_read) {
- err = _get_platform_error()
- return
- }
+ _ = read_memory_as_slice(ph, process_params.CommandLine.Buffer, cmdline_w) or_return
+
if .Command_Line in selection {
- cmdline, cmdline_err := windows.utf16_to_utf8(cmdline_w, allocator)
- if cmdline_err != nil {
- err = cmdline_err
- return
- }
- info.fields |= {.Command_Line}
+ cmdline := win32.utf16_to_utf8(cmdline_w, allocator) or_return
+ info.fields += {.Command_Line}
info.command_line = cmdline
}
if .Command_Args in selection {
- args, args_err := _parse_command_line(raw_data(cmdline_w), allocator)
- if args_err != nil {
- err = args_err
- return
- }
+ args := _parse_command_line(raw_data(cmdline_w), allocator) or_return
info.fields += {.Command_Args}
info.command_args = args
}
@@ -196,62 +178,44 @@ _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator
TEMP_ALLOCATOR_GUARD()
env_len := process_params.EnvironmentSize / 2
envs_w := make([]u16, env_len, temp_allocator())
- if !read_slice(ph, process_params.Environment, envs_w, &bytes_read) {
- err = _get_platform_error()
- return
- }
- envs, envs_err := _parse_environment_block(raw_data(envs_w), allocator)
- if envs_err != nil {
- err = envs_err
- return
- }
- info.fields |= {.Environment}
+ _ = read_memory_as_slice(ph, process_params.Environment, envs_w) or_return
+
+ envs := _parse_environment_block(raw_data(envs_w), allocator) or_return
+
+ info.fields += {.Environment}
info.environment = envs
}
if .Working_Dir in selection {
TEMP_ALLOCATOR_GUARD()
cwd_w := make([]u16, process_params.CurrentDirectoryPath.Length, temp_allocator())
- if !read_slice(ph, process_params.CurrentDirectoryPath.Buffer, cwd_w, &bytes_read) {
- err = _get_platform_error()
- return
- }
- cwd, cwd_err := windows.utf16_to_utf8(cwd_w, allocator)
- if cwd_err != nil {
- err = cwd_err
- return
- }
- info.fields |= {.Working_Dir}
+ _ = read_memory_as_slice(ph, process_params.CurrentDirectoryPath.Buffer, cwd_w) or_return
+
+ cwd := win32.utf16_to_utf8(cwd_w, allocator) or_return
+
+ info.fields += {.Working_Dir}
info.working_dir = cwd
}
}
+
if .Username in selection {
- username, username_err := _get_process_user(ph, allocator)
- if username_err != nil {
- err = username_err
- return
- }
- info.fields |= {.Username}
+ username := _get_process_user(ph, allocator) or_return
+ info.fields += {.Username}
info.username = username
}
err = nil
return
}
+@(private="package")
_process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) {
pid := process.pid
info.pid = pid
defer if err != nil {
free_process_info(info, allocator)
}
- need_snapprocess := \
- .PPid in selection ||
- .Priority in selection
- need_snapmodule := \
- .Executable_Path in selection
- need_peb := \
- .Command_Line in selection ||
- .Environment in selection ||
- .Working_Dir in selection
+ need_snapprocess := selection >= {.PPid, .Priority}
+ need_snapmodule := .Executable_Path in selection
+ need_peb := selection >= {.Command_Line, .Environment, .Working_Dir}
// Data obtained from process snapshots
if need_snapprocess {
entry, entry_err := _process_entry_by_pid(info.pid)
@@ -260,39 +224,24 @@ _process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields
return
}
if .PPid in selection {
- info.fields |= {.PPid}
+ info.fields += {.PPid}
info.ppid = int(entry.th32ParentProcessID)
}
if .Priority in selection {
- info.fields |= {.Priority}
+ info.fields += {.Priority}
info.priority = int(entry.pcPriClassBase)
}
}
if need_snapmodule {
- exe_path, exe_path_err := _process_exe_by_pid(pid, allocator)
- if exe_path_err != nil {
- err = exe_path_err
- return
- }
- info.fields |= {.Executable_Path}
+ exe_path := _process_exe_by_pid(pid, allocator) or_return
+ info.fields += {.Executable_Path}
info.executable_path = exe_path
}
- ph := cast(windows.HANDLE) process.handle
+ ph := win32.HANDLE(process.handle)
if need_peb {
- ntdll_lib := windows.LoadLibraryW(windows.L("ntdll.dll"))
- if ntdll_lib == nil {
- err = _get_platform_error()
- return
- }
- defer windows.FreeLibrary(ntdll_lib)
- NtQueryInformationProcess := cast(NtQueryInformationProcess_T) windows.GetProcAddress(ntdll_lib, "NtQueryInformationProcess")
- if NtQueryInformationProcess == nil {
- err = _get_platform_error()
- return
- }
- process_info_size: u32 = ---
- process_info: PROCESS_BASIC_INFORMATION = ---
- status := NtQueryInformationProcess(ph, .ProcessBasicInformation, &process_info, size_of(process_info), &process_info_size)
+ process_info_size: u32
+ process_info: win32.PROCESS_BASIC_INFORMATION
+ status := win32.NtQueryInformationProcess(ph, .ProcessBasicInformation, &process_info, size_of(process_info), &process_info_size)
if status != 0 {
// TODO(flysand): There's probably a mismatch between NTSTATUS and
// windows userland error codes, I haven't checked.
@@ -304,96 +253,64 @@ _process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields
err = General_Error.Unsupported
return
}
- process_peb: PEB = ---
- bytes_read: uint = ---
- read_struct :: proc(h: windows.HANDLE, addr: rawptr, dest: ^$T, br: ^uint) -> windows.BOOL {
- return windows.ReadProcessMemory(h, addr, dest, size_of(T), br)
- }
- read_slice :: proc(h: windows.HANDLE, addr: rawptr, dest: []$T, br: ^uint) -> windows.BOOL {
- return windows.ReadProcessMemory(h, addr, raw_data(dest), len(dest)*size_of(T), br)
- }
- if !read_struct(ph, process_info.PebBaseAddress, &process_peb, &bytes_read) {
- err = _get_platform_error()
- return
- }
- process_params: RTL_USER_PROCESS_PARAMETERS = ---
- if !read_struct(ph, process_peb.ProcessParameters, &process_params, &bytes_read) {
- err = _get_platform_error()
- return
- }
+
+ process_peb: win32.PEB
+ _ = read_memory_as_struct(ph, process_info.PebBaseAddress, &process_peb) or_return
+
+ process_params: win32.RTL_USER_PROCESS_PARAMETERS
+ _ = read_memory_as_struct(ph, process_peb.ProcessParameters, &process_params) or_return
+
if .Command_Line in selection || .Command_Args in selection {
TEMP_ALLOCATOR_GUARD()
cmdline_w := make([]u16, process_params.CommandLine.Length, temp_allocator())
- if !read_slice(ph, process_params.CommandLine.Buffer, cmdline_w, &bytes_read) {
- err = _get_platform_error()
- return
- }
+ _ = read_memory_as_slice(ph, process_params.CommandLine.Buffer, cmdline_w) or_return
+
if .Command_Line in selection {
- cmdline, cmdline_err := windows.utf16_to_utf8(cmdline_w, allocator)
- if cmdline_err != nil {
- err = cmdline_err
- return
- }
- info.fields |= {.Command_Line}
+ cmdline := win32.utf16_to_utf8(cmdline_w, allocator) or_return
+
+ info.fields += {.Command_Line}
info.command_line = cmdline
}
if .Command_Args in selection {
- args, args_err := _parse_command_line(raw_data(cmdline_w), allocator)
- if args_err != nil {
- err = args_err
- return
- }
+ args := _parse_command_line(raw_data(cmdline_w), allocator) or_return
info.fields += {.Command_Args}
info.command_args = args
}
}
+
if .Environment in selection {
TEMP_ALLOCATOR_GUARD()
env_len := process_params.EnvironmentSize / 2
envs_w := make([]u16, env_len, temp_allocator())
- if !read_slice(ph, process_params.Environment, envs_w, &bytes_read) {
- err = _get_platform_error()
- return
- }
- envs, envs_err := _parse_environment_block(raw_data(envs_w), allocator)
- if envs_err != nil {
- err = envs_err
- return
- }
- info.fields |= {.Environment}
+ _ = read_memory_as_slice(ph, process_params.Environment, envs_w) or_return
+
+ envs := _parse_environment_block(raw_data(envs_w), allocator) or_return
+ info.fields += {.Environment}
info.environment = envs
}
+
if .Working_Dir in selection {
TEMP_ALLOCATOR_GUARD()
cwd_w := make([]u16, process_params.CurrentDirectoryPath.Length, temp_allocator())
- if !read_slice(ph, process_params.CurrentDirectoryPath.Buffer, cwd_w, &bytes_read) {
- err = _get_platform_error()
- return
- }
- cwd, cwd_err := windows.utf16_to_utf8(cwd_w, allocator)
- if cwd_err != nil {
- err = cwd_err
- return
- }
- info.fields |= {.Working_Dir}
+ _ = read_memory_as_slice(ph, process_params.CurrentDirectoryPath.Buffer, cwd_w) or_return
+
+ cwd := win32.utf16_to_utf8(cwd_w, allocator) or_return
+ info.fields += {.Working_Dir}
info.working_dir = cwd
}
}
if .Username in selection {
- username, username_err := _get_process_user(ph, allocator)
- if username_err != nil {
- err = username_err
- return
- }
- info.fields |= {.Username}
+ username := _get_process_user(ph, allocator) or_return
+ info.fields += {.Username}
info.username = username
}
err = nil
return
}
+@(private="package")
_current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) {
- info.pid = cast(int) windows.GetCurrentProcessId()
+ info.pid = get_pid()
defer if err != nil {
free_process_info(info, allocator)
}
@@ -415,53 +332,33 @@ _current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime
}
if .Executable_Path in selection {
exe_filename_w: [256]u16
- path_len := windows.GetModuleFileNameW(nil, raw_data(exe_filename_w[:]), len(exe_filename_w))
- exe_filename, exe_filename_err := windows.utf16_to_utf8(exe_filename_w[:path_len], allocator)
- if exe_filename_err != nil {
- err = exe_filename_err
- return
- }
+ path_len := win32.GetModuleFileNameW(nil, raw_data(exe_filename_w[:]), len(exe_filename_w))
+ exe_filename := win32.utf16_to_utf8(exe_filename_w[:path_len], allocator) or_return
info.fields += {.Executable_Path}
info.executable_path = exe_filename
}
if .Command_Line in selection || .Command_Args in selection {
- command_line_w := windows.GetCommandLineW()
+ command_line_w := win32.GetCommandLineW()
if .Command_Line in selection {
- command_line, command_line_err := windows.wstring_to_utf8(command_line_w, -1, allocator)
- if command_line_err != nil {
- err = command_line_err
- return
- }
+ command_line := win32.wstring_to_utf8(command_line_w, -1, allocator) or_return
info.fields += {.Command_Line}
info.command_line = command_line
}
if .Command_Args in selection {
- args, args_err := _parse_command_line(command_line_w, allocator)
- if args_err != nil {
- err = args_err
- return
- }
+ args := _parse_command_line(command_line_w, allocator) or_return
info.fields += {.Command_Args}
info.command_args = args
}
}
if .Environment in selection {
- env_block := windows.GetEnvironmentStringsW()
- envs, envs_err := _parse_environment_block(env_block, allocator)
- if envs_err != nil {
- err = envs_err
- return
- }
+ env_block := win32.GetEnvironmentStringsW()
+ envs := _parse_environment_block(env_block, allocator) or_return
info.fields += {.Environment}
info.environment = envs
}
if .Username in selection {
- process_handle := windows.GetCurrentProcess()
- username, username_err := _get_process_user(process_handle, allocator)
- if username_err != nil {
- err = username_err
- return
- }
+ process_handle := win32.GetCurrentProcess()
+ username := _get_process_user(process_handle, allocator) or_return
info.fields += {.Username}
info.username = username
}
@@ -474,74 +371,76 @@ _current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime
return
}
-_process_open :: proc(pid: int, flags: Process_Open_Flags) -> (Process, Error) {
+@(private="package")
+_process_open :: proc(pid: int, flags: Process_Open_Flags) -> (process: Process, err: Error) {
// Note(flysand): The handle will be used for querying information so we
// take the necessary permissions right away.
- dwDesiredAccess := windows.PROCESS_QUERY_LIMITED_INFORMATION | windows.SYNCHRONIZE
+ dwDesiredAccess := win32.PROCESS_QUERY_LIMITED_INFORMATION | win32.SYNCHRONIZE
if .Mem_Read in flags {
- dwDesiredAccess |= windows.PROCESS_VM_READ
+ dwDesiredAccess |= win32.PROCESS_VM_READ
}
if .Mem_Write in flags {
- dwDesiredAccess |= windows.PROCESS_VM_WRITE
+ dwDesiredAccess |= win32.PROCESS_VM_WRITE
}
- handle := windows.OpenProcess(
+ handle := win32.OpenProcess(
dwDesiredAccess,
false,
u32(pid),
)
- if handle == windows.INVALID_HANDLE_VALUE {
- return {}, _get_platform_error()
+ if handle == win32.INVALID_HANDLE_VALUE {
+ err = _get_platform_error()
+ } else {
+ process = {pid = pid, handle = uintptr(handle)}
}
- return Process {
- pid = pid,
- handle = cast(uintptr) handle,
- }, nil
+ return
}
+@(private="package")
_Sys_Process_Attributes :: struct {}
+@(private="package")
_process_start :: proc(desc: Process_Desc) -> (Process, Error) {
TEMP_ALLOCATOR_GUARD()
command_line := _build_command_line(desc.command, temp_allocator())
- command_line_w := windows.utf8_to_wstring(command_line, temp_allocator())
+ command_line_w := win32.utf8_to_wstring(command_line, temp_allocator())
environment := desc.env
if desc.env == nil {
environment = environ(temp_allocator())
}
- environment_block := _build_environment_block(environment, temp_allocator())
- environment_block_w := windows.utf8_to_utf16(environment_block, temp_allocator())
- stderr_handle := windows.GetStdHandle(windows.STD_ERROR_HANDLE)
- stdout_handle := windows.GetStdHandle(windows.STD_OUTPUT_HANDLE)
- stdin_handle := windows.GetStdHandle(windows.STD_INPUT_HANDLE)
+ environment_block := _build_environment_block(environment, temp_allocator())
+ environment_block_w := win32.utf8_to_utf16(environment_block, temp_allocator())
+ stderr_handle := win32.GetStdHandle(win32.STD_ERROR_HANDLE)
+ stdout_handle := win32.GetStdHandle(win32.STD_OUTPUT_HANDLE)
+ stdin_handle := win32.GetStdHandle(win32.STD_INPUT_HANDLE)
if desc.stdout != nil {
- stdout_handle = windows.HANDLE((^File_Impl)(desc.stdout.impl).fd)
+ stdout_handle = win32.HANDLE((^File_Impl)(desc.stdout.impl).fd)
}
if desc.stderr != nil {
- stderr_handle = windows.HANDLE((^File_Impl)(desc.stderr.impl).fd)
+ stderr_handle = win32.HANDLE((^File_Impl)(desc.stderr.impl).fd)
}
if desc.stdin != nil {
- stdin_handle = windows.HANDLE((^File_Impl)(desc.stderr.impl).fd)
+ stdin_handle = win32.HANDLE((^File_Impl)(desc.stderr.impl).fd)
}
- working_dir_w := windows.wstring(nil)
+ working_dir_w := win32.wstring(nil)
if len(desc.working_dir) > 0 {
- working_dir_w = windows.utf8_to_wstring(desc.working_dir, temp_allocator())
+ working_dir_w = win32.utf8_to_wstring(desc.working_dir, temp_allocator())
}
- process_info: windows.PROCESS_INFORMATION = ---
- process_ok := windows.CreateProcessW(
+ process_info: win32.PROCESS_INFORMATION
+ process_ok := win32.CreateProcessW(
nil,
command_line_w,
nil,
nil,
true,
- windows.CREATE_UNICODE_ENVIRONMENT|windows.NORMAL_PRIORITY_CLASS,
+ win32.CREATE_UNICODE_ENVIRONMENT|win32.NORMAL_PRIORITY_CLASS,
raw_data(environment_block_w),
working_dir_w,
- &windows.STARTUPINFOW {
- cb = size_of(windows.STARTUPINFOW),
+ &win32.STARTUPINFOW {
+ cb = size_of(win32.STARTUPINFOW),
hStdError = stderr_handle,
hStdOutput = stdout_handle,
hStdInput = stdin_handle,
- dwFlags = windows.STARTF_USESTDHANDLES,
+ dwFlags = win32.STARTF_USESTDHANDLES,
},
&process_info,
)
@@ -549,83 +448,89 @@ _process_start :: proc(desc: Process_Desc) -> (Process, Error) {
return {}, _get_platform_error()
}
return Process {
- pid = cast(int) process_info.dwProcessId,
- handle = cast(uintptr) process_info.hProcess,
+ pid = int(process_info.dwProcessId),
+ handle = uintptr(process_info.hProcess),
}, nil
}
-_process_wait :: proc(process: Process, timeout: time.Duration) -> (Process_State, Error) {
- handle := windows.HANDLE(process.handle)
- timeout_ms := u32(timeout / time.Millisecond) if timeout > 0 else windows.INFINITE
- wait_result := windows.WaitForSingleObject(handle, timeout_ms)
+@(private="package")
+_process_wait :: proc(process: Process, timeout: time.Duration) -> (process_state: Process_State, err: Error) {
+ handle := win32.HANDLE(process.handle)
+ timeout_ms := u32(timeout / time.Millisecond) if timeout > 0 else win32.INFINITE
+ wait_result := win32.WaitForSingleObject(handle, timeout_ms)
switch wait_result {
- case windows.WAIT_OBJECT_0:
- exit_code: u32 = ---
- if !windows.GetExitCodeProcess(handle, &exit_code) {
- return {}, _get_platform_error()
- }
- time_created: windows.FILETIME = ---
- time_exited: windows.FILETIME = ---
- time_kernel: windows.FILETIME = ---
- time_user: windows.FILETIME = ---
- if !windows.GetProcessTimes(handle, &time_created, &time_exited, &time_kernel, &time_user) {
- return {}, _get_platform_error()
- }
- return Process_State {
- exit_code = cast(int) exit_code,
- exited = true,
- pid = process.pid,
- success = true,
+ case win32.WAIT_OBJECT_0:
+ exit_code: u32
+ if !win32.GetExitCodeProcess(handle, &exit_code) {
+ err =_get_platform_error()
+ return
+ }
+ time_created: win32.FILETIME
+ time_exited: win32.FILETIME
+ time_kernel: win32.FILETIME
+ time_user: win32.FILETIME
+ if !win32.GetProcessTimes(handle, &time_created, &time_exited, &time_kernel, &time_user) {
+ err = _get_platform_error()
+ return
+ }
+ process_state = {
+ exit_code = int(exit_code),
+ exited = true,
+ pid = process.pid,
+ success = true,
system_time = _filetime_to_duration(time_kernel),
- user_time = _filetime_to_duration(time_user),
- }, nil
- case windows.WAIT_TIMEOUT:
- return {}, General_Error.Timeout
+ user_time = _filetime_to_duration(time_user),
+ }
+ return
+ case win32.WAIT_TIMEOUT:
+ err = General_Error.Timeout
+ return
case:
- return {}, _get_platform_error()
+ err = _get_platform_error()
+ return
}
}
-_process_close :: proc(process: Process) -> (Error) {
- if !windows.CloseHandle(cast(windows.HANDLE) process.handle) {
+@(private="package")
+_process_close :: proc(process: Process) -> Error {
+ if !win32.CloseHandle(win32.HANDLE(process.handle)) {
return _get_platform_error()
}
return nil
}
-_process_kill :: proc(process: Process) -> (Error) {
+@(private="package")
+_process_kill :: proc(process: Process) -> Error {
// Note(flysand): This is different than what the task manager's "kill process"
// functionality does, as we don't try to send WM_CLOSE message first. This
// is quite a rough way to kill the process, which should be consistent with
// linux. The error code 9 is to mimic SIGKILL event.
- if !windows.TerminateProcess(windows.HANDLE(process.handle), 9) {
+ if !win32.TerminateProcess(win32.HANDLE(process.handle), 9) {
return _get_platform_error()
}
return nil
}
-@(private)
-_filetime_to_duration :: proc(filetime: windows.FILETIME) -> time.Duration {
+_filetime_to_duration :: proc(filetime: win32.FILETIME) -> time.Duration {
ticks := u64(filetime.dwHighDateTime)<<32 | u64(filetime.dwLowDateTime)
return time.Duration(ticks * 100)
}
-@(private)
-_process_entry_by_pid :: proc(pid: int) -> (windows.PROCESSENTRY32W, Error) {
- snap := windows.CreateToolhelp32Snapshot(windows.TH32CS_SNAPPROCESS, 0)
- if snap == windows.INVALID_HANDLE_VALUE {
+_process_entry_by_pid :: proc(pid: int) -> (win32.PROCESSENTRY32W, Error) {
+ snap := win32.CreateToolhelp32Snapshot(win32.TH32CS_SNAPPROCESS, 0)
+ if snap == win32.INVALID_HANDLE_VALUE {
return {}, _get_platform_error()
}
- defer windows.CloseHandle(snap)
- entry := windows.PROCESSENTRY32W { dwSize = size_of(windows.PROCESSENTRY32W) }
- status := windows.Process32FirstW(snap, &entry)
+ defer win32.CloseHandle(snap)
+ entry := win32.PROCESSENTRY32W { dwSize = size_of(win32.PROCESSENTRY32W) }
+ status := win32.Process32FirstW(snap, &entry)
found := false
for status {
if u32(pid) == entry.th32ProcessID {
found = true
break
}
- status = windows.Process32NextW(snap, &entry)
+ status = win32.Process32NextW(snap, &entry)
}
if !found {
return {}, General_Error.Not_Exist
@@ -638,102 +543,83 @@ _process_entry_by_pid :: proc(pid: int) -> (windows.PROCESSENTRY32W, Error) {
// a slight suspicion that if both exe path and command line are desired,
// it's faster to just read both from PEB, but maybe the toolhelp snapshots
// are just better...?
-@(private)
-_process_exe_by_pid :: proc(pid: int, allocator: runtime.Allocator) -> (string, Error) {
- snap := windows.CreateToolhelp32Snapshot(
- windows.TH32CS_SNAPMODULE|windows.TH32CS_SNAPMODULE32,
+@(private="package")
+_process_exe_by_pid :: proc(pid: int, allocator: runtime.Allocator) -> (exe_path: string, err: Error) {
+ snap := win32.CreateToolhelp32Snapshot(
+ win32.TH32CS_SNAPMODULE|win32.TH32CS_SNAPMODULE32,
u32(pid),
)
- if snap == windows.INVALID_HANDLE_VALUE {
- return "", _get_platform_error()
+ if snap == win32.INVALID_HANDLE_VALUE {
+ err =_get_platform_error()
+ return
}
- defer windows.CloseHandle(snap)
- entry := windows.MODULEENTRY32W { dwSize = size_of(windows.MODULEENTRY32W) }
- status := windows.Module32FirstW(snap, &entry)
+ defer win32.CloseHandle(snap)
+
+ entry := win32.MODULEENTRY32W { dwSize = size_of(win32.MODULEENTRY32W) }
+ status := win32.Module32FirstW(snap, &entry)
if !status {
- return "", _get_platform_error()
- }
- exe_path, err := windows.wstring_to_utf8(raw_data(entry.szExePath[:]), -1, allocator)
- if err != nil {
- return "", err
+ err =_get_platform_error()
+ return
}
- return exe_path, nil
+ return win32.wstring_to_utf8(raw_data(entry.szExePath[:]), -1, allocator)
}
-@(private)
-_get_process_user :: proc(process_handle: windows.HANDLE, allocator: runtime.Allocator) -> (full_username: string, err: Error) {
+_get_process_user :: proc(process_handle: win32.HANDLE, allocator: runtime.Allocator) -> (full_username: string, err: Error) {
TEMP_ALLOCATOR_GUARD()
- token_handle: windows.HANDLE = ---
- if !windows.OpenProcessToken(process_handle, windows.TOKEN_QUERY, &token_handle) {
+ token_handle: win32.HANDLE
+ if !win32.OpenProcessToken(process_handle, win32.TOKEN_QUERY, &token_handle) {
err = _get_platform_error()
return
}
- token_user_size: u32 = ---
- if !windows.GetTokenInformation(token_handle, .TokenUser, nil, 0, &token_user_size) {
+ token_user_size: u32
+ if !win32.GetTokenInformation(token_handle, .TokenUser, nil, 0, &token_user_size) {
// Note(flysand): Make sure the buffer too small error comes out, and not any other error
err = _get_platform_error()
if v, ok := err.(Platform_Error); !ok || int(v) != 0x7a {
return
}
+ err = nil
}
- token_user := cast(^windows.TOKEN_USER) raw_data(make([]u8, token_user_size, temp_allocator()))
- if !windows.GetTokenInformation(token_handle, .TokenUser, token_user, token_user_size, &token_user_size) {
+ token_user := (^win32.TOKEN_USER)(raw_data(make([]u8, token_user_size, temp_allocator())))
+ if !win32.GetTokenInformation(token_handle, .TokenUser, token_user, token_user_size, &token_user_size) {
err = _get_platform_error()
return
}
- sid_type: windows.SID_NAME_USE = ---
- username_w: [256]u16 = ---
- domain_w: [256]u16 = ---
- username_chrs: u32 = 256
- domain_chrs: u32 = 256
- if !windows.LookupAccountSidW(nil, token_user.User.Sid, &username_w[0], &username_chrs, &domain_w[0], &domain_chrs, &sid_type) {
+
+ sid_type: win32.SID_NAME_USE
+ username_w: [256]u16
+ domain_w: [256]u16
+ username_chrs := u32(256)
+ domain_chrs := u32(256)
+
+ if !win32.LookupAccountSidW(nil, token_user.User.Sid, &username_w[0], &username_chrs, &domain_w[0], &domain_chrs, &sid_type) {
err = _get_platform_error()
return
}
- username, username_err := windows.utf16_to_utf8(username_w[:username_chrs], temp_allocator())
- if username_err != nil {
- err = username_err
- return
- }
- domain, domain_err := windows.utf16_to_utf8(domain_w[:domain_chrs], temp_allocator())
- if domain_err != nil {
- err = domain_err
- return
- }
- full_name, full_name_err := strings.concatenate([]string {domain, "\\", username}, allocator)
- if full_name_err != nil {
- err = full_name_err
- return
- }
- return full_name, nil
+ username := win32.utf16_to_utf8(username_w[:username_chrs], temp_allocator()) or_return
+ domain := win32.utf16_to_utf8(domain_w[:domain_chrs], temp_allocator()) or_return
+ return strings.concatenate({domain, "\\", username}, allocator)
}
-@(private)
-_parse_command_line :: proc(cmd_line_w: [^]u16, allocator: runtime.Allocator) -> ([]string, Error) {
- argc: i32 = ---
- argv_w := windows.CommandLineToArgvW(cmd_line_w, &argc)
+_parse_command_line :: proc(cmd_line_w: [^]u16, allocator: runtime.Allocator) -> (argv: []string, err: Error) {
+ argc: i32
+ argv_w := win32.CommandLineToArgvW(cmd_line_w, &argc)
if argv_w == nil {
return nil, _get_platform_error()
}
- argv, argv_err := make([]string, argc, allocator)
- if argv_err != nil {
- return nil, argv_err
+ argv = make([]string, argc, allocator) or_return
+ defer if err != nil {
+ for arg in argv {
+ delete(arg, allocator)
+ }
+ delete(argv, allocator)
}
for arg_w, i in argv_w[:argc] {
- arg, arg_err := windows.wstring_to_utf8(arg_w, -1, allocator)
- if arg_err != nil {
- for s in argv[:i] {
- delete(s, allocator)
- }
- delete(argv, allocator)
- return nil, arg_err
- }
- argv[i] = arg
+ argv[i] = win32.wstring_to_utf8(arg_w, -1, allocator) or_return
}
- return argv, nil
+ return
}
-@(private)
_build_command_line :: proc(command: []string, allocator: runtime.Allocator) -> string {
_write_byte_n_times :: #force_inline proc(builder: ^strings.Builder, b: byte, n: int) {
for _ in 0 ..< n {
@@ -770,8 +656,7 @@ _build_command_line :: proc(command: []string, allocator: runtime.Allocator) ->
return strings.to_string(builder)
}
-@(private)
-_parse_environment_block :: proc(block: [^]u16, allocator: runtime.Allocator) -> ([]string, Error) {
+_parse_environment_block :: proc(block: [^]u16, allocator: runtime.Allocator) -> (envs: []string, err: Error) {
zt_count := 0
for idx := 0; true; {
if block[idx] == 0x0000 {
@@ -783,13 +668,21 @@ _parse_environment_block :: proc(block: [^]u16, allocator: runtime.Allocator) ->
}
idx += 1
}
+
// Note(flysand): Each string in the environment block is terminated
// by a NUL character. In addition, the environment block itself is
// terminated by a NUL character. So the number of strings in the
// environment block is the number of NUL character minus the
// block terminator.
env_count := zt_count - 1
- envs := make([]string, env_count, allocator)
+ envs = make([]string, env_count, allocator) or_return
+ defer if err != nil {
+ for env in envs {
+ delete(env, allocator)
+ }
+ delete(envs, allocator)
+ }
+
env_idx := 0
last_idx := 0
idx := 0
@@ -798,24 +691,19 @@ _parse_environment_block :: proc(block: [^]u16, allocator: runtime.Allocator) ->
idx += 1
}
env_w := block[last_idx:idx]
- env, env_err := windows.utf16_to_utf8(env_w, allocator)
- if env_err != nil {
- return nil, env_err
- }
- envs[env_idx] = env
+ envs[env_idx] = win32.utf16_to_utf8(env_w, allocator) or_return
env_idx += 1
idx += 1
last_idx = idx
}
- return envs, nil
+ return
}
-@(private)
_build_environment_block :: proc(environment: []string, allocator: runtime.Allocator) -> string {
builder := strings.builder_make(allocator)
#reverse for kv, cur_idx in environment {
eq_idx := strings.index_byte(kv, '=')
- assert(eq_idx != -1, "Malformed environment string. Expected '=' to separate keys and values")
+ assert(eq_idx >= 0, "Malformed environment string. Expected '=' to separate keys and values")
key := kv[:eq_idx]
already_handled := false
for old_kv in environment[cur_idx+1:] {
@@ -836,109 +724,3 @@ _build_environment_block :: proc(environment: []string, allocator: runtime.Alloc
strings.write_byte(&builder, 0)
return strings.to_string(builder)
}
-
-@(private="file")
-PROCESSINFOCLASS :: enum i32 {
- ProcessBasicInformation = 0,
- ProcessDebugPort = 7,
- ProcessWow64Information = 26,
- ProcessImageFileName = 27,
- ProcessBreakOnTermination = 29,
- ProcessTelemetryIdInformation = 64,
- ProcessSubsystemInformation = 75,
-}
-
-@(private="file")
-NtQueryInformationProcess_T :: #type proc (
- ProcessHandle: windows.HANDLE,
- ProcessInformationClass: PROCESSINFOCLASS,
- ProcessInformation: rawptr,
- ProcessInformationLength: u32,
- ReturnLength: ^u32,
-) -> u32
-
-@(private="file")
-PROCESS_BASIC_INFORMATION :: struct {
- _: rawptr,
- PebBaseAddress: ^PEB,
- _: [2]rawptr,
- UniqueProcessId: ^u32,
- _: rawptr,
-}
-
-@(private="file")
-PEB :: struct {
- _: [2]u8,
- BeingDebugged: u8,
- _: [1]u8,
- _: [2]rawptr,
- Ldr: ^PEB_LDR_DATA,
- ProcessParameters: ^RTL_USER_PROCESS_PARAMETERS,
- _: [104]u8,
- _: [52]rawptr,
- PostProcessInitRoutine: #type proc "stdcall" (),
- _: [128]u8,
- _: [1]rawptr,
- SessionId: u32,
-}
-
-@(private="file")
-PEB_LDR_DATA :: struct {
- _: [8]u8,
- _: [3]rawptr,
- InMemoryOrderModuleList: LIST_ENTRY,
-}
-
-@(private="file")
-RTL_USER_PROCESS_PARAMETERS :: struct {
- MaximumLength: u32,
- Length: u32,
- Flags: u32,
- DebugFlags: u32,
- ConsoleHandle: rawptr,
- ConsoleFlags: u32,
- StdInputHandle: rawptr,
- StdOutputHandle: rawptr,
- StdErrorHandle: rawptr,
- CurrentDirectoryPath: UNICODE_STRING,
- CurrentDirectoryHandle: rawptr,
- DllPath: UNICODE_STRING,
- ImagePathName: UNICODE_STRING,
- CommandLine: UNICODE_STRING,
- Environment: rawptr,
- StartingPositionLeft: u32,
- StartingPositionTop: u32,
- Width: u32,
- Height: u32,
- CharWidth: u32,
- CharHeight: u32,
- ConsoleTextAttributes: u32,
- WindowFlags: u32,
- ShowWindowFlags: u32,
- WindowTitle: UNICODE_STRING,
- DesktopName: UNICODE_STRING,
- ShellInfo: UNICODE_STRING,
- RuntimeData: UNICODE_STRING,
- DLCurrentDirectory: [32]RTL_DRIVE_LETTER_CURDIR,
- EnvironmentSize: u32,
-}
-
-RTL_DRIVE_LETTER_CURDIR :: struct {
- Flags: u16,
- Length: u16,
- TimeStamp: u32,
- DosPath: UNICODE_STRING,
-}
-
-@(private="file")
-UNICODE_STRING :: struct {
- Length: u16,
- MaximumLength: u16,
- Buffer: [^]u16,
-}
-
-@(private="file")
-LIST_ENTRY :: struct {
- Flink: ^LIST_ENTRY,
- Blink: ^LIST_ENTRY,
-} \ No newline at end of file
diff --git a/core/sys/windows/ntdll.odin b/core/sys/windows/ntdll.odin
index 56c24f1a2..b75ac695a 100644
--- a/core/sys/windows/ntdll.odin
+++ b/core/sys/windows/ntdll.odin
@@ -6,4 +6,110 @@ foreign import ntdll_lib "system:ntdll.lib"
@(default_calling_convention="system")
foreign ntdll_lib {
RtlGetVersion :: proc(lpVersionInformation: ^OSVERSIONINFOEXW) -> NTSTATUS ---
+
+
+ NtQueryInformationProcess :: proc(
+ ProcessHandle: HANDLE,
+ ProcessInformationClass: PROCESS_INFO_CLASS,
+ ProcessInformation: rawptr,
+ ProcessInformationLength: u32,
+ ReturnLength: ^u32,
+ ) -> u32 ---
+}
+
+
+PROCESS_INFO_CLASS :: enum i32 {
+ ProcessBasicInformation = 0,
+ ProcessDebugPort = 7,
+ ProcessWow64Information = 26,
+ ProcessImageFileName = 27,
+ ProcessBreakOnTermination = 29,
+ ProcessTelemetryIdInformation = 64,
+ ProcessSubsystemInformation = 75,
+}
+
+
+
+PROCESS_BASIC_INFORMATION :: struct {
+ ExitStatus: NTSTATUS,
+ PebBaseAddress: ^PEB,
+ AffinityMask: ULONG_PTR,
+ BasePriority: KPRIORITY,
+ UniqueProcessId: ULONG_PTR,
+ InheritedFromUniqueProcessId: ULONG_PTR,
+}
+
+KPRIORITY :: rawptr
+
+PPS_POST_PROCESS_INIT_ROUTINE :: proc "system" ()
+
+
+PEB :: struct {
+ _: [2]u8,
+ BeingDebugged: u8,
+ _: [1]u8,
+ _: [2]rawptr,
+ Ldr: ^PEB_LDR_DATA,
+ ProcessParameters: ^RTL_USER_PROCESS_PARAMETERS,
+ _: [104]u8,
+ _: [52]rawptr,
+ PostProcessInitRoutine: PPS_POST_PROCESS_INIT_ROUTINE,
+ _: [128]u8,
+ _: [1]rawptr,
+ SessionId: u32,
+}
+
+
+
+
+PEB_LDR_DATA :: struct {
+ _: [8]u8,
+ _: [3]rawptr,
+ InMemoryOrderModuleList: LIST_ENTRY,
+}
+
+RTL_USER_PROCESS_PARAMETERS :: struct {
+ MaximumLength: u32,
+ Length: u32,
+ Flags: u32,
+ DebugFlags: u32,
+ ConsoleHandle: rawptr,
+ ConsoleFlags: u32,
+ StdInputHandle: rawptr,
+ StdOutputHandle: rawptr,
+ StdErrorHandle: rawptr,
+ CurrentDirectoryPath: UNICODE_STRING,
+ CurrentDirectoryHandle: rawptr,
+ DllPath: UNICODE_STRING,
+ ImagePathName: UNICODE_STRING,
+ CommandLine: UNICODE_STRING,
+ Environment: rawptr,
+ StartingPositionLeft: u32,
+ StartingPositionTop: u32,
+ Width: u32,
+ Height: u32,
+ CharWidth: u32,
+ CharHeight: u32,
+ ConsoleTextAttributes: u32,
+ WindowFlags: u32,
+ ShowWindowFlags: u32,
+ WindowTitle: UNICODE_STRING,
+ DesktopName: UNICODE_STRING,
+ ShellInfo: UNICODE_STRING,
+ RuntimeData: UNICODE_STRING,
+ DLCurrentDirectory: [32]RTL_DRIVE_LETTER_CURDIR,
+ EnvironmentSize: u32,
+}
+
+RTL_DRIVE_LETTER_CURDIR :: struct {
+ Flags: u16,
+ Length: u16,
+ TimeStamp: u32,
+ DosPath: UNICODE_STRING,
+}
+
+
+LIST_ENTRY :: struct {
+ Flink: ^LIST_ENTRY,
+ Blink: ^LIST_ENTRY,
} \ No newline at end of file
diff --git a/core/sys/windows/types.odin b/core/sys/windows/types.odin
index 92c6023eb..35f4174eb 100644
--- a/core/sys/windows/types.odin
+++ b/core/sys/windows/types.odin
@@ -2495,9 +2495,9 @@ OBJECT_ATTRIBUTES :: struct {
}
UNICODE_STRING :: struct {
- Length: u16,
- MaximumLength: u16,
- Buffer: ^u16,
+ Length: u16 `fmt:"-"`,
+ MaximumLength: u16 `fmt:"-"`,
+ Buffer: [^]u16 `fmt:"s,Length"`,
}
OVERLAPPED :: struct {