struct windows [src]
Alias for std.os.windows
This file contains thin wrappers around Windows-specific APIs, with these
specific goals in mind:
Convert "errno"-style error codes into Zig errors.
When null-terminated or WTF16LE byte buffers are required, provide APIs which accept
slices as well as APIs which accept null-terminated WTF16LE byte buffers.
Members
- accept (Function)
- ACCESS_MASK (Type)
- ACTIVATION_CONTEXT_DATA (opaque)
- advapi32 (struct)
- ALTERNATIVE_ARCHITECTURE_TYPE (enum)
- ASSEMBLY_STORAGE_MAP (opaque)
- ATOM (Type)
- bind (Function)
- BOOL (Type)
- BOOLEAN (Type)
- BSTR (Type)
- BY_HANDLE_FILE_INFORMATION (extern struct)
- BYTE (Type)
- callWSAStartup (Function)
- CHAR (Type)
- CLIENT_ID (extern struct)
- CloseHandle (Function)
- closesocket (Function)
- COINIT (struct)
- CONDITION_VARIABLE (extern struct)
- CONDITION_VARIABLE_INIT (Constant)
- CONSOLE_SCREEN_BUFFER_INFO (extern struct)
- CONTEXT (Type)
- COORD (extern struct)
- CREATE_ALWAYS (Constant)
- CREATE_EVENT_INITIAL_SET (Constant)
- CREATE_EVENT_MANUAL_RESET (Constant)
- CREATE_NEW (Constant)
- CREATE_UNICODE_ENVIRONMENT (Constant)
- CreateEventEx (Function)
- CreateEventExW (Function)
- CreateIoCompletionPort (Function)
- CreateIoCompletionPortError (Error Set)
- CreatePipe (Function)
- CreatePipeError (Error Set)
- CreateProcessError (Error Set)
- CreateProcessFlags (struct)
- CreateProcessW (Function)
- CreateSymbolicLink (Function)
- CreateSymbolicLinkError (Error Set)
- CRITICAL_SECTION (extern struct)
- crypt32 (struct)
- cStrToPrefixedFileW (Function)
- CTL_CODE (Function)
- CTRL_BREAK_EVENT (Constant)
- CTRL_C_EVENT (Constant)
- CTRL_CLOSE_EVENT (Constant)
- CTRL_LOGOFF_EVENT (Constant)
- CTRL_SHUTDOWN_EVENT (Constant)
- CURDIR (extern struct)
- DELETE (Constant)
- DeleteFile (Function)
- DeleteFileError (Error Set)
- DeleteFileOptions (struct)
- DEVICE_TYPE (Type)
- DeviceIoControl (Function)
- DeviceIoControlError (Error Set)
- DISABLE_NEWLINE_AUTO_RETURN (Constant)
- DUPLICATE_SAME_ACCESS (Constant)
- DWORD (Type)
- DWORD_PTR (Type)
- DWORD64 (Type)
- E_ABORT (Constant)
- E_ACCESSDENIED (Constant)
- E_FAIL (Constant)
- E_HANDLE (Constant)
- E_INVALIDARG (Constant)
- E_NOINTERFACE (Constant)
- E_NOTIMPL (Constant)
- E_OUTOFMEMORY (Constant)
- E_POINTER (Constant)
- E_UNEXPECTED (Constant)
- ENABLE_VIRTUAL_TERMINAL_PROCESSING (Constant)
- ENUM_PAGE_FILE_INFORMATION (extern struct)
- eqlIgnoreCaseWTF16 (Function)
- eqlIgnoreCaseWtf8 (Function)
- EVENT_ALL_ACCESS (Constant)
- EVENT_MODIFY_STATE (Constant)
- EXCEPTION_ACCESS_VIOLATION (Constant)
- EXCEPTION_CONTINUE_SEARCH (Constant)
- EXCEPTION_DATATYPE_MISALIGNMENT (Constant)
- EXCEPTION_DISPOSITION (Type)
- EXCEPTION_ILLEGAL_INSTRUCTION (Constant)
- EXCEPTION_POINTERS (extern struct)
- EXCEPTION_RECORD (extern struct)
- EXCEPTION_REGISTRATION_RECORD (extern struct)
- EXCEPTION_ROUTINE (Type)
- EXCEPTION_STACK_OVERFLOW (Constant)
- FALSE (Constant)
- FARPROC (Type)
- FILE_ACCESS_INFORMATION (extern struct)
- FILE_ACTION_ADDED (Constant)
- FILE_ACTION_MODIFIED (Constant)
- FILE_ACTION_REMOVED (Constant)
- FILE_ACTION_RENAMED_NEW_NAME (Constant)
- FILE_ACTION_RENAMED_OLD_NAME (Constant)
- FILE_ADD_FILE (Constant)
- FILE_ADD_SUBDIRECTORY (Constant)
- FILE_ALIGNMENT_INFORMATION (extern struct)
- FILE_ALL_ACCESS (Constant)
- FILE_ALL_INFORMATION (extern struct)
- FILE_ANY_ACCESS (Constant)
- FILE_APPEND_DATA (Constant)
- FILE_ATTRIBUTE_ARCHIVE (Constant)
- FILE_ATTRIBUTE_COMPRESSED (Constant)
- FILE_ATTRIBUTE_DEVICE (Constant)
- FILE_ATTRIBUTE_DIRECTORY (Constant)
- FILE_ATTRIBUTE_ENCRYPTED (Constant)
- FILE_ATTRIBUTE_HIDDEN (Constant)
- FILE_ATTRIBUTE_INTEGRITY_STREAM (Constant)
- FILE_ATTRIBUTE_NO_SCRUB_DATA (Constant)
- FILE_ATTRIBUTE_NORMAL (Constant)
- FILE_ATTRIBUTE_NOT_CONTENT_INDEXED (Constant)
- FILE_ATTRIBUTE_OFFLINE (Constant)
- FILE_ATTRIBUTE_READONLY (Constant)
- FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS (Constant)
- FILE_ATTRIBUTE_RECALL_ON_OPEN (Constant)
- FILE_ATTRIBUTE_REPARSE_POINT (Constant)
- FILE_ATTRIBUTE_SPARSE_FILE (Constant)
- FILE_ATTRIBUTE_SYSTEM (Constant)
- FILE_ATTRIBUTE_TAG_INFO (extern struct)
- FILE_ATTRIBUTE_TEMPORARY (Constant)
- FILE_ATTRIBUTE_VIRTUAL (Constant)
- FILE_BASIC_INFORMATION (extern struct)
- FILE_BEGIN (Constant)
- FILE_BOTH_DIR_INFORMATION (extern struct)
- FILE_BOTH_DIRECTORY_INFORMATION (extern struct)
- FILE_COMPLETE_IF_OPLOCKED (Constant)
- FILE_CREATE (Constant)
- FILE_CREATE_PIPE_INSTANCE (Constant)
- FILE_CREATE_TREE_CONNECTION (Constant)
- FILE_CURRENT (Constant)
- FILE_DELETE_CHILD (Constant)
- FILE_DELETE_ON_CLOSE (Constant)
- FILE_DEVICE_8042_PORT (Constant)
- FILE_DEVICE_ACPI (Constant)
- FILE_DEVICE_BATTERY (Constant)
- FILE_DEVICE_BEEP (Constant)
- FILE_DEVICE_BIOMETRIC (Constant)
- FILE_DEVICE_BLUETOOTH (Constant)
- FILE_DEVICE_BUS_EXTENDER (Constant)
- FILE_DEVICE_CD_ROM (Constant)
- FILE_DEVICE_CD_ROM_FILE_SYSTEM (Constant)
- FILE_DEVICE_CHANGER (Constant)
- FILE_DEVICE_CONSOLE (Constant)
- FILE_DEVICE_CONTROLLER (Constant)
- FILE_DEVICE_CRYPT_PROVIDER (Constant)
- FILE_DEVICE_DATALINK (Constant)
- FILE_DEVICE_DEVAPI (Constant)
- FILE_DEVICE_DFS (Constant)
- FILE_DEVICE_DFS_FILE_SYSTEM (Constant)
- FILE_DEVICE_DFS_VOLUME (Constant)
- FILE_DEVICE_DISK (Constant)
- FILE_DEVICE_DISK_FILE_SYSTEM (Constant)
- FILE_DEVICE_DVD (Constant)
- FILE_DEVICE_EHSTOR (Constant)
- FILE_DEVICE_FILE_SYSTEM (Constant)
- FILE_DEVICE_FIPS (Constant)
- FILE_DEVICE_FULLSCREEN_VIDEO (Constant)
- FILE_DEVICE_GPIO (Constant)
- FILE_DEVICE_HOLOGRAPHIC (Constant)
- FILE_DEVICE_INFINIBAND (Constant)
- FILE_DEVICE_INPORT_PORT (Constant)
- FILE_DEVICE_KEYBOARD (Constant)
- FILE_DEVICE_KS (Constant)
- FILE_DEVICE_KSEC (Constant)
- FILE_DEVICE_MAILSLOT (Constant)
- FILE_DEVICE_MASS_STORAGE (Constant)
- FILE_DEVICE_MIDI_IN (Constant)
- FILE_DEVICE_MIDI_OUT (Constant)
- FILE_DEVICE_MODEM (Constant)
- FILE_DEVICE_MOUSE (Constant)
- FILE_DEVICE_MT_COMPOSITE (Constant)
- FILE_DEVICE_MT_TRANSPORT (Constant)
- FILE_DEVICE_MULTI_UNC_PROVIDER (Constant)
- FILE_DEVICE_NAMED_PIPE (Constant)
- FILE_DEVICE_NETWORK (Constant)
- FILE_DEVICE_NETWORK_BROWSER (Constant)
- FILE_DEVICE_NETWORK_FILE_SYSTEM (Constant)
- FILE_DEVICE_NETWORK_REDIRECTOR (Constant)
- FILE_DEVICE_NFP (Constant)
- FILE_DEVICE_NULL (Constant)
- FILE_DEVICE_NVDIMM (Constant)
- FILE_DEVICE_PARALLEL_PORT (Constant)
- FILE_DEVICE_PERSISTENT_MEMORY (Constant)
- FILE_DEVICE_PHYSICAL_NETCARD (Constant)
- FILE_DEVICE_PMI (Constant)
- FILE_DEVICE_POINT_OF_SERVICE (Constant)
- FILE_DEVICE_PRINTER (Constant)
- FILE_DEVICE_SCANNER (Constant)
- FILE_DEVICE_SCREEN (Constant)
- FILE_DEVICE_SDFXHCI (Constant)
- FILE_DEVICE_SERENUM (Constant)
- FILE_DEVICE_SERIAL_MOUSE_PORT (Constant)
- FILE_DEVICE_SERIAL_PORT (Constant)
- FILE_DEVICE_SMARTCARD (Constant)
- FILE_DEVICE_SMB (Constant)
- FILE_DEVICE_SOUND (Constant)
- FILE_DEVICE_STORAGE_REPLICATION (Constant)
- FILE_DEVICE_STREAMS (Constant)
- FILE_DEVICE_SYSENV (Constant)
- FILE_DEVICE_TAPE (Constant)
- FILE_DEVICE_TAPE_FILE_SYSTEM (Constant)
- FILE_DEVICE_TERMSRV (Constant)
- FILE_DEVICE_TRANSPORT (Constant)
- FILE_DEVICE_TRUST_ENV (Constant)
- FILE_DEVICE_UCM (Constant)
- FILE_DEVICE_UCMTCPCI (Constant)
- FILE_DEVICE_UNKNOWN (Constant)
- FILE_DEVICE_USBEX (Constant)
- FILE_DEVICE_VDM (Constant)
- FILE_DEVICE_VIDEO (Constant)
- FILE_DEVICE_VIRTUAL_BLOCK (Constant)
- FILE_DEVICE_VIRTUAL_DISK (Constant)
- FILE_DEVICE_VMBUS (Constant)
- FILE_DEVICE_WAVE_IN (Constant)
- FILE_DEVICE_WAVE_OUT (Constant)
- FILE_DEVICE_WPD (Constant)
- FILE_DIRECTORY_FILE (Constant)
- FILE_DIRECTORY_INFORMATION (extern struct)
- FILE_DISPOSITION_INFORMATION (extern struct)
- FILE_DISPOSITION_INFORMATION_EX (extern struct)
- FILE_EA_INFORMATION (extern struct)
- FILE_END (Constant)
- FILE_END_OF_FILE_INFORMATION (extern struct)
- FILE_EXECUTE (Constant)
- FILE_FLAG_BACKUP_SEMANTICS (Constant)
- FILE_FLAG_DELETE_ON_CLOSE (Constant)
- FILE_FLAG_NO_BUFFERING (Constant)
- FILE_FLAG_OPEN_NO_RECALL (Constant)
- FILE_FLAG_OPEN_REPARSE_POINT (Constant)
- FILE_FLAG_OVERLAPPED (Constant)
- FILE_FLAG_POSIX_SEMANTICS (Constant)
- FILE_FLAG_RANDOM_ACCESS (Constant)
- FILE_FLAG_SEQUENTIAL_SCAN (Constant)
- FILE_FLAG_SESSION_AWARE (Constant)
- FILE_FLAG_WRITE_THROUGH (Constant)
- FILE_FS_DEVICE_INFORMATION (extern struct)
- FILE_FS_VOLUME_INFORMATION (extern struct)
- FILE_GENERIC_EXECUTE (Constant)
- FILE_GENERIC_READ (Constant)
- FILE_GENERIC_WRITE (Constant)
- FILE_INFO_BY_HANDLE_CLASS (enum)
- FILE_INFORMATION_CLASS (enum)
- FILE_INTERNAL_INFORMATION (extern struct)
- FILE_LIST_DIRECTORY (Constant)
- FILE_MAXIMUM_DISPOSITION (Constant)
- FILE_MODE_INFORMATION (extern struct)
- FILE_NAME_INFO (extern struct)
- FILE_NAME_INFORMATION (extern struct)
- FILE_NAME_NORMALIZED (Constant)
- FILE_NAME_OPENED (Constant)
- FILE_NO_COMPRESSION (Constant)
- FILE_NO_EA_KNOWLEDGE (Constant)
- FILE_NO_INTERMEDIATE_BUFFERING (Constant)
- FILE_NON_DIRECTORY_FILE (Constant)
- FILE_NOTIFY_INFORMATION (extern struct)
- FILE_OPEN (Constant)
- FILE_OPEN_BY_FILE_ID (Constant)
- FILE_OPEN_FOR_BACKUP_INTENT (Constant)
- FILE_OPEN_FOR_FREE_SPACE_QUERY (Constant)
- FILE_OPEN_FOR_RECOVERY (Constant)
- FILE_OPEN_IF (Constant)
- FILE_OPEN_OFFLINE_FILE (Constant)
- FILE_OPEN_REPARSE_POINT (Constant)
- FILE_OVERWRITE (Constant)
- FILE_OVERWRITE_IF (Constant)
- FILE_PIPE_ACCEPT_REMOTE_CLIENTS (Constant)
- FILE_PIPE_BYTE_STREAM_MODE (Constant)
- FILE_PIPE_BYTE_STREAM_TYPE (Constant)
- FILE_PIPE_COMPLETE_OPERATION (Constant)
- FILE_PIPE_MESSAGE_MODE (Constant)
- FILE_PIPE_MESSAGE_TYPE (Constant)
- FILE_PIPE_QUEUE_OPERATION (Constant)
- FILE_PIPE_REJECT_REMOTE_CLIENTS (Constant)
- FILE_PIPE_TYPE_VALID_MASK (Constant)
- FILE_POSITION_INFORMATION (extern struct)
- FILE_RANDOM_ACCESS (Constant)
- FILE_READ_ACCESS (Constant)
- FILE_READ_ATTRIBUTES (Constant)
- FILE_READ_DATA (Constant)
- FILE_READ_EA (Constant)
- FILE_RENAME_FORCE_RESIZE_SOURCE_SR (Constant)
- FILE_RENAME_FORCE_RESIZE_SR (Constant)
- FILE_RENAME_FORCE_RESIZE_TARGET_SR (Constant)
- FILE_RENAME_IGNORE_READONLY_ATTRIBUTE (Constant)
- FILE_RENAME_INFORMATION (extern struct)
- FILE_RENAME_INFORMATION_EX (extern struct)
- FILE_RENAME_NO_DECREASE_AVAILABLE_SPACE (Constant)
- FILE_RENAME_NO_INCREASE_AVAILABLE_SPACE (Constant)
- FILE_RENAME_POSIX_SEMANTICS (Constant)
- FILE_RENAME_PRESERVE_AVAILABLE_SPACE (Constant)
- FILE_RENAME_REPLACE_IF_EXISTS (Constant)
- FILE_RENAME_SUPPRESS_PIN_STATE_INHERITANCE (Constant)
- FILE_RENAME_SUPPRESS_STORAGE_RESERVE_INHERITANCE (Constant)
- FILE_RESERVE_OPFILTER (Constant)
- FILE_SEQUENTIAL_ONLY (Constant)
- FILE_SHARE_DELETE (Constant)
- FILE_SHARE_READ (Constant)
- FILE_SHARE_WRITE (Constant)
- FILE_SKIP_COMPLETION_PORT_ON_SUCCESS (Constant)
- FILE_SKIP_SET_EVENT_ON_HANDLE (Constant)
- FILE_STANDARD_INFORMATION (extern struct)
- FILE_SUPERSEDE (Constant)
- FILE_SYNCHRONOUS_IO_ALERT (Constant)
- FILE_SYNCHRONOUS_IO_NONALERT (Constant)
- FILE_TRAVERSE (Constant)
- FILE_WRITE_ACCESS (Constant)
- FILE_WRITE_ATTRIBUTES (Constant)
- FILE_WRITE_DATA (Constant)
- FILE_WRITE_EA (Constant)
- FILE_WRITE_THROUGH (Constant)
- FileInformationIterator (Type Function)
- FileNotifyChangeFilter (struct)
- FILETIME (extern struct)
- fileTimeToNanoSeconds (Function)
- FindClose (Function)
- FLOAT (Type)
- FLOATING_SAVE_AREA (Type)
- FLS_CALLBACK_INFO (opaque)
- FOLDERID_LocalAppData (Constant)
- FOREGROUND_BLUE (Constant)
- FOREGROUND_GREEN (Constant)
- FOREGROUND_INTENSITY (Constant)
- FOREGROUND_RED (Constant)
- FORMAT_MESSAGE_ALLOCATE_BUFFER (Constant)
- FORMAT_MESSAGE_ARGUMENT_ARRAY (Constant)
- FORMAT_MESSAGE_FROM_HMODULE (Constant)
- FORMAT_MESSAGE_FROM_STRING (Constant)
- FORMAT_MESSAGE_FROM_SYSTEM (Constant)
- FORMAT_MESSAGE_IGNORE_INSERTS (Constant)
- FORMAT_MESSAGE_MAX_WIDTH_MASK (Constant)
- FreeEnvironmentStringsW (Function)
- FreeLibrary (Function)
- fromSysTime (Function)
- FS_INFORMATION_CLASS (enum)
- FSCTL_GET_REPARSE_POINT (Constant)
- FSCTL_SET_REPARSE_POINT (Constant)
- GENERIC_ALL (Constant)
- GENERIC_EXECUTE (Constant)
- GENERIC_READ (Constant)
- GENERIC_WRITE (Constant)
- GetCurrentDirectory (Function)
- GetCurrentDirectoryError (Error Set)
- GetCurrentProcess (Function)
- GetCurrentProcessId (Function)
- GetCurrentThread (Function)
- GetCurrentThreadId (Function)
- GetEnvironmentStringsError (Error Set)
- GetEnvironmentStringsW (Function)
- GetEnvironmentVariableError (Error Set)
- GetEnvironmentVariableW (Function)
- GetFileAttributes (Function)
- GetFileAttributesError (Error Set)
- GetFileAttributesW (Function)
- GetFileSizeError (Error Set)
- GetFileSizeEx (Function)
- GetFinalPathNameByHandle (Function)
- GetFinalPathNameByHandleError (Error Set)
- GetFinalPathNameByHandleFormat (struct)
- GetLastError (Function)
- GetModuleFileNameW (Function)
- getNamespacePrefix (Function)
- GetOverlappedResult (Function)
- getpeername (Function)
- GetProcessMemoryInfo (Function)
- GetProcessMemoryInfoError (Error Set)
- GetQueuedCompletionStatus (Function)
- GetQueuedCompletionStatusError (Error Set)
- GetQueuedCompletionStatusEx (Function)
- GetQueuedCompletionStatusResult (enum)
- getsockname (Function)
- GetStdHandle (Function)
- GetStdHandleError (Error Set)
- getUnprefixedPathType (Function)
- GUID (extern struct)
- HANDLE (Type)
- HANDLE_FLAG_INHERIT (Constant)
- HANDLE_FLAG_PROTECT_FROM_CLOSE (Constant)
- HANDLER_ROUTINE (Type)
- HBRUSH (Type)
- HCRYPTPROV (Type)
- HCURSOR (Type)
- HDC (Type)
- HEAP_CREATE_ENABLE_EXECUTE (Constant)
- HEAP_GENERATE_EXCEPTIONS (Constant)
- HEAP_NO_SERIALIZE (Constant)
- HEAP_REALLOC_IN_PLACE_ONLY (Constant)
- HGLRC (Type)
- HICON (Type)
- HINSTANCE (Type)
- HKEY (Type)
- HKEY_CLASSES_ROOT (Constant)
- HKEY_CURRENT_CONFIG (Constant)
- HKEY_CURRENT_USER (Constant)
- HKEY_CURRENT_USER_LOCAL_SETTINGS (Constant)
- HKEY_DYN_DATA (Constant)
- HKEY_LOCAL_MACHINE (Constant)
- HKEY_PERFORMANCE_DATA (Constant)
- HKEY_PERFORMANCE_NLSTEXT (Constant)
- HKEY_PERFORMANCE_TEXT (Constant)
- HKEY_USERS (Constant)
- HLOCAL (Type)
- HMENU (Type)
- HMODULE (Type)
- HRESULT (Type)
- HRESULT_CODE (Function)
- HWND (Type)
- IMAGE_TLS_DIRECTORY (extern struct)
- IMAGE_TLS_DIRECTORY32 (extern struct)
- IMAGE_TLS_DIRECTORY64 (extern struct)
- INFINITE (Constant)
- INIT_ONCE (extern struct)
- INIT_ONCE_FN (Type)
- INIT_ONCE_STATIC_INIT (Constant)
- InitOnceExecuteOnce (Function)
- INT (Type)
- INVALID_FILE_ATTRIBUTES (Constant)
- INVALID_HANDLE_VALUE (Constant)
- IO_APC_ROUTINE (Type)
- IO_REPARSE_TAG_MOUNT_POINT (Constant)
- IO_REPARSE_TAG_SYMLINK (Constant)
- IO_STATUS_BLOCK (extern struct)
- IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH (Constant)
- IOCTL_MOUNTMGR_QUERY_POINTS (Constant)
- IsProcessorFeaturePresent (Function)
- KAFFINITY (Type)
- kernel32 (struct)
- KEY_ALL_ACCESS (Constant)
- KEY_CREATE_LINK (Constant)
- KEY_CREATE_SUB_KEY (Constant)
- KEY_ENUMERATE_SUB_KEYS (Constant)
- KEY_EXECUTE (Constant)
- KEY_NOTIFY (Constant)
- KEY_QUERY_VALUE (Constant)
- KEY_READ (Constant)
- KEY_SET_VALUE (Constant)
- KEY_WOW64_32KEY (Constant)
- KEY_WOW64_64KEY (Constant)
- KEY_WRITE (Constant)
- KF_FLAG_ALIAS_ONLY (Constant)
- KF_FLAG_CREATE (Constant)
- KF_FLAG_DEFAULT (Constant)
- KF_FLAG_DEFAULT_PATH (Constant)
- KF_FLAG_DONT_UNEXPAND (Constant)
- KF_FLAG_DONT_VERIFY (Constant)
- KF_FLAG_INIT (Constant)
- KF_FLAG_NO_ALIAS (Constant)
- KF_FLAG_NO_APPCONTAINER_REDIRECTION (Constant)
- KF_FLAG_NOT_PARENT_RELATIVE (Constant)
- KF_FLAG_SIMPLE_IDLIST (Constant)
- KNONVOLATILE_CONTEXT_POINTERS (Type)
- KNOWNFOLDERID (extern struct)
- KPRIORITY (Type)
- KSYSTEM_TIME (extern struct)
- KUSER_SHARED_DATA (extern struct)
- LANG (struct)
- LANGID (Type)
- LARGE_INTEGER (Type)
- LDR_DATA_TABLE_ENTRY (extern struct)
- LIST_ENTRY (extern struct)
- listen (Function)
- LoadLibraryError (Error Set)
- LoadLibraryExW (Function)
- LoadLibraryFlags (enum)
- LoadLibraryW (Function)
- loadWinsockExtensionFunction (Function)
- LockFile (Function)
- LockFileError (Error Set)
- LONG (Type)
- LONG_PTR (Type)
- LONGLONG (Type)
- LPARAM (Type)
- LPCSTR (Type)
- LPCTSTR (Constant)
- LPCVOID (Type)
- LPCWSTR (Type)
- LPOVERLAPPED_COMPLETION_ROUTINE (Type)
- LPSTR (Type)
- LPTHREAD_START_ROUTINE (Type)
- LPTSTR (Constant)
- LPVOID (Type)
- LPWSTR (Type)
- LRESULT (Type)
- LSTATUS (Type)
- M128A (Type)
- MAX_MODULE_NAME32 (Constant)
- MAX_PATH (Constant)
- MAX_WOW64_SHARED_ENTRIES (Constant)
- MAXIMUM_ALLOWED (Constant)
- MAXIMUM_REPARSE_DATA_BUFFER_SIZE (Constant)
- MAXIMUM_WAIT_OBJECTS (Constant)
- MAXIMUM_XSTATE_FEATURES (Constant)
- MEM_COALESCE_PLACEHOLDERS (Constant)
- MEM_COMMIT (Constant)
- MEM_DECOMMIT (Constant)
- MEM_FREE (Constant)
- MEM_IMAGE (Constant)
- MEM_LARGE_PAGES (Constant)
- MEM_MAPPED (Constant)
- MEM_PHYSICAL (Constant)
- MEM_PRESERVE_PLACEHOLDER (Constant)
- MEM_PRIVATE (Constant)
- MEM_RELEASE (Constant)
- MEM_RESERVE (Constant)
- MEM_RESERVE_PLACEHOLDER (Constant)
- MEM_RESERVE_PLACEHOLDERS (Constant)
- MEM_RESET (Constant)
- MEM_RESET_UNDO (Constant)
- MEM_TOP_DOWN (Constant)
- MEM_WRITE_WATCH (Constant)
- MEMORY_BASIC_INFORMATION (extern struct)
- MODULEENTRY32 (extern struct)
- MODULEINFO (extern struct)
- MOUNT_POINT_REPARSE_BUFFER (extern struct)
- MOUNTMGR_MOUNT_POINT (extern struct)
- MOUNTMGR_MOUNT_POINTS (extern struct)
- MOUNTMGR_TARGET_NAME (extern struct)
- MOUNTMGR_VOLUME_PATHS (extern struct)
- MOUNTMGRCONTROLTYPE (Constant)
- MOVEFILE_COPY_ALLOWED (Constant)
- MOVEFILE_CREATE_HARDLINK (Constant)
- MOVEFILE_DELAY_UNTIL_REBOOT (Constant)
- MOVEFILE_FAIL_IF_NOT_TRACKABLE (Constant)
- MOVEFILE_REPLACE_EXISTING (Constant)
- MOVEFILE_WRITE_THROUGH (Constant)
- MoveFileError (Error Set)
- MoveFileEx (Function)
- MoveFileExW (Function)
- NAME_MAX (Constant)
- NamespacePrefix (enum)
- nanoSecondsToFileTime (Function)
- NEON128 (Type)
- nls (struct)
- normalizePath (Function)
- NT_PRODUCT_TYPE (enum)
- NT_TIB (extern struct)
- NtAllocateVirtualMemory (Function)
- NtAllocateVirtualMemoryError (Error Set)
- ntdll (struct)
- NtFreeVirtualMemory (Function)
- NtFreeVirtualMemoryError (Error Set)
- NTSTATUS (enum)
- ntToWin32Namespace (Function)
- OBJ_CASE_INSENSITIVE (Constant)
- OBJ_EXCLUSIVE (Constant)
- OBJ_INHERIT (Constant)
- OBJ_KERNEL_HANDLE (Constant)
- OBJ_OPENIF (Constant)
- OBJ_OPENLINK (Constant)
- OBJ_PERMANENT (Constant)
- OBJ_VALID_ATTRIBUTES (Constant)
- OBJECT_ATTRIBUTES (extern struct)
- OBJECT_INFORMATION_CLASS (enum)
- OBJECT_NAME_INFORMATION (extern struct)
- OPEN_ALWAYS (Constant)
- OPEN_EXISTING (Constant)
- OpenError (Error Set)
- OpenFile (Function)
- OpenFileOptions (struct)
- OSVERSIONINFOW (extern struct)
- OVERLAPPED (extern struct)
- OVERLAPPED_ENTRY (extern struct)
- PAGE_EXECUTE (Constant)
- PAGE_EXECUTE_READ (Constant)
- PAGE_EXECUTE_READWRITE (Constant)
- PAGE_EXECUTE_WRITECOPY (Constant)
- PAGE_GUARD (Constant)
- PAGE_NOACCESS (Constant)
- PAGE_NOCACHE (Constant)
- PAGE_READONLY (Constant)
- PAGE_READWRITE (Constant)
- PAGE_TARGETS_INVALID (Constant)
- PAGE_TARGETS_NO_UPDATE (Constant)
- PAGE_WRITECOMBINE (Constant)
- PAGE_WRITECOPY (Constant)
- PATH_MAX_WIDE (Constant)
- PathSpace (struct)
- PCTSTR (Constant)
- PCWSTR (Type)
- peb (Function)
- PEB (extern struct)
- PEB_LDR_DATA (extern struct)
- PENUM_PAGE_FILE_CALLBACKA (Type)
- PENUM_PAGE_FILE_CALLBACKW (Type)
- PERFORMANCE_INFORMATION (extern struct)
- PF (enum)
- PIMAGE_TLS_CALLBACK (Type)
- PIPE_ACCESS_DUPLEX (Constant)
- PIPE_ACCESS_INBOUND (Constant)
- PIPE_ACCESS_OUTBOUND (Constant)
- PIPE_NOWAIT (Constant)
- PIPE_READMODE_BYTE (Constant)
- PIPE_READMODE_MESSAGE (Constant)
- PIPE_TYPE_BYTE (Constant)
- PIPE_TYPE_MESSAGE (Constant)
- PIPE_WAIT (Constant)
- PMEMORY_BASIC_INFORMATION (Type)
- POINT (extern struct)
- poll (Function)
- PostQueuedCompletionStatus (Function)
- PostQueuedCompletionStatusError (Error Set)
- PPS_POST_PROCESS_INIT_ROUTINE (Type)
- PROC (Type)
- PROCESS_BASIC_INFORMATION (extern struct)
- PROCESS_INFORMATION (extern struct)
- PROCESS_MEMORY_COUNTERS (extern struct)
- PROCESS_MEMORY_COUNTERS_EX (extern struct)
- ProcessBaseAddress (Function)
- ProcessBaseAddressError (Error Set)
- PROCESSINFOCLASS (enum)
- PROCESSOR_FEATURE_MAX (Constant)
- PROV_RSA_FULL (Constant)
- PSAPI_WS_WATCH_INFORMATION (extern struct)
- PSAPI_WS_WATCH_INFORMATION_EX (extern struct)
- PTHREAD_START_ROUTINE (Type)
- PTSTR (Constant)
- PVOID (Type)
- PWSTR (Type)
- QueryObjectName (Function)
- QueryObjectNameError (Error Set)
- QueryPerformanceCounter (Function)
- QueryPerformanceFrequency (Function)
- READ_CONTROL (Constant)
- ReadFile (Function)
- ReadFileError (Error Set)
- ReadLink (Function)
- ReadLinkError (Error Set)
- ReadMemoryError (Error Set)
- ReadProcessMemory (Function)
- RECT (extern struct)
- recvfrom (Function)
- REG (struct)
- REG_OPTION_OPEN_LINK (Constant)
- REGSAM (Type)
- RemoveDotDirsError (Error Set)
- removeDotDirsSanitized (Function)
- REPARSE_DATA_BUFFER (extern struct)
- reparse_tag_name_surrogate_bit (Constant)
- RTL_BITMAP (opaque)
- RTL_CRITICAL_SECTION (extern struct)
- RTL_CRITICAL_SECTION_DEBUG (extern struct)
- RTL_DRIVE_LETTER_CURDIR (extern struct)
- RTL_OSVERSIONINFOW (extern struct)
- RTL_QUERY_REGISTRY_DELETE (Constant)
- RTL_QUERY_REGISTRY_DIRECT (Constant)
- RTL_QUERY_REGISTRY_NOEXPAND (Constant)
- RTL_QUERY_REGISTRY_NOVALUE (Constant)
- RTL_QUERY_REGISTRY_REQUIRED (Constant)
- RTL_QUERY_REGISTRY_ROUTINE (Type)
- RTL_QUERY_REGISTRY_SUBKEY (Constant)
- RTL_QUERY_REGISTRY_TABLE (extern struct)
- RTL_QUERY_REGISTRY_TOPKEY (Constant)
- RTL_QUERY_REGISTRY_TYPECHECK (Constant)
- RTL_REGISTRY_ABSOLUTE (Constant)
- RTL_REGISTRY_CONTROL (Constant)
- RTL_REGISTRY_DEVICEMAP (Constant)
- RTL_REGISTRY_HANDLE (Constant)
- RTL_REGISTRY_MAXIMUM (Constant)
- RTL_REGISTRY_OPTIONAL (Constant)
- RTL_REGISTRY_SERVICES (Constant)
- RTL_REGISTRY_USER (Constant)
- RTL_REGISTRY_WINDOWS_NT (Constant)
- RTL_RUN_ONCE (extern struct)
- RTL_RUN_ONCE_INIT (Constant)
- RTL_USER_PROCESS_PARAMETERS (extern struct)
- RtlGenRandom (Function)
- RtlGenRandomError (Error Set)
- RUNTIME_FUNCTION (Type)
- S_FALSE (Constant)
- S_OK (Constant)
- SEC_64K_PAGES (Constant)
- SEC_COMMIT (Constant)
- SEC_FILE (Constant)
- SEC_IMAGE (Constant)
- SEC_IMAGE_NO_EXECUTE (Constant)
- SEC_LARGE_PAGES (Constant)
- SEC_NOCACHE (Constant)
- SEC_PROTECTED_IMAGE (Constant)
- SEC_RESERVE (Constant)
- SEC_WRITECOMBINE (Constant)
- SECTION_ALL_ACCESS (Constant)
- SECTION_EXTEND_SIZE (Constant)
- SECTION_INHERIT (enum)
- SECTION_MAP_EXECUTE (Constant)
- SECTION_MAP_READ (Constant)
- SECTION_MAP_WRITE (Constant)
- SECTION_QUERY (Constant)
- SECURITY_ATTRIBUTES (extern struct)
- self_process_handle (Constant)
- sendmsg (Function)
- sendto (Function)
- SetConsoleCtrlHandler (Function)
- SetConsoleTextAttribute (Function)
- SetConsoleTextAttributeError (Error Set)
- SetCurrentDirectory (Function)
- SetCurrentDirectoryError (Error Set)
- SetFileCompletionNotificationModes (Function)
- SetFilePointerError (Error Set)
- SetFilePointerEx_BEGIN (Function)
- SetFilePointerEx_CURRENT (Function)
- SetFilePointerEx_CURRENT_get (Function)
- SetFilePointerEx_END (Function)
- SetFileTime (Function)
- SetFileTimeError (Error Set)
- SetHandleInformation (Function)
- SetHandleInformationError (Error Set)
- SharedUserData (Constant)
- SHORT (Type)
- SIZE_T (Type)
- sliceToPrefixedFileW (Function)
- SMALL_RECT (extern struct)
- SRWLOCK (extern struct)
- SRWLOCK_INIT (Constant)
- STANDARD_RIGHTS_EXECUTE (Constant)
- STANDARD_RIGHTS_READ (Constant)
- STANDARD_RIGHTS_REQUIRED (Constant)
- STANDARD_RIGHTS_WRITE (Constant)
- STARTF_FORCEOFFFEEDBACK (Constant)
- STARTF_FORCEONFEEDBACK (Constant)
- STARTF_PREVENTPINNING (Constant)
- STARTF_RUNFULLSCREEN (Constant)
- STARTF_TITLEISAPPID (Constant)
- STARTF_TITLEISLINKNAME (Constant)
- STARTF_UNTRUSTEDSOURCE (Constant)
- STARTF_USECOUNTCHARS (Constant)
- STARTF_USEFILLATTRIBUTE (Constant)
- STARTF_USEHOTKEY (Constant)
- STARTF_USEPOSITION (Constant)
- STARTF_USESHOWWINDOW (Constant)
- STARTF_USESIZE (Constant)
- STARTF_USESTDHANDLES (Constant)
- STARTUPINFOW (extern struct)
- STD_ERROR_HANDLE (Constant)
- STD_INPUT_HANDLE (Constant)
- STD_OUTPUT_HANDLE (Constant)
- SUBLANG (struct)
- SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE (Constant)
- SYMBOLIC_LINK_FLAG_DIRECTORY (Constant)
- SYMBOLIC_LINK_REPARSE_BUFFER (extern struct)
- SYMLINK_FLAG_RELATIVE (Constant)
- SYNCHRONIZE (Constant)
- SYSTEM_BASIC_INFORMATION (extern struct)
- SYSTEM_INFO (extern struct)
- SYSTEM_INFORMATION_CLASS (enum)
- TCHAR (Constant)
- teb (Function)
- TEB (extern struct)
- TerminateProcess (Function)
- TerminateProcessError (Error Set)
- TH32CS_INHERIT (Constant)
- TH32CS_SNAPALL (Constant)
- TH32CS_SNAPHEAPLIST (Constant)
- TH32CS_SNAPMODULE (Constant)
- TH32CS_SNAPMODULE32 (Constant)
- TH32CS_SNAPPROCESS (Constant)
- TH32CS_SNAPTHREAD (Constant)
- THREAD_BASIC_INFORMATION (extern struct)
- THREADINFOCLASS (enum)
- TLS_OUT_OF_INDEXES (Constant)
- toSysTime (Function)
- TransferType (enum)
- TRUE (Constant)
- TRUNCATE_EXISTING (Constant)
- UCHAR (Type)
- UINT (Type)
- ULARGE_INTEGER (Type)
- ULONG (Type)
- ULONG_PTR (Type)
- ULONG64 (Type)
- ULONGLONG (Type)
- unexpectedError (Function)
- unexpectedStatus (Function)
- unexpectedWSAError (Function)
- UNICODE_STRING (extern struct)
- UnlockFile (Function)
- UnlockFileError (Error Set)
- UnprefixedPathType (enum)
- UNW_FLAG_CHAININFO (Constant)
- UNW_FLAG_EHANDLER (Constant)
- UNW_FLAG_NHANDLER (Constant)
- UNW_FLAG_UHANDLER (Constant)
- UNWIND_HISTORY_TABLE (extern struct)
- UNWIND_HISTORY_TABLE_ENTRY (extern struct)
- UNWIND_HISTORY_TABLE_SIZE (Constant)
- USHORT (Type)
- va_list (Type)
- VECTORED_EXCEPTION_HANDLER (Type)
- VirtualAlloc (Function)
- VirtualAllocError (Error Set)
- VirtualFree (Function)
- VirtualProtect (Function)
- VirtualProtectError (Error Set)
- VirtualProtectEx (Function)
- VirtualQuery (Function)
- VirtualQueryError (Error Set)
- VM_COUNTERS (extern struct)
- VOLUME_NAME_DOS (Constant)
- VOLUME_NAME_GUID (Constant)
- VOLUME_NAME_NONE (Constant)
- VOLUME_NAME_NT (Constant)
- WAIT_ABANDONED (Constant)
- WAIT_ABANDONED_0 (Constant)
- WAIT_FAILED (Constant)
- WAIT_OBJECT_0 (Constant)
- WAIT_TIMEOUT (Constant)
- WaitForMultipleObjectsEx (Function)
- WaitForSingleObject (Function)
- WaitForSingleObjectError (Error Set)
- WaitForSingleObjectEx (Function)
- WCHAR (Type)
- WIN32_FIND_DATAW (extern struct)
- Win32Error (enum)
- WINAPI (Constant)
- WORD (Type)
- WPARAM (Type)
- WRITE_DAC (Constant)
- WRITE_OWNER (Constant)
- WriteFile (Function)
- WriteFileError (Error Set)
- WriteMemoryError (Error Set)
- WriteProcessMemory (Function)
- ws2_32 (struct)
- WSACleanup (Function)
- WSAIoctl (Function)
- WSASocketW (Function)
- WSAStartup (Function)
- Wtf16ToPrefixedFileWError (Error Set)
- Wtf8ToPrefixedFileWError (Error Set)
- wToPrefixedFileW (Function)
- XMM_SAVE_AREA32 (Type)
- XSTATE_CONFIGURATION (extern struct)
- XSTATE_FEATURE (extern struct)
Source
//! This file contains thin wrappers around Windows-specific APIs, with these
//! specific goals in mind:
//! * Convert "errno"-style error codes into Zig errors.
//! * When null-terminated or WTF16LE byte buffers are required, provide APIs which accept
//! slices as well as APIs which accept null-terminated WTF16LE byte buffers.
const builtin = @import("builtin");
const std = @import("../std.zig");
const mem = std.mem;
const assert = std.debug.assert;
const math = std.math;
const maxInt = std.math.maxInt;
const native_arch = builtin.cpu.arch;
const UnexpectedError = std.posix.UnexpectedError;
test {
if (builtin.os.tag == .windows) {
_ = @import("windows/test.zig");
}
}
pub const advapi32 = @import("windows/advapi32.zig");
pub const kernel32 = @import("windows/kernel32.zig");
pub const ntdll = @import("windows/ntdll.zig");
pub const ws2_32 = @import("windows/ws2_32.zig");
pub const crypt32 = @import("windows/crypt32.zig");
pub const nls = @import("windows/nls.zig");
pub const self_process_handle = @as(HANDLE, @ptrFromInt(maxInt(usize)));
const Self = @This();
pub const OpenError = error{
IsDir,
NotDir,
FileNotFound,
NoDevice,
AccessDenied,
PipeBusy,
PathAlreadyExists,
Unexpected,
NameTooLong,
WouldBlock,
NetworkNotFound,
AntivirusInterference,
BadPathName,
};
pub const OpenFileOptions = struct {
access_mask: ACCESS_MASK,
dir: ?HANDLE = null,
sa: ?*SECURITY_ATTRIBUTES = null,
share_access: ULONG = FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
creation: ULONG,
/// If true, tries to open path as a directory.
/// Defaults to false.
filter: Filter = .file_only,
/// If false, tries to open path as a reparse point without dereferencing it.
/// Defaults to true.
follow_symlinks: bool = true,
pub const Filter = enum {
/// Causes `OpenFile` to return `error.IsDir` if the opened handle would be a directory.
file_only,
/// Causes `OpenFile` to return `error.NotDir` if the opened handle would be a file.
dir_only,
/// `OpenFile` does not discriminate between opening files and directories.
any,
};
};
pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HANDLE {
if (mem.eql(u16, sub_path_w, &[_]u16{'.'}) and options.filter == .file_only) {
return error.IsDir;
}
if (mem.eql(u16, sub_path_w, &[_]u16{ '.', '.' }) and options.filter == .file_only) {
return error.IsDir;
}
var result: HANDLE = undefined;
const path_len_bytes = math.cast(u16, sub_path_w.len * 2) orelse return error.NameTooLong;
var nt_name = UNICODE_STRING{
.Length = path_len_bytes,
.MaximumLength = path_len_bytes,
.Buffer = @constCast(sub_path_w.ptr),
};
var attr = OBJECT_ATTRIBUTES{
.Length = @sizeOf(OBJECT_ATTRIBUTES),
.RootDirectory = if (std.fs.path.isAbsoluteWindowsWTF16(sub_path_w)) null else options.dir,
.Attributes = if (options.sa) |ptr| blk: { // Note we do not use OBJ_CASE_INSENSITIVE here.
const inherit: ULONG = if (ptr.bInheritHandle == TRUE) OBJ_INHERIT else 0;
break :blk inherit;
} else 0,
.ObjectName = &nt_name,
.SecurityDescriptor = if (options.sa) |ptr| ptr.lpSecurityDescriptor else null,
.SecurityQualityOfService = null,
};
var io: IO_STATUS_BLOCK = undefined;
const blocking_flag: ULONG = FILE_SYNCHRONOUS_IO_NONALERT;
const file_or_dir_flag: ULONG = switch (options.filter) {
.file_only => FILE_NON_DIRECTORY_FILE,
.dir_only => FILE_DIRECTORY_FILE,
.any => 0,
};
// If we're not following symlinks, we need to ensure we don't pass in any synchronization flags such as FILE_SYNCHRONOUS_IO_NONALERT.
const flags: ULONG = if (options.follow_symlinks) file_or_dir_flag | blocking_flag else file_or_dir_flag | FILE_OPEN_REPARSE_POINT;
while (true) {
const rc = ntdll.NtCreateFile(
&result,
options.access_mask,
&attr,
&io,
null,
FILE_ATTRIBUTE_NORMAL,
options.share_access,
options.creation,
flags,
null,
0,
);
switch (rc) {
.SUCCESS => return result,
.OBJECT_NAME_INVALID => return error.BadPathName,
.OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
.OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
.BAD_NETWORK_PATH => return error.NetworkNotFound, // \\server was not found
.BAD_NETWORK_NAME => return error.NetworkNotFound, // \\server was found but \\server\share wasn't
.NO_MEDIA_IN_DEVICE => return error.NoDevice,
.INVALID_PARAMETER => unreachable,
.SHARING_VIOLATION => return error.AccessDenied,
.ACCESS_DENIED => return error.AccessDenied,
.PIPE_BUSY => return error.PipeBusy,
.PIPE_NOT_AVAILABLE => return error.NoDevice,
.OBJECT_PATH_SYNTAX_BAD => unreachable,
.OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
.FILE_IS_A_DIRECTORY => return error.IsDir,
.NOT_A_DIRECTORY => return error.NotDir,
.USER_MAPPED_FILE => return error.AccessDenied,
.INVALID_HANDLE => unreachable,
.DELETE_PENDING => {
// This error means that there *was* a file in this location on
// the file system, but it was deleted. However, the OS is not
// finished with the deletion operation, and so this CreateFile
// call has failed. There is not really a sane way to handle
// this other than retrying the creation after the OS finishes
// the deletion.
std.time.sleep(std.time.ns_per_ms);
continue;
},
.VIRUS_INFECTED, .VIRUS_DELETED => return error.AntivirusInterference,
else => return unexpectedStatus(rc),
}
}
}
pub fn GetCurrentProcess() HANDLE {
const process_pseudo_handle: usize = @bitCast(@as(isize, -1));
return @ptrFromInt(process_pseudo_handle);
}
pub fn GetCurrentProcessId() DWORD {
return @truncate(@intFromPtr(teb().ClientId.UniqueProcess));
}
pub fn GetCurrentThread() HANDLE {
const thread_pseudo_handle: usize = @bitCast(@as(isize, -2));
return @ptrFromInt(thread_pseudo_handle);
}
pub fn GetCurrentThreadId() DWORD {
return @truncate(@intFromPtr(teb().ClientId.UniqueThread));
}
pub fn GetLastError() Win32Error {
return @enumFromInt(teb().LastErrorValue);
}
pub const CreatePipeError = error{ Unexpected, SystemResources };
var npfs: ?HANDLE = null;
/// A Zig wrapper around `NtCreateNamedPipeFile` and `NtCreateFile` syscalls.
/// It implements similar behavior to `CreatePipe` and is meant to serve
/// as a direct substitute for that call.
pub fn CreatePipe(rd: *HANDLE, wr: *HANDLE, sattr: *const SECURITY_ATTRIBUTES) CreatePipeError!void {
// Up to NT 5.2 (Windows XP/Server 2003), `CreatePipe` would generate a pipe similar to:
//
// \??\pipe\Win32Pipes.{pid}.{count}
//
// where `pid` is the process id and count is a incrementing counter.
// The implementation was changed after NT 6.0 (Vista) to open a handle to the Named Pipe File System
// and use that as the root directory for `NtCreateNamedPipeFile`.
// This object is visible under the NPFS but has no filename attached to it.
//
// This implementation replicates how `CreatePipe` works in modern Windows versions.
const opt_dev_handle = @atomicLoad(?HANDLE, &npfs, .seq_cst);
const dev_handle = opt_dev_handle orelse blk: {
const str = std.unicode.utf8ToUtf16LeStringLiteral("\\Device\\NamedPipe\\");
const len: u16 = @truncate(str.len * @sizeOf(u16));
const name = UNICODE_STRING{
.Length = len,
.MaximumLength = len,
.Buffer = @constCast(@ptrCast(str)),
};
const attrs = OBJECT_ATTRIBUTES{
.ObjectName = @constCast(&name),
.Length = @sizeOf(OBJECT_ATTRIBUTES),
.RootDirectory = null,
.Attributes = 0,
.SecurityDescriptor = null,
.SecurityQualityOfService = null,
};
var iosb: IO_STATUS_BLOCK = undefined;
var handle: HANDLE = undefined;
switch (ntdll.NtCreateFile(
&handle,
GENERIC_READ | SYNCHRONIZE,
@constCast(&attrs),
&iosb,
null,
0,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_OPEN,
FILE_SYNCHRONOUS_IO_NONALERT,
null,
0,
)) {
.SUCCESS => {},
// Judging from the ReactOS sources this is technically possible.
.INSUFFICIENT_RESOURCES => return error.SystemResources,
.INVALID_PARAMETER => unreachable,
else => |e| return unexpectedStatus(e),
}
if (@cmpxchgStrong(?HANDLE, &npfs, null, handle, .seq_cst, .seq_cst)) |xchg| {
CloseHandle(handle);
break :blk xchg.?;
} else break :blk handle;
};
const name = UNICODE_STRING{ .Buffer = null, .Length = 0, .MaximumLength = 0 };
var attrs = OBJECT_ATTRIBUTES{
.ObjectName = @constCast(&name),
.Length = @sizeOf(OBJECT_ATTRIBUTES),
.RootDirectory = dev_handle,
.Attributes = OBJ_CASE_INSENSITIVE,
.SecurityDescriptor = sattr.lpSecurityDescriptor,
.SecurityQualityOfService = null,
};
if (sattr.bInheritHandle != 0) attrs.Attributes |= OBJ_INHERIT;
// 120 second relative timeout in 100ns units.
const default_timeout: LARGE_INTEGER = (-120 * std.time.ns_per_s) / 100;
var iosb: IO_STATUS_BLOCK = undefined;
var read: HANDLE = undefined;
switch (ntdll.NtCreateNamedPipeFile(
&read,
GENERIC_READ | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
&attrs,
&iosb,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_CREATE,
FILE_SYNCHRONOUS_IO_NONALERT,
FILE_PIPE_BYTE_STREAM_TYPE,
FILE_PIPE_BYTE_STREAM_MODE,
FILE_PIPE_QUEUE_OPERATION,
1,
4096,
4096,
@constCast(&default_timeout),
)) {
.SUCCESS => {},
.INVALID_PARAMETER => unreachable,
.INSUFFICIENT_RESOURCES => return error.SystemResources,
else => |e| return unexpectedStatus(e),
}
errdefer CloseHandle(read);
attrs.RootDirectory = read;
var write: HANDLE = undefined;
switch (ntdll.NtCreateFile(
&write,
GENERIC_WRITE | SYNCHRONIZE | FILE_READ_ATTRIBUTES,
&attrs,
&iosb,
null,
0,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_OPEN,
FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE,
null,
0,
)) {
.SUCCESS => {},
.INVALID_PARAMETER => unreachable,
.INSUFFICIENT_RESOURCES => return error.SystemResources,
else => |e| return unexpectedStatus(e),
}
rd.* = read;
wr.* = write;
}
pub fn CreateEventEx(attributes: ?*SECURITY_ATTRIBUTES, name: []const u8, flags: DWORD, desired_access: DWORD) !HANDLE {
const nameW = try sliceToPrefixedFileW(null, name);
return CreateEventExW(attributes, nameW.span().ptr, flags, desired_access);
}
pub fn CreateEventExW(attributes: ?*SECURITY_ATTRIBUTES, nameW: ?LPCWSTR, flags: DWORD, desired_access: DWORD) !HANDLE {
const handle = kernel32.CreateEventExW(attributes, nameW, flags, desired_access);
if (handle) |h| {
return h;
} else {
switch (GetLastError()) {
else => |err| return unexpectedError(err),
}
}
}
pub const DeviceIoControlError = error{
AccessDenied,
/// The volume does not contain a recognized file system. File system
/// drivers might not be loaded, or the volume may be corrupt.
UnrecognizedVolume,
Unexpected,
};
/// A Zig wrapper around `NtDeviceIoControlFile` and `NtFsControlFile` syscalls.
/// It implements similar behavior to `DeviceIoControl` and is meant to serve
/// as a direct substitute for that call.
/// TODO work out if we need to expose other arguments to the underlying syscalls.
pub fn DeviceIoControl(
h: HANDLE,
ioControlCode: ULONG,
in: ?[]const u8,
out: ?[]u8,
) DeviceIoControlError!void {
// Logic from: https://doxygen.reactos.org/d3/d74/deviceio_8c.html
const is_fsctl = (ioControlCode >> 16) == FILE_DEVICE_FILE_SYSTEM;
var io: IO_STATUS_BLOCK = undefined;
const in_ptr = if (in) |i| i.ptr else null;
const in_len = if (in) |i| @as(ULONG, @intCast(i.len)) else 0;
const out_ptr = if (out) |o| o.ptr else null;
const out_len = if (out) |o| @as(ULONG, @intCast(o.len)) else 0;
const rc = blk: {
if (is_fsctl) {
break :blk ntdll.NtFsControlFile(
h,
null,
null,
null,
&io,
ioControlCode,
in_ptr,
in_len,
out_ptr,
out_len,
);
} else {
break :blk ntdll.NtDeviceIoControlFile(
h,
null,
null,
null,
&io,
ioControlCode,
in_ptr,
in_len,
out_ptr,
out_len,
);
}
};
switch (rc) {
.SUCCESS => {},
.PRIVILEGE_NOT_HELD => return error.AccessDenied,
.ACCESS_DENIED => return error.AccessDenied,
.INVALID_DEVICE_REQUEST => return error.AccessDenied, // Not supported by the underlying filesystem
.INVALID_PARAMETER => unreachable,
.UNRECOGNIZED_VOLUME => return error.UnrecognizedVolume,
else => return unexpectedStatus(rc),
}
}
pub fn GetOverlappedResult(h: HANDLE, overlapped: *OVERLAPPED, wait: bool) !DWORD {
var bytes: DWORD = undefined;
if (kernel32.GetOverlappedResult(h, overlapped, &bytes, @intFromBool(wait)) == 0) {
switch (GetLastError()) {
.IO_INCOMPLETE => if (!wait) return error.WouldBlock else unreachable,
else => |err| return unexpectedError(err),
}
}
return bytes;
}
pub const SetHandleInformationError = error{Unexpected};
pub fn SetHandleInformation(h: HANDLE, mask: DWORD, flags: DWORD) SetHandleInformationError!void {
if (kernel32.SetHandleInformation(h, mask, flags) == 0) {
switch (GetLastError()) {
else => |err| return unexpectedError(err),
}
}
}
pub const RtlGenRandomError = error{Unexpected};
/// Call RtlGenRandom() instead of CryptGetRandom() on Windows
/// https://github.com/rust-lang-nursery/rand/issues/111
/// https://bugzilla.mozilla.org/show_bug.cgi?id=504270
pub fn RtlGenRandom(output: []u8) RtlGenRandomError!void {
var total_read: usize = 0;
var buff: []u8 = output[0..];
const max_read_size: ULONG = maxInt(ULONG);
while (total_read < output.len) {
const to_read: ULONG = @min(buff.len, max_read_size);
if (advapi32.RtlGenRandom(buff.ptr, to_read) == 0) {
return unexpectedError(GetLastError());
}
total_read += to_read;
buff = buff[to_read..];
}
}
pub const WaitForSingleObjectError = error{
WaitAbandoned,
WaitTimeOut,
Unexpected,
};
pub fn WaitForSingleObject(handle: HANDLE, milliseconds: DWORD) WaitForSingleObjectError!void {
return WaitForSingleObjectEx(handle, milliseconds, false);
}
pub fn WaitForSingleObjectEx(handle: HANDLE, milliseconds: DWORD, alertable: bool) WaitForSingleObjectError!void {
switch (kernel32.WaitForSingleObjectEx(handle, milliseconds, @intFromBool(alertable))) {
WAIT_ABANDONED => return error.WaitAbandoned,
WAIT_OBJECT_0 => return,
WAIT_TIMEOUT => return error.WaitTimeOut,
WAIT_FAILED => switch (GetLastError()) {
else => |err| return unexpectedError(err),
},
else => return error.Unexpected,
}
}
pub fn WaitForMultipleObjectsEx(handles: []const HANDLE, waitAll: bool, milliseconds: DWORD, alertable: bool) !u32 {
assert(handles.len > 0 and handles.len <= MAXIMUM_WAIT_OBJECTS);
const nCount: DWORD = @as(DWORD, @intCast(handles.len));
switch (kernel32.WaitForMultipleObjectsEx(
nCount,
handles.ptr,
@intFromBool(waitAll),
milliseconds,
@intFromBool(alertable),
)) {
WAIT_OBJECT_0...WAIT_OBJECT_0 + MAXIMUM_WAIT_OBJECTS => |n| {
const handle_index = n - WAIT_OBJECT_0;
assert(handle_index < nCount);
return handle_index;
},
WAIT_ABANDONED_0...WAIT_ABANDONED_0 + MAXIMUM_WAIT_OBJECTS => |n| {
const handle_index = n - WAIT_ABANDONED_0;
assert(handle_index < nCount);
return error.WaitAbandoned;
},
WAIT_TIMEOUT => return error.WaitTimeOut,
WAIT_FAILED => switch (GetLastError()) {
else => |err| return unexpectedError(err),
},
else => return error.Unexpected,
}
}
pub const CreateIoCompletionPortError = error{Unexpected};
pub fn CreateIoCompletionPort(
file_handle: HANDLE,
existing_completion_port: ?HANDLE,
completion_key: usize,
concurrent_thread_count: DWORD,
) CreateIoCompletionPortError!HANDLE {
const handle = kernel32.CreateIoCompletionPort(file_handle, existing_completion_port, completion_key, concurrent_thread_count) orelse {
switch (GetLastError()) {
.INVALID_PARAMETER => unreachable,
else => |err| return unexpectedError(err),
}
};
return handle;
}
pub const PostQueuedCompletionStatusError = error{Unexpected};
pub fn PostQueuedCompletionStatus(
completion_port: HANDLE,
bytes_transferred_count: DWORD,
completion_key: usize,
lpOverlapped: ?*OVERLAPPED,
) PostQueuedCompletionStatusError!void {
if (kernel32.PostQueuedCompletionStatus(completion_port, bytes_transferred_count, completion_key, lpOverlapped) == 0) {
switch (GetLastError()) {
else => |err| return unexpectedError(err),
}
}
}
pub const GetQueuedCompletionStatusResult = enum {
Normal,
Aborted,
Cancelled,
EOF,
Timeout,
};
pub fn GetQueuedCompletionStatus(
completion_port: HANDLE,
bytes_transferred_count: *DWORD,
lpCompletionKey: *usize,
lpOverlapped: *?*OVERLAPPED,
dwMilliseconds: DWORD,
) GetQueuedCompletionStatusResult {
if (kernel32.GetQueuedCompletionStatus(
completion_port,
bytes_transferred_count,
lpCompletionKey,
lpOverlapped,
dwMilliseconds,
) == FALSE) {
switch (GetLastError()) {
.ABANDONED_WAIT_0 => return GetQueuedCompletionStatusResult.Aborted,
.OPERATION_ABORTED => return GetQueuedCompletionStatusResult.Cancelled,
.HANDLE_EOF => return GetQueuedCompletionStatusResult.EOF,
.WAIT_TIMEOUT => return GetQueuedCompletionStatusResult.Timeout,
else => |err| {
if (std.debug.runtime_safety) {
@setEvalBranchQuota(2500);
std.debug.panic("unexpected error: {}\n", .{err});
}
},
}
}
return GetQueuedCompletionStatusResult.Normal;
}
pub const GetQueuedCompletionStatusError = error{
Aborted,
Cancelled,
EOF,
Timeout,
} || UnexpectedError;
pub fn GetQueuedCompletionStatusEx(
completion_port: HANDLE,
completion_port_entries: []OVERLAPPED_ENTRY,
timeout_ms: ?DWORD,
alertable: bool,
) GetQueuedCompletionStatusError!u32 {
var num_entries_removed: u32 = 0;
const success = kernel32.GetQueuedCompletionStatusEx(
completion_port,
completion_port_entries.ptr,
@as(ULONG, @intCast(completion_port_entries.len)),
&num_entries_removed,
timeout_ms orelse INFINITE,
@intFromBool(alertable),
);
if (success == FALSE) {
return switch (GetLastError()) {
.ABANDONED_WAIT_0 => error.Aborted,
.OPERATION_ABORTED => error.Cancelled,
.HANDLE_EOF => error.EOF,
.WAIT_TIMEOUT => error.Timeout,
else => |err| unexpectedError(err),
};
}
return num_entries_removed;
}
pub fn CloseHandle(hObject: HANDLE) void {
assert(ntdll.NtClose(hObject) == .SUCCESS);
}
pub fn FindClose(hFindFile: HANDLE) void {
assert(kernel32.FindClose(hFindFile) != 0);
}
pub const ReadFileError = error{
BrokenPipe,
/// The specified network name is no longer available.
ConnectionResetByPeer,
OperationAborted,
/// Unable to read file due to lock.
LockViolation,
Unexpected,
};
/// If buffer's length exceeds what a Windows DWORD integer can hold, it will be broken into
/// multiple non-atomic reads.
pub fn ReadFile(in_hFile: HANDLE, buffer: []u8, offset: ?u64) ReadFileError!usize {
while (true) {
const want_read_count: DWORD = @min(@as(DWORD, maxInt(DWORD)), buffer.len);
var amt_read: DWORD = undefined;
var overlapped_data: OVERLAPPED = undefined;
const overlapped: ?*OVERLAPPED = if (offset) |off| blk: {
overlapped_data = .{
.Internal = 0,
.InternalHigh = 0,
.DUMMYUNIONNAME = .{
.DUMMYSTRUCTNAME = .{
.Offset = @as(u32, @truncate(off)),
.OffsetHigh = @as(u32, @truncate(off >> 32)),
},
},
.hEvent = null,
};
break :blk &overlapped_data;
} else null;
if (kernel32.ReadFile(in_hFile, buffer.ptr, want_read_count, &amt_read, overlapped) == 0) {
switch (GetLastError()) {
.IO_PENDING => unreachable,
.OPERATION_ABORTED => continue,
.BROKEN_PIPE => return 0,
.HANDLE_EOF => return 0,
.NETNAME_DELETED => return error.ConnectionResetByPeer,
.LOCK_VIOLATION => return error.LockViolation,
else => |err| return unexpectedError(err),
}
}
return amt_read;
}
}
pub const WriteFileError = error{
SystemResources,
OperationAborted,
BrokenPipe,
NotOpenForWriting,
/// The process cannot access the file because another process has locked
/// a portion of the file.
LockViolation,
/// The specified network name is no longer available.
ConnectionResetByPeer,
Unexpected,
};
pub fn WriteFile(
handle: HANDLE,
bytes: []const u8,
offset: ?u64,
) WriteFileError!usize {
var bytes_written: DWORD = undefined;
var overlapped_data: OVERLAPPED = undefined;
const overlapped: ?*OVERLAPPED = if (offset) |off| blk: {
overlapped_data = .{
.Internal = 0,
.InternalHigh = 0,
.DUMMYUNIONNAME = .{
.DUMMYSTRUCTNAME = .{
.Offset = @truncate(off),
.OffsetHigh = @truncate(off >> 32),
},
},
.hEvent = null,
};
break :blk &overlapped_data;
} else null;
const adjusted_len = math.cast(u32, bytes.len) orelse maxInt(u32);
if (kernel32.WriteFile(handle, bytes.ptr, adjusted_len, &bytes_written, overlapped) == 0) {
switch (GetLastError()) {
.INVALID_USER_BUFFER => return error.SystemResources,
.NOT_ENOUGH_MEMORY => return error.SystemResources,
.OPERATION_ABORTED => return error.OperationAborted,
.NOT_ENOUGH_QUOTA => return error.SystemResources,
.IO_PENDING => unreachable,
.NO_DATA => return error.BrokenPipe,
.INVALID_HANDLE => return error.NotOpenForWriting,
.LOCK_VIOLATION => return error.LockViolation,
.NETNAME_DELETED => return error.ConnectionResetByPeer,
else => |err| return unexpectedError(err),
}
}
return bytes_written;
}
pub const SetCurrentDirectoryError = error{
NameTooLong,
FileNotFound,
NotDir,
AccessDenied,
NoDevice,
BadPathName,
Unexpected,
};
pub fn SetCurrentDirectory(path_name: []const u16) SetCurrentDirectoryError!void {
const path_len_bytes = math.cast(u16, path_name.len * 2) orelse return error.NameTooLong;
var nt_name = UNICODE_STRING{
.Length = path_len_bytes,
.MaximumLength = path_len_bytes,
.Buffer = @constCast(path_name.ptr),
};
const rc = ntdll.RtlSetCurrentDirectory_U(&nt_name);
switch (rc) {
.SUCCESS => {},
.OBJECT_NAME_INVALID => return error.BadPathName,
.OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
.OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
.NO_MEDIA_IN_DEVICE => return error.NoDevice,
.INVALID_PARAMETER => unreachable,
.ACCESS_DENIED => return error.AccessDenied,
.OBJECT_PATH_SYNTAX_BAD => unreachable,
.NOT_A_DIRECTORY => return error.NotDir,
else => return unexpectedStatus(rc),
}
}
pub const GetCurrentDirectoryError = error{
NameTooLong,
Unexpected,
};
/// The result is a slice of `buffer`, indexed from 0.
/// The result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
pub fn GetCurrentDirectory(buffer: []u8) GetCurrentDirectoryError![]u8 {
var wtf16le_buf: [PATH_MAX_WIDE:0]u16 = undefined;
const result = kernel32.GetCurrentDirectoryW(wtf16le_buf.len + 1, &wtf16le_buf);
if (result == 0) {
switch (GetLastError()) {
else => |err| return unexpectedError(err),
}
}
assert(result <= wtf16le_buf.len);
const wtf16le_slice = wtf16le_buf[0..result];
var end_index: usize = 0;
var it = std.unicode.Wtf16LeIterator.init(wtf16le_slice);
while (it.nextCodepoint()) |codepoint| {
const seq_len = std.unicode.utf8CodepointSequenceLength(codepoint) catch unreachable;
if (end_index + seq_len >= buffer.len)
return error.NameTooLong;
end_index += std.unicode.wtf8Encode(codepoint, buffer[end_index..]) catch unreachable;
}
return buffer[0..end_index];
}
pub const CreateSymbolicLinkError = error{
AccessDenied,
PathAlreadyExists,
FileNotFound,
NameTooLong,
NoDevice,
NetworkNotFound,
BadPathName,
/// The volume does not contain a recognized file system. File system
/// drivers might not be loaded, or the volume may be corrupt.
UnrecognizedVolume,
Unexpected,
};
/// Needs either:
/// - `SeCreateSymbolicLinkPrivilege` privilege
/// or
/// - Developer mode on Windows 10
/// otherwise fails with `error.AccessDenied`. In which case `sym_link_path` may still
/// be created on the file system but will lack reparse processing data applied to it.
pub fn CreateSymbolicLink(
dir: ?HANDLE,
sym_link_path: []const u16,
target_path: [:0]const u16,
is_directory: bool,
) CreateSymbolicLinkError!void {
const SYMLINK_DATA = extern struct {
ReparseTag: ULONG,
ReparseDataLength: USHORT,
Reserved: USHORT,
SubstituteNameOffset: USHORT,
SubstituteNameLength: USHORT,
PrintNameOffset: USHORT,
PrintNameLength: USHORT,
Flags: ULONG,
};
const symlink_handle = OpenFile(sym_link_path, .{
.access_mask = SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE,
.dir = dir,
.creation = FILE_CREATE,
.filter = if (is_directory) .dir_only else .file_only,
}) catch |err| switch (err) {
error.IsDir => return error.PathAlreadyExists,
error.NotDir => return error.Unexpected,
error.WouldBlock => return error.Unexpected,
error.PipeBusy => return error.Unexpected,
error.NoDevice => return error.Unexpected,
error.AntivirusInterference => return error.Unexpected,
else => |e| return e,
};
defer CloseHandle(symlink_handle);
// Relevant portions of the documentation:
// > Relative links are specified using the following conventions:
// > - Root relative—for example, "\Windows\System32" resolves to "current drive:\Windows\System32".
// > - Current working directory–relative—for example, if the current working directory is
// > C:\Windows\System32, "C:File.txt" resolves to "C:\Windows\System32\File.txt".
// > Note: If you specify a current working directory–relative link, it is created as an absolute
// > link, due to the way the current working directory is processed based on the user and the thread.
// https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createsymboliclinkw
var is_target_absolute = false;
const final_target_path = target_path: {
switch (getNamespacePrefix(u16, target_path)) {
.none => switch (getUnprefixedPathType(u16, target_path)) {
// Rooted paths need to avoid getting put through wToPrefixedFileW
// (and they are treated as relative in this context)
// Note: It seems that rooted paths in symbolic links are relative to
// the drive that the symbolic exists on, not to the CWD's drive.
// So, if the symlink is on C:\ and the CWD is on D:\,
// it will still resolve the path relative to the root of
// the C:\ drive.
.rooted => break :target_path target_path,
// Keep relative paths relative, but anything else needs to get NT-prefixed.
else => if (!std.fs.path.isAbsoluteWindowsWTF16(target_path))
break :target_path target_path,
},
// Already an NT path, no need to do anything to it
.nt => break :target_path target_path,
else => {},
}
var prefixed_target_path = try wToPrefixedFileW(dir, target_path);
// We do this after prefixing to ensure that drive-relative paths are treated as absolute
is_target_absolute = std.fs.path.isAbsoluteWindowsWTF16(prefixed_target_path.span());
break :target_path prefixed_target_path.span();
};
// prepare reparse data buffer
var buffer: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 = undefined;
const buf_len = @sizeOf(SYMLINK_DATA) + final_target_path.len * 4;
const header_len = @sizeOf(ULONG) + @sizeOf(USHORT) * 2;
const target_is_absolute = std.fs.path.isAbsoluteWindowsWTF16(final_target_path);
const symlink_data = SYMLINK_DATA{
.ReparseTag = IO_REPARSE_TAG_SYMLINK,
.ReparseDataLength = @intCast(buf_len - header_len),
.Reserved = 0,
.SubstituteNameOffset = @intCast(final_target_path.len * 2),
.SubstituteNameLength = @intCast(final_target_path.len * 2),
.PrintNameOffset = 0,
.PrintNameLength = @intCast(final_target_path.len * 2),
.Flags = if (!target_is_absolute) SYMLINK_FLAG_RELATIVE else 0,
};
@memcpy(buffer[0..@sizeOf(SYMLINK_DATA)], std.mem.asBytes(&symlink_data));
@memcpy(buffer[@sizeOf(SYMLINK_DATA)..][0 .. final_target_path.len * 2], @as([*]const u8, @ptrCast(final_target_path)));
const paths_start = @sizeOf(SYMLINK_DATA) + final_target_path.len * 2;
@memcpy(buffer[paths_start..][0 .. final_target_path.len * 2], @as([*]const u8, @ptrCast(final_target_path)));
_ = try DeviceIoControl(symlink_handle, FSCTL_SET_REPARSE_POINT, buffer[0..buf_len], null);
}
pub const ReadLinkError = error{
FileNotFound,
NetworkNotFound,
AccessDenied,
Unexpected,
NameTooLong,
UnsupportedReparsePointType,
};
pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u8) ReadLinkError![]u8 {
// Here, we use `NtCreateFile` to shave off one syscall if we were to use `OpenFile` wrapper.
// With the latter, we'd need to call `NtCreateFile` twice, once for file symlink, and if that
// failed, again for dir symlink. Omitting any mention of file/dir flags makes it possible
// to open the symlink there and then.
const path_len_bytes = math.cast(u16, sub_path_w.len * 2) orelse return error.NameTooLong;
var nt_name = UNICODE_STRING{
.Length = path_len_bytes,
.MaximumLength = path_len_bytes,
.Buffer = @constCast(sub_path_w.ptr),
};
var attr = OBJECT_ATTRIBUTES{
.Length = @sizeOf(OBJECT_ATTRIBUTES),
.RootDirectory = if (std.fs.path.isAbsoluteWindowsWTF16(sub_path_w)) null else dir,
.Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
.ObjectName = &nt_name,
.SecurityDescriptor = null,
.SecurityQualityOfService = null,
};
var result_handle: HANDLE = undefined;
var io: IO_STATUS_BLOCK = undefined;
const rc = ntdll.NtCreateFile(
&result_handle,
FILE_READ_ATTRIBUTES | SYNCHRONIZE,
&attr,
&io,
null,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_OPEN,
FILE_OPEN_REPARSE_POINT | FILE_SYNCHRONOUS_IO_NONALERT,
null,
0,
);
switch (rc) {
.SUCCESS => {},
.OBJECT_NAME_INVALID => unreachable,
.OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
.OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
.NO_MEDIA_IN_DEVICE => return error.FileNotFound,
.BAD_NETWORK_PATH => return error.NetworkNotFound, // \\server was not found
.BAD_NETWORK_NAME => return error.NetworkNotFound, // \\server was found but \\server\share wasn't
.INVALID_PARAMETER => unreachable,
.SHARING_VIOLATION => return error.AccessDenied,
.ACCESS_DENIED => return error.AccessDenied,
.PIPE_BUSY => return error.AccessDenied,
.OBJECT_PATH_SYNTAX_BAD => unreachable,
.OBJECT_NAME_COLLISION => unreachable,
.FILE_IS_A_DIRECTORY => unreachable,
else => return unexpectedStatus(rc),
}
defer CloseHandle(result_handle);
var reparse_buf: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 align(@alignOf(REPARSE_DATA_BUFFER)) = undefined;
_ = DeviceIoControl(result_handle, FSCTL_GET_REPARSE_POINT, null, reparse_buf[0..]) catch |err| switch (err) {
error.AccessDenied => return error.Unexpected,
error.UnrecognizedVolume => return error.Unexpected,
else => |e| return e,
};
const reparse_struct: *const REPARSE_DATA_BUFFER = @ptrCast(@alignCast(&reparse_buf[0]));
switch (reparse_struct.ReparseTag) {
IO_REPARSE_TAG_SYMLINK => {
const buf: *const SYMBOLIC_LINK_REPARSE_BUFFER = @ptrCast(@alignCast(&reparse_struct.DataBuffer[0]));
const offset = buf.SubstituteNameOffset >> 1;
const len = buf.SubstituteNameLength >> 1;
const path_buf = @as([*]const u16, &buf.PathBuffer);
const is_relative = buf.Flags & SYMLINK_FLAG_RELATIVE != 0;
return parseReadlinkPath(path_buf[offset..][0..len], is_relative, out_buffer);
},
IO_REPARSE_TAG_MOUNT_POINT => {
const buf: *const MOUNT_POINT_REPARSE_BUFFER = @ptrCast(@alignCast(&reparse_struct.DataBuffer[0]));
const offset = buf.SubstituteNameOffset >> 1;
const len = buf.SubstituteNameLength >> 1;
const path_buf = @as([*]const u16, &buf.PathBuffer);
return parseReadlinkPath(path_buf[offset..][0..len], false, out_buffer);
},
else => |value| {
std.debug.print("unsupported symlink type: {}", .{value});
return error.UnsupportedReparsePointType;
},
}
}
/// Asserts that there is enough space is `out_buffer`.
/// The result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
fn parseReadlinkPath(path: []const u16, is_relative: bool, out_buffer: []u8) []u8 {
const win32_namespace_path = path: {
if (is_relative) break :path path;
const win32_path = ntToWin32Namespace(path) catch |err| switch (err) {
error.NameTooLong => unreachable,
error.NotNtPath => break :path path,
};
break :path win32_path.span();
};
const out_len = std.unicode.wtf16LeToWtf8(out_buffer, win32_namespace_path);
return out_buffer[0..out_len];
}
pub const DeleteFileError = error{
FileNotFound,
AccessDenied,
NameTooLong,
/// Also known as sharing violation.
FileBusy,
Unexpected,
NotDir,
IsDir,
DirNotEmpty,
NetworkNotFound,
};
pub const DeleteFileOptions = struct {
dir: ?HANDLE,
remove_dir: bool = false,
};
pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFileError!void {
const create_options_flags: ULONG = if (options.remove_dir)
FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT
else
FILE_NON_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT; // would we ever want to delete the target instead?
const path_len_bytes = @as(u16, @intCast(sub_path_w.len * 2));
var nt_name = UNICODE_STRING{
.Length = path_len_bytes,
.MaximumLength = path_len_bytes,
// The Windows API makes this mutable, but it will not mutate here.
.Buffer = @constCast(sub_path_w.ptr),
};
if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
// Windows does not recognize this, but it does work with empty string.
nt_name.Length = 0;
}
if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) {
// Can't remove the parent directory with an open handle.
return error.FileBusy;
}
var attr = OBJECT_ATTRIBUTES{
.Length = @sizeOf(OBJECT_ATTRIBUTES),
.RootDirectory = if (std.fs.path.isAbsoluteWindowsWTF16(sub_path_w)) null else options.dir,
.Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
.ObjectName = &nt_name,
.SecurityDescriptor = null,
.SecurityQualityOfService = null,
};
var io: IO_STATUS_BLOCK = undefined;
var tmp_handle: HANDLE = undefined;
var rc = ntdll.NtCreateFile(
&tmp_handle,
SYNCHRONIZE | DELETE,
&attr,
&io,
null,
0,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_OPEN,
create_options_flags,
null,
0,
);
switch (rc) {
.SUCCESS => {},
.OBJECT_NAME_INVALID => unreachable,
.OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
.OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
.BAD_NETWORK_PATH => return error.NetworkNotFound, // \\server was not found
.BAD_NETWORK_NAME => return error.NetworkNotFound, // \\server was found but \\server\share wasn't
.INVALID_PARAMETER => unreachable,
.FILE_IS_A_DIRECTORY => return error.IsDir,
.NOT_A_DIRECTORY => return error.NotDir,
.SHARING_VIOLATION => return error.FileBusy,
.ACCESS_DENIED => return error.AccessDenied,
.DELETE_PENDING => return,
else => return unexpectedStatus(rc),
}
defer CloseHandle(tmp_handle);
// FileDispositionInformationEx (and therefore FILE_DISPOSITION_POSIX_SEMANTICS and FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE)
// are only supported on NTFS filesystems, so the version check on its own is only a partial solution. To support non-NTFS filesystems
// like FAT32, we need to fallback to FileDispositionInformation if the usage of FileDispositionInformationEx gives
// us INVALID_PARAMETER.
// The same reasoning for win10_rs5 as in os.renameatW() applies (FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE requires >= win10_rs5).
var need_fallback = true;
if (comptime builtin.target.os.version_range.windows.min.isAtLeast(.win10_rs5)) {
// Deletion with posix semantics if the filesystem supports it.
var info = FILE_DISPOSITION_INFORMATION_EX{
.Flags = FILE_DISPOSITION_DELETE |
FILE_DISPOSITION_POSIX_SEMANTICS |
FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE,
};
rc = ntdll.NtSetInformationFile(
tmp_handle,
&io,
&info,
@sizeOf(FILE_DISPOSITION_INFORMATION_EX),
.FileDispositionInformationEx,
);
switch (rc) {
.SUCCESS => return,
// INVALID_PARAMETER here means that the filesystem does not support FileDispositionInformationEx
.INVALID_PARAMETER => {},
// For all other statuses, fall down to the switch below to handle them.
else => need_fallback = false,
}
}
if (need_fallback) {
// Deletion with file pending semantics, which requires waiting or moving
// files to get them removed (from here).
var file_dispo = FILE_DISPOSITION_INFORMATION{
.DeleteFile = TRUE,
};
rc = ntdll.NtSetInformationFile(
tmp_handle,
&io,
&file_dispo,
@sizeOf(FILE_DISPOSITION_INFORMATION),
.FileDispositionInformation,
);
}
switch (rc) {
.SUCCESS => {},
.DIRECTORY_NOT_EMPTY => return error.DirNotEmpty,
.INVALID_PARAMETER => unreachable,
.CANNOT_DELETE => return error.AccessDenied,
.MEDIA_WRITE_PROTECTED => return error.AccessDenied,
.ACCESS_DENIED => return error.AccessDenied,
else => return unexpectedStatus(rc),
}
}
pub const MoveFileError = error{ FileNotFound, AccessDenied, Unexpected };
pub fn MoveFileEx(old_path: []const u8, new_path: []const u8, flags: DWORD) (MoveFileError || Wtf8ToPrefixedFileWError)!void {
const old_path_w = try sliceToPrefixedFileW(null, old_path);
const new_path_w = try sliceToPrefixedFileW(null, new_path);
return MoveFileExW(old_path_w.span().ptr, new_path_w.span().ptr, flags);
}
pub fn MoveFileExW(old_path: [*:0]const u16, new_path: [*:0]const u16, flags: DWORD) MoveFileError!void {
if (kernel32.MoveFileExW(old_path, new_path, flags) == 0) {
switch (GetLastError()) {
.FILE_NOT_FOUND => return error.FileNotFound,
.ACCESS_DENIED => return error.AccessDenied,
else => |err| return unexpectedError(err),
}
}
}
pub const GetStdHandleError = error{
NoStandardHandleAttached,
Unexpected,
};
pub fn GetStdHandle(handle_id: DWORD) GetStdHandleError!HANDLE {
const handle = kernel32.GetStdHandle(handle_id) orelse return error.NoStandardHandleAttached;
if (handle == INVALID_HANDLE_VALUE) {
switch (GetLastError()) {
else => |err| return unexpectedError(err),
}
}
return handle;
}
pub const SetFilePointerError = error{Unexpected};
/// The SetFilePointerEx function with the `dwMoveMethod` parameter set to `FILE_BEGIN`.
pub fn SetFilePointerEx_BEGIN(handle: HANDLE, offset: u64) SetFilePointerError!void {
// "The starting point is zero or the beginning of the file. If [FILE_BEGIN]
// is specified, then the liDistanceToMove parameter is interpreted as an unsigned value."
// https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-setfilepointerex
const ipos = @as(LARGE_INTEGER, @bitCast(offset));
if (kernel32.SetFilePointerEx(handle, ipos, null, FILE_BEGIN) == 0) {
switch (GetLastError()) {
.INVALID_PARAMETER => unreachable,
.INVALID_HANDLE => unreachable,
else => |err| return unexpectedError(err),
}
}
}
/// The SetFilePointerEx function with the `dwMoveMethod` parameter set to `FILE_CURRENT`.
pub fn SetFilePointerEx_CURRENT(handle: HANDLE, offset: i64) SetFilePointerError!void {
if (kernel32.SetFilePointerEx(handle, offset, null, FILE_CURRENT) == 0) {
switch (GetLastError()) {
.INVALID_PARAMETER => unreachable,
.INVALID_HANDLE => unreachable,
else => |err| return unexpectedError(err),
}
}
}
/// The SetFilePointerEx function with the `dwMoveMethod` parameter set to `FILE_END`.
pub fn SetFilePointerEx_END(handle: HANDLE, offset: i64) SetFilePointerError!void {
if (kernel32.SetFilePointerEx(handle, offset, null, FILE_END) == 0) {
switch (GetLastError()) {
.INVALID_PARAMETER => unreachable,
.INVALID_HANDLE => unreachable,
else => |err| return unexpectedError(err),
}
}
}
/// The SetFilePointerEx function with parameters to get the current offset.
pub fn SetFilePointerEx_CURRENT_get(handle: HANDLE) SetFilePointerError!u64 {
var result: LARGE_INTEGER = undefined;
if (kernel32.SetFilePointerEx(handle, 0, &result, FILE_CURRENT) == 0) {
switch (GetLastError()) {
.INVALID_PARAMETER => unreachable,
.INVALID_HANDLE => unreachable,
else => |err| return unexpectedError(err),
}
}
// Based on the docs for FILE_BEGIN, it seems that the returned signed integer
// should be interpreted as an unsigned integer.
return @as(u64, @bitCast(result));
}
pub const QueryObjectNameError = error{
AccessDenied,
InvalidHandle,
NameTooLong,
Unexpected,
};
pub fn QueryObjectName(handle: HANDLE, out_buffer: []u16) QueryObjectNameError![]u16 {
const out_buffer_aligned = mem.alignInSlice(out_buffer, @alignOf(OBJECT_NAME_INFORMATION)) orelse return error.NameTooLong;
const info = @as(*OBJECT_NAME_INFORMATION, @ptrCast(out_buffer_aligned));
// buffer size is specified in bytes
const out_buffer_len = std.math.cast(ULONG, out_buffer_aligned.len * 2) orelse std.math.maxInt(ULONG);
// last argument would return the length required for full_buffer, not exposed here
return switch (ntdll.NtQueryObject(handle, .ObjectNameInformation, info, out_buffer_len, null)) {
.SUCCESS => blk: {
// info.Name.Buffer from ObQueryNameString is documented to be null (and MaximumLength == 0)
// if the object was "unnamed", not sure if this can happen for file handles
if (info.Name.MaximumLength == 0) break :blk error.Unexpected;
// resulting string length is specified in bytes
const path_length_unterminated = @divExact(info.Name.Length, 2);
break :blk info.Name.Buffer.?[0..path_length_unterminated];
},
.ACCESS_DENIED => error.AccessDenied,
.INVALID_HANDLE => error.InvalidHandle,
// triggered when the buffer is too small for the OBJECT_NAME_INFORMATION object (.INFO_LENGTH_MISMATCH),
// or if the buffer is too small for the file path returned (.BUFFER_OVERFLOW, .BUFFER_TOO_SMALL)
.INFO_LENGTH_MISMATCH, .BUFFER_OVERFLOW, .BUFFER_TOO_SMALL => error.NameTooLong,
else => |e| unexpectedStatus(e),
};
}
test QueryObjectName {
if (builtin.os.tag != .windows)
return;
//any file will do; canonicalization works on NTFS junctions and symlinks, hardlinks remain separate paths.
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
const handle = tmp.dir.fd;
var out_buffer: [PATH_MAX_WIDE]u16 = undefined;
const result_path = try QueryObjectName(handle, &out_buffer);
const required_len_in_u16 = result_path.len + @divExact(@intFromPtr(result_path.ptr) - @intFromPtr(&out_buffer), 2) + 1;
//insufficient size
try std.testing.expectError(error.NameTooLong, QueryObjectName(handle, out_buffer[0 .. required_len_in_u16 - 1]));
//exactly-sufficient size
_ = try QueryObjectName(handle, out_buffer[0..required_len_in_u16]);
}
pub const GetFinalPathNameByHandleError = error{
AccessDenied,
BadPathName,
FileNotFound,
NameTooLong,
/// The volume does not contain a recognized file system. File system
/// drivers might not be loaded, or the volume may be corrupt.
UnrecognizedVolume,
Unexpected,
};
/// Specifies how to format volume path in the result of `GetFinalPathNameByHandle`.
/// Defaults to DOS volume names.
pub const GetFinalPathNameByHandleFormat = struct {
volume_name: enum {
/// Format as DOS volume name
Dos,
/// Format as NT volume name
Nt,
} = .Dos,
};
/// Returns canonical (normalized) path of handle.
/// Use `GetFinalPathNameByHandleFormat` to specify whether the path is meant to include
/// NT or DOS volume name (e.g., `\Device\HarddiskVolume0\foo.txt` versus `C:\foo.txt`).
/// If DOS volume name format is selected, note that this function does *not* prepend
/// `\\?\` prefix to the resultant path.
pub fn GetFinalPathNameByHandle(
hFile: HANDLE,
fmt: GetFinalPathNameByHandleFormat,
out_buffer: []u16,
) GetFinalPathNameByHandleError![]u16 {
const final_path = QueryObjectName(hFile, out_buffer) catch |err| switch (err) {
// we assume InvalidHandle is close enough to FileNotFound in semantics
// to not further complicate the error set
error.InvalidHandle => return error.FileNotFound,
else => |e| return e,
};
switch (fmt.volume_name) {
.Nt => {
// the returned path is already in .Nt format
return final_path;
},
.Dos => {
// parse the string to separate volume path from file path
const expected_prefix = std.unicode.utf8ToUtf16LeStringLiteral("\\Device\\");
// TODO find out if a path can start with something besides `\Device\`,
// and if we need to handle it differently
// (i.e. how to determine the start and end of the volume name in that case)
if (!mem.eql(u16, expected_prefix, final_path[0..expected_prefix.len])) return error.Unexpected;
const file_path_begin_index = mem.indexOfPos(u16, final_path, expected_prefix.len, &[_]u16{'\\'}) orelse unreachable;
const volume_name_u16 = final_path[0..file_path_begin_index];
const device_name_u16 = volume_name_u16[expected_prefix.len..];
const file_name_u16 = final_path[file_path_begin_index..];
// MUP is Multiple UNC Provider, and indicates that the path is a UNC
// path. In this case, the canonical UNC path can be gotten by just
// dropping the \Device\Mup\ and making sure the path begins with \\
if (mem.eql(u16, device_name_u16, std.unicode.utf8ToUtf16LeStringLiteral("Mup"))) {
out_buffer[0] = '\\';
mem.copyForwards(u16, out_buffer[1..][0..file_name_u16.len], file_name_u16);
return out_buffer[0 .. 1 + file_name_u16.len];
}
// Get DOS volume name. DOS volume names are actually symbolic link objects to the
// actual NT volume. For example:
// (NT) \Device\HarddiskVolume4 => (DOS) \DosDevices\C: == (DOS) C:
const MIN_SIZE = @sizeOf(MOUNTMGR_MOUNT_POINT) + MAX_PATH;
// We initialize the input buffer to all zeros for convenience since
// `DeviceIoControl` with `IOCTL_MOUNTMGR_QUERY_POINTS` expects this.
var input_buf: [MIN_SIZE]u8 align(@alignOf(MOUNTMGR_MOUNT_POINT)) = [_]u8{0} ** MIN_SIZE;
var output_buf: [MIN_SIZE * 4]u8 align(@alignOf(MOUNTMGR_MOUNT_POINTS)) = undefined;
// This surprising path is a filesystem path to the mount manager on Windows.
// Source: https://stackoverflow.com/questions/3012828/using-ioctl-mountmgr-query-points
// This is the NT namespaced version of \\.\MountPointManager
const mgmt_path_u16 = std.unicode.utf8ToUtf16LeStringLiteral("\\??\\MountPointManager");
const mgmt_handle = OpenFile(mgmt_path_u16, .{
.access_mask = SYNCHRONIZE,
.share_access = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
.creation = FILE_OPEN,
}) catch |err| switch (err) {
error.IsDir => return error.Unexpected,
error.NotDir => return error.Unexpected,
error.NoDevice => return error.Unexpected,
error.AccessDenied => return error.Unexpected,
error.PipeBusy => return error.Unexpected,
error.PathAlreadyExists => return error.Unexpected,
error.WouldBlock => return error.Unexpected,
error.NetworkNotFound => return error.Unexpected,
error.AntivirusInterference => return error.Unexpected,
else => |e| return e,
};
defer CloseHandle(mgmt_handle);
var input_struct: *MOUNTMGR_MOUNT_POINT = @ptrCast(&input_buf[0]);
input_struct.DeviceNameOffset = @sizeOf(MOUNTMGR_MOUNT_POINT);
input_struct.DeviceNameLength = @intCast(volume_name_u16.len * 2);
@memcpy(input_buf[@sizeOf(MOUNTMGR_MOUNT_POINT)..][0 .. volume_name_u16.len * 2], @as([*]const u8, @ptrCast(volume_name_u16.ptr)));
DeviceIoControl(mgmt_handle, IOCTL_MOUNTMGR_QUERY_POINTS, &input_buf, &output_buf) catch |err| switch (err) {
error.AccessDenied => return error.Unexpected,
else => |e| return e,
};
const mount_points_struct: *const MOUNTMGR_MOUNT_POINTS = @ptrCast(&output_buf[0]);
const mount_points = @as(
[*]const MOUNTMGR_MOUNT_POINT,
@ptrCast(&mount_points_struct.MountPoints[0]),
)[0..mount_points_struct.NumberOfMountPoints];
for (mount_points) |mount_point| {
const symlink = @as(
[*]const u16,
@ptrCast(@alignCast(&output_buf[mount_point.SymbolicLinkNameOffset])),
)[0 .. mount_point.SymbolicLinkNameLength / 2];
// Look for `\DosDevices\` prefix. We don't really care if there are more than one symlinks
// with traditional DOS drive letters, so pick the first one available.
var prefix_buf = std.unicode.utf8ToUtf16LeStringLiteral("\\DosDevices\\");
const prefix = prefix_buf[0..prefix_buf.len];
if (mem.startsWith(u16, symlink, prefix)) {
const drive_letter = symlink[prefix.len..];
if (out_buffer.len < drive_letter.len + file_name_u16.len) return error.NameTooLong;
@memcpy(out_buffer[0..drive_letter.len], drive_letter);
mem.copyForwards(u16, out_buffer[drive_letter.len..][0..file_name_u16.len], file_name_u16);
const total_len = drive_letter.len + file_name_u16.len;
// Validate that DOS does not contain any spurious nul bytes.
if (mem.indexOfScalar(u16, out_buffer[0..total_len], 0)) |_| {
return error.BadPathName;
}
return out_buffer[0..total_len];
} else if (mountmgrIsVolumeName(symlink)) {
// If the symlink is a volume GUID like \??\Volume{383da0b0-717f-41b6-8c36-00500992b58d},
// then it is a volume mounted as a path rather than a drive letter. We need to
// query the mount manager again to get the DOS path for the volume.
// 49 is the maximum length accepted by mountmgrIsVolumeName
const vol_input_size = @sizeOf(MOUNTMGR_TARGET_NAME) + (49 * 2);
var vol_input_buf: [vol_input_size]u8 align(@alignOf(MOUNTMGR_TARGET_NAME)) = [_]u8{0} ** vol_input_size;
// Note: If the path exceeds MAX_PATH, the Disk Management GUI doesn't accept the full path,
// and instead if must be specified using a shortened form (e.g. C:\FOO~1\BAR~1\<...>).
// However, just to be sure we can handle any path length, we use PATH_MAX_WIDE here.
const min_output_size = @sizeOf(MOUNTMGR_VOLUME_PATHS) + (PATH_MAX_WIDE * 2);
var vol_output_buf: [min_output_size]u8 align(@alignOf(MOUNTMGR_VOLUME_PATHS)) = undefined;
var vol_input_struct: *MOUNTMGR_TARGET_NAME = @ptrCast(&vol_input_buf[0]);
vol_input_struct.DeviceNameLength = @intCast(symlink.len * 2);
@memcpy(@as([*]WCHAR, &vol_input_struct.DeviceName)[0..symlink.len], symlink);
DeviceIoControl(mgmt_handle, IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH, &vol_input_buf, &vol_output_buf) catch |err| switch (err) {
error.AccessDenied => return error.Unexpected,
else => |e| return e,
};
const volume_paths_struct: *const MOUNTMGR_VOLUME_PATHS = @ptrCast(&vol_output_buf[0]);
const volume_path = std.mem.sliceTo(@as(
[*]const u16,
&volume_paths_struct.MultiSz,
)[0 .. volume_paths_struct.MultiSzLength / 2], 0);
if (out_buffer.len < volume_path.len + file_name_u16.len) return error.NameTooLong;
// `out_buffer` currently contains the memory of `file_name_u16`, so it can overlap with where
// we want to place the filename before returning. Here are the possible overlapping cases:
//
// out_buffer: [filename]
// dest: [___(a)___] [___(b)___]
//
// In the case of (a), we need to copy forwards, and in the case of (b) we need
// to copy backwards. We also need to do this before copying the volume path because
// it could overwrite the file_name_u16 memory.
const file_name_dest = out_buffer[volume_path.len..][0..file_name_u16.len];
const file_name_byte_offset = @intFromPtr(file_name_u16.ptr) - @intFromPtr(out_buffer.ptr);
const file_name_index = file_name_byte_offset / @sizeOf(u16);
if (volume_path.len > file_name_index)
mem.copyBackwards(u16, file_name_dest, file_name_u16)
else
mem.copyForwards(u16, file_name_dest, file_name_u16);
@memcpy(out_buffer[0..volume_path.len], volume_path);
const total_len = volume_path.len + file_name_u16.len;
// Validate that DOS does not contain any spurious nul bytes.
if (mem.indexOfScalar(u16, out_buffer[0..total_len], 0)) |_| {
return error.BadPathName;
}
return out_buffer[0..total_len];
}
}
// If we've ended up here, then something went wrong/is corrupted in the OS,
// so error out!
return error.FileNotFound;
},
}
}
/// Equivalent to the MOUNTMGR_IS_VOLUME_NAME macro in mountmgr.h
fn mountmgrIsVolumeName(name: []const u16) bool {
return (name.len == 48 or (name.len == 49 and name[48] == mem.nativeToLittle(u16, '\\'))) and
name[0] == mem.nativeToLittle(u16, '\\') and
(name[1] == mem.nativeToLittle(u16, '?') or name[1] == mem.nativeToLittle(u16, '\\')) and
name[2] == mem.nativeToLittle(u16, '?') and
name[3] == mem.nativeToLittle(u16, '\\') and
mem.startsWith(u16, name[4..], std.unicode.utf8ToUtf16LeStringLiteral("Volume{")) and
name[19] == mem.nativeToLittle(u16, '-') and
name[24] == mem.nativeToLittle(u16, '-') and
name[29] == mem.nativeToLittle(u16, '-') and
name[34] == mem.nativeToLittle(u16, '-') and
name[47] == mem.nativeToLittle(u16, '}');
}
test mountmgrIsVolumeName {
@setEvalBranchQuota(2000);
const L = std.unicode.utf8ToUtf16LeStringLiteral;
try std.testing.expect(mountmgrIsVolumeName(L("\\\\?\\Volume{383da0b0-717f-41b6-8c36-00500992b58d}")));
try std.testing.expect(mountmgrIsVolumeName(L("\\??\\Volume{383da0b0-717f-41b6-8c36-00500992b58d}")));
try std.testing.expect(mountmgrIsVolumeName(L("\\\\?\\Volume{383da0b0-717f-41b6-8c36-00500992b58d}\\")));
try std.testing.expect(mountmgrIsVolumeName(L("\\??\\Volume{383da0b0-717f-41b6-8c36-00500992b58d}\\")));
try std.testing.expect(!mountmgrIsVolumeName(L("\\\\.\\Volume{383da0b0-717f-41b6-8c36-00500992b58d}")));
try std.testing.expect(!mountmgrIsVolumeName(L("\\??\\Volume{383da0b0-717f-41b6-8c36-00500992b58d}\\foo")));
try std.testing.expect(!mountmgrIsVolumeName(L("\\??\\Volume{383da0b0-717f-41b6-8c36-00500992b58}")));
}
test GetFinalPathNameByHandle {
if (builtin.os.tag != .windows)
return;
//any file will do
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
const handle = tmp.dir.fd;
var buffer: [PATH_MAX_WIDE]u16 = undefined;
//check with sufficient size
const nt_path = try GetFinalPathNameByHandle(handle, .{ .volume_name = .Nt }, &buffer);
_ = try GetFinalPathNameByHandle(handle, .{ .volume_name = .Dos }, &buffer);
const required_len_in_u16 = nt_path.len + @divExact(@intFromPtr(nt_path.ptr) - @intFromPtr(&buffer), 2) + 1;
//check with insufficient size
try std.testing.expectError(error.NameTooLong, GetFinalPathNameByHandle(handle, .{ .volume_name = .Nt }, buffer[0 .. required_len_in_u16 - 1]));
try std.testing.expectError(error.NameTooLong, GetFinalPathNameByHandle(handle, .{ .volume_name = .Dos }, buffer[0 .. required_len_in_u16 - 1]));
//check with exactly-sufficient size
_ = try GetFinalPathNameByHandle(handle, .{ .volume_name = .Nt }, buffer[0..required_len_in_u16]);
_ = try GetFinalPathNameByHandle(handle, .{ .volume_name = .Dos }, buffer[0..required_len_in_u16]);
}
pub const GetFileSizeError = error{Unexpected};
pub fn GetFileSizeEx(hFile: HANDLE) GetFileSizeError!u64 {
var file_size: LARGE_INTEGER = undefined;
if (kernel32.GetFileSizeEx(hFile, &file_size) == 0) {
switch (GetLastError()) {
else => |err| return unexpectedError(err),
}
}
return @as(u64, @bitCast(file_size));
}
pub const GetFileAttributesError = error{
FileNotFound,
AccessDenied,
Unexpected,
};
pub fn GetFileAttributes(filename: []const u8) (GetFileAttributesError || Wtf8ToPrefixedFileWError)!DWORD {
const filename_w = try sliceToPrefixedFileW(null, filename);
return GetFileAttributesW(filename_w.span().ptr);
}
pub fn GetFileAttributesW(lpFileName: [*:0]const u16) GetFileAttributesError!DWORD {
const rc = kernel32.GetFileAttributesW(lpFileName);
if (rc == INVALID_FILE_ATTRIBUTES) {
switch (GetLastError()) {
.FILE_NOT_FOUND => return error.FileNotFound,
.PATH_NOT_FOUND => return error.FileNotFound,
.ACCESS_DENIED => return error.AccessDenied,
else => |err| return unexpectedError(err),
}
}
return rc;
}
pub fn WSAStartup(majorVersion: u8, minorVersion: u8) !ws2_32.WSADATA {
var wsadata: ws2_32.WSADATA = undefined;
return switch (ws2_32.WSAStartup((@as(WORD, minorVersion) << 8) | majorVersion, &wsadata)) {
0 => wsadata,
else => |err_int| switch (@as(ws2_32.WinsockError, @enumFromInt(@as(u16, @intCast(err_int))))) {
.WSASYSNOTREADY => return error.SystemNotAvailable,
.WSAVERNOTSUPPORTED => return error.VersionNotSupported,
.WSAEINPROGRESS => return error.BlockingOperationInProgress,
.WSAEPROCLIM => return error.ProcessFdQuotaExceeded,
else => |err| return unexpectedWSAError(err),
},
};
}
pub fn WSACleanup() !void {
return switch (ws2_32.WSACleanup()) {
0 => {},
ws2_32.SOCKET_ERROR => switch (ws2_32.WSAGetLastError()) {
.WSANOTINITIALISED => return error.NotInitialized,
.WSAENETDOWN => return error.NetworkNotAvailable,
.WSAEINPROGRESS => return error.BlockingOperationInProgress,
else => |err| return unexpectedWSAError(err),
},
else => unreachable,
};
}
var wsa_startup_mutex: std.Thread.Mutex = .{};
pub fn callWSAStartup() !void {
wsa_startup_mutex.lock();
defer wsa_startup_mutex.unlock();
// Here we could use a flag to prevent multiple threads to prevent
// multiple calls to WSAStartup, but it doesn't matter. We're globally
// leaking the resource intentionally, and the mutex already prevents
// data races within the WSAStartup function.
_ = WSAStartup(2, 2) catch |err| switch (err) {
error.SystemNotAvailable => return error.SystemResources,
error.VersionNotSupported => return error.Unexpected,
error.BlockingOperationInProgress => return error.Unexpected,
error.ProcessFdQuotaExceeded => return error.ProcessFdQuotaExceeded,
error.Unexpected => return error.Unexpected,
};
}
/// Microsoft requires WSAStartup to be called to initialize, or else
/// WSASocketW will return WSANOTINITIALISED.
/// Since this is a standard library, we do not have the luxury of
/// putting initialization code anywhere, because we would not want
/// to pay the cost of calling WSAStartup if there ended up being no
/// networking. Also, if Zig code is used as a library, Zig is not in
/// charge of the start code, and we couldn't put in any initialization
/// code even if we wanted to.
/// The documentation for WSAStartup mentions that there must be a
/// matching WSACleanup call. It is not possible for the Zig Standard
/// Library to honor this for the same reason - there is nowhere to put
/// deinitialization code.
/// So, API users of the zig std lib have two options:
/// * (recommended) The simple, cross-platform way: just call `WSASocketW`
/// and don't worry about it. Zig will call WSAStartup() in a thread-safe
/// manner and never deinitialize networking. This is ideal for an
/// application which has the capability to do networking.
/// * The getting-your-hands-dirty way: call `WSAStartup()` before doing
/// networking, so that the error handling code for WSANOTINITIALISED never
/// gets run, which then allows the application or library to call `WSACleanup()`.
/// This could make sense for a library, which has init and deinit
/// functions for the whole library's lifetime.
pub fn WSASocketW(
af: i32,
socket_type: i32,
protocol: i32,
protocolInfo: ?*ws2_32.WSAPROTOCOL_INFOW,
g: ws2_32.GROUP,
dwFlags: DWORD,
) !ws2_32.SOCKET {
var first = true;
while (true) {
const rc = ws2_32.WSASocketW(af, socket_type, protocol, protocolInfo, g, dwFlags);
if (rc == ws2_32.INVALID_SOCKET) {
switch (ws2_32.WSAGetLastError()) {
.WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported,
.WSAEMFILE => return error.ProcessFdQuotaExceeded,
.WSAENOBUFS => return error.SystemResources,
.WSAEPROTONOSUPPORT => return error.ProtocolNotSupported,
.WSANOTINITIALISED => {
if (!first) return error.Unexpected;
first = false;
try callWSAStartup();
continue;
},
else => |err| return unexpectedWSAError(err),
}
}
return rc;
}
}
pub fn bind(s: ws2_32.SOCKET, name: *const ws2_32.sockaddr, namelen: ws2_32.socklen_t) i32 {
return ws2_32.bind(s, name, @as(i32, @intCast(namelen)));
}
pub fn listen(s: ws2_32.SOCKET, backlog: u31) i32 {
return ws2_32.listen(s, backlog);
}
pub fn closesocket(s: ws2_32.SOCKET) !void {
switch (ws2_32.closesocket(s)) {
0 => {},
ws2_32.SOCKET_ERROR => switch (ws2_32.WSAGetLastError()) {
else => |err| return unexpectedWSAError(err),
},
else => unreachable,
}
}
pub fn accept(s: ws2_32.SOCKET, name: ?*ws2_32.sockaddr, namelen: ?*ws2_32.socklen_t) ws2_32.SOCKET {
assert((name == null) == (namelen == null));
return ws2_32.accept(s, name, @as(?*i32, @ptrCast(namelen)));
}
pub fn getsockname(s: ws2_32.SOCKET, name: *ws2_32.sockaddr, namelen: *ws2_32.socklen_t) i32 {
return ws2_32.getsockname(s, name, @as(*i32, @ptrCast(namelen)));
}
pub fn getpeername(s: ws2_32.SOCKET, name: *ws2_32.sockaddr, namelen: *ws2_32.socklen_t) i32 {
return ws2_32.getpeername(s, name, @as(*i32, @ptrCast(namelen)));
}
pub fn sendmsg(
s: ws2_32.SOCKET,
msg: *ws2_32.WSAMSG_const,
flags: u32,
) i32 {
var bytes_send: DWORD = undefined;
if (ws2_32.WSASendMsg(s, msg, flags, &bytes_send, null, null) == ws2_32.SOCKET_ERROR) {
return ws2_32.SOCKET_ERROR;
} else {
return @as(i32, @as(u31, @intCast(bytes_send)));
}
}
pub fn sendto(s: ws2_32.SOCKET, buf: [*]const u8, len: usize, flags: u32, to: ?*const ws2_32.sockaddr, to_len: ws2_32.socklen_t) i32 {
var buffer = ws2_32.WSABUF{ .len = @as(u31, @truncate(len)), .buf = @constCast(buf) };
var bytes_send: DWORD = undefined;
if (ws2_32.WSASendTo(s, @as([*]ws2_32.WSABUF, @ptrCast(&buffer)), 1, &bytes_send, flags, to, @as(i32, @intCast(to_len)), null, null) == ws2_32.SOCKET_ERROR) {
return ws2_32.SOCKET_ERROR;
} else {
return @as(i32, @as(u31, @intCast(bytes_send)));
}
}
pub fn recvfrom(s: ws2_32.SOCKET, buf: [*]u8, len: usize, flags: u32, from: ?*ws2_32.sockaddr, from_len: ?*ws2_32.socklen_t) i32 {
var buffer = ws2_32.WSABUF{ .len = @as(u31, @truncate(len)), .buf = buf };
var bytes_received: DWORD = undefined;
var flags_inout = flags;
if (ws2_32.WSARecvFrom(s, @as([*]ws2_32.WSABUF, @ptrCast(&buffer)), 1, &bytes_received, &flags_inout, from, @as(?*i32, @ptrCast(from_len)), null, null) == ws2_32.SOCKET_ERROR) {
return ws2_32.SOCKET_ERROR;
} else {
return @as(i32, @as(u31, @intCast(bytes_received)));
}
}
pub fn poll(fds: [*]ws2_32.pollfd, n: c_ulong, timeout: i32) i32 {
return ws2_32.WSAPoll(fds, n, timeout);
}
pub fn WSAIoctl(
s: ws2_32.SOCKET,
dwIoControlCode: DWORD,
inBuffer: ?[]const u8,
outBuffer: []u8,
overlapped: ?*OVERLAPPED,
completionRoutine: ?ws2_32.LPWSAOVERLAPPED_COMPLETION_ROUTINE,
) !DWORD {
var bytes: DWORD = undefined;
switch (ws2_32.WSAIoctl(
s,
dwIoControlCode,
if (inBuffer) |i| i.ptr else null,
if (inBuffer) |i| @as(DWORD, @intCast(i.len)) else 0,
outBuffer.ptr,
@as(DWORD, @intCast(outBuffer.len)),
&bytes,
overlapped,
completionRoutine,
)) {
0 => {},
ws2_32.SOCKET_ERROR => switch (ws2_32.WSAGetLastError()) {
else => |err| return unexpectedWSAError(err),
},
else => unreachable,
}
return bytes;
}
const GetModuleFileNameError = error{Unexpected};
pub fn GetModuleFileNameW(hModule: ?HMODULE, buf_ptr: [*]u16, buf_len: DWORD) GetModuleFileNameError![:0]u16 {
const rc = kernel32.GetModuleFileNameW(hModule, buf_ptr, buf_len);
if (rc == 0) {
switch (GetLastError()) {
else => |err| return unexpectedError(err),
}
}
return buf_ptr[0..rc :0];
}
pub const TerminateProcessError = error{ AccessDenied, Unexpected };
pub fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) TerminateProcessError!void {
if (kernel32.TerminateProcess(hProcess, uExitCode) == 0) {
switch (GetLastError()) {
Win32Error.ACCESS_DENIED => return error.AccessDenied,
else => |err| return unexpectedError(err),
}
}
}
pub const NtAllocateVirtualMemoryError = error{
AccessDenied,
InvalidParameter,
NoMemory,
Unexpected,
};
pub fn NtAllocateVirtualMemory(hProcess: HANDLE, addr: ?*PVOID, zero_bits: ULONG_PTR, size: ?*SIZE_T, alloc_type: ULONG, protect: ULONG) NtAllocateVirtualMemoryError!void {
return switch (ntdll.NtAllocateVirtualMemory(hProcess, addr, zero_bits, size, alloc_type, protect)) {
.SUCCESS => return,
.ACCESS_DENIED => NtAllocateVirtualMemoryError.AccessDenied,
.INVALID_PARAMETER => NtAllocateVirtualMemoryError.InvalidParameter,
.NO_MEMORY => NtAllocateVirtualMemoryError.NoMemory,
else => |st| unexpectedStatus(st),
};
}
pub const NtFreeVirtualMemoryError = error{
AccessDenied,
InvalidParameter,
Unexpected,
};
pub fn NtFreeVirtualMemory(hProcess: HANDLE, addr: ?*PVOID, size: *SIZE_T, free_type: ULONG) NtFreeVirtualMemoryError!void {
return switch (ntdll.NtFreeVirtualMemory(hProcess, addr, size, free_type)) {
.SUCCESS => return,
.ACCESS_DENIED => NtFreeVirtualMemoryError.AccessDenied,
.INVALID_PARAMETER => NtFreeVirtualMemoryError.InvalidParameter,
else => NtFreeVirtualMemoryError.Unexpected,
};
}
pub const VirtualAllocError = error{Unexpected};
pub fn VirtualAlloc(addr: ?LPVOID, size: usize, alloc_type: DWORD, flProtect: DWORD) VirtualAllocError!LPVOID {
return kernel32.VirtualAlloc(addr, size, alloc_type, flProtect) orelse {
switch (GetLastError()) {
else => |err| return unexpectedError(err),
}
};
}
pub fn VirtualFree(lpAddress: ?LPVOID, dwSize: usize, dwFreeType: DWORD) void {
assert(kernel32.VirtualFree(lpAddress, dwSize, dwFreeType) != 0);
}
pub const VirtualProtectError = error{
InvalidAddress,
Unexpected,
};
pub fn VirtualProtect(lpAddress: ?LPVOID, dwSize: SIZE_T, flNewProtect: DWORD, lpflOldProtect: *DWORD) VirtualProtectError!void {
// ntdll takes an extra level of indirection here
var addr = lpAddress;
var size = dwSize;
switch (ntdll.NtProtectVirtualMemory(self_process_handle, &addr, &size, flNewProtect, lpflOldProtect)) {
.SUCCESS => {},
.INVALID_ADDRESS => return error.InvalidAddress,
else => |st| return unexpectedStatus(st),
}
}
pub fn VirtualProtectEx(handle: HANDLE, addr: ?LPVOID, size: SIZE_T, new_prot: DWORD) VirtualProtectError!DWORD {
var old_prot: DWORD = undefined;
var out_addr = addr;
var out_size = size;
switch (ntdll.NtProtectVirtualMemory(
handle,
&out_addr,
&out_size,
new_prot,
&old_prot,
)) {
.SUCCESS => return old_prot,
.INVALID_ADDRESS => return error.InvalidAddress,
// TODO: map errors
else => |rc| return unexpectedStatus(rc),
}
}
pub const VirtualQueryError = error{Unexpected};
pub fn VirtualQuery(lpAddress: ?LPVOID, lpBuffer: PMEMORY_BASIC_INFORMATION, dwLength: SIZE_T) VirtualQueryError!SIZE_T {
const rc = kernel32.VirtualQuery(lpAddress, lpBuffer, dwLength);
if (rc == 0) {
switch (GetLastError()) {
else => |err| return unexpectedError(err),
}
}
return rc;
}
pub const SetConsoleTextAttributeError = error{Unexpected};
pub fn SetConsoleTextAttribute(hConsoleOutput: HANDLE, wAttributes: WORD) SetConsoleTextAttributeError!void {
if (kernel32.SetConsoleTextAttribute(hConsoleOutput, wAttributes) == 0) {
switch (GetLastError()) {
else => |err| return unexpectedError(err),
}
}
}
pub fn SetConsoleCtrlHandler(handler_routine: ?HANDLER_ROUTINE, add: bool) !void {
const success = kernel32.SetConsoleCtrlHandler(
handler_routine,
if (add) TRUE else FALSE,
);
if (success == FALSE) {
return switch (GetLastError()) {
else => |err| unexpectedError(err),
};
}
}
pub fn SetFileCompletionNotificationModes(handle: HANDLE, flags: UCHAR) !void {
const success = kernel32.SetFileCompletionNotificationModes(handle, flags);
if (success == FALSE) {
return switch (GetLastError()) {
else => |err| unexpectedError(err),
};
}
}
pub const GetEnvironmentStringsError = error{OutOfMemory};
pub fn GetEnvironmentStringsW() GetEnvironmentStringsError![*:0]u16 {
return kernel32.GetEnvironmentStringsW() orelse return error.OutOfMemory;
}
pub fn FreeEnvironmentStringsW(penv: [*:0]u16) void {
assert(kernel32.FreeEnvironmentStringsW(penv) != 0);
}
pub const GetEnvironmentVariableError = error{
EnvironmentVariableNotFound,
Unexpected,
};
pub fn GetEnvironmentVariableW(lpName: LPWSTR, lpBuffer: [*]u16, nSize: DWORD) GetEnvironmentVariableError!DWORD {
const rc = kernel32.GetEnvironmentVariableW(lpName, lpBuffer, nSize);
if (rc == 0) {
switch (GetLastError()) {
.ENVVAR_NOT_FOUND => return error.EnvironmentVariableNotFound,
else => |err| return unexpectedError(err),
}
}
return rc;
}
pub const CreateProcessError = error{
FileNotFound,
AccessDenied,
InvalidName,
NameTooLong,
InvalidExe,
Unexpected,
};
pub const CreateProcessFlags = packed struct(u32) {
debug_process: bool = false,
debug_only_this_process: bool = false,
create_suspended: bool = false,
detached_process: bool = false,
create_new_console: bool = false,
normal_priority_class: bool = false,
idle_priority_class: bool = false,
high_priority_class: bool = false,
realtime_priority_class: bool = false,
create_new_process_group: bool = false,
create_unicode_environment: bool = false,
create_separate_wow_vdm: bool = false,
create_shared_wow_vdm: bool = false,
create_forcedos: bool = false,
below_normal_priority_class: bool = false,
above_normal_priority_class: bool = false,
inherit_parent_affinity: bool = false,
inherit_caller_priority: bool = false,
create_protected_process: bool = false,
extended_startupinfo_present: bool = false,
process_mode_background_begin: bool = false,
process_mode_background_end: bool = false,
create_secure_process: bool = false,
_reserved: bool = false,
create_breakaway_from_job: bool = false,
create_preserve_code_authz_level: bool = false,
create_default_error_mode: bool = false,
create_no_window: bool = false,
profile_user: bool = false,
profile_kernel: bool = false,
profile_server: bool = false,
create_ignore_system_default: bool = false,
};
pub fn CreateProcessW(
lpApplicationName: ?LPCWSTR,
lpCommandLine: ?LPWSTR,
lpProcessAttributes: ?*SECURITY_ATTRIBUTES,
lpThreadAttributes: ?*SECURITY_ATTRIBUTES,
bInheritHandles: BOOL,
dwCreationFlags: CreateProcessFlags,
lpEnvironment: ?*anyopaque,
lpCurrentDirectory: ?LPCWSTR,
lpStartupInfo: *STARTUPINFOW,
lpProcessInformation: *PROCESS_INFORMATION,
) CreateProcessError!void {
if (kernel32.CreateProcessW(
lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwCreationFlags,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation,
) == 0) {
switch (GetLastError()) {
.FILE_NOT_FOUND => return error.FileNotFound,
.PATH_NOT_FOUND => return error.FileNotFound,
.ACCESS_DENIED => return error.AccessDenied,
.INVALID_PARAMETER => unreachable,
.INVALID_NAME => return error.InvalidName,
.FILENAME_EXCED_RANGE => return error.NameTooLong,
// These are all the system errors that are mapped to ENOEXEC by
// the undocumented _dosmaperr (old CRT) or __acrt_errno_map_os_error
// (newer CRT) functions. Their code can be found in crt/src/dosmap.c (old SDK)
// or urt/misc/errno.cpp (newer SDK) in the Windows SDK.
.BAD_FORMAT,
.INVALID_STARTING_CODESEG, // MIN_EXEC_ERROR in errno.cpp
.INVALID_STACKSEG,
.INVALID_MODULETYPE,
.INVALID_EXE_SIGNATURE,
.EXE_MARKED_INVALID,
.BAD_EXE_FORMAT,
.ITERATED_DATA_EXCEEDS_64k,
.INVALID_MINALLOCSIZE,
.DYNLINK_FROM_INVALID_RING,
.IOPL_NOT_ENABLED,
.INVALID_SEGDPL,
.AUTODATASEG_EXCEEDS_64k,
.RING2SEG_MUST_BE_MOVABLE,
.RELOC_CHAIN_XEEDS_SEGLIM,
.INFLOOP_IN_RELOC_CHAIN, // MAX_EXEC_ERROR in errno.cpp
// This one is not mapped to ENOEXEC but it is possible, for example
// when calling CreateProcessW on a plain text file with a .exe extension
.EXE_MACHINE_TYPE_MISMATCH,
=> return error.InvalidExe,
else => |err| return unexpectedError(err),
}
}
}
pub const LoadLibraryError = error{
FileNotFound,
Unexpected,
};
pub fn LoadLibraryW(lpLibFileName: [*:0]const u16) LoadLibraryError!HMODULE {
return kernel32.LoadLibraryW(lpLibFileName) orelse {
switch (GetLastError()) {
.FILE_NOT_FOUND => return error.FileNotFound,
.PATH_NOT_FOUND => return error.FileNotFound,
.MOD_NOT_FOUND => return error.FileNotFound,
else => |err| return unexpectedError(err),
}
};
}
pub const LoadLibraryFlags = enum(DWORD) {
none = 0,
dont_resolve_dll_references = 0x00000001,
load_ignore_code_authz_level = 0x00000010,
load_library_as_datafile = 0x00000002,
load_library_as_datafile_exclusive = 0x00000040,
load_library_as_image_resource = 0x00000020,
load_library_search_application_dir = 0x00000200,
load_library_search_default_dirs = 0x00001000,
load_library_search_dll_load_dir = 0x00000100,
load_library_search_system32 = 0x00000800,
load_library_search_user_dirs = 0x00000400,
load_with_altered_search_path = 0x00000008,
load_library_require_signed_target = 0x00000080,
load_library_safe_current_dirs = 0x00002000,
};
pub fn LoadLibraryExW(lpLibFileName: [*:0]const u16, dwFlags: LoadLibraryFlags) LoadLibraryError!HMODULE {
return kernel32.LoadLibraryExW(lpLibFileName, null, @intFromEnum(dwFlags)) orelse {
switch (GetLastError()) {
.FILE_NOT_FOUND => return error.FileNotFound,
.PATH_NOT_FOUND => return error.FileNotFound,
.MOD_NOT_FOUND => return error.FileNotFound,
else => |err| return unexpectedError(err),
}
};
}
pub fn FreeLibrary(hModule: HMODULE) void {
assert(kernel32.FreeLibrary(hModule) != 0);
}
pub fn QueryPerformanceFrequency() u64 {
// "On systems that run Windows XP or later, the function will always succeed"
// https://docs.microsoft.com/en-us/windows/desktop/api/profileapi/nf-profileapi-queryperformancefrequency
var result: LARGE_INTEGER = undefined;
assert(ntdll.RtlQueryPerformanceFrequency(&result) != 0);
// The kernel treats this integer as unsigned.
return @as(u64, @bitCast(result));
}
pub fn QueryPerformanceCounter() u64 {
// "On systems that run Windows XP or later, the function will always succeed"
// https://docs.microsoft.com/en-us/windows/desktop/api/profileapi/nf-profileapi-queryperformancecounter
var result: LARGE_INTEGER = undefined;
assert(ntdll.RtlQueryPerformanceCounter(&result) != 0);
// The kernel treats this integer as unsigned.
return @as(u64, @bitCast(result));
}
pub fn InitOnceExecuteOnce(InitOnce: *INIT_ONCE, InitFn: INIT_ONCE_FN, Parameter: ?*anyopaque, Context: ?*anyopaque) void {
assert(kernel32.InitOnceExecuteOnce(InitOnce, InitFn, Parameter, Context) != 0);
}
pub const SetFileTimeError = error{Unexpected};
pub fn SetFileTime(
hFile: HANDLE,
lpCreationTime: ?*const FILETIME,
lpLastAccessTime: ?*const FILETIME,
lpLastWriteTime: ?*const FILETIME,
) SetFileTimeError!void {
const rc = kernel32.SetFileTime(hFile, lpCreationTime, lpLastAccessTime, lpLastWriteTime);
if (rc == 0) {
switch (GetLastError()) {
else => |err| return unexpectedError(err),
}
}
}
pub const LockFileError = error{
SystemResources,
WouldBlock,
} || UnexpectedError;
pub fn LockFile(
FileHandle: HANDLE,
Event: ?HANDLE,
ApcRoutine: ?*IO_APC_ROUTINE,
ApcContext: ?*anyopaque,
IoStatusBlock: *IO_STATUS_BLOCK,
ByteOffset: *const LARGE_INTEGER,
Length: *const LARGE_INTEGER,
Key: ?*ULONG,
FailImmediately: BOOLEAN,
ExclusiveLock: BOOLEAN,
) !void {
const rc = ntdll.NtLockFile(
FileHandle,
Event,
ApcRoutine,
ApcContext,
IoStatusBlock,
ByteOffset,
Length,
Key,
FailImmediately,
ExclusiveLock,
);
switch (rc) {
.SUCCESS => return,
.INSUFFICIENT_RESOURCES => return error.SystemResources,
.LOCK_NOT_GRANTED => return error.WouldBlock,
.ACCESS_VIOLATION => unreachable, // bad io_status_block pointer
else => return unexpectedStatus(rc),
}
}
pub const UnlockFileError = error{
RangeNotLocked,
} || UnexpectedError;
pub fn UnlockFile(
FileHandle: HANDLE,
IoStatusBlock: *IO_STATUS_BLOCK,
ByteOffset: *const LARGE_INTEGER,
Length: *const LARGE_INTEGER,
Key: ?*ULONG,
) !void {
const rc = ntdll.NtUnlockFile(FileHandle, IoStatusBlock, ByteOffset, Length, Key);
switch (rc) {
.SUCCESS => return,
.RANGE_NOT_LOCKED => return error.RangeNotLocked,
.ACCESS_VIOLATION => unreachable, // bad io_status_block pointer
else => return unexpectedStatus(rc),
}
}
/// This is a workaround for the C backend until zig has the ability to put
/// C code in inline assembly.
extern fn zig_thumb_windows_teb() callconv(.c) *anyopaque;
extern fn zig_aarch64_windows_teb() callconv(.c) *anyopaque;
extern fn zig_x86_windows_teb() callconv(.c) *anyopaque;
extern fn zig_x86_64_windows_teb() callconv(.c) *anyopaque;
pub fn teb() *TEB {
return switch (native_arch) {
.thumb => if (builtin.zig_backend == .stage2_c)
@ptrCast(@alignCast(zig_thumb_windows_teb()))
else
asm (
\\ mrc p15, 0, %[ptr], c13, c0, 2
: [ptr] "=r" (-> *TEB),
),
.aarch64 => if (builtin.zig_backend == .stage2_c)
@ptrCast(@alignCast(zig_aarch64_windows_teb()))
else
asm (
\\ mov %[ptr], x18
: [ptr] "=r" (-> *TEB),
),
.x86 => if (builtin.zig_backend == .stage2_c)
@ptrCast(@alignCast(zig_x86_windows_teb()))
else
asm (
\\ movl %%fs:0x18, %[ptr]
: [ptr] "=r" (-> *TEB),
),
.x86_64 => if (builtin.zig_backend == .stage2_c)
@ptrCast(@alignCast(zig_x86_64_windows_teb()))
else
asm (
\\ movq %%gs:0x30, %[ptr]
: [ptr] "=r" (-> *TEB),
),
else => @compileError("unsupported arch"),
};
}
pub fn peb() *PEB {
return teb().ProcessEnvironmentBlock;
}
/// A file time is a 64-bit value that represents the number of 100-nanosecond
/// intervals that have elapsed since 12:00 A.M. January 1, 1601 Coordinated
/// Universal Time (UTC).
/// This function returns the number of nanoseconds since the canonical epoch,
/// which is the POSIX one (Jan 01, 1970 AD).
pub fn fromSysTime(hns: i64) i128 {
const adjusted_epoch: i128 = hns + std.time.epoch.windows * (std.time.ns_per_s / 100);
return adjusted_epoch * 100;
}
pub fn toSysTime(ns: i128) i64 {
const hns = @divFloor(ns, 100);
return @as(i64, @intCast(hns)) - std.time.epoch.windows * (std.time.ns_per_s / 100);
}
pub fn fileTimeToNanoSeconds(ft: FILETIME) i128 {
const hns = (@as(i64, ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
return fromSysTime(hns);
}
/// Converts a number of nanoseconds since the POSIX epoch to a Windows FILETIME.
pub fn nanoSecondsToFileTime(ns: i128) FILETIME {
const adjusted: u64 = @bitCast(toSysTime(ns));
return FILETIME{
.dwHighDateTime = @as(u32, @truncate(adjusted >> 32)),
.dwLowDateTime = @as(u32, @truncate(adjusted)),
};
}
/// Compares two WTF16 strings using the equivalent functionality of
/// `RtlEqualUnicodeString` (with case insensitive comparison enabled).
/// This function can be called on any target.
pub fn eqlIgnoreCaseWTF16(a: []const u16, b: []const u16) bool {
if (@inComptime() or builtin.os.tag != .windows) {
// This function compares the strings code unit by code unit (aka u16-to-u16),
// so any length difference implies inequality. In other words, there's no possible
// conversion that changes the number of WTF-16 code units needed for the uppercase/lowercase
// version in the conversion table since only codepoints <= max(u16) are eligible
// for conversion at all.
if (a.len != b.len) return false;
for (a, b) |a_c, b_c| {
// The slices are always WTF-16 LE, so need to convert the elements to native
// endianness for the uppercasing
const a_c_native = std.mem.littleToNative(u16, a_c);
const b_c_native = std.mem.littleToNative(u16, b_c);
if (a_c != b_c and nls.upcaseW(a_c_native) != nls.upcaseW(b_c_native)) {
return false;
}
}
return true;
}
// Use RtlEqualUnicodeString on Windows when not in comptime to avoid including a
// redundant copy of the uppercase data.
const a_bytes = @as(u16, @intCast(a.len * 2));
const a_string = UNICODE_STRING{
.Length = a_bytes,
.MaximumLength = a_bytes,
.Buffer = @constCast(a.ptr),
};
const b_bytes = @as(u16, @intCast(b.len * 2));
const b_string = UNICODE_STRING{
.Length = b_bytes,
.MaximumLength = b_bytes,
.Buffer = @constCast(b.ptr),
};
return ntdll.RtlEqualUnicodeString(&a_string, &b_string, TRUE) == TRUE;
}
/// Compares two WTF-8 strings using the equivalent functionality of
/// `RtlEqualUnicodeString` (with case insensitive comparison enabled).
/// This function can be called on any target.
/// Assumes `a` and `b` are valid WTF-8.
pub fn eqlIgnoreCaseWtf8(a: []const u8, b: []const u8) bool {
// A length equality check is not possible here because there are
// some codepoints that have a different length uppercase UTF-8 representations
// than their lowercase counterparts, e.g. U+0250 (2 bytes) <-> U+2C6F (3 bytes).
// There are 7 such codepoints in the uppercase data used by Windows.
var a_wtf8_it = std.unicode.Wtf8View.initUnchecked(a).iterator();
var b_wtf8_it = std.unicode.Wtf8View.initUnchecked(b).iterator();
// Use RtlUpcaseUnicodeChar on Windows when not in comptime to avoid including a
// redundant copy of the uppercase data.
const upcaseImpl = switch (builtin.os.tag) {
.windows => if (@inComptime()) nls.upcaseW else ntdll.RtlUpcaseUnicodeChar,
else => nls.upcaseW,
};
while (true) {
const a_cp = a_wtf8_it.nextCodepoint() orelse break;
const b_cp = b_wtf8_it.nextCodepoint() orelse return false;
if (a_cp <= std.math.maxInt(u16) and b_cp <= std.math.maxInt(u16)) {
if (a_cp != b_cp and upcaseImpl(@intCast(a_cp)) != upcaseImpl(@intCast(b_cp))) {
return false;
}
} else if (a_cp != b_cp) {
return false;
}
}
// Make sure there are no leftover codepoints in b
if (b_wtf8_it.nextCodepoint() != null) return false;
return true;
}
fn testEqlIgnoreCase(comptime expect_eql: bool, comptime a: []const u8, comptime b: []const u8) !void {
try std.testing.expectEqual(expect_eql, eqlIgnoreCaseWtf8(a, b));
try std.testing.expectEqual(expect_eql, eqlIgnoreCaseWTF16(
std.unicode.utf8ToUtf16LeStringLiteral(a),
std.unicode.utf8ToUtf16LeStringLiteral(b),
));
try comptime std.testing.expect(expect_eql == eqlIgnoreCaseWtf8(a, b));
try comptime std.testing.expect(expect_eql == eqlIgnoreCaseWTF16(
std.unicode.utf8ToUtf16LeStringLiteral(a),
std.unicode.utf8ToUtf16LeStringLiteral(b),
));
}
test "eqlIgnoreCaseWTF16/Wtf8" {
try testEqlIgnoreCase(true, "\x01 a B Λ ɐ", "\x01 A b λ Ɐ");
// does not do case-insensitive comparison for codepoints >= U+10000
try testEqlIgnoreCase(false, "𐓏", "𐓷");
}
pub const PathSpace = struct {
data: [PATH_MAX_WIDE:0]u16,
len: usize,
pub fn span(self: *const PathSpace) [:0]const u16 {
return self.data[0..self.len :0];
}
};
/// The error type for `removeDotDirsSanitized`
pub const RemoveDotDirsError = error{TooManyParentDirs};
/// Removes '.' and '..' path components from a "sanitized relative path".
/// A "sanitized path" is one where:
/// 1) all forward slashes have been replaced with back slashes
/// 2) all repeating back slashes have been collapsed
/// 3) the path is a relative one (does not start with a back slash)
pub fn removeDotDirsSanitized(comptime T: type, path: []T) RemoveDotDirsError!usize {
std.debug.assert(path.len == 0 or path[0] != '\\');
var write_idx: usize = 0;
var read_idx: usize = 0;
while (read_idx < path.len) {
if (path[read_idx] == '.') {
if (read_idx + 1 == path.len)
return write_idx;
const after_dot = path[read_idx + 1];
if (after_dot == '\\') {
read_idx += 2;
continue;
}
if (after_dot == '.' and (read_idx + 2 == path.len or path[read_idx + 2] == '\\')) {
if (write_idx == 0) return error.TooManyParentDirs;
std.debug.assert(write_idx >= 2);
write_idx -= 1;
while (true) {
write_idx -= 1;
if (write_idx == 0) break;
if (path[write_idx] == '\\') {
write_idx += 1;
break;
}
}
if (read_idx + 2 == path.len)
return write_idx;
read_idx += 3;
continue;
}
}
// skip to the next path separator
while (true) : (read_idx += 1) {
if (read_idx == path.len)
return write_idx;
path[write_idx] = path[read_idx];
write_idx += 1;
if (path[read_idx] == '\\')
break;
}
read_idx += 1;
}
return write_idx;
}
/// Normalizes a Windows path with the following steps:
/// 1) convert all forward slashes to back slashes
/// 2) collapse duplicate back slashes
/// 3) remove '.' and '..' directory parts
/// Returns the length of the new path.
pub fn normalizePath(comptime T: type, path: []T) RemoveDotDirsError!usize {
mem.replaceScalar(T, path, '/', '\\');
const new_len = mem.collapseRepeatsLen(T, path, '\\');
const prefix_len: usize = init: {
if (new_len >= 1 and path[0] == '\\') break :init 1;
if (new_len >= 2 and path[1] == ':')
break :init if (new_len >= 3 and path[2] == '\\') @as(usize, 3) else @as(usize, 2);
break :init 0;
};
return prefix_len + try removeDotDirsSanitized(T, path[prefix_len..new_len]);
}
pub const Wtf8ToPrefixedFileWError = error{InvalidWtf8} || Wtf16ToPrefixedFileWError;
/// Same as `sliceToPrefixedFileW` but accepts a pointer
/// to a null-terminated WTF-8 encoded path.
/// https://simonsapin.github.io/wtf-8/
pub fn cStrToPrefixedFileW(dir: ?HANDLE, s: [*:0]const u8) Wtf8ToPrefixedFileWError!PathSpace {
return sliceToPrefixedFileW(dir, mem.sliceTo(s, 0));
}
/// Same as `wToPrefixedFileW` but accepts a WTF-8 encoded path.
/// https://simonsapin.github.io/wtf-8/
pub fn sliceToPrefixedFileW(dir: ?HANDLE, path: []const u8) Wtf8ToPrefixedFileWError!PathSpace {
var temp_path: PathSpace = undefined;
temp_path.len = try std.unicode.wtf8ToWtf16Le(&temp_path.data, path);
temp_path.data[temp_path.len] = 0;
return wToPrefixedFileW(dir, temp_path.span());
}
pub const Wtf16ToPrefixedFileWError = error{
AccessDenied,
BadPathName,
FileNotFound,
NameTooLong,
Unexpected,
};
/// Converts the `path` to WTF16, null-terminated. If the path contains any
/// namespace prefix, or is anything but a relative path (rooted, drive relative,
/// etc) the result will have the NT-style prefix `\??\`.
///
/// Similar to RtlDosPathNameToNtPathName_U with a few differences:
/// - Does not allocate on the heap.
/// - Relative paths are kept as relative unless they contain too many ..
/// components, in which case they are resolved against the `dir` if it
/// is non-null, or the CWD if it is null.
/// - Special case device names like COM1, NUL, etc are not handled specially (TODO)
/// - . and space are not stripped from the end of relative paths (potential TODO)
pub fn wToPrefixedFileW(dir: ?HANDLE, path: [:0]const u16) Wtf16ToPrefixedFileWError!PathSpace {
const nt_prefix = [_]u16{ '\\', '?', '?', '\\' };
switch (getNamespacePrefix(u16, path)) {
// TODO: Figure out a way to design an API that can avoid the copy for .nt,
// since it is always returned fully unmodified.
.nt, .verbatim => {
var path_space: PathSpace = undefined;
path_space.data[0..nt_prefix.len].* = nt_prefix;
const len_after_prefix = path.len - nt_prefix.len;
@memcpy(path_space.data[nt_prefix.len..][0..len_after_prefix], path[nt_prefix.len..]);
path_space.len = path.len;
path_space.data[path_space.len] = 0;
return path_space;
},
.local_device, .fake_verbatim => {
var path_space: PathSpace = undefined;
const path_byte_len = ntdll.RtlGetFullPathName_U(
path.ptr,
path_space.data.len * 2,
&path_space.data,
null,
);
if (path_byte_len == 0) {
// TODO: This may not be the right error
return error.BadPathName;
} else if (path_byte_len / 2 > path_space.data.len) {
return error.NameTooLong;
}
path_space.len = path_byte_len / 2;
// Both prefixes will be normalized but retained, so all
// we need to do now is replace them with the NT prefix
path_space.data[0..nt_prefix.len].* = nt_prefix;
return path_space;
},
.none => {
const path_type = getUnprefixedPathType(u16, path);
var path_space: PathSpace = undefined;
relative: {
if (path_type == .relative) {
// TODO: Handle special case device names like COM1, AUX, NUL, CONIN$, CONOUT$, etc.
// See https://googleprojectzero.blogspot.com/2016/02/the-definitive-guide-on-win32-to-nt.html
// TODO: Potentially strip all trailing . and space characters from the
// end of the path. This is something that both RtlDosPathNameToNtPathName_U
// and RtlGetFullPathName_U do. Technically, trailing . and spaces
// are allowed, but such paths may not interact well with Windows (i.e.
// files with these paths can't be deleted from explorer.exe, etc).
// This could be something that normalizePath may want to do.
@memcpy(path_space.data[0..path.len], path);
// Try to normalize, but if we get too many parent directories,
// then we need to start over and use RtlGetFullPathName_U instead.
path_space.len = normalizePath(u16, path_space.data[0..path.len]) catch |err| switch (err) {
error.TooManyParentDirs => break :relative,
};
path_space.data[path_space.len] = 0;
return path_space;
}
}
// We now know we are going to return an absolute NT path, so
// we can unconditionally prefix it with the NT prefix.
path_space.data[0..nt_prefix.len].* = nt_prefix;
if (path_type == .root_local_device) {
// `\\.` and `\\?` always get converted to `\??\` exactly, so
// we can just stop here
path_space.len = nt_prefix.len;
path_space.data[path_space.len] = 0;
return path_space;
}
const path_buf_offset = switch (path_type) {
// UNC paths will always start with `\\`. However, we want to
// end up with something like `\??\UNC\server\share`, so to get
// RtlGetFullPathName to write into the spot we want the `server`
// part to end up, we need to provide an offset such that
// the `\\` part gets written where the `C\` of `UNC\` will be
// in the final NT path.
.unc_absolute => nt_prefix.len + 2,
else => nt_prefix.len,
};
const buf_len: u32 = @intCast(path_space.data.len - path_buf_offset);
const path_to_get: [:0]const u16 = path_to_get: {
// If dir is null, then we don't need to bother with GetFinalPathNameByHandle because
// RtlGetFullPathName_U will resolve relative paths against the CWD for us.
if (path_type != .relative or dir == null) {
break :path_to_get path;
}
// We can also skip GetFinalPathNameByHandle if the handle matches
// the handle returned by fs.cwd()
if (dir.? == std.fs.cwd().fd) {
break :path_to_get path;
}
// At this point, we know we have a relative path that had too many
// `..` components to be resolved by normalizePath, so we need to
// convert it into an absolute path and let RtlGetFullPathName_U
// canonicalize it. We do this by getting the path of the `dir`
// and appending the relative path to it.
var dir_path_buf: [PATH_MAX_WIDE:0]u16 = undefined;
const dir_path = GetFinalPathNameByHandle(dir.?, .{}, &dir_path_buf) catch |err| switch (err) {
// This mapping is not correct; it is actually expected
// that calling GetFinalPathNameByHandle might return
// error.UnrecognizedVolume, and in fact has been observed
// in the wild. The problem is that wToPrefixedFileW was
// never intended to make *any* OS syscall APIs. It's only
// supposed to convert a string to one that is eligible to
// be used in the ntdll syscalls.
//
// To solve this, this function needs to no longer call
// GetFinalPathNameByHandle under any conditions, or the
// calling function needs to get reworked to not need to
// call this function.
//
// This may involve making breaking API changes.
error.UnrecognizedVolume => return error.Unexpected,
else => |e| return e,
};
if (dir_path.len + 1 + path.len > PATH_MAX_WIDE) {
return error.NameTooLong;
}
// We don't have to worry about potentially doubling up path separators
// here since RtlGetFullPathName_U will handle canonicalizing it.
dir_path_buf[dir_path.len] = '\\';
@memcpy(dir_path_buf[dir_path.len + 1 ..][0..path.len], path);
const full_len = dir_path.len + 1 + path.len;
dir_path_buf[full_len] = 0;
break :path_to_get dir_path_buf[0..full_len :0];
};
const path_byte_len = ntdll.RtlGetFullPathName_U(
path_to_get.ptr,
buf_len * 2,
path_space.data[path_buf_offset..].ptr,
null,
);
if (path_byte_len == 0) {
// TODO: This may not be the right error
return error.BadPathName;
} else if (path_byte_len / 2 > buf_len) {
return error.NameTooLong;
}
path_space.len = path_buf_offset + (path_byte_len / 2);
if (path_type == .unc_absolute) {
// Now add in the UNC, the `C` should overwrite the first `\` of the
// FullPathName, ultimately resulting in `\??\UNC\`
std.debug.assert(path_space.data[path_buf_offset] == '\\');
std.debug.assert(path_space.data[path_buf_offset + 1] == '\\');
const unc = [_]u16{ 'U', 'N', 'C' };
path_space.data[nt_prefix.len..][0..unc.len].* = unc;
}
return path_space;
},
}
}
pub const NamespacePrefix = enum {
none,
/// `\\.\` (path separators can be `\` or `/`)
local_device,
/// `\\?\`
/// When converted to an NT path, everything past the prefix is left
/// untouched and `\\?\` is replaced by `\??\`.
verbatim,
/// `\\?\` without all path separators being `\`.
/// This seems to be recognized as a prefix, but the 'verbatim' aspect
/// is not respected (i.e. if `//?/C:/foo` is converted to an NT path,
/// it will become `\??\C:\foo` [it will be canonicalized and the //?/ won't
/// be treated as part of the final path])
fake_verbatim,
/// `\??\`
nt,
};
/// If `T` is `u16`, then `path` should be encoded as WTF-16LE.
pub fn getNamespacePrefix(comptime T: type, path: []const T) NamespacePrefix {
if (path.len < 4) return .none;
var all_backslash = switch (mem.littleToNative(T, path[0])) {
'\\' => true,
'/' => false,
else => return .none,
};
all_backslash = all_backslash and switch (mem.littleToNative(T, path[3])) {
'\\' => true,
'/' => false,
else => return .none,
};
switch (mem.littleToNative(T, path[1])) {
'?' => if (mem.littleToNative(T, path[2]) == '?' and all_backslash) return .nt else return .none,
'\\' => {},
'/' => all_backslash = false,
else => return .none,
}
return switch (mem.littleToNative(T, path[2])) {
'?' => if (all_backslash) .verbatim else .fake_verbatim,
'.' => .local_device,
else => .none,
};
}
test getNamespacePrefix {
try std.testing.expectEqual(NamespacePrefix.none, getNamespacePrefix(u8, ""));
try std.testing.expectEqual(NamespacePrefix.nt, getNamespacePrefix(u8, "\\??\\"));
try std.testing.expectEqual(NamespacePrefix.none, getNamespacePrefix(u8, "/??/"));
try std.testing.expectEqual(NamespacePrefix.none, getNamespacePrefix(u8, "/??\\"));
try std.testing.expectEqual(NamespacePrefix.none, getNamespacePrefix(u8, "\\?\\\\"));
try std.testing.expectEqual(NamespacePrefix.local_device, getNamespacePrefix(u8, "\\\\.\\"));
try std.testing.expectEqual(NamespacePrefix.local_device, getNamespacePrefix(u8, "\\\\./"));
try std.testing.expectEqual(NamespacePrefix.local_device, getNamespacePrefix(u8, "/\\./"));
try std.testing.expectEqual(NamespacePrefix.local_device, getNamespacePrefix(u8, "//./"));
try std.testing.expectEqual(NamespacePrefix.none, getNamespacePrefix(u8, "/.//"));
try std.testing.expectEqual(NamespacePrefix.verbatim, getNamespacePrefix(u8, "\\\\?\\"));
try std.testing.expectEqual(NamespacePrefix.fake_verbatim, getNamespacePrefix(u8, "\\/?\\"));
try std.testing.expectEqual(NamespacePrefix.fake_verbatim, getNamespacePrefix(u8, "\\/?/"));
try std.testing.expectEqual(NamespacePrefix.fake_verbatim, getNamespacePrefix(u8, "//?/"));
}
pub const UnprefixedPathType = enum {
unc_absolute,
drive_absolute,
drive_relative,
rooted,
relative,
root_local_device,
};
/// Get the path type of a path that is known to not have any namespace prefixes
/// (`\\?\`, `\\.\`, `\??\`).
/// If `T` is `u16`, then `path` should be encoded as WTF-16LE.
pub fn getUnprefixedPathType(comptime T: type, path: []const T) UnprefixedPathType {
if (path.len < 1) return .relative;
if (std.debug.runtime_safety) {
std.debug.assert(getNamespacePrefix(T, path) == .none);
}
const windows_path = std.fs.path.PathType.windows;
if (windows_path.isSep(T, mem.littleToNative(T, path[0]))) {
// \x
if (path.len < 2 or !windows_path.isSep(T, mem.littleToNative(T, path[1]))) return .rooted;
// exactly \\. or \\? with nothing trailing
if (path.len == 3 and (mem.littleToNative(T, path[2]) == '.' or mem.littleToNative(T, path[2]) == '?')) return .root_local_device;
// \\x
return .unc_absolute;
} else {
// x
if (path.len < 2 or mem.littleToNative(T, path[1]) != ':') return .relative;
// x:\
if (path.len > 2 and windows_path.isSep(T, mem.littleToNative(T, path[2]))) return .drive_absolute;
// x:
return .drive_relative;
}
}
test getUnprefixedPathType {
try std.testing.expectEqual(UnprefixedPathType.relative, getUnprefixedPathType(u8, ""));
try std.testing.expectEqual(UnprefixedPathType.relative, getUnprefixedPathType(u8, "x"));
try std.testing.expectEqual(UnprefixedPathType.relative, getUnprefixedPathType(u8, "x\\"));
try std.testing.expectEqual(UnprefixedPathType.root_local_device, getUnprefixedPathType(u8, "//."));
try std.testing.expectEqual(UnprefixedPathType.root_local_device, getUnprefixedPathType(u8, "/\\?"));
try std.testing.expectEqual(UnprefixedPathType.root_local_device, getUnprefixedPathType(u8, "\\\\?"));
try std.testing.expectEqual(UnprefixedPathType.unc_absolute, getUnprefixedPathType(u8, "\\\\x"));
try std.testing.expectEqual(UnprefixedPathType.unc_absolute, getUnprefixedPathType(u8, "//x"));
try std.testing.expectEqual(UnprefixedPathType.rooted, getUnprefixedPathType(u8, "\\x"));
try std.testing.expectEqual(UnprefixedPathType.rooted, getUnprefixedPathType(u8, "/"));
try std.testing.expectEqual(UnprefixedPathType.drive_relative, getUnprefixedPathType(u8, "x:"));
try std.testing.expectEqual(UnprefixedPathType.drive_relative, getUnprefixedPathType(u8, "x:abc"));
try std.testing.expectEqual(UnprefixedPathType.drive_relative, getUnprefixedPathType(u8, "x:a/b/c"));
try std.testing.expectEqual(UnprefixedPathType.drive_absolute, getUnprefixedPathType(u8, "x:\\"));
try std.testing.expectEqual(UnprefixedPathType.drive_absolute, getUnprefixedPathType(u8, "x:\\abc"));
try std.testing.expectEqual(UnprefixedPathType.drive_absolute, getUnprefixedPathType(u8, "x:/a/b/c"));
}
/// Similar to `RtlNtPathNameToDosPathName` but does not do any heap allocation.
/// The possible transformations are:
/// \??\C:\Some\Path -> C:\Some\Path
/// \??\UNC\server\share\foo -> \\server\share\foo
/// If the path does not have the NT namespace prefix, then `error.NotNtPath` is returned.
///
/// Functionality is based on the ReactOS test cases found here:
/// https://github.com/reactos/reactos/blob/master/modules/rostests/apitests/ntdll/RtlNtPathNameToDosPathName.c
///
/// `path` should be encoded as WTF-16LE.
pub fn ntToWin32Namespace(path: []const u16) !PathSpace {
if (path.len > PATH_MAX_WIDE) return error.NameTooLong;
var path_space: PathSpace = undefined;
const namespace_prefix = getNamespacePrefix(u16, path);
switch (namespace_prefix) {
.nt => {
var dest_index: usize = 0;
var after_prefix = path[4..]; // after the `\??\`
// The prefix \??\UNC\ means this is a UNC path, in which case the
// `\??\UNC\` should be replaced by `\\` (two backslashes)
// TODO: the "UNC" should technically be matched case-insensitively, but
// it's unlikely to matter since most/all paths passed into this
// function will have come from the OS meaning it should have
// the 'canonical' uppercase UNC.
const is_unc = after_prefix.len >= 4 and
std.mem.eql(u16, after_prefix[0..3], std.unicode.utf8ToUtf16LeStringLiteral("UNC")) and
std.fs.path.PathType.windows.isSep(u16, std.mem.littleToNative(u16, after_prefix[3]));
if (is_unc) {
path_space.data[0] = comptime std.mem.nativeToLittle(u16, '\\');
dest_index += 1;
// We want to include the last `\` of `\??\UNC\`
after_prefix = path[7..];
}
@memcpy(path_space.data[dest_index..][0..after_prefix.len], after_prefix);
path_space.len = dest_index + after_prefix.len;
path_space.data[path_space.len] = 0;
return path_space;
},
else => return error.NotNtPath,
}
}
test ntToWin32Namespace {
const L = std.unicode.utf8ToUtf16LeStringLiteral;
try testNtToWin32Namespace(L("UNC"), L("\\??\\UNC"));
try testNtToWin32Namespace(L("\\\\"), L("\\??\\UNC\\"));
try testNtToWin32Namespace(L("\\\\path1"), L("\\??\\UNC\\path1"));
try testNtToWin32Namespace(L("\\\\path1\\path2"), L("\\??\\UNC\\path1\\path2"));
try testNtToWin32Namespace(L(""), L("\\??\\"));
try testNtToWin32Namespace(L("C:"), L("\\??\\C:"));
try testNtToWin32Namespace(L("C:\\"), L("\\??\\C:\\"));
try testNtToWin32Namespace(L("C:\\test"), L("\\??\\C:\\test"));
try testNtToWin32Namespace(L("C:\\test\\"), L("\\??\\C:\\test\\"));
try std.testing.expectError(error.NotNtPath, ntToWin32Namespace(L("foo")));
try std.testing.expectError(error.NotNtPath, ntToWin32Namespace(L("C:\\test")));
try std.testing.expectError(error.NotNtPath, ntToWin32Namespace(L("\\\\.\\test")));
}
fn testNtToWin32Namespace(expected: []const u16, path: []const u16) !void {
const converted = try ntToWin32Namespace(path);
try std.testing.expectEqualSlices(u16, expected, converted.span());
}
fn getFullPathNameW(path: [*:0]const u16, out: []u16) !usize {
const result = kernel32.GetFullPathNameW(path, @as(u32, @intCast(out.len)), out.ptr, null);
if (result == 0) {
switch (GetLastError()) {
else => |err| return unexpectedError(err),
}
}
return result;
}
inline fn MAKELANGID(p: c_ushort, s: c_ushort) LANGID {
return (s << 10) | p;
}
/// Loads a Winsock extension function in runtime specified by a GUID.
pub fn loadWinsockExtensionFunction(comptime T: type, sock: ws2_32.SOCKET, guid: GUID) !T {
var function: T = undefined;
var num_bytes: DWORD = undefined;
const rc = ws2_32.WSAIoctl(
sock,
ws2_32.SIO_GET_EXTENSION_FUNCTION_POINTER,
&guid,
@sizeOf(GUID),
@as(?*anyopaque, @ptrFromInt(@intFromPtr(&function))),
@sizeOf(T),
&num_bytes,
null,
null,
);
if (rc == ws2_32.SOCKET_ERROR) {
return switch (ws2_32.WSAGetLastError()) {
.WSAEOPNOTSUPP => error.OperationNotSupported,
.WSAENOTSOCK => error.FileDescriptorNotASocket,
else => |err| unexpectedWSAError(err),
};
}
if (num_bytes != @sizeOf(T)) {
return error.ShortRead;
}
return function;
}
/// Call this when you made a windows DLL call or something that does SetLastError
/// and you get an unexpected error.
pub fn unexpectedError(err: Win32Error) UnexpectedError {
if (std.posix.unexpected_error_tracing) {
// 614 is the length of the longest windows error description
var buf_wstr: [614:0]WCHAR = undefined;
const len = kernel32.FormatMessageW(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
null,
err,
MAKELANGID(LANG.NEUTRAL, SUBLANG.DEFAULT),
&buf_wstr,
buf_wstr.len,
null,
);
std.debug.print("error.Unexpected: GetLastError({}): {}\n", .{
@intFromEnum(err),
std.unicode.fmtUtf16Le(buf_wstr[0..len]),
});
std.debug.dumpCurrentStackTrace(@returnAddress());
}
return error.Unexpected;
}
pub fn unexpectedWSAError(err: ws2_32.WinsockError) UnexpectedError {
return unexpectedError(@as(Win32Error, @enumFromInt(@intFromEnum(err))));
}
/// Call this when you made a windows NtDll call
/// and you get an unexpected status.
pub fn unexpectedStatus(status: NTSTATUS) UnexpectedError {
if (std.posix.unexpected_error_tracing) {
std.debug.print("error.Unexpected NTSTATUS=0x{x}\n", .{@intFromEnum(status)});
std.debug.dumpCurrentStackTrace(@returnAddress());
}
return error.Unexpected;
}
pub const Win32Error = @import("windows/win32error.zig").Win32Error;
pub const NTSTATUS = @import("windows/ntstatus.zig").NTSTATUS;
pub const LANG = @import("windows/lang.zig");
pub const SUBLANG = @import("windows/sublang.zig");
/// The standard input device. Initially, this is the console input buffer, CONIN$.
pub const STD_INPUT_HANDLE = maxInt(DWORD) - 10 + 1;
/// The standard output device. Initially, this is the active console screen buffer, CONOUT$.
pub const STD_OUTPUT_HANDLE = maxInt(DWORD) - 11 + 1;
/// The standard error device. Initially, this is the active console screen buffer, CONOUT$.
pub const STD_ERROR_HANDLE = maxInt(DWORD) - 12 + 1;
/// Deprecated; use `std.builtin.CallingConvention.winapi` instead.
pub const WINAPI: std.builtin.CallingConvention = .winapi;
pub const BOOL = c_int;
pub const BOOLEAN = BYTE;
pub const BYTE = u8;
pub const CHAR = u8;
pub const UCHAR = u8;
pub const FLOAT = f32;
pub const HANDLE = *anyopaque;
pub const HCRYPTPROV = ULONG_PTR;
pub const ATOM = u16;
pub const HBRUSH = *opaque {};
pub const HCURSOR = *opaque {};
pub const HICON = *opaque {};
pub const HINSTANCE = *opaque {};
pub const HMENU = *opaque {};
pub const HMODULE = *opaque {};
pub const HWND = *opaque {};
pub const HDC = *opaque {};
pub const HGLRC = *opaque {};
pub const FARPROC = *opaque {};
pub const PROC = *opaque {};
pub const INT = c_int;
pub const LPCSTR = [*:0]const CHAR;
pub const LPCVOID = *const anyopaque;
pub const LPSTR = [*:0]CHAR;
pub const LPVOID = *anyopaque;
pub const LPWSTR = [*:0]WCHAR;
pub const LPCWSTR = [*:0]const WCHAR;
pub const PVOID = *anyopaque;
pub const PWSTR = [*:0]WCHAR;
pub const PCWSTR = [*:0]const WCHAR;
/// Allocated by SysAllocString, freed by SysFreeString
pub const BSTR = [*:0]WCHAR;
pub const SIZE_T = usize;
pub const UINT = c_uint;
pub const ULONG_PTR = usize;
pub const LONG_PTR = isize;
pub const DWORD_PTR = ULONG_PTR;
pub const WCHAR = u16;
pub const WORD = u16;
pub const DWORD = u32;
pub const DWORD64 = u64;
pub const LARGE_INTEGER = i64;
pub const ULARGE_INTEGER = u64;
pub const USHORT = u16;
pub const SHORT = i16;
pub const ULONG = u32;
pub const LONG = i32;
pub const ULONG64 = u64;
pub const ULONGLONG = u64;
pub const LONGLONG = i64;
pub const HLOCAL = HANDLE;
pub const LANGID = c_ushort;
pub const WPARAM = usize;
pub const LPARAM = LONG_PTR;
pub const LRESULT = LONG_PTR;
pub const va_list = *opaque {};
pub const TCHAR = @compileError("Deprecated: choose between `CHAR` or `WCHAR` directly instead.");
pub const LPTSTR = @compileError("Deprecated: choose between `LPSTR` or `LPWSTR` directly instead.");
pub const LPCTSTR = @compileError("Deprecated: choose between `LPCSTR` or `LPCWSTR` directly instead.");
pub const PTSTR = @compileError("Deprecated: choose between `PSTR` or `PWSTR` directly instead.");
pub const PCTSTR = @compileError("Deprecated: choose between `PCSTR` or `PCWSTR` directly instead.");
pub const TRUE = 1;
pub const FALSE = 0;
pub const DEVICE_TYPE = ULONG;
pub const FILE_DEVICE_BEEP: DEVICE_TYPE = 0x0001;
pub const FILE_DEVICE_CD_ROM: DEVICE_TYPE = 0x0002;
pub const FILE_DEVICE_CD_ROM_FILE_SYSTEM: DEVICE_TYPE = 0x0003;
pub const FILE_DEVICE_CONTROLLER: DEVICE_TYPE = 0x0004;
pub const FILE_DEVICE_DATALINK: DEVICE_TYPE = 0x0005;
pub const FILE_DEVICE_DFS: DEVICE_TYPE = 0x0006;
pub const FILE_DEVICE_DISK: DEVICE_TYPE = 0x0007;
pub const FILE_DEVICE_DISK_FILE_SYSTEM: DEVICE_TYPE = 0x0008;
pub const FILE_DEVICE_FILE_SYSTEM: DEVICE_TYPE = 0x0009;
pub const FILE_DEVICE_INPORT_PORT: DEVICE_TYPE = 0x000a;
pub const FILE_DEVICE_KEYBOARD: DEVICE_TYPE = 0x000b;
pub const FILE_DEVICE_MAILSLOT: DEVICE_TYPE = 0x000c;
pub const FILE_DEVICE_MIDI_IN: DEVICE_TYPE = 0x000d;
pub const FILE_DEVICE_MIDI_OUT: DEVICE_TYPE = 0x000e;
pub const FILE_DEVICE_MOUSE: DEVICE_TYPE = 0x000f;
pub const FILE_DEVICE_MULTI_UNC_PROVIDER: DEVICE_TYPE = 0x0010;
pub const FILE_DEVICE_NAMED_PIPE: DEVICE_TYPE = 0x0011;
pub const FILE_DEVICE_NETWORK: DEVICE_TYPE = 0x0012;
pub const FILE_DEVICE_NETWORK_BROWSER: DEVICE_TYPE = 0x0013;
pub const FILE_DEVICE_NETWORK_FILE_SYSTEM: DEVICE_TYPE = 0x0014;
pub const FILE_DEVICE_NULL: DEVICE_TYPE = 0x0015;
pub const FILE_DEVICE_PARALLEL_PORT: DEVICE_TYPE = 0x0016;
pub const FILE_DEVICE_PHYSICAL_NETCARD: DEVICE_TYPE = 0x0017;
pub const FILE_DEVICE_PRINTER: DEVICE_TYPE = 0x0018;
pub const FILE_DEVICE_SCANNER: DEVICE_TYPE = 0x0019;
pub const FILE_DEVICE_SERIAL_MOUSE_PORT: DEVICE_TYPE = 0x001a;
pub const FILE_DEVICE_SERIAL_PORT: DEVICE_TYPE = 0x001b;
pub const FILE_DEVICE_SCREEN: DEVICE_TYPE = 0x001c;
pub const FILE_DEVICE_SOUND: DEVICE_TYPE = 0x001d;
pub const FILE_DEVICE_STREAMS: DEVICE_TYPE = 0x001e;
pub const FILE_DEVICE_TAPE: DEVICE_TYPE = 0x001f;
pub const FILE_DEVICE_TAPE_FILE_SYSTEM: DEVICE_TYPE = 0x0020;
pub const FILE_DEVICE_TRANSPORT: DEVICE_TYPE = 0x0021;
pub const FILE_DEVICE_UNKNOWN: DEVICE_TYPE = 0x0022;
pub const FILE_DEVICE_VIDEO: DEVICE_TYPE = 0x0023;
pub const FILE_DEVICE_VIRTUAL_DISK: DEVICE_TYPE = 0x0024;
pub const FILE_DEVICE_WAVE_IN: DEVICE_TYPE = 0x0025;
pub const FILE_DEVICE_WAVE_OUT: DEVICE_TYPE = 0x0026;
pub const FILE_DEVICE_8042_PORT: DEVICE_TYPE = 0x0027;
pub const FILE_DEVICE_NETWORK_REDIRECTOR: DEVICE_TYPE = 0x0028;
pub const FILE_DEVICE_BATTERY: DEVICE_TYPE = 0x0029;
pub const FILE_DEVICE_BUS_EXTENDER: DEVICE_TYPE = 0x002a;
pub const FILE_DEVICE_MODEM: DEVICE_TYPE = 0x002b;
pub const FILE_DEVICE_VDM: DEVICE_TYPE = 0x002c;
pub const FILE_DEVICE_MASS_STORAGE: DEVICE_TYPE = 0x002d;
pub const FILE_DEVICE_SMB: DEVICE_TYPE = 0x002e;
pub const FILE_DEVICE_KS: DEVICE_TYPE = 0x002f;
pub const FILE_DEVICE_CHANGER: DEVICE_TYPE = 0x0030;
pub const FILE_DEVICE_SMARTCARD: DEVICE_TYPE = 0x0031;
pub const FILE_DEVICE_ACPI: DEVICE_TYPE = 0x0032;
pub const FILE_DEVICE_DVD: DEVICE_TYPE = 0x0033;
pub const FILE_DEVICE_FULLSCREEN_VIDEO: DEVICE_TYPE = 0x0034;
pub const FILE_DEVICE_DFS_FILE_SYSTEM: DEVICE_TYPE = 0x0035;
pub const FILE_DEVICE_DFS_VOLUME: DEVICE_TYPE = 0x0036;
pub const FILE_DEVICE_SERENUM: DEVICE_TYPE = 0x0037;
pub const FILE_DEVICE_TERMSRV: DEVICE_TYPE = 0x0038;
pub const FILE_DEVICE_KSEC: DEVICE_TYPE = 0x0039;
pub const FILE_DEVICE_FIPS: DEVICE_TYPE = 0x003a;
pub const FILE_DEVICE_INFINIBAND: DEVICE_TYPE = 0x003b;
// TODO: missing values?
pub const FILE_DEVICE_VMBUS: DEVICE_TYPE = 0x003e;
pub const FILE_DEVICE_CRYPT_PROVIDER: DEVICE_TYPE = 0x003f;
pub const FILE_DEVICE_WPD: DEVICE_TYPE = 0x0040;
pub const FILE_DEVICE_BLUETOOTH: DEVICE_TYPE = 0x0041;
pub const FILE_DEVICE_MT_COMPOSITE: DEVICE_TYPE = 0x0042;
pub const FILE_DEVICE_MT_TRANSPORT: DEVICE_TYPE = 0x0043;
pub const FILE_DEVICE_BIOMETRIC: DEVICE_TYPE = 0x0044;
pub const FILE_DEVICE_PMI: DEVICE_TYPE = 0x0045;
pub const FILE_DEVICE_EHSTOR: DEVICE_TYPE = 0x0046;
pub const FILE_DEVICE_DEVAPI: DEVICE_TYPE = 0x0047;
pub const FILE_DEVICE_GPIO: DEVICE_TYPE = 0x0048;
pub const FILE_DEVICE_USBEX: DEVICE_TYPE = 0x0049;
pub const FILE_DEVICE_CONSOLE: DEVICE_TYPE = 0x0050;
pub const FILE_DEVICE_NFP: DEVICE_TYPE = 0x0051;
pub const FILE_DEVICE_SYSENV: DEVICE_TYPE = 0x0052;
pub const FILE_DEVICE_VIRTUAL_BLOCK: DEVICE_TYPE = 0x0053;
pub const FILE_DEVICE_POINT_OF_SERVICE: DEVICE_TYPE = 0x0054;
pub const FILE_DEVICE_STORAGE_REPLICATION: DEVICE_TYPE = 0x0055;
pub const FILE_DEVICE_TRUST_ENV: DEVICE_TYPE = 0x0056;
pub const FILE_DEVICE_UCM: DEVICE_TYPE = 0x0057;
pub const FILE_DEVICE_UCMTCPCI: DEVICE_TYPE = 0x0058;
pub const FILE_DEVICE_PERSISTENT_MEMORY: DEVICE_TYPE = 0x0059;
pub const FILE_DEVICE_NVDIMM: DEVICE_TYPE = 0x005a;
pub const FILE_DEVICE_HOLOGRAPHIC: DEVICE_TYPE = 0x005b;
pub const FILE_DEVICE_SDFXHCI: DEVICE_TYPE = 0x005c;
/// https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/buffer-descriptions-for-i-o-control-codes
pub const TransferType = enum(u2) {
METHOD_BUFFERED = 0,
METHOD_IN_DIRECT = 1,
METHOD_OUT_DIRECT = 2,
METHOD_NEITHER = 3,
};
pub const FILE_ANY_ACCESS = 0;
pub const FILE_READ_ACCESS = 1;
pub const FILE_WRITE_ACCESS = 2;
/// https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/defining-i-o-control-codes
pub fn CTL_CODE(deviceType: u16, function: u12, method: TransferType, access: u2) DWORD {
return (@as(DWORD, deviceType) << 16) |
(@as(DWORD, access) << 14) |
(@as(DWORD, function) << 2) |
@intFromEnum(method);
}
pub const INVALID_HANDLE_VALUE = @as(HANDLE, @ptrFromInt(maxInt(usize)));
pub const INVALID_FILE_ATTRIBUTES = @as(DWORD, maxInt(DWORD));
pub const FILE_ALL_INFORMATION = extern struct {
BasicInformation: FILE_BASIC_INFORMATION,
StandardInformation: FILE_STANDARD_INFORMATION,
InternalInformation: FILE_INTERNAL_INFORMATION,
EaInformation: FILE_EA_INFORMATION,
AccessInformation: FILE_ACCESS_INFORMATION,
PositionInformation: FILE_POSITION_INFORMATION,
ModeInformation: FILE_MODE_INFORMATION,
AlignmentInformation: FILE_ALIGNMENT_INFORMATION,
NameInformation: FILE_NAME_INFORMATION,
};
pub const FILE_BASIC_INFORMATION = extern struct {
CreationTime: LARGE_INTEGER,
LastAccessTime: LARGE_INTEGER,
LastWriteTime: LARGE_INTEGER,
ChangeTime: LARGE_INTEGER,
FileAttributes: ULONG,
};
pub const FILE_STANDARD_INFORMATION = extern struct {
AllocationSize: LARGE_INTEGER,
EndOfFile: LARGE_INTEGER,
NumberOfLinks: ULONG,
DeletePending: BOOLEAN,
Directory: BOOLEAN,
};
pub const FILE_INTERNAL_INFORMATION = extern struct {
IndexNumber: LARGE_INTEGER,
};
pub const FILE_EA_INFORMATION = extern struct {
EaSize: ULONG,
};
pub const FILE_ACCESS_INFORMATION = extern struct {
AccessFlags: ACCESS_MASK,
};
pub const FILE_POSITION_INFORMATION = extern struct {
CurrentByteOffset: LARGE_INTEGER,
};
pub const FILE_END_OF_FILE_INFORMATION = extern struct {
EndOfFile: LARGE_INTEGER,
};
pub const FILE_MODE_INFORMATION = extern struct {
Mode: ULONG,
};
pub const FILE_ALIGNMENT_INFORMATION = extern struct {
AlignmentRequirement: ULONG,
};
pub const FILE_NAME_INFORMATION = extern struct {
FileNameLength: ULONG,
FileName: [1]WCHAR,
};
pub const FILE_DISPOSITION_INFORMATION_EX = extern struct {
/// combination of FILE_DISPOSITION_* flags
Flags: ULONG,
};
const FILE_DISPOSITION_DO_NOT_DELETE: ULONG = 0x00000000;
const FILE_DISPOSITION_DELETE: ULONG = 0x00000001;
const FILE_DISPOSITION_POSIX_SEMANTICS: ULONG = 0x00000002;
const FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK: ULONG = 0x00000004;
const FILE_DISPOSITION_ON_CLOSE: ULONG = 0x00000008;
const FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE: ULONG = 0x00000010;
// FILE_RENAME_INFORMATION.Flags
pub const FILE_RENAME_REPLACE_IF_EXISTS = 0x00000001;
pub const FILE_RENAME_POSIX_SEMANTICS = 0x00000002;
pub const FILE_RENAME_SUPPRESS_PIN_STATE_INHERITANCE = 0x00000004;
pub const FILE_RENAME_SUPPRESS_STORAGE_RESERVE_INHERITANCE = 0x00000008;
pub const FILE_RENAME_NO_INCREASE_AVAILABLE_SPACE = 0x00000010;
pub const FILE_RENAME_NO_DECREASE_AVAILABLE_SPACE = 0x00000020;
pub const FILE_RENAME_PRESERVE_AVAILABLE_SPACE = 0x00000030;
pub const FILE_RENAME_IGNORE_READONLY_ATTRIBUTE = 0x00000040;
pub const FILE_RENAME_FORCE_RESIZE_TARGET_SR = 0x00000080;
pub const FILE_RENAME_FORCE_RESIZE_SOURCE_SR = 0x00000100;
pub const FILE_RENAME_FORCE_RESIZE_SR = 0x00000180;
pub const FILE_RENAME_INFORMATION = extern struct {
Flags: BOOLEAN,
RootDirectory: ?HANDLE,
FileNameLength: ULONG,
FileName: [1]WCHAR,
};
// FileRenameInformationEx (since .win10_rs1)
pub const FILE_RENAME_INFORMATION_EX = extern struct {
Flags: ULONG,
RootDirectory: ?HANDLE,
FileNameLength: ULONG,
FileName: [1]WCHAR,
};
pub const IO_STATUS_BLOCK = extern struct {
// "DUMMYUNIONNAME" expands to "u"
u: extern union {
Status: NTSTATUS,
Pointer: ?*anyopaque,
},
Information: ULONG_PTR,
};
pub const FILE_INFORMATION_CLASS = enum(c_int) {
FileDirectoryInformation = 1,
FileFullDirectoryInformation,
FileBothDirectoryInformation,
FileBasicInformation,
FileStandardInformation,
FileInternalInformation,
FileEaInformation,
FileAccessInformation,
FileNameInformation,
FileRenameInformation,
FileLinkInformation,
FileNamesInformation,
FileDispositionInformation,
FilePositionInformation,
FileFullEaInformation,
FileModeInformation,
FileAlignmentInformation,
FileAllInformation,
FileAllocationInformation,
FileEndOfFileInformation,
FileAlternateNameInformation,
FileStreamInformation,
FilePipeInformation,
FilePipeLocalInformation,
FilePipeRemoteInformation,
FileMailslotQueryInformation,
FileMailslotSetInformation,
FileCompressionInformation,
FileObjectIdInformation,
FileCompletionInformation,
FileMoveClusterInformation,
FileQuotaInformation,
FileReparsePointInformation,
FileNetworkOpenInformation,
FileAttributeTagInformation,
FileTrackingInformation,
FileIdBothDirectoryInformation,
FileIdFullDirectoryInformation,
FileValidDataLengthInformation,
FileShortNameInformation,
FileIoCompletionNotificationInformation,
FileIoStatusBlockRangeInformation,
FileIoPriorityHintInformation,
FileSfioReserveInformation,
FileSfioVolumeInformation,
FileHardLinkInformation,
FileProcessIdsUsingFileInformation,
FileNormalizedNameInformation,
FileNetworkPhysicalNameInformation,
FileIdGlobalTxDirectoryInformation,
FileIsRemoteDeviceInformation,
FileUnusedInformation,
FileNumaNodeInformation,
FileStandardLinkInformation,
FileRemoteProtocolInformation,
FileRenameInformationBypassAccessCheck,
FileLinkInformationBypassAccessCheck,
FileVolumeNameInformation,
FileIdInformation,
FileIdExtdDirectoryInformation,
FileReplaceCompletionInformation,
FileHardLinkFullIdInformation,
FileIdExtdBothDirectoryInformation,
FileDispositionInformationEx,
FileRenameInformationEx,
FileRenameInformationExBypassAccessCheck,
FileDesiredStorageClassInformation,
FileStatInformation,
FileMemoryPartitionInformation,
FileStatLxInformation,
FileCaseSensitiveInformation,
FileLinkInformationEx,
FileLinkInformationExBypassAccessCheck,
FileStorageReserveIdInformation,
FileCaseSensitiveInformationForceAccessCheck,
FileMaximumInformation,
};
pub const FILE_ATTRIBUTE_TAG_INFO = extern struct {
FileAttributes: DWORD,
ReparseTag: DWORD,
};
/// "If this bit is set, the file or directory represents another named entity in the system."
/// https://learn.microsoft.com/en-us/windows/win32/fileio/reparse-point-tags
pub const reparse_tag_name_surrogate_bit = 0x20000000;
pub const FILE_DISPOSITION_INFORMATION = extern struct {
DeleteFile: BOOLEAN,
};
pub const FILE_FS_DEVICE_INFORMATION = extern struct {
DeviceType: DEVICE_TYPE,
Characteristics: ULONG,
};
pub const FILE_FS_VOLUME_INFORMATION = extern struct {
VolumeCreationTime: LARGE_INTEGER,
VolumeSerialNumber: ULONG,
VolumeLabelLength: ULONG,
SupportsObjects: BOOLEAN,
// Flexible array member
VolumeLabel: [1]WCHAR,
};
pub const FS_INFORMATION_CLASS = enum(c_int) {
FileFsVolumeInformation = 1,
FileFsLabelInformation,
FileFsSizeInformation,
FileFsDeviceInformation,
FileFsAttributeInformation,
FileFsControlInformation,
FileFsFullSizeInformation,
FileFsObjectIdInformation,
FileFsDriverPathInformation,
FileFsVolumeFlagsInformation,
FileFsSectorSizeInformation,
FileFsDataCopyInformation,
FileFsMetadataSizeInformation,
FileFsFullSizeInformationEx,
FileFsMaximumInformation,
};
pub const OVERLAPPED = extern struct {
Internal: ULONG_PTR,
InternalHigh: ULONG_PTR,
DUMMYUNIONNAME: extern union {
DUMMYSTRUCTNAME: extern struct {
Offset: DWORD,
OffsetHigh: DWORD,
},
Pointer: ?PVOID,
},
hEvent: ?HANDLE,
};
pub const OVERLAPPED_ENTRY = extern struct {
lpCompletionKey: ULONG_PTR,
lpOverlapped: *OVERLAPPED,
Internal: ULONG_PTR,
dwNumberOfBytesTransferred: DWORD,
};
pub const MAX_PATH = 260;
pub const FILE_INFO_BY_HANDLE_CLASS = enum(u32) {
FileBasicInfo = 0,
FileStandardInfo = 1,
FileNameInfo = 2,
FileRenameInfo = 3,
FileDispositionInfo = 4,
FileAllocationInfo = 5,
FileEndOfFileInfo = 6,
FileStreamInfo = 7,
FileCompressionInfo = 8,
FileAttributeTagInfo = 9,
FileIdBothDirectoryInfo = 10,
FileIdBothDirectoryRestartInfo = 11,
FileIoPriorityHintInfo = 12,
FileRemoteProtocolInfo = 13,
FileFullDirectoryInfo = 14,
FileFullDirectoryRestartInfo = 15,
FileStorageInfo = 16,
FileAlignmentInfo = 17,
FileIdInfo = 18,
FileIdExtdDirectoryInfo = 19,
FileIdExtdDirectoryRestartInfo = 20,
};
pub const BY_HANDLE_FILE_INFORMATION = extern struct {
dwFileAttributes: DWORD,
ftCreationTime: FILETIME,
ftLastAccessTime: FILETIME,
ftLastWriteTime: FILETIME,
dwVolumeSerialNumber: DWORD,
nFileSizeHigh: DWORD,
nFileSizeLow: DWORD,
nNumberOfLinks: DWORD,
nFileIndexHigh: DWORD,
nFileIndexLow: DWORD,
};
pub const FILE_NAME_INFO = extern struct {
FileNameLength: DWORD,
FileName: [1]WCHAR,
};
/// Return the normalized drive name. This is the default.
pub const FILE_NAME_NORMALIZED = 0x0;
/// Return the opened file name (not normalized).
pub const FILE_NAME_OPENED = 0x8;
/// Return the path with the drive letter. This is the default.
pub const VOLUME_NAME_DOS = 0x0;
/// Return the path with a volume GUID path instead of the drive name.
pub const VOLUME_NAME_GUID = 0x1;
/// Return the path with no drive information.
pub const VOLUME_NAME_NONE = 0x4;
/// Return the path with the volume device path.
pub const VOLUME_NAME_NT = 0x2;
pub const SECURITY_ATTRIBUTES = extern struct {
nLength: DWORD,
lpSecurityDescriptor: ?*anyopaque,
bInheritHandle: BOOL,
};
pub const PIPE_ACCESS_INBOUND = 0x00000001;
pub const PIPE_ACCESS_OUTBOUND = 0x00000002;
pub const PIPE_ACCESS_DUPLEX = 0x00000003;
pub const PIPE_TYPE_BYTE = 0x00000000;
pub const PIPE_TYPE_MESSAGE = 0x00000004;
pub const PIPE_READMODE_BYTE = 0x00000000;
pub const PIPE_READMODE_MESSAGE = 0x00000002;
pub const PIPE_WAIT = 0x00000000;
pub const PIPE_NOWAIT = 0x00000001;
pub const GENERIC_READ = 0x80000000;
pub const GENERIC_WRITE = 0x40000000;
pub const GENERIC_EXECUTE = 0x20000000;
pub const GENERIC_ALL = 0x10000000;
pub const FILE_SHARE_DELETE = 0x00000004;
pub const FILE_SHARE_READ = 0x00000001;
pub const FILE_SHARE_WRITE = 0x00000002;
pub const DELETE = 0x00010000;
pub const READ_CONTROL = 0x00020000;
pub const WRITE_DAC = 0x00040000;
pub const WRITE_OWNER = 0x00080000;
pub const SYNCHRONIZE = 0x00100000;
pub const STANDARD_RIGHTS_READ = READ_CONTROL;
pub const STANDARD_RIGHTS_WRITE = READ_CONTROL;
pub const STANDARD_RIGHTS_EXECUTE = READ_CONTROL;
pub const STANDARD_RIGHTS_REQUIRED = DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER;
pub const MAXIMUM_ALLOWED = 0x02000000;
// disposition for NtCreateFile
pub const FILE_SUPERSEDE = 0;
pub const FILE_OPEN = 1;
pub const FILE_CREATE = 2;
pub const FILE_OPEN_IF = 3;
pub const FILE_OVERWRITE = 4;
pub const FILE_OVERWRITE_IF = 5;
pub const FILE_MAXIMUM_DISPOSITION = 5;
// flags for NtCreateFile and NtOpenFile
pub const FILE_READ_DATA = 0x00000001;
pub const FILE_LIST_DIRECTORY = 0x00000001;
pub const FILE_WRITE_DATA = 0x00000002;
pub const FILE_ADD_FILE = 0x00000002;
pub const FILE_APPEND_DATA = 0x00000004;
pub const FILE_ADD_SUBDIRECTORY = 0x00000004;
pub const FILE_CREATE_PIPE_INSTANCE = 0x00000004;
pub const FILE_READ_EA = 0x00000008;
pub const FILE_WRITE_EA = 0x00000010;
pub const FILE_EXECUTE = 0x00000020;
pub const FILE_TRAVERSE = 0x00000020;
pub const FILE_DELETE_CHILD = 0x00000040;
pub const FILE_READ_ATTRIBUTES = 0x00000080;
pub const FILE_WRITE_ATTRIBUTES = 0x00000100;
pub const FILE_DIRECTORY_FILE = 0x00000001;
pub const FILE_WRITE_THROUGH = 0x00000002;
pub const FILE_SEQUENTIAL_ONLY = 0x00000004;
pub const FILE_NO_INTERMEDIATE_BUFFERING = 0x00000008;
pub const FILE_SYNCHRONOUS_IO_ALERT = 0x00000010;
pub const FILE_SYNCHRONOUS_IO_NONALERT = 0x00000020;
pub const FILE_NON_DIRECTORY_FILE = 0x00000040;
pub const FILE_CREATE_TREE_CONNECTION = 0x00000080;
pub const FILE_COMPLETE_IF_OPLOCKED = 0x00000100;
pub const FILE_NO_EA_KNOWLEDGE = 0x00000200;
pub const FILE_OPEN_FOR_RECOVERY = 0x00000400;
pub const FILE_RANDOM_ACCESS = 0x00000800;
pub const FILE_DELETE_ON_CLOSE = 0x00001000;
pub const FILE_OPEN_BY_FILE_ID = 0x00002000;
pub const FILE_OPEN_FOR_BACKUP_INTENT = 0x00004000;
pub const FILE_NO_COMPRESSION = 0x00008000;
pub const FILE_RESERVE_OPFILTER = 0x00100000;
pub const FILE_OPEN_REPARSE_POINT = 0x00200000;
pub const FILE_OPEN_OFFLINE_FILE = 0x00400000;
pub const FILE_OPEN_FOR_FREE_SPACE_QUERY = 0x00800000;
pub const CREATE_ALWAYS = 2;
pub const CREATE_NEW = 1;
pub const OPEN_ALWAYS = 4;
pub const OPEN_EXISTING = 3;
pub const TRUNCATE_EXISTING = 5;
pub const FILE_ATTRIBUTE_ARCHIVE = 0x20;
pub const FILE_ATTRIBUTE_COMPRESSED = 0x800;
pub const FILE_ATTRIBUTE_DEVICE = 0x40;
pub const FILE_ATTRIBUTE_DIRECTORY = 0x10;
pub const FILE_ATTRIBUTE_ENCRYPTED = 0x4000;
pub const FILE_ATTRIBUTE_HIDDEN = 0x2;
pub const FILE_ATTRIBUTE_INTEGRITY_STREAM = 0x8000;
pub const FILE_ATTRIBUTE_NORMAL = 0x80;
pub const FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x2000;
pub const FILE_ATTRIBUTE_NO_SCRUB_DATA = 0x20000;
pub const FILE_ATTRIBUTE_OFFLINE = 0x1000;
pub const FILE_ATTRIBUTE_READONLY = 0x1;
pub const FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS = 0x400000;
pub const FILE_ATTRIBUTE_RECALL_ON_OPEN = 0x40000;
pub const FILE_ATTRIBUTE_REPARSE_POINT = 0x400;
pub const FILE_ATTRIBUTE_SPARSE_FILE = 0x200;
pub const FILE_ATTRIBUTE_SYSTEM = 0x4;
pub const FILE_ATTRIBUTE_TEMPORARY = 0x100;
pub const FILE_ATTRIBUTE_VIRTUAL = 0x10000;
pub const FILE_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1ff;
pub const FILE_GENERIC_READ = STANDARD_RIGHTS_READ | FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_READ_EA | SYNCHRONIZE;
pub const FILE_GENERIC_WRITE = STANDARD_RIGHTS_WRITE | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA | SYNCHRONIZE;
pub const FILE_GENERIC_EXECUTE = STANDARD_RIGHTS_EXECUTE | FILE_READ_ATTRIBUTES | FILE_EXECUTE | SYNCHRONIZE;
// Flags for NtCreateNamedPipeFile
// NamedPipeType
pub const FILE_PIPE_BYTE_STREAM_TYPE = 0x0;
pub const FILE_PIPE_MESSAGE_TYPE = 0x1;
pub const FILE_PIPE_ACCEPT_REMOTE_CLIENTS = 0x0;
pub const FILE_PIPE_REJECT_REMOTE_CLIENTS = 0x2;
pub const FILE_PIPE_TYPE_VALID_MASK = 0x3;
// CompletionMode
pub const FILE_PIPE_QUEUE_OPERATION = 0x0;
pub const FILE_PIPE_COMPLETE_OPERATION = 0x1;
// ReadMode
pub const FILE_PIPE_BYTE_STREAM_MODE = 0x0;
pub const FILE_PIPE_MESSAGE_MODE = 0x1;
// flags for CreateEvent
pub const CREATE_EVENT_INITIAL_SET = 0x00000002;
pub const CREATE_EVENT_MANUAL_RESET = 0x00000001;
pub const EVENT_ALL_ACCESS = 0x1F0003;
pub const EVENT_MODIFY_STATE = 0x0002;
// MEMORY_BASIC_INFORMATION.Type flags for VirtualQuery
pub const MEM_IMAGE = 0x1000000;
pub const MEM_MAPPED = 0x40000;
pub const MEM_PRIVATE = 0x20000;
pub const PROCESS_INFORMATION = extern struct {
hProcess: HANDLE,
hThread: HANDLE,
dwProcessId: DWORD,
dwThreadId: DWORD,
};
pub const STARTUPINFOW = extern struct {
cb: DWORD,
lpReserved: ?LPWSTR,
lpDesktop: ?LPWSTR,
lpTitle: ?LPWSTR,
dwX: DWORD,
dwY: DWORD,
dwXSize: DWORD,
dwYSize: DWORD,
dwXCountChars: DWORD,
dwYCountChars: DWORD,
dwFillAttribute: DWORD,
dwFlags: DWORD,
wShowWindow: WORD,
cbReserved2: WORD,
lpReserved2: ?*BYTE,
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 MAXIMUM_WAIT_OBJECTS = 64;
pub const WAIT_ABANDONED = 0x00000080;
pub const WAIT_ABANDONED_0 = WAIT_ABANDONED + 0;
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;
pub const MOVEFILE_COPY_ALLOWED = 2;
pub const MOVEFILE_CREATE_HARDLINK = 16;
pub const MOVEFILE_DELAY_UNTIL_REBOOT = 4;
pub const MOVEFILE_FAIL_IF_NOT_TRACKABLE = 32;
pub const MOVEFILE_REPLACE_EXISTING = 1;
pub const MOVEFILE_WRITE_THROUGH = 8;
pub const FILE_BEGIN = 0;
pub const FILE_CURRENT = 1;
pub const FILE_END = 2;
pub const HEAP_CREATE_ENABLE_EXECUTE = 0x00040000;
pub const HEAP_REALLOC_IN_PLACE_ONLY = 0x00000010;
pub const HEAP_GENERATE_EXCEPTIONS = 0x00000004;
pub const HEAP_NO_SERIALIZE = 0x00000001;
// AllocationType values
pub const MEM_COMMIT = 0x1000;
pub const MEM_RESERVE = 0x2000;
pub const MEM_FREE = 0x10000;
pub const MEM_RESET = 0x80000;
pub const MEM_RESET_UNDO = 0x1000000;
pub const MEM_LARGE_PAGES = 0x20000000;
pub const MEM_PHYSICAL = 0x400000;
pub const MEM_TOP_DOWN = 0x100000;
pub const MEM_WRITE_WATCH = 0x200000;
pub const MEM_RESERVE_PLACEHOLDER = 0x00040000;
pub const MEM_PRESERVE_PLACEHOLDER = 0x00000400;
// Protect values
pub const PAGE_EXECUTE = 0x10;
pub const PAGE_EXECUTE_READ = 0x20;
pub const PAGE_EXECUTE_READWRITE = 0x40;
pub const PAGE_EXECUTE_WRITECOPY = 0x80;
pub const PAGE_NOACCESS = 0x01;
pub const PAGE_READONLY = 0x02;
pub const PAGE_READWRITE = 0x04;
pub const PAGE_WRITECOPY = 0x08;
pub const PAGE_TARGETS_INVALID = 0x40000000;
pub const PAGE_TARGETS_NO_UPDATE = 0x40000000; // Same as PAGE_TARGETS_INVALID
pub const PAGE_GUARD = 0x100;
pub const PAGE_NOCACHE = 0x200;
pub const PAGE_WRITECOMBINE = 0x400;
// FreeType values
pub const MEM_COALESCE_PLACEHOLDERS = 0x1;
pub const MEM_RESERVE_PLACEHOLDERS = 0x2;
pub const MEM_DECOMMIT = 0x4000;
pub const MEM_RELEASE = 0x8000;
pub const PTHREAD_START_ROUTINE = *const fn (LPVOID) callconv(.winapi) DWORD;
pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE;
pub const WIN32_FIND_DATAW = extern struct {
dwFileAttributes: DWORD,
ftCreationTime: FILETIME,
ftLastAccessTime: FILETIME,
ftLastWriteTime: FILETIME,
nFileSizeHigh: DWORD,
nFileSizeLow: DWORD,
dwReserved0: DWORD,
dwReserved1: DWORD,
cFileName: [260]u16,
cAlternateFileName: [14]u16,
};
pub const FILETIME = extern struct {
dwLowDateTime: DWORD,
dwHighDateTime: DWORD,
};
pub const SYSTEM_INFO = extern struct {
anon1: extern union {
dwOemId: DWORD,
anon2: extern struct {
wProcessorArchitecture: WORD,
wReserved: WORD,
},
},
dwPageSize: DWORD,
lpMinimumApplicationAddress: LPVOID,
lpMaximumApplicationAddress: LPVOID,
dwActiveProcessorMask: DWORD_PTR,
dwNumberOfProcessors: DWORD,
dwProcessorType: DWORD,
dwAllocationGranularity: DWORD,
wProcessorLevel: WORD,
wProcessorRevision: WORD,
};
pub const HRESULT = c_long;
pub const KNOWNFOLDERID = GUID;
pub const GUID = extern struct {
Data1: u32,
Data2: u16,
Data3: u16,
Data4: [8]u8,
const hex_offsets = switch (builtin.target.cpu.arch.endian()) {
.big => [16]u6{
0, 2, 4, 6,
9, 11, 14, 16,
19, 21, 24, 26,
28, 30, 32, 34,
},
.little => [16]u6{
6, 4, 2, 0,
11, 9, 16, 14,
19, 21, 24, 26,
28, 30, 32, 34,
},
};
pub fn parse(s: []const u8) GUID {
assert(s[0] == '{');
assert(s[37] == '}');
return parseNoBraces(s[1 .. s.len - 1]) catch @panic("invalid GUID string");
}
pub fn parseNoBraces(s: []const u8) !GUID {
assert(s.len == 36);
assert(s[8] == '-');
assert(s[13] == '-');
assert(s[18] == '-');
assert(s[23] == '-');
var bytes: [16]u8 = undefined;
for (hex_offsets, 0..) |hex_offset, i| {
bytes[i] = (try std.fmt.charToDigit(s[hex_offset], 16)) << 4 |
try std.fmt.charToDigit(s[hex_offset + 1], 16);
}
return @as(GUID, @bitCast(bytes));
}
};
test GUID {
try std.testing.expectEqual(
GUID{
.Data1 = 0x01234567,
.Data2 = 0x89ab,
.Data3 = 0xef10,
.Data4 = "\x32\x54\x76\x98\xba\xdc\xfe\x91".*,
},
GUID.parse("{01234567-89AB-EF10-3254-7698badcfe91}"),
);
}
pub const FOLDERID_LocalAppData = GUID.parse("{F1B32785-6FBA-4FCF-9D55-7B8E7F157091}");
pub const KF_FLAG_DEFAULT = 0;
pub const KF_FLAG_NO_APPCONTAINER_REDIRECTION = 65536;
pub const KF_FLAG_CREATE = 32768;
pub const KF_FLAG_DONT_VERIFY = 16384;
pub const KF_FLAG_DONT_UNEXPAND = 8192;
pub const KF_FLAG_NO_ALIAS = 4096;
pub const KF_FLAG_INIT = 2048;
pub const KF_FLAG_DEFAULT_PATH = 1024;
pub const KF_FLAG_NOT_PARENT_RELATIVE = 512;
pub const KF_FLAG_SIMPLE_IDLIST = 256;
pub const KF_FLAG_ALIAS_ONLY = -2147483648;
pub const S_OK = 0;
pub const S_FALSE = 0x00000001;
pub const E_NOTIMPL = @as(c_long, @bitCast(@as(c_ulong, 0x80004001)));
pub const E_NOINTERFACE = @as(c_long, @bitCast(@as(c_ulong, 0x80004002)));
pub const E_POINTER = @as(c_long, @bitCast(@as(c_ulong, 0x80004003)));
pub const E_ABORT = @as(c_long, @bitCast(@as(c_ulong, 0x80004004)));
pub const E_FAIL = @as(c_long, @bitCast(@as(c_ulong, 0x80004005)));
pub const E_UNEXPECTED = @as(c_long, @bitCast(@as(c_ulong, 0x8000FFFF)));
pub const E_ACCESSDENIED = @as(c_long, @bitCast(@as(c_ulong, 0x80070005)));
pub const E_HANDLE = @as(c_long, @bitCast(@as(c_ulong, 0x80070006)));
pub const E_OUTOFMEMORY = @as(c_long, @bitCast(@as(c_ulong, 0x8007000E)));
pub const E_INVALIDARG = @as(c_long, @bitCast(@as(c_ulong, 0x80070057)));
pub fn HRESULT_CODE(hr: HRESULT) Win32Error {
return @enumFromInt(hr & 0xFFFF);
}
pub const FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
pub const FILE_FLAG_DELETE_ON_CLOSE = 0x04000000;
pub const FILE_FLAG_NO_BUFFERING = 0x20000000;
pub const FILE_FLAG_OPEN_NO_RECALL = 0x00100000;
pub const FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000;
pub const FILE_FLAG_OVERLAPPED = 0x40000000;
pub const FILE_FLAG_POSIX_SEMANTICS = 0x0100000;
pub const FILE_FLAG_RANDOM_ACCESS = 0x10000000;
pub const FILE_FLAG_SESSION_AWARE = 0x00800000;
pub const FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000;
pub const FILE_FLAG_WRITE_THROUGH = 0x80000000;
pub const RECT = extern struct {
left: LONG,
top: LONG,
right: LONG,
bottom: LONG,
};
pub const SMALL_RECT = extern struct {
Left: SHORT,
Top: SHORT,
Right: SHORT,
Bottom: SHORT,
};
pub const POINT = extern struct {
x: LONG,
y: LONG,
};
pub const COORD = extern struct {
X: SHORT,
Y: SHORT,
};
pub const CREATE_UNICODE_ENVIRONMENT = 1024;
pub const TLS_OUT_OF_INDEXES = 4294967295;
pub const IMAGE_TLS_DIRECTORY = extern struct {
StartAddressOfRawData: usize,
EndAddressOfRawData: usize,
AddressOfIndex: usize,
AddressOfCallBacks: usize,
SizeOfZeroFill: u32,
Characteristics: u32,
};
pub const IMAGE_TLS_DIRECTORY64 = IMAGE_TLS_DIRECTORY;
pub const IMAGE_TLS_DIRECTORY32 = IMAGE_TLS_DIRECTORY;
pub const PIMAGE_TLS_CALLBACK = ?*const fn (PVOID, DWORD, PVOID) callconv(.winapi) void;
pub const PROV_RSA_FULL = 1;
pub const REGSAM = ACCESS_MASK;
pub const ACCESS_MASK = DWORD;
pub const LSTATUS = LONG;
pub const SECTION_INHERIT = enum(c_int) {
ViewShare = 0,
ViewUnmap = 1,
};
pub const SECTION_QUERY = 0x0001;
pub const SECTION_MAP_WRITE = 0x0002;
pub const SECTION_MAP_READ = 0x0004;
pub const SECTION_MAP_EXECUTE = 0x0008;
pub const SECTION_EXTEND_SIZE = 0x0010;
pub const SECTION_ALL_ACCESS =
STANDARD_RIGHTS_REQUIRED |
SECTION_QUERY |
SECTION_MAP_WRITE |
SECTION_MAP_READ |
SECTION_MAP_EXECUTE |
SECTION_EXTEND_SIZE;
pub const SEC_64K_PAGES = 0x80000;
pub const SEC_FILE = 0x800000;
pub const SEC_IMAGE = 0x1000000;
pub const SEC_PROTECTED_IMAGE = 0x2000000;
pub const SEC_RESERVE = 0x4000000;
pub const SEC_COMMIT = 0x8000000;
pub const SEC_IMAGE_NO_EXECUTE = SEC_IMAGE | SEC_NOCACHE;
pub const SEC_NOCACHE = 0x10000000;
pub const SEC_WRITECOMBINE = 0x40000000;
pub const SEC_LARGE_PAGES = 0x80000000;
pub const HKEY = *opaque {};
pub const HKEY_CLASSES_ROOT: HKEY = @ptrFromInt(0x80000000);
pub const HKEY_CURRENT_USER: HKEY = @ptrFromInt(0x80000001);
pub const HKEY_LOCAL_MACHINE: HKEY = @ptrFromInt(0x80000002);
pub const HKEY_USERS: HKEY = @ptrFromInt(0x80000003);
pub const HKEY_PERFORMANCE_DATA: HKEY = @ptrFromInt(0x80000004);
pub const HKEY_PERFORMANCE_TEXT: HKEY = @ptrFromInt(0x80000050);
pub const HKEY_PERFORMANCE_NLSTEXT: HKEY = @ptrFromInt(0x80000060);
pub const HKEY_CURRENT_CONFIG: HKEY = @ptrFromInt(0x80000005);
pub const HKEY_DYN_DATA: HKEY = @ptrFromInt(0x80000006);
pub const HKEY_CURRENT_USER_LOCAL_SETTINGS: HKEY = @ptrFromInt(0x80000007);
/// Combines the STANDARD_RIGHTS_REQUIRED, KEY_QUERY_VALUE, KEY_SET_VALUE, KEY_CREATE_SUB_KEY,
/// KEY_ENUMERATE_SUB_KEYS, KEY_NOTIFY, and KEY_CREATE_LINK access rights.
pub const KEY_ALL_ACCESS = 0xF003F;
/// Reserved for system use.
pub const KEY_CREATE_LINK = 0x0020;
/// Required to create a subkey of a registry key.
pub const KEY_CREATE_SUB_KEY = 0x0004;
/// Required to enumerate the subkeys of a registry key.
pub const KEY_ENUMERATE_SUB_KEYS = 0x0008;
/// Equivalent to KEY_READ.
pub const KEY_EXECUTE = 0x20019;
/// Required to request change notifications for a registry key or for subkeys of a registry key.
pub const KEY_NOTIFY = 0x0010;
/// Required to query the values of a registry key.
pub const KEY_QUERY_VALUE = 0x0001;
/// Combines the STANDARD_RIGHTS_READ, KEY_QUERY_VALUE, KEY_ENUMERATE_SUB_KEYS, and KEY_NOTIFY values.
pub const KEY_READ = 0x20019;
/// Required to create, delete, or set a registry value.
pub const KEY_SET_VALUE = 0x0002;
/// Indicates that an application on 64-bit Windows should operate on the 32-bit registry view.
/// This flag is ignored by 32-bit Windows.
pub const KEY_WOW64_32KEY = 0x0200;
/// Indicates that an application on 64-bit Windows should operate on the 64-bit registry view.
/// This flag is ignored by 32-bit Windows.
pub const KEY_WOW64_64KEY = 0x0100;
/// Combines the STANDARD_RIGHTS_WRITE, KEY_SET_VALUE, and KEY_CREATE_SUB_KEY access rights.
pub const KEY_WRITE = 0x20006;
/// Open symbolic link.
pub const REG_OPTION_OPEN_LINK: DWORD = 0x8;
pub const RTL_QUERY_REGISTRY_TABLE = extern struct {
QueryRoutine: RTL_QUERY_REGISTRY_ROUTINE,
Flags: ULONG,
Name: ?PWSTR,
EntryContext: ?*anyopaque,
DefaultType: ULONG,
DefaultData: ?*anyopaque,
DefaultLength: ULONG,
};
pub const RTL_QUERY_REGISTRY_ROUTINE = ?*const fn (
PWSTR,
ULONG,
?*anyopaque,
ULONG,
?*anyopaque,
?*anyopaque,
) callconv(.winapi) NTSTATUS;
/// Path is a full path
pub const RTL_REGISTRY_ABSOLUTE = 0;
/// \Registry\Machine\System\CurrentControlSet\Services
pub const RTL_REGISTRY_SERVICES = 1;
/// \Registry\Machine\System\CurrentControlSet\Control
pub const RTL_REGISTRY_CONTROL = 2;
/// \Registry\Machine\Software\Microsoft\Windows NT\CurrentVersion
pub const RTL_REGISTRY_WINDOWS_NT = 3;
/// \Registry\Machine\Hardware\DeviceMap
pub const RTL_REGISTRY_DEVICEMAP = 4;
/// \Registry\User\CurrentUser
pub const RTL_REGISTRY_USER = 5;
pub const RTL_REGISTRY_MAXIMUM = 6;
/// Low order bits are registry handle
pub const RTL_REGISTRY_HANDLE = 0x40000000;
/// Indicates the key node is optional
pub const RTL_REGISTRY_OPTIONAL = 0x80000000;
/// Name is a subkey and remainder of table or until next subkey are value
/// names for that subkey to look at.
pub const RTL_QUERY_REGISTRY_SUBKEY = 0x00000001;
/// Reset current key to original key for this and all following table entries.
pub const RTL_QUERY_REGISTRY_TOPKEY = 0x00000002;
/// Fail if no match found for this table entry.
pub const RTL_QUERY_REGISTRY_REQUIRED = 0x00000004;
/// Used to mark a table entry that has no value name, just wants a call out, not
/// an enumeration of all values.
pub const RTL_QUERY_REGISTRY_NOVALUE = 0x00000008;
/// Used to suppress the expansion of REG_MULTI_SZ into multiple callouts or
/// to prevent the expansion of environment variable values in REG_EXPAND_SZ.
pub const RTL_QUERY_REGISTRY_NOEXPAND = 0x00000010;
/// QueryRoutine field ignored. EntryContext field points to location to store value.
/// For null terminated strings, EntryContext points to UNICODE_STRING structure that
/// that describes maximum size of buffer. If .Buffer field is NULL then a buffer is
/// allocated.
pub const RTL_QUERY_REGISTRY_DIRECT = 0x00000020;
/// Used to delete value keys after they are queried.
pub const RTL_QUERY_REGISTRY_DELETE = 0x00000040;
/// Use this flag with the RTL_QUERY_REGISTRY_DIRECT flag to verify that the REG_XXX type
/// of the stored registry value matches the type expected by the caller.
/// If the types do not match, the call fails.
pub const RTL_QUERY_REGISTRY_TYPECHECK = 0x00000100;
pub const REG = struct {
/// No value type
pub const NONE: ULONG = 0;
/// Unicode nul terminated string
pub const SZ: ULONG = 1;
/// Unicode nul terminated string (with environment variable references)
pub const EXPAND_SZ: ULONG = 2;
/// Free form binary
pub const BINARY: ULONG = 3;
/// 32-bit number
pub const DWORD: ULONG = 4;
/// 32-bit number (same as REG_DWORD)
pub const DWORD_LITTLE_ENDIAN: ULONG = 4;
/// 32-bit number
pub const DWORD_BIG_ENDIAN: ULONG = 5;
/// Symbolic Link (unicode)
pub const LINK: ULONG = 6;
/// Multiple Unicode strings
pub const MULTI_SZ: ULONG = 7;
/// Resource list in the resource map
pub const RESOURCE_LIST: ULONG = 8;
/// Resource list in the hardware description
pub const FULL_RESOURCE_DESCRIPTOR: ULONG = 9;
pub const RESOURCE_REQUIREMENTS_LIST: ULONG = 10;
/// 64-bit number
pub const QWORD: ULONG = 11;
/// 64-bit number (same as REG_QWORD)
pub const QWORD_LITTLE_ENDIAN: ULONG = 11;
};
pub const FILE_NOTIFY_INFORMATION = extern struct {
NextEntryOffset: DWORD,
Action: DWORD,
FileNameLength: DWORD,
// Flexible array member
// FileName: [1]WCHAR,
};
pub const FILE_ACTION_ADDED = 0x00000001;
pub const FILE_ACTION_REMOVED = 0x00000002;
pub const FILE_ACTION_MODIFIED = 0x00000003;
pub const FILE_ACTION_RENAMED_OLD_NAME = 0x00000004;
pub const FILE_ACTION_RENAMED_NEW_NAME = 0x00000005;
pub const LPOVERLAPPED_COMPLETION_ROUTINE = ?*const fn (DWORD, DWORD, *OVERLAPPED) callconv(.winapi) void;
pub const FileNotifyChangeFilter = packed struct(DWORD) {
file_name: bool = false,
dir_name: bool = false,
attributes: bool = false,
size: bool = false,
last_write: bool = false,
last_access: bool = false,
creation: bool = false,
ea: bool = false,
security: bool = false,
stream_name: bool = false,
stream_size: bool = false,
stream_write: bool = false,
_pad: u20 = 0,
};
pub const CONSOLE_SCREEN_BUFFER_INFO = extern struct {
dwSize: COORD,
dwCursorPosition: COORD,
wAttributes: WORD,
srWindow: SMALL_RECT,
dwMaximumWindowSize: COORD,
};
pub const ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4;
pub const DISABLE_NEWLINE_AUTO_RETURN = 0x8;
pub const FOREGROUND_BLUE = 1;
pub const FOREGROUND_GREEN = 2;
pub const FOREGROUND_RED = 4;
pub const FOREGROUND_INTENSITY = 8;
pub const LIST_ENTRY = extern struct {
Flink: *LIST_ENTRY,
Blink: *LIST_ENTRY,
};
pub const RTL_CRITICAL_SECTION_DEBUG = extern struct {
Type: WORD,
CreatorBackTraceIndex: WORD,
CriticalSection: *RTL_CRITICAL_SECTION,
ProcessLocksList: LIST_ENTRY,
EntryCount: DWORD,
ContentionCount: DWORD,
Flags: DWORD,
CreatorBackTraceIndexHigh: WORD,
SpareWORD: WORD,
};
pub const RTL_CRITICAL_SECTION = extern struct {
DebugInfo: *RTL_CRITICAL_SECTION_DEBUG,
LockCount: LONG,
RecursionCount: LONG,
OwningThread: HANDLE,
LockSemaphore: HANDLE,
SpinCount: ULONG_PTR,
};
pub const CRITICAL_SECTION = RTL_CRITICAL_SECTION;
pub const INIT_ONCE = RTL_RUN_ONCE;
pub const INIT_ONCE_STATIC_INIT = RTL_RUN_ONCE_INIT;
pub const INIT_ONCE_FN = *const fn (InitOnce: *INIT_ONCE, Parameter: ?*anyopaque, Context: ?*anyopaque) callconv(.winapi) BOOL;
pub const RTL_RUN_ONCE = extern struct {
Ptr: ?*anyopaque,
};
pub const RTL_RUN_ONCE_INIT = RTL_RUN_ONCE{ .Ptr = null };
pub const COINIT = struct {
pub const APARTMENTTHREADED = 2;
pub const MULTITHREADED = 0;
pub const DISABLE_OLE1DDE = 4;
pub const SPEED_OVER_MEMORY = 8;
};
pub const MEMORY_BASIC_INFORMATION = extern struct {
BaseAddress: PVOID,
AllocationBase: PVOID,
AllocationProtect: DWORD,
PartitionId: WORD,
RegionSize: SIZE_T,
State: DWORD,
Protect: DWORD,
Type: DWORD,
};
pub const PMEMORY_BASIC_INFORMATION = *MEMORY_BASIC_INFORMATION;
/// > The maximum path of 32,767 characters is approximate, because the "\\?\"
/// > prefix may be expanded to a longer string by the system at run time, and
/// > this expansion applies to the total length.
/// from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation
pub const PATH_MAX_WIDE = 32767;
/// > [Each file name component can be] up to the value returned in the
/// > lpMaximumComponentLength parameter of the GetVolumeInformation function
/// > (this value is commonly 255 characters)
/// from https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation
///
/// > The value that is stored in the variable that *lpMaximumComponentLength points to is
/// > used to indicate that a specified file system supports long names. For example, for
/// > a FAT file system that supports long names, the function stores the value 255, rather
/// > than the previous 8.3 indicator. Long names can also be supported on systems that use
/// > the NTFS file system.
/// from https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getvolumeinformationw
///
/// The assumption being made here is that while lpMaximumComponentLength may vary, it will never
/// be larger than 255.
///
/// TODO: More verification of this assumption.
pub const NAME_MAX = 255;
pub const FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100;
pub const FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000;
pub const FORMAT_MESSAGE_FROM_HMODULE = 0x00000800;
pub const FORMAT_MESSAGE_FROM_STRING = 0x00000400;
pub const FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;
pub const FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200;
pub const FORMAT_MESSAGE_MAX_WIDTH_MASK = 0x000000FF;
pub const EXCEPTION_DATATYPE_MISALIGNMENT = 0x80000002;
pub const EXCEPTION_ACCESS_VIOLATION = 0xc0000005;
pub const EXCEPTION_ILLEGAL_INSTRUCTION = 0xc000001d;
pub const EXCEPTION_STACK_OVERFLOW = 0xc00000fd;
pub const EXCEPTION_CONTINUE_SEARCH = 0;
pub const EXCEPTION_RECORD = extern struct {
ExceptionCode: u32,
ExceptionFlags: u32,
ExceptionRecord: *EXCEPTION_RECORD,
ExceptionAddress: *anyopaque,
NumberParameters: u32,
ExceptionInformation: [15]usize,
};
pub const FLOATING_SAVE_AREA = switch (native_arch) {
.x86 => extern struct {
ControlWord: DWORD,
StatusWord: DWORD,
TagWord: DWORD,
ErrorOffset: DWORD,
ErrorSelector: DWORD,
DataOffset: DWORD,
DataSelector: DWORD,
RegisterArea: [80]BYTE,
Cr0NpxState: DWORD,
},
else => @compileError("FLOATING_SAVE_AREA only defined on x86"),
};
pub const M128A = switch (native_arch) {
.x86_64 => extern struct {
Low: ULONGLONG,
High: LONGLONG,
},
else => @compileError("M128A only defined on x86_64"),
};
pub const XMM_SAVE_AREA32 = switch (native_arch) {
.x86_64 => extern struct {
ControlWord: WORD,
StatusWord: WORD,
TagWord: BYTE,
Reserved1: BYTE,
ErrorOpcode: WORD,
ErrorOffset: DWORD,
ErrorSelector: WORD,
Reserved2: WORD,
DataOffset: DWORD,
DataSelector: WORD,
Reserved3: WORD,
MxCsr: DWORD,
MxCsr_Mask: DWORD,
FloatRegisters: [8]M128A,
XmmRegisters: [16]M128A,
Reserved4: [96]BYTE,
},
else => @compileError("XMM_SAVE_AREA32 only defined on x86_64"),
};
pub const NEON128 = switch (native_arch) {
.thumb => extern struct {
Low: ULONGLONG,
High: LONGLONG,
},
.aarch64 => extern union {
DUMMYSTRUCTNAME: extern struct {
Low: ULONGLONG,
High: LONGLONG,
},
D: [2]f64,
S: [4]f32,
H: [8]WORD,
B: [16]BYTE,
},
else => @compileError("NEON128 only defined on aarch64"),
};
pub const CONTEXT = switch (native_arch) {
.x86 => extern struct {
ContextFlags: DWORD,
Dr0: DWORD,
Dr1: DWORD,
Dr2: DWORD,
Dr3: DWORD,
Dr6: DWORD,
Dr7: DWORD,
FloatSave: FLOATING_SAVE_AREA,
SegGs: DWORD,
SegFs: DWORD,
SegEs: DWORD,
SegDs: DWORD,
Edi: DWORD,
Esi: DWORD,
Ebx: DWORD,
Edx: DWORD,
Ecx: DWORD,
Eax: DWORD,
Ebp: DWORD,
Eip: DWORD,
SegCs: DWORD,
EFlags: DWORD,
Esp: DWORD,
SegSs: DWORD,
ExtendedRegisters: [512]BYTE,
pub fn getRegs(ctx: *const CONTEXT) struct { bp: usize, ip: usize } {
return .{ .bp = ctx.Ebp, .ip = ctx.Eip };
}
},
.x86_64 => extern struct {
P1Home: DWORD64 align(16),
P2Home: DWORD64,
P3Home: DWORD64,
P4Home: DWORD64,
P5Home: DWORD64,
P6Home: DWORD64,
ContextFlags: DWORD,
MxCsr: DWORD,
SegCs: WORD,
SegDs: WORD,
SegEs: WORD,
SegFs: WORD,
SegGs: WORD,
SegSs: WORD,
EFlags: DWORD,
Dr0: DWORD64,
Dr1: DWORD64,
Dr2: DWORD64,
Dr3: DWORD64,
Dr6: DWORD64,
Dr7: DWORD64,
Rax: DWORD64,
Rcx: DWORD64,
Rdx: DWORD64,
Rbx: DWORD64,
Rsp: DWORD64,
Rbp: DWORD64,
Rsi: DWORD64,
Rdi: DWORD64,
R8: DWORD64,
R9: DWORD64,
R10: DWORD64,
R11: DWORD64,
R12: DWORD64,
R13: DWORD64,
R14: DWORD64,
R15: DWORD64,
Rip: DWORD64,
DUMMYUNIONNAME: extern union {
FltSave: XMM_SAVE_AREA32,
FloatSave: XMM_SAVE_AREA32,
DUMMYSTRUCTNAME: extern struct {
Header: [2]M128A,
Legacy: [8]M128A,
Xmm0: M128A,
Xmm1: M128A,
Xmm2: M128A,
Xmm3: M128A,
Xmm4: M128A,
Xmm5: M128A,
Xmm6: M128A,
Xmm7: M128A,
Xmm8: M128A,
Xmm9: M128A,
Xmm10: M128A,
Xmm11: M128A,
Xmm12: M128A,
Xmm13: M128A,
Xmm14: M128A,
Xmm15: M128A,
},
},
VectorRegister: [26]M128A,
VectorControl: DWORD64,
DebugControl: DWORD64,
LastBranchToRip: DWORD64,
LastBranchFromRip: DWORD64,
LastExceptionToRip: DWORD64,
LastExceptionFromRip: DWORD64,
pub fn getRegs(ctx: *const CONTEXT) struct { bp: usize, ip: usize, sp: usize } {
return .{ .bp = ctx.Rbp, .ip = ctx.Rip, .sp = ctx.Rsp };
}
pub fn setIp(ctx: *CONTEXT, ip: usize) void {
ctx.Rip = ip;
}
pub fn setSp(ctx: *CONTEXT, sp: usize) void {
ctx.Rsp = sp;
}
},
.thumb => extern struct {
ContextFlags: ULONG,
R0: ULONG,
R1: ULONG,
R2: ULONG,
R3: ULONG,
R4: ULONG,
R5: ULONG,
R6: ULONG,
R7: ULONG,
R8: ULONG,
R9: ULONG,
R10: ULONG,
R11: ULONG,
R12: ULONG,
Sp: ULONG,
Lr: ULONG,
Pc: ULONG,
Cpsr: ULONG,
Fpcsr: ULONG,
Padding: ULONG,
DUMMYUNIONNAME: extern union {
Q: [16]NEON128,
D: [32]ULONGLONG,
S: [32]ULONG,
},
Bvr: [8]ULONG,
Bcr: [8]ULONG,
Wvr: [1]ULONG,
Wcr: [1]ULONG,
Padding2: [2]ULONG,
pub fn getRegs(ctx: *const CONTEXT) struct { bp: usize, ip: usize, sp: usize } {
return .{
.bp = ctx.DUMMYUNIONNAME.S[11],
.ip = ctx.Pc,
.sp = ctx.Sp,
};
}
pub fn setIp(ctx: *CONTEXT, ip: usize) void {
ctx.Pc = ip;
}
pub fn setSp(ctx: *CONTEXT, sp: usize) void {
ctx.Sp = sp;
}
},
.aarch64 => extern struct {
ContextFlags: ULONG align(16),
Cpsr: ULONG,
DUMMYUNIONNAME: extern union {
DUMMYSTRUCTNAME: extern struct {
X0: DWORD64,
X1: DWORD64,
X2: DWORD64,
X3: DWORD64,
X4: DWORD64,
X5: DWORD64,
X6: DWORD64,
X7: DWORD64,
X8: DWORD64,
X9: DWORD64,
X10: DWORD64,
X11: DWORD64,
X12: DWORD64,
X13: DWORD64,
X14: DWORD64,
X15: DWORD64,
X16: DWORD64,
X17: DWORD64,
X18: DWORD64,
X19: DWORD64,
X20: DWORD64,
X21: DWORD64,
X22: DWORD64,
X23: DWORD64,
X24: DWORD64,
X25: DWORD64,
X26: DWORD64,
X27: DWORD64,
X28: DWORD64,
Fp: DWORD64,
Lr: DWORD64,
},
X: [31]DWORD64,
},
Sp: DWORD64,
Pc: DWORD64,
V: [32]NEON128,
Fpcr: DWORD,
Fpsr: DWORD,
Bcr: [8]DWORD,
Bvr: [8]DWORD64,
Wcr: [2]DWORD,
Wvr: [2]DWORD64,
pub fn getRegs(ctx: *const CONTEXT) struct { bp: usize, ip: usize, sp: usize } {
return .{
.bp = ctx.DUMMYUNIONNAME.DUMMYSTRUCTNAME.Fp,
.ip = ctx.Pc,
.sp = ctx.Sp,
};
}
pub fn setIp(ctx: *CONTEXT, ip: usize) void {
ctx.Pc = ip;
}
pub fn setSp(ctx: *CONTEXT, sp: usize) void {
ctx.Sp = sp;
}
},
else => @compileError("CONTEXT is not defined for this architecture"),
};
pub const RUNTIME_FUNCTION = switch (native_arch) {
.x86_64 => extern struct {
BeginAddress: DWORD,
EndAddress: DWORD,
UnwindData: DWORD,
},
.thumb => extern struct {
BeginAddress: DWORD,
DUMMYUNIONNAME: extern union {
UnwindData: DWORD,
DUMMYSTRUCTNAME: packed struct {
Flag: u2,
FunctionLength: u11,
Ret: u2,
H: u1,
Reg: u3,
R: u1,
L: u1,
C: u1,
StackAdjust: u10,
},
},
},
.aarch64 => extern struct {
BeginAddress: DWORD,
DUMMYUNIONNAME: extern union {
UnwindData: DWORD,
DUMMYSTRUCTNAME: packed struct {
Flag: u2,
FunctionLength: u11,
RegF: u3,
RegI: u4,
H: u1,
CR: u2,
FrameSize: u9,
},
},
},
else => @compileError("RUNTIME_FUNCTION is not defined for this architecture"),
};
pub const KNONVOLATILE_CONTEXT_POINTERS = switch (native_arch) {
.x86_64 => extern struct {
FloatingContext: [16]?*M128A,
IntegerContext: [16]?*ULONG64,
},
.thumb => extern struct {
R4: ?*DWORD,
R5: ?*DWORD,
R6: ?*DWORD,
R7: ?*DWORD,
R8: ?*DWORD,
R9: ?*DWORD,
R10: ?*DWORD,
R11: ?*DWORD,
Lr: ?*DWORD,
D8: ?*ULONGLONG,
D9: ?*ULONGLONG,
D10: ?*ULONGLONG,
D11: ?*ULONGLONG,
D12: ?*ULONGLONG,
D13: ?*ULONGLONG,
D14: ?*ULONGLONG,
D15: ?*ULONGLONG,
},
.aarch64 => extern struct {
X19: ?*DWORD64,
X20: ?*DWORD64,
X21: ?*DWORD64,
X22: ?*DWORD64,
X23: ?*DWORD64,
X24: ?*DWORD64,
X25: ?*DWORD64,
X26: ?*DWORD64,
X27: ?*DWORD64,
X28: ?*DWORD64,
Fp: ?*DWORD64,
Lr: ?*DWORD64,
D8: ?*DWORD64,
D9: ?*DWORD64,
D10: ?*DWORD64,
D11: ?*DWORD64,
D12: ?*DWORD64,
D13: ?*DWORD64,
D14: ?*DWORD64,
D15: ?*DWORD64,
},
else => @compileError("KNONVOLATILE_CONTEXT_POINTERS is not defined for this architecture"),
};
pub const EXCEPTION_POINTERS = extern struct {
ExceptionRecord: *EXCEPTION_RECORD,
ContextRecord: *CONTEXT,
};
pub const VECTORED_EXCEPTION_HANDLER = *const fn (ExceptionInfo: *EXCEPTION_POINTERS) callconv(.winapi) c_long;
pub const EXCEPTION_DISPOSITION = i32;
pub const EXCEPTION_ROUTINE = *const fn (
ExceptionRecord: ?*EXCEPTION_RECORD,
EstablisherFrame: PVOID,
ContextRecord: *(Self.CONTEXT),
DispatcherContext: PVOID,
) callconv(.winapi) EXCEPTION_DISPOSITION;
pub const UNWIND_HISTORY_TABLE_SIZE = 12;
pub const UNWIND_HISTORY_TABLE_ENTRY = extern struct {
ImageBase: ULONG64,
FunctionEntry: *Self.RUNTIME_FUNCTION,
};
pub const UNWIND_HISTORY_TABLE = extern struct {
Count: ULONG,
LocalHint: BYTE,
GlobalHint: BYTE,
Search: BYTE,
Once: BYTE,
LowAddress: ULONG64,
HighAddress: ULONG64,
Entry: [UNWIND_HISTORY_TABLE_SIZE]UNWIND_HISTORY_TABLE_ENTRY,
};
pub const UNW_FLAG_NHANDLER = 0x0;
pub const UNW_FLAG_EHANDLER = 0x1;
pub const UNW_FLAG_UHANDLER = 0x2;
pub const UNW_FLAG_CHAININFO = 0x4;
pub const OBJECT_ATTRIBUTES = extern struct {
Length: ULONG,
RootDirectory: ?HANDLE,
ObjectName: *UNICODE_STRING,
Attributes: ULONG,
SecurityDescriptor: ?*anyopaque,
SecurityQualityOfService: ?*anyopaque,
};
pub const OBJ_INHERIT = 0x00000002;
pub const OBJ_PERMANENT = 0x00000010;
pub const OBJ_EXCLUSIVE = 0x00000020;
pub const OBJ_CASE_INSENSITIVE = 0x00000040;
pub const OBJ_OPENIF = 0x00000080;
pub const OBJ_OPENLINK = 0x00000100;
pub const OBJ_KERNEL_HANDLE = 0x00000200;
pub const OBJ_VALID_ATTRIBUTES = 0x000003F2;
pub const UNICODE_STRING = extern struct {
Length: c_ushort,
MaximumLength: c_ushort,
Buffer: ?[*]WCHAR,
};
pub const ACTIVATION_CONTEXT_DATA = opaque {};
pub const ASSEMBLY_STORAGE_MAP = opaque {};
pub const FLS_CALLBACK_INFO = opaque {};
pub const RTL_BITMAP = opaque {};
pub const KAFFINITY = usize;
pub const KPRIORITY = i32;
pub const CLIENT_ID = extern struct {
UniqueProcess: HANDLE,
UniqueThread: HANDLE,
};
pub const THREAD_BASIC_INFORMATION = extern struct {
ExitStatus: NTSTATUS,
TebBaseAddress: PVOID,
ClientId: CLIENT_ID,
AffinityMask: KAFFINITY,
Priority: KPRIORITY,
BasePriority: KPRIORITY,
};
pub const TEB = extern struct {
NtTib: NT_TIB,
EnvironmentPointer: PVOID,
ClientId: CLIENT_ID,
ActiveRpcHandle: PVOID,
ThreadLocalStoragePointer: PVOID,
ProcessEnvironmentBlock: *PEB,
LastErrorValue: ULONG,
Reserved2: [399 * @sizeOf(PVOID) - @sizeOf(ULONG)]u8,
Reserved3: [1952]u8,
TlsSlots: [64]PVOID,
Reserved4: [8]u8,
Reserved5: [26]PVOID,
ReservedForOle: PVOID,
Reserved6: [4]PVOID,
TlsExpansionSlots: PVOID,
};
comptime {
// Offsets taken from WinDbg info and Geoff Chappell[1] (RIP)
// [1]: https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/pebteb/teb/index.htm
assert(@offsetOf(TEB, "NtTib") == 0x00);
if (@sizeOf(usize) == 4) {
assert(@offsetOf(TEB, "EnvironmentPointer") == 0x1C);
assert(@offsetOf(TEB, "ClientId") == 0x20);
assert(@offsetOf(TEB, "ActiveRpcHandle") == 0x28);
assert(@offsetOf(TEB, "ThreadLocalStoragePointer") == 0x2C);
assert(@offsetOf(TEB, "ProcessEnvironmentBlock") == 0x30);
assert(@offsetOf(TEB, "LastErrorValue") == 0x34);
assert(@offsetOf(TEB, "TlsSlots") == 0xe10);
} else if (@sizeOf(usize) == 8) {
assert(@offsetOf(TEB, "EnvironmentPointer") == 0x38);
assert(@offsetOf(TEB, "ClientId") == 0x40);
assert(@offsetOf(TEB, "ActiveRpcHandle") == 0x50);
assert(@offsetOf(TEB, "ThreadLocalStoragePointer") == 0x58);
assert(@offsetOf(TEB, "ProcessEnvironmentBlock") == 0x60);
assert(@offsetOf(TEB, "LastErrorValue") == 0x68);
assert(@offsetOf(TEB, "TlsSlots") == 0x1480);
}
}
pub const EXCEPTION_REGISTRATION_RECORD = extern struct {
Next: ?*EXCEPTION_REGISTRATION_RECORD,
Handler: ?*EXCEPTION_DISPOSITION,
};
pub const NT_TIB = extern struct {
ExceptionList: ?*EXCEPTION_REGISTRATION_RECORD,
StackBase: PVOID,
StackLimit: PVOID,
SubSystemTib: PVOID,
DUMMYUNIONNAME: extern union { FiberData: PVOID, Version: DWORD },
ArbitraryUserPointer: PVOID,
Self: ?*@This(),
};
/// Process Environment Block
/// Microsoft documentation of this is incomplete, the fields here are taken from various resources including:
/// - https://github.com/wine-mirror/wine/blob/1aff1e6a370ee8c0213a0fd4b220d121da8527aa/include/winternl.h#L269
/// - https://www.geoffchappell.com/studies/windows/win32/ntdll/structs/peb/index.htm
pub const PEB = extern struct {
// Versions: All
InheritedAddressSpace: BOOLEAN,
// Versions: 3.51+
ReadImageFileExecOptions: BOOLEAN,
BeingDebugged: BOOLEAN,
// Versions: 5.2+ (previously was padding)
BitField: UCHAR,
// Versions: all
Mutant: HANDLE,
ImageBaseAddress: HMODULE,
Ldr: *PEB_LDR_DATA,
ProcessParameters: *RTL_USER_PROCESS_PARAMETERS,
SubSystemData: PVOID,
ProcessHeap: HANDLE,
// Versions: 5.1+
FastPebLock: *RTL_CRITICAL_SECTION,
// Versions: 5.2+
AtlThunkSListPtr: PVOID,
IFEOKey: PVOID,
// Versions: 6.0+
/// https://www.geoffchappell.com/studies/windows/win32/ntdll/structs/peb/crossprocessflags.htm
CrossProcessFlags: ULONG,
// Versions: 6.0+
union1: extern union {
KernelCallbackTable: PVOID,
UserSharedInfoPtr: PVOID,
},
// Versions: 5.1+
SystemReserved: ULONG,
// Versions: 5.1, (not 5.2, not 6.0), 6.1+
AtlThunkSListPtr32: ULONG,
// Versions: 6.1+
ApiSetMap: PVOID,
// Versions: all
TlsExpansionCounter: ULONG,
// note: there is padding here on 64 bit
TlsBitmap: *RTL_BITMAP,
TlsBitmapBits: [2]ULONG,
ReadOnlySharedMemoryBase: PVOID,
// Versions: 1703+
SharedData: PVOID,
// Versions: all
ReadOnlyStaticServerData: *PVOID,
AnsiCodePageData: PVOID,
OemCodePageData: PVOID,
UnicodeCaseTableData: PVOID,
// Versions: 3.51+
NumberOfProcessors: ULONG,
NtGlobalFlag: ULONG,
// Versions: all
CriticalSectionTimeout: LARGE_INTEGER,
// End of Original PEB size
// Fields appended in 3.51:
HeapSegmentReserve: ULONG_PTR,
HeapSegmentCommit: ULONG_PTR,
HeapDeCommitTotalFreeThreshold: ULONG_PTR,
HeapDeCommitFreeBlockThreshold: ULONG_PTR,
NumberOfHeaps: ULONG,
MaximumNumberOfHeaps: ULONG,
ProcessHeaps: *PVOID,
// Fields appended in 4.0:
GdiSharedHandleTable: PVOID,
ProcessStarterHelper: PVOID,
GdiDCAttributeList: ULONG,
// note: there is padding here on 64 bit
LoaderLock: *RTL_CRITICAL_SECTION,
OSMajorVersion: ULONG,
OSMinorVersion: ULONG,
OSBuildNumber: USHORT,
OSCSDVersion: USHORT,
OSPlatformId: ULONG,
ImageSubSystem: ULONG,
ImageSubSystemMajorVersion: ULONG,
ImageSubSystemMinorVersion: ULONG,
// note: there is padding here on 64 bit
ActiveProcessAffinityMask: KAFFINITY,
GdiHandleBuffer: [
switch (@sizeOf(usize)) {
4 => 0x22,
8 => 0x3C,
else => unreachable,
}
]ULONG,
// Fields appended in 5.0 (Windows 2000):
PostProcessInitRoutine: PVOID,
TlsExpansionBitmap: *RTL_BITMAP,
TlsExpansionBitmapBits: [32]ULONG,
SessionId: ULONG,
// note: there is padding here on 64 bit
// Versions: 5.1+
AppCompatFlags: ULARGE_INTEGER,
AppCompatFlagsUser: ULARGE_INTEGER,
ShimData: PVOID,
// Versions: 5.0+
AppCompatInfo: PVOID,
CSDVersion: UNICODE_STRING,
// Fields appended in 5.1 (Windows XP):
ActivationContextData: *const ACTIVATION_CONTEXT_DATA,
ProcessAssemblyStorageMap: *ASSEMBLY_STORAGE_MAP,
SystemDefaultActivationData: *const ACTIVATION_CONTEXT_DATA,
SystemAssemblyStorageMap: *ASSEMBLY_STORAGE_MAP,
MinimumStackCommit: ULONG_PTR,
// Fields appended in 5.2 (Windows Server 2003):
FlsCallback: *FLS_CALLBACK_INFO,
FlsListHead: LIST_ENTRY,
FlsBitmap: *RTL_BITMAP,
FlsBitmapBits: [4]ULONG,
FlsHighIndex: ULONG,
// Fields appended in 6.0 (Windows Vista):
WerRegistrationData: PVOID,
WerShipAssertPtr: PVOID,
// Fields appended in 6.1 (Windows 7):
pUnused: PVOID, // previously pContextData
pImageHeaderHash: PVOID,
/// TODO: https://www.geoffchappell.com/studies/windows/win32/ntdll/structs/peb/tracingflags.htm
TracingFlags: ULONG,
// Fields appended in 6.2 (Windows 8):
CsrServerReadOnlySharedMemoryBase: ULONGLONG,
// Fields appended in 1511:
TppWorkerpListLock: ULONG,
TppWorkerpList: LIST_ENTRY,
WaitOnAddressHashTable: [0x80]PVOID,
// Fields appended in 1709:
TelemetryCoverageHeader: PVOID,
CloudFileFlags: ULONG,
};
/// The `PEB_LDR_DATA` structure is the main record of what modules are loaded in a process.
/// It is essentially the head of three double-linked lists of `LDR_DATA_TABLE_ENTRY` structures which each represent one loaded module.
///
/// Microsoft documentation of this is incomplete, the fields here are taken from various resources including:
/// - https://www.geoffchappell.com/studies/windows/win32/ntdll/structs/peb_ldr_data.htm
pub const PEB_LDR_DATA = extern struct {
// Versions: 3.51 and higher
/// The size in bytes of the structure
Length: ULONG,
/// TRUE if the structure is prepared.
Initialized: BOOLEAN,
SsHandle: PVOID,
InLoadOrderModuleList: LIST_ENTRY,
InMemoryOrderModuleList: LIST_ENTRY,
InInitializationOrderModuleList: LIST_ENTRY,
// Versions: 5.1 and higher
/// No known use of this field is known in Windows 8 and higher.
EntryInProgress: PVOID,
// Versions: 6.0 from Windows Vista SP1, and higher
ShutdownInProgress: BOOLEAN,
/// Though ShutdownThreadId is declared as a HANDLE,
/// it is indeed the thread ID as suggested by its name.
/// It is picked up from the UniqueThread member of the CLIENT_ID in the
/// TEB of the thread that asks to terminate the process.
ShutdownThreadId: HANDLE,
};
/// Microsoft documentation of this is incomplete, the fields here are taken from various resources including:
/// - https://docs.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb_ldr_data
/// - https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntldr/ldr_data_table_entry.htm
pub const LDR_DATA_TABLE_ENTRY = extern struct {
InLoadOrderLinks: LIST_ENTRY,
InMemoryOrderLinks: LIST_ENTRY,
InInitializationOrderLinks: LIST_ENTRY,
DllBase: PVOID,
EntryPoint: PVOID,
SizeOfImage: ULONG,
FullDllName: UNICODE_STRING,
BaseDllName: UNICODE_STRING,
Reserved5: [3]PVOID,
DUMMYUNIONNAME: extern union {
CheckSum: ULONG,
Reserved6: PVOID,
},
TimeDateStamp: ULONG,
};
pub const RTL_USER_PROCESS_PARAMETERS = extern struct {
AllocationSize: ULONG,
Size: ULONG,
Flags: ULONG,
DebugFlags: ULONG,
ConsoleHandle: HANDLE,
ConsoleFlags: ULONG,
hStdInput: HANDLE,
hStdOutput: HANDLE,
hStdError: HANDLE,
CurrentDirectory: CURDIR,
DllPath: UNICODE_STRING,
ImagePathName: UNICODE_STRING,
CommandLine: UNICODE_STRING,
Environment: [*:0]WCHAR,
dwX: ULONG,
dwY: ULONG,
dwXSize: ULONG,
dwYSize: ULONG,
dwXCountChars: ULONG,
dwYCountChars: ULONG,
dwFillAttribute: ULONG,
dwFlags: ULONG,
dwShowWindow: ULONG,
WindowTitle: UNICODE_STRING,
Desktop: UNICODE_STRING,
ShellInfo: UNICODE_STRING,
RuntimeInfo: UNICODE_STRING,
DLCurrentDirectory: [0x20]RTL_DRIVE_LETTER_CURDIR,
};
pub const RTL_DRIVE_LETTER_CURDIR = extern struct {
Flags: c_ushort,
Length: c_ushort,
TimeStamp: ULONG,
DosPath: UNICODE_STRING,
};
pub const PPS_POST_PROCESS_INIT_ROUTINE = ?*const fn () callconv(.winapi) void;
pub const FILE_DIRECTORY_INFORMATION = extern struct {
NextEntryOffset: ULONG,
FileIndex: ULONG,
CreationTime: LARGE_INTEGER,
LastAccessTime: LARGE_INTEGER,
LastWriteTime: LARGE_INTEGER,
ChangeTime: LARGE_INTEGER,
EndOfFile: LARGE_INTEGER,
AllocationSize: LARGE_INTEGER,
FileAttributes: ULONG,
FileNameLength: ULONG,
FileName: [1]WCHAR,
};
pub const FILE_BOTH_DIR_INFORMATION = extern struct {
NextEntryOffset: ULONG,
FileIndex: ULONG,
CreationTime: LARGE_INTEGER,
LastAccessTime: LARGE_INTEGER,
LastWriteTime: LARGE_INTEGER,
ChangeTime: LARGE_INTEGER,
EndOfFile: LARGE_INTEGER,
AllocationSize: LARGE_INTEGER,
FileAttributes: ULONG,
FileNameLength: ULONG,
EaSize: ULONG,
ShortNameLength: CHAR,
ShortName: [12]WCHAR,
FileName: [1]WCHAR,
};
pub const FILE_BOTH_DIRECTORY_INFORMATION = FILE_BOTH_DIR_INFORMATION;
/// Helper for iterating a byte buffer of FILE_*_INFORMATION structures (from
/// things like NtQueryDirectoryFile calls).
pub fn FileInformationIterator(comptime FileInformationType: type) type {
return struct {
byte_offset: usize = 0,
buf: []u8 align(@alignOf(FileInformationType)),
pub fn next(self: *@This()) ?*FileInformationType {
if (self.byte_offset >= self.buf.len) return null;
const cur: *FileInformationType = @ptrCast(@alignCast(&self.buf[self.byte_offset]));
if (cur.NextEntryOffset == 0) {
self.byte_offset = self.buf.len;
} else {
self.byte_offset += cur.NextEntryOffset;
}
return cur;
}
};
}
pub const IO_APC_ROUTINE = *const fn (PVOID, *IO_STATUS_BLOCK, ULONG) callconv(.winapi) void;
pub const CURDIR = extern struct {
DosPath: UNICODE_STRING,
Handle: HANDLE,
};
pub const DUPLICATE_SAME_ACCESS = 2;
pub const MODULEINFO = extern struct {
lpBaseOfDll: LPVOID,
SizeOfImage: DWORD,
EntryPoint: LPVOID,
};
pub const PSAPI_WS_WATCH_INFORMATION = extern struct {
FaultingPc: LPVOID,
FaultingVa: LPVOID,
};
pub const VM_COUNTERS = extern struct {
PeakVirtualSize: SIZE_T,
VirtualSize: SIZE_T,
PageFaultCount: ULONG,
PeakWorkingSetSize: SIZE_T,
WorkingSetSize: SIZE_T,
QuotaPeakPagedPoolUsage: SIZE_T,
QuotaPagedPoolUsage: SIZE_T,
QuotaPeakNonPagedPoolUsage: SIZE_T,
QuotaNonPagedPoolUsage: SIZE_T,
PagefileUsage: SIZE_T,
PeakPagefileUsage: SIZE_T,
};
pub const PROCESS_MEMORY_COUNTERS = extern struct {
cb: DWORD,
PageFaultCount: DWORD,
PeakWorkingSetSize: SIZE_T,
WorkingSetSize: SIZE_T,
QuotaPeakPagedPoolUsage: SIZE_T,
QuotaPagedPoolUsage: SIZE_T,
QuotaPeakNonPagedPoolUsage: SIZE_T,
QuotaNonPagedPoolUsage: SIZE_T,
PagefileUsage: SIZE_T,
PeakPagefileUsage: SIZE_T,
};
pub const PROCESS_MEMORY_COUNTERS_EX = extern struct {
cb: DWORD,
PageFaultCount: DWORD,
PeakWorkingSetSize: SIZE_T,
WorkingSetSize: SIZE_T,
QuotaPeakPagedPoolUsage: SIZE_T,
QuotaPagedPoolUsage: SIZE_T,
QuotaPeakNonPagedPoolUsage: SIZE_T,
QuotaNonPagedPoolUsage: SIZE_T,
PagefileUsage: SIZE_T,
PeakPagefileUsage: SIZE_T,
PrivateUsage: SIZE_T,
};
pub const GetProcessMemoryInfoError = error{
AccessDenied,
InvalidHandle,
Unexpected,
};
pub fn GetProcessMemoryInfo(hProcess: HANDLE) GetProcessMemoryInfoError!VM_COUNTERS {
var vmc: VM_COUNTERS = undefined;
const rc = ntdll.NtQueryInformationProcess(hProcess, .ProcessVmCounters, &vmc, @sizeOf(VM_COUNTERS), null);
switch (rc) {
.SUCCESS => return vmc,
.ACCESS_DENIED => return error.AccessDenied,
.INVALID_HANDLE => return error.InvalidHandle,
.INVALID_PARAMETER => unreachable,
else => return unexpectedStatus(rc),
}
}
pub const PERFORMANCE_INFORMATION = extern struct {
cb: DWORD,
CommitTotal: SIZE_T,
CommitLimit: SIZE_T,
CommitPeak: SIZE_T,
PhysicalTotal: SIZE_T,
PhysicalAvailable: SIZE_T,
SystemCache: SIZE_T,
KernelTotal: SIZE_T,
KernelPaged: SIZE_T,
KernelNonpaged: SIZE_T,
PageSize: SIZE_T,
HandleCount: DWORD,
ProcessCount: DWORD,
ThreadCount: DWORD,
};
pub const ENUM_PAGE_FILE_INFORMATION = extern struct {
cb: DWORD,
Reserved: DWORD,
TotalSize: SIZE_T,
TotalInUse: SIZE_T,
PeakUsage: SIZE_T,
};
pub const PENUM_PAGE_FILE_CALLBACKW = ?*const fn (?LPVOID, *ENUM_PAGE_FILE_INFORMATION, LPCWSTR) callconv(.winapi) BOOL;
pub const PENUM_PAGE_FILE_CALLBACKA = ?*const fn (?LPVOID, *ENUM_PAGE_FILE_INFORMATION, LPCSTR) callconv(.winapi) BOOL;
pub const PSAPI_WS_WATCH_INFORMATION_EX = extern struct {
BasicInfo: PSAPI_WS_WATCH_INFORMATION,
FaultingThreadId: ULONG_PTR,
Flags: ULONG_PTR,
};
pub const OSVERSIONINFOW = extern struct {
dwOSVersionInfoSize: ULONG,
dwMajorVersion: ULONG,
dwMinorVersion: ULONG,
dwBuildNumber: ULONG,
dwPlatformId: ULONG,
szCSDVersion: [128]WCHAR,
};
pub const RTL_OSVERSIONINFOW = OSVERSIONINFOW;
pub const REPARSE_DATA_BUFFER = extern struct {
ReparseTag: ULONG,
ReparseDataLength: USHORT,
Reserved: USHORT,
DataBuffer: [1]UCHAR,
};
pub const SYMBOLIC_LINK_REPARSE_BUFFER = extern struct {
SubstituteNameOffset: USHORT,
SubstituteNameLength: USHORT,
PrintNameOffset: USHORT,
PrintNameLength: USHORT,
Flags: ULONG,
PathBuffer: [1]WCHAR,
};
pub const MOUNT_POINT_REPARSE_BUFFER = extern struct {
SubstituteNameOffset: USHORT,
SubstituteNameLength: USHORT,
PrintNameOffset: USHORT,
PrintNameLength: USHORT,
PathBuffer: [1]WCHAR,
};
pub const MAXIMUM_REPARSE_DATA_BUFFER_SIZE: ULONG = 16 * 1024;
pub const FSCTL_SET_REPARSE_POINT: DWORD = 0x900a4;
pub const FSCTL_GET_REPARSE_POINT: DWORD = 0x900a8;
pub const IO_REPARSE_TAG_SYMLINK: ULONG = 0xa000000c;
pub const IO_REPARSE_TAG_MOUNT_POINT: ULONG = 0xa0000003;
pub const SYMLINK_FLAG_RELATIVE: ULONG = 0x1;
pub const SYMBOLIC_LINK_FLAG_DIRECTORY: DWORD = 0x1;
pub const SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE: DWORD = 0x2;
pub const MOUNTMGRCONTROLTYPE = 0x0000006D;
pub const MOUNTMGR_MOUNT_POINT = extern struct {
SymbolicLinkNameOffset: ULONG,
SymbolicLinkNameLength: USHORT,
Reserved1: USHORT,
UniqueIdOffset: ULONG,
UniqueIdLength: USHORT,
Reserved2: USHORT,
DeviceNameOffset: ULONG,
DeviceNameLength: USHORT,
Reserved3: USHORT,
};
pub const MOUNTMGR_MOUNT_POINTS = extern struct {
Size: ULONG,
NumberOfMountPoints: ULONG,
MountPoints: [1]MOUNTMGR_MOUNT_POINT,
};
pub const IOCTL_MOUNTMGR_QUERY_POINTS = CTL_CODE(MOUNTMGRCONTROLTYPE, 2, .METHOD_BUFFERED, FILE_ANY_ACCESS);
pub const MOUNTMGR_TARGET_NAME = extern struct {
DeviceNameLength: USHORT,
DeviceName: [1]WCHAR,
};
pub const MOUNTMGR_VOLUME_PATHS = extern struct {
MultiSzLength: ULONG,
MultiSz: [1]WCHAR,
};
pub const IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH = CTL_CODE(MOUNTMGRCONTROLTYPE, 12, .METHOD_BUFFERED, FILE_ANY_ACCESS);
pub const OBJECT_INFORMATION_CLASS = enum(c_int) {
ObjectBasicInformation = 0,
ObjectNameInformation = 1,
ObjectTypeInformation = 2,
ObjectTypesInformation = 3,
ObjectHandleFlagInformation = 4,
ObjectSessionInformation = 5,
MaxObjectInfoClass,
};
pub const OBJECT_NAME_INFORMATION = extern struct {
Name: UNICODE_STRING,
};
pub const SRWLOCK_INIT = SRWLOCK{};
pub const SRWLOCK = extern struct {
Ptr: ?PVOID = null,
};
pub const CONDITION_VARIABLE_INIT = CONDITION_VARIABLE{};
pub const CONDITION_VARIABLE = extern struct {
Ptr: ?PVOID = null,
};
pub const FILE_SKIP_COMPLETION_PORT_ON_SUCCESS = 0x1;
pub const FILE_SKIP_SET_EVENT_ON_HANDLE = 0x2;
pub const CTRL_C_EVENT: DWORD = 0;
pub const CTRL_BREAK_EVENT: DWORD = 1;
pub const CTRL_CLOSE_EVENT: DWORD = 2;
pub const CTRL_LOGOFF_EVENT: DWORD = 5;
pub const CTRL_SHUTDOWN_EVENT: DWORD = 6;
pub const HANDLER_ROUTINE = *const fn (dwCtrlType: DWORD) callconv(.winapi) BOOL;
/// Processor feature enumeration.
pub const PF = enum(DWORD) {
/// On a Pentium, a floating-point precision error can occur in rare circumstances.
FLOATING_POINT_PRECISION_ERRATA = 0,
/// Floating-point operations are emulated using software emulator.
/// This function returns a nonzero value if floating-point operations are emulated; otherwise, it returns zero.
FLOATING_POINT_EMULATED = 1,
/// The atomic compare and exchange operation (cmpxchg) is available.
COMPARE_EXCHANGE_DOUBLE = 2,
/// The MMX instruction set is available.
MMX_INSTRUCTIONS_AVAILABLE = 3,
PPC_MOVEMEM_64BIT_OK = 4,
ALPHA_BYTE_INSTRUCTIONS = 5,
/// The SSE instruction set is available.
XMMI_INSTRUCTIONS_AVAILABLE = 6,
/// The 3D-Now instruction is available.
@"3DNOW_INSTRUCTIONS_AVAILABLE" = 7,
/// The RDTSC instruction is available.
RDTSC_INSTRUCTION_AVAILABLE = 8,
/// The processor is PAE-enabled.
PAE_ENABLED = 9,
/// The SSE2 instruction set is available.
XMMI64_INSTRUCTIONS_AVAILABLE = 10,
SSE_DAZ_MODE_AVAILABLE = 11,
/// Data execution prevention is enabled.
NX_ENABLED = 12,
/// The SSE3 instruction set is available.
SSE3_INSTRUCTIONS_AVAILABLE = 13,
/// The atomic compare and exchange 128-bit operation (cmpxchg16b) is available.
COMPARE_EXCHANGE128 = 14,
/// The atomic compare 64 and exchange 128-bit operation (cmp8xchg16) is available.
COMPARE64_EXCHANGE128 = 15,
/// The processor channels are enabled.
CHANNELS_ENABLED = 16,
/// The processor implements the XSAVI and XRSTOR instructions.
XSAVE_ENABLED = 17,
/// The VFP/Neon: 32 x 64bit register bank is present.
/// This flag has the same meaning as PF_ARM_VFP_EXTENDED_REGISTERS.
ARM_VFP_32_REGISTERS_AVAILABLE = 18,
/// This ARM processor implements the ARM v8 NEON instruction set.
ARM_NEON_INSTRUCTIONS_AVAILABLE = 19,
/// Second Level Address Translation is supported by the hardware.
SECOND_LEVEL_ADDRESS_TRANSLATION = 20,
/// Virtualization is enabled in the firmware and made available by the operating system.
VIRT_FIRMWARE_ENABLED = 21,
/// RDFSBASE, RDGSBASE, WRFSBASE, and WRGSBASE instructions are available.
RDWRFSGBASE_AVAILABLE = 22,
/// _fastfail() is available.
FASTFAIL_AVAILABLE = 23,
/// The divide instruction_available.
ARM_DIVIDE_INSTRUCTION_AVAILABLE = 24,
/// The 64-bit load/store atomic instructions are available.
ARM_64BIT_LOADSTORE_ATOMIC = 25,
/// The external cache is available.
ARM_EXTERNAL_CACHE_AVAILABLE = 26,
/// The floating-point multiply-accumulate instruction is available.
ARM_FMAC_INSTRUCTIONS_AVAILABLE = 27,
RDRAND_INSTRUCTION_AVAILABLE = 28,
/// This ARM processor implements the ARM v8 instructions set.
ARM_V8_INSTRUCTIONS_AVAILABLE = 29,
/// This ARM processor implements the ARM v8 extra cryptographic instructions (i.e., AES, SHA1 and SHA2).
ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE = 30,
/// This ARM processor implements the ARM v8 extra CRC32 instructions.
ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE = 31,
RDTSCP_INSTRUCTION_AVAILABLE = 32,
RDPID_INSTRUCTION_AVAILABLE = 33,
/// This ARM processor implements the ARM v8.1 atomic instructions (e.g., CAS, SWP).
ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE = 34,
MONITORX_INSTRUCTION_AVAILABLE = 35,
/// The SSSE3 instruction set is available.
SSSE3_INSTRUCTIONS_AVAILABLE = 36,
/// The SSE4_1 instruction set is available.
SSE4_1_INSTRUCTIONS_AVAILABLE = 37,
/// The SSE4_2 instruction set is available.
SSE4_2_INSTRUCTIONS_AVAILABLE = 38,
/// The AVX instruction set is available.
AVX_INSTRUCTIONS_AVAILABLE = 39,
/// The AVX2 instruction set is available.
AVX2_INSTRUCTIONS_AVAILABLE = 40,
/// The AVX512F instruction set is available.
AVX512F_INSTRUCTIONS_AVAILABLE = 41,
ERMS_AVAILABLE = 42,
/// This ARM processor implements the ARM v8.2 Dot Product (DP) instructions.
ARM_V82_DP_INSTRUCTIONS_AVAILABLE = 43,
/// This ARM processor implements the ARM v8.3 JavaScript conversion (JSCVT) instructions.
ARM_V83_JSCVT_INSTRUCTIONS_AVAILABLE = 44,
};
pub const MAX_WOW64_SHARED_ENTRIES = 16;
pub const PROCESSOR_FEATURE_MAX = 64;
pub const MAXIMUM_XSTATE_FEATURES = 64;
pub const KSYSTEM_TIME = extern struct {
LowPart: ULONG,
High1Time: LONG,
High2Time: LONG,
};
pub const NT_PRODUCT_TYPE = enum(INT) {
NtProductWinNt = 1,
NtProductLanManNt,
NtProductServer,
};
pub const ALTERNATIVE_ARCHITECTURE_TYPE = enum(INT) {
StandardDesign,
NEC98x86,
EndAlternatives,
};
pub const XSTATE_FEATURE = extern struct {
Offset: ULONG,
Size: ULONG,
};
pub const XSTATE_CONFIGURATION = extern struct {
EnabledFeatures: ULONG64,
Size: ULONG,
OptimizedSave: ULONG,
Features: [MAXIMUM_XSTATE_FEATURES]XSTATE_FEATURE,
};
/// Shared Kernel User Data
pub const KUSER_SHARED_DATA = extern struct {
TickCountLowDeprecated: ULONG,
TickCountMultiplier: ULONG,
InterruptTime: KSYSTEM_TIME,
SystemTime: KSYSTEM_TIME,
TimeZoneBias: KSYSTEM_TIME,
ImageNumberLow: USHORT,
ImageNumberHigh: USHORT,
NtSystemRoot: [260]WCHAR,
MaxStackTraceDepth: ULONG,
CryptoExponent: ULONG,
TimeZoneId: ULONG,
LargePageMinimum: ULONG,
AitSamplingValue: ULONG,
AppCompatFlag: ULONG,
RNGSeedVersion: ULONGLONG,
GlobalValidationRunlevel: ULONG,
TimeZoneBiasStamp: LONG,
NtBuildNumber: ULONG,
NtProductType: NT_PRODUCT_TYPE,
ProductTypeIsValid: BOOLEAN,
Reserved0: [1]BOOLEAN,
NativeProcessorArchitecture: USHORT,
NtMajorVersion: ULONG,
NtMinorVersion: ULONG,
ProcessorFeatures: [PROCESSOR_FEATURE_MAX]BOOLEAN,
Reserved1: ULONG,
Reserved3: ULONG,
TimeSlip: ULONG,
AlternativeArchitecture: ALTERNATIVE_ARCHITECTURE_TYPE,
BootId: ULONG,
SystemExpirationDate: LARGE_INTEGER,
SuiteMaskY: ULONG,
KdDebuggerEnabled: BOOLEAN,
DummyUnion1: extern union {
MitigationPolicies: UCHAR,
Alt: packed struct {
NXSupportPolicy: u2,
SEHValidationPolicy: u2,
CurDirDevicesSkippedForDlls: u2,
Reserved: u2,
},
},
CyclesPerYield: USHORT,
ActiveConsoleId: ULONG,
DismountCount: ULONG,
ComPlusPackage: ULONG,
LastSystemRITEventTickCount: ULONG,
NumberOfPhysicalPages: ULONG,
SafeBootMode: BOOLEAN,
DummyUnion2: extern union {
VirtualizationFlags: UCHAR,
Alt: packed struct {
ArchStartedInEl2: u1,
QcSlIsSupported: u1,
SpareBits: u6,
},
},
Reserved12: [2]UCHAR,
DummyUnion3: extern union {
SharedDataFlags: ULONG,
Alt: packed struct {
DbgErrorPortPresent: u1,
DbgElevationEnabled: u1,
DbgVirtEnabled: u1,
DbgInstallerDetectEnabled: u1,
DbgLkgEnabled: u1,
DbgDynProcessorEnabled: u1,
DbgConsoleBrokerEnabled: u1,
DbgSecureBootEnabled: u1,
DbgMultiSessionSku: u1,
DbgMultiUsersInSessionSku: u1,
DbgStateSeparationEnabled: u1,
SpareBits: u21,
},
},
DataFlagsPad: [1]ULONG,
TestRetInstruction: ULONGLONG,
QpcFrequency: LONGLONG,
SystemCall: ULONG,
Reserved2: ULONG,
SystemCallPad: [2]ULONGLONG,
DummyUnion4: extern union {
TickCount: KSYSTEM_TIME,
TickCountQuad: ULONG64,
Alt: extern struct {
ReservedTickCountOverlay: [3]ULONG,
TickCountPad: [1]ULONG,
},
},
Cookie: ULONG,
CookiePad: [1]ULONG,
ConsoleSessionForegroundProcessId: LONGLONG,
TimeUpdateLock: ULONGLONG,
BaselineSystemTimeQpc: ULONGLONG,
BaselineInterruptTimeQpc: ULONGLONG,
QpcSystemTimeIncrement: ULONGLONG,
QpcInterruptTimeIncrement: ULONGLONG,
QpcSystemTimeIncrementShift: UCHAR,
QpcInterruptTimeIncrementShift: UCHAR,
UnparkedProcessorCount: USHORT,
EnclaveFeatureMask: [4]ULONG,
TelemetryCoverageRound: ULONG,
UserModeGlobalLogger: [16]USHORT,
ImageFileExecutionOptions: ULONG,
LangGenerationCount: ULONG,
Reserved4: ULONGLONG,
InterruptTimeBias: ULONGLONG,
QpcBias: ULONGLONG,
ActiveProcessorCount: ULONG,
ActiveGroupCount: UCHAR,
Reserved9: UCHAR,
DummyUnion5: extern union {
QpcData: USHORT,
Alt: extern struct {
QpcBypassEnabled: UCHAR,
QpcShift: UCHAR,
},
},
TimeZoneBiasEffectiveStart: LARGE_INTEGER,
TimeZoneBiasEffectiveEnd: LARGE_INTEGER,
XState: XSTATE_CONFIGURATION,
FeatureConfigurationChangeStamp: KSYSTEM_TIME,
Spare: ULONG,
UserPointerAuthMask: ULONG64,
};
/// Read-only user-mode address for the shared data.
/// https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntexapi_x/kuser_shared_data/index.htm
/// https://msrc-blog.microsoft.com/2022/04/05/randomizing-the-kuser_shared_data-structure-on-windows/
pub const SharedUserData: *const KUSER_SHARED_DATA = @as(*const KUSER_SHARED_DATA, @ptrFromInt(0x7FFE0000));
pub fn IsProcessorFeaturePresent(feature: PF) bool {
if (@intFromEnum(feature) >= PROCESSOR_FEATURE_MAX) return false;
return SharedUserData.ProcessorFeatures[@intFromEnum(feature)] == 1;
}
pub const TH32CS_SNAPHEAPLIST = 0x00000001;
pub const TH32CS_SNAPPROCESS = 0x00000002;
pub const TH32CS_SNAPTHREAD = 0x00000004;
pub const TH32CS_SNAPMODULE = 0x00000008;
pub const TH32CS_SNAPMODULE32 = 0x00000010;
pub const TH32CS_SNAPALL = TH32CS_SNAPHEAPLIST | TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD | TH32CS_SNAPMODULE;
pub const TH32CS_INHERIT = 0x80000000;
pub const MAX_MODULE_NAME32 = 255;
pub const MODULEENTRY32 = extern struct {
dwSize: DWORD,
th32ModuleID: DWORD,
th32ProcessID: DWORD,
GlblcntUsage: DWORD,
ProccntUsage: DWORD,
modBaseAddr: *BYTE,
modBaseSize: DWORD,
hModule: HMODULE,
szModule: [MAX_MODULE_NAME32 + 1]CHAR,
szExePath: [MAX_PATH]CHAR,
};
pub const SYSTEM_INFORMATION_CLASS = enum(c_int) {
SystemBasicInformation = 0,
SystemPerformanceInformation = 2,
SystemTimeOfDayInformation = 3,
SystemProcessInformation = 5,
SystemProcessorPerformanceInformation = 8,
SystemInterruptInformation = 23,
SystemExceptionInformation = 33,
SystemRegistryQuotaInformation = 37,
SystemLookasideInformation = 45,
SystemCodeIntegrityInformation = 103,
SystemPolicyInformation = 134,
};
pub const SYSTEM_BASIC_INFORMATION = extern struct {
Reserved: ULONG,
TimerResolution: ULONG,
PageSize: ULONG,
NumberOfPhysicalPages: ULONG,
LowestPhysicalPageNumber: ULONG,
HighestPhysicalPageNumber: ULONG,
AllocationGranularity: ULONG,
MinimumUserModeAddress: ULONG_PTR,
MaximumUserModeAddress: ULONG_PTR,
ActiveProcessorsAffinityMask: KAFFINITY,
NumberOfProcessors: UCHAR,
};
pub const THREADINFOCLASS = enum(c_int) {
ThreadBasicInformation,
ThreadTimes,
ThreadPriority,
ThreadBasePriority,
ThreadAffinityMask,
ThreadImpersonationToken,
ThreadDescriptorTableEntry,
ThreadEnableAlignmentFaultFixup,
ThreadEventPair_Reusable,
ThreadQuerySetWin32StartAddress,
ThreadZeroTlsCell,
ThreadPerformanceCount,
ThreadAmILastThread,
ThreadIdealProcessor,
ThreadPriorityBoost,
ThreadSetTlsArrayAddress,
ThreadIsIoPending,
// Windows 2000+ from here
ThreadHideFromDebugger,
// Windows XP+ from here
ThreadBreakOnTermination,
ThreadSwitchLegacyState,
ThreadIsTerminated,
// Windows Vista+ from here
ThreadLastSystemCall,
ThreadIoPriority,
ThreadCycleTime,
ThreadPagePriority,
ThreadActualBasePriority,
ThreadTebInformation,
ThreadCSwitchMon,
// Windows 7+ from here
ThreadCSwitchPmu,
ThreadWow64Context,
ThreadGroupInformation,
ThreadUmsInformation,
ThreadCounterProfiling,
ThreadIdealProcessorEx,
// Windows 8+ from here
ThreadCpuAccountingInformation,
// Windows 8.1+ from here
ThreadSuspendCount,
// Windows 10+ from here
ThreadHeterogeneousCpuPolicy,
ThreadContainerId,
ThreadNameInformation,
ThreadSelectedCpuSets,
ThreadSystemThreadInformation,
ThreadActualGroupAffinity,
};
pub const PROCESSINFOCLASS = enum(c_int) {
ProcessBasicInformation,
ProcessQuotaLimits,
ProcessIoCounters,
ProcessVmCounters,
ProcessTimes,
ProcessBasePriority,
ProcessRaisePriority,
ProcessDebugPort,
ProcessExceptionPort,
ProcessAccessToken,
ProcessLdtInformation,
ProcessLdtSize,
ProcessDefaultHardErrorMode,
ProcessIoPortHandlers,
ProcessPooledUsageAndLimits,
ProcessWorkingSetWatch,
ProcessUserModeIOPL,
ProcessEnableAlignmentFaultFixup,
ProcessPriorityClass,
ProcessWx86Information,
ProcessHandleCount,
ProcessAffinityMask,
ProcessPriorityBoost,
ProcessDeviceMap,
ProcessSessionInformation,
ProcessForegroundInformation,
ProcessWow64Information,
ProcessImageFileName,
ProcessLUIDDeviceMapsEnabled,
ProcessBreakOnTermination,
ProcessDebugObjectHandle,
ProcessDebugFlags,
ProcessHandleTracing,
ProcessIoPriority,
ProcessExecuteFlags,
ProcessTlsInformation,
ProcessCookie,
ProcessImageInformation,
ProcessCycleTime,
ProcessPagePriority,
ProcessInstrumentationCallback,
ProcessThreadStackAllocation,
ProcessWorkingSetWatchEx,
ProcessImageFileNameWin32,
ProcessImageFileMapping,
ProcessAffinityUpdateMode,
ProcessMemoryAllocationMode,
ProcessGroupInformation,
ProcessTokenVirtualizationEnabled,
ProcessConsoleHostProcess,
ProcessWindowInformation,
MaxProcessInfoClass,
};
pub const PROCESS_BASIC_INFORMATION = extern struct {
ExitStatus: NTSTATUS,
PebBaseAddress: *PEB,
AffinityMask: ULONG_PTR,
BasePriority: KPRIORITY,
UniqueProcessId: ULONG_PTR,
InheritedFromUniqueProcessId: ULONG_PTR,
};
pub const ReadMemoryError = error{
Unexpected,
};
pub fn ReadProcessMemory(handle: HANDLE, addr: ?LPVOID, buffer: []u8) ReadMemoryError![]u8 {
var nread: usize = 0;
switch (ntdll.NtReadVirtualMemory(
handle,
addr,
buffer.ptr,
buffer.len,
&nread,
)) {
.SUCCESS => return buffer[0..nread],
// TODO: map errors
else => |rc| return unexpectedStatus(rc),
}
}
pub const WriteMemoryError = error{
Unexpected,
};
pub fn WriteProcessMemory(handle: HANDLE, addr: ?LPVOID, buffer: []const u8) WriteMemoryError!usize {
var nwritten: usize = 0;
switch (ntdll.NtWriteVirtualMemory(
handle,
addr,
buffer.ptr,
buffer.len,
&nwritten,
)) {
.SUCCESS => return nwritten,
// TODO: map errors
else => |rc| return unexpectedStatus(rc),
}
}
pub const ProcessBaseAddressError = GetProcessMemoryInfoError || ReadMemoryError;
/// Returns the base address of the process loaded into memory.
pub fn ProcessBaseAddress(handle: HANDLE) ProcessBaseAddressError!HMODULE {
var info: PROCESS_BASIC_INFORMATION = undefined;
var nread: DWORD = 0;
const rc = ntdll.NtQueryInformationProcess(
handle,
.ProcessBasicInformation,
&info,
@sizeOf(PROCESS_BASIC_INFORMATION),
&nread,
);
switch (rc) {
.SUCCESS => {},
.ACCESS_DENIED => return error.AccessDenied,
.INVALID_HANDLE => return error.InvalidHandle,
.INVALID_PARAMETER => unreachable,
else => return unexpectedStatus(rc),
}
var peb_buf: [@sizeOf(PEB)]u8 align(@alignOf(PEB)) = undefined;
const peb_out = try ReadProcessMemory(handle, info.PebBaseAddress, &peb_buf);
const ppeb: *const PEB = @ptrCast(@alignCast(peb_out.ptr));
return ppeb.ImageBaseAddress;
}