1#![cfg(windows)]
5#![expect(unsafe_code)]
8#![expect(clippy::undocumented_unsafe_blocks, clippy::missing_safety_doc)]
9
10pub mod afd;
11pub mod alpc;
12pub mod fs;
13pub mod job;
14pub mod pipe;
15pub mod process;
16pub mod security;
17pub mod tp;
18
19use self::security::SecurityDescriptor;
20use handleapi::INVALID_HANDLE_VALUE;
21use ntapi::ntioapi::FILE_COMPLETION_INFORMATION;
22use ntapi::ntioapi::FileReplaceCompletionInformation;
23use ntapi::ntioapi::IO_STATUS_BLOCK;
24use ntapi::ntioapi::NtAssociateWaitCompletionPacket;
25use ntapi::ntioapi::NtCancelWaitCompletionPacket;
26use ntapi::ntioapi::NtCreateWaitCompletionPacket;
27use ntapi::ntioapi::NtSetInformationFile;
28use ntapi::ntobapi::NtCreateDirectoryObject;
29use ntapi::ntobapi::NtOpenDirectoryObject;
30use ntapi::ntrtl;
31use ntdef::ANSI_STRING;
32use ntdef::UNICODE_STRING;
33use ntrtl::RtlAllocateHeap;
34use ntrtl::RtlDosPathNameToNtPathName_U_WithStatus;
35use ntrtl::RtlFreeUnicodeString;
36use ntrtl::RtlNtStatusToDosErrorNoTeb;
37use processthreadsapi::GetExitCodeProcess;
38use std::cell::UnsafeCell;
39use std::ffi::OsStr;
40use std::ffi::c_void;
41use std::fs::File;
42use std::io;
43use std::io::Error;
44use std::io::Result;
45use std::marker::PhantomData;
46use std::mem::zeroed;
47use std::os::windows::prelude::*;
48use std::path::Path;
49use std::ptr::NonNull;
50use std::ptr::addr_of;
51use std::ptr::null_mut;
52use std::sync::Once;
53use std::sync::atomic::AtomicUsize;
54use std::sync::atomic::Ordering;
55use std::time::Duration;
56use widestring::U16CString;
57use widestring::Utf16Str;
58use winapi::shared::ntdef;
59use winapi::shared::ntdef::NTSTATUS;
60use winapi::shared::ntstatus;
61use winapi::shared::ntstatus::STATUS_PENDING;
62use winapi::shared::winerror::ERROR_BAD_PATHNAME;
63use winapi::shared::ws2def;
64use winapi::um::errhandlingapi::GetErrorMode;
65use winapi::um::errhandlingapi::SetErrorMode;
66use winapi::um::handleapi;
67use winapi::um::handleapi::CloseHandle;
68use winapi::um::heapapi::GetProcessHeap;
69use winapi::um::ioapiset::CreateIoCompletionPort;
70use winapi::um::ioapiset::GetQueuedCompletionStatusEx;
71use winapi::um::ioapiset::PostQueuedCompletionStatus;
72use winapi::um::minwinbase::OVERLAPPED;
73use winapi::um::minwinbase::OVERLAPPED_ENTRY;
74use winapi::um::processenv::SetStdHandle;
75use winapi::um::processthreadsapi;
76use winapi::um::processthreadsapi::TerminateProcess;
77use winapi::um::synchapi;
78use winapi::um::winbase::INFINITE;
79use winapi::um::winbase::SEM_FAILCRITICALERRORS;
80use winapi::um::winbase::STD_OUTPUT_HANDLE;
81use winapi::um::winbase::SetFileCompletionNotificationModes;
82use winapi::um::winnt;
83use winapi::um::winsock2;
84
85#[repr(transparent)]
86#[derive(Debug, Copy, Clone, PartialEq, Eq)]
87pub struct SendSyncRawHandle(pub RawHandle);
88
89unsafe impl Send for SendSyncRawHandle {}
90unsafe impl Sync for SendSyncRawHandle {}
91
92pub trait BorrowedHandleExt: Sized {
93 fn duplicate(&self, inherit: bool, access: Option<u32>) -> Result<OwnedHandle>;
94}
95
96impl BorrowedHandleExt for BorrowedHandle<'_> {
97 fn duplicate(&self, inherit: bool, access: Option<u32>) -> Result<OwnedHandle> {
98 let mut handle = null_mut();
99 let options = if access.is_some() {
100 0
101 } else {
102 winnt::DUPLICATE_SAME_ACCESS
103 };
104 unsafe {
105 let process = processthreadsapi::GetCurrentProcess();
106 if handleapi::DuplicateHandle(
107 process,
108 self.as_raw_handle(),
109 process,
110 &mut handle,
111 access.unwrap_or(0),
112 inherit.into(),
113 options,
114 ) == 0
115 {
116 return Err(Error::last_os_error());
117 }
118 Ok(OwnedHandle::from_raw_handle(handle))
119 }
120 }
121}
122
123pub trait OwnedSocketExt: Sized {
124 fn prepare_to_send(&mut self) -> Result<BorrowedHandle<'_>>;
130
131 fn from_handle(handle: OwnedHandle) -> Result<Self>;
135}
136
137const SIO_SOCKET_TRANSFER_BEGIN: u32 = ws2def::IOC_IN | ws2def::IOC_VENDOR | 301;
138const SIO_SOCKET_TRANSFER_END: u32 = ws2def::IOC_IN | ws2def::IOC_VENDOR | 302;
139
140fn init_winsock() {
142 static INIT: Once = Once::new();
143
144 INIT.call_once(|| {
145 let _ = socket2::Socket::new(socket2::Domain::IPV4, socket2::Type::DGRAM, None);
148 });
149}
150
151impl OwnedSocketExt for OwnedSocket {
152 fn prepare_to_send(&mut self) -> Result<BorrowedHandle<'_>> {
153 let mut catalog_id: u32 = 0;
154 let mut bytes = 0;
155 unsafe {
157 if winsock2::WSAIoctl(
158 self.as_raw_socket() as _,
159 SIO_SOCKET_TRANSFER_BEGIN,
160 null_mut(),
161 0,
162 std::ptr::from_mut(&mut catalog_id).cast(),
163 size_of_val(&catalog_id) as u32,
164 &mut bytes,
165 null_mut(),
166 None,
167 ) != 0
168 {
169 return Err(Error::from_raw_os_error(winsock2::WSAGetLastError()));
170 }
171 Ok(BorrowedHandle::borrow_raw(self.as_raw_socket() as RawHandle))
172 }
173 }
174
175 fn from_handle(handle: OwnedHandle) -> Result<Self> {
176 init_winsock();
178
179 let mut catalog_id: u32 = 0;
180 let mut bytes = 0;
181 let mut socket = handle.as_raw_handle() as winsock2::SOCKET;
182 unsafe {
184 if winsock2::WSAIoctl(
185 socket,
186 SIO_SOCKET_TRANSFER_END,
187 std::ptr::from_mut(&mut catalog_id).cast(),
188 size_of_val(&catalog_id) as u32,
189 std::ptr::from_mut(&mut socket).cast(),
190 size_of_val(&socket) as u32,
191 &mut bytes,
192 null_mut(),
193 None,
194 ) != 0
195 {
196 return Err(Error::from_raw_os_error(winsock2::WSAGetLastError()));
197 }
198 let _gone = handle.into_raw_handle();
201 Ok(Self::from_raw_socket(socket as RawSocket))
202 }
203 }
204}
205
206#[repr(transparent)]
207#[derive(Debug)]
208struct WaitObject(OwnedHandle);
209
210impl WaitObject {
211 fn wait(&self) {
212 assert!(unsafe { synchapi::WaitForSingleObject(self.0.as_raw_handle(), INFINITE) } == 0);
213 }
214}
215
216impl Clone for WaitObject {
217 fn clone(&self) -> Self {
218 Self(
219 self.0
220 .try_clone()
221 .expect("out of resources cloning wait object"),
222 )
223 }
224}
225
226#[derive(Debug, Clone)]
227pub struct Process(WaitObject);
228
229impl Process {
230 pub fn wait(&self) {
231 self.0.wait()
232 }
233
234 pub fn id(&self) -> u32 {
235 unsafe {
236 let pid = processthreadsapi::GetProcessId(self.as_handle().as_raw_handle());
237 assert_ne!(pid, 0);
238 pid
239 }
240 }
241
242 pub fn exit_code(&self) -> u32 {
243 let mut code = 0;
244 unsafe {
245 assert!(GetExitCodeProcess(self.as_handle().as_raw_handle(), &mut code) != 0);
246 }
247 code
248 }
249
250 pub fn kill(&self, exit_code: u32) -> Result<()> {
252 unsafe {
254 if TerminateProcess(self.as_handle().as_raw_handle(), exit_code) == 0 {
255 return Err(Error::last_os_error());
256 }
257 }
258 Ok(())
259 }
260}
261
262impl From<OwnedHandle> for Process {
263 fn from(handle: OwnedHandle) -> Self {
264 Self(WaitObject(handle))
265 }
266}
267
268impl AsHandle for Process {
269 fn as_handle(&self) -> BorrowedHandle<'_> {
270 (self.0).0.as_handle()
271 }
272}
273
274impl From<Process> for OwnedHandle {
275 fn from(value: Process) -> OwnedHandle {
276 (value.0).0
277 }
278}
279
280#[derive(Debug)]
281pub struct IoCompletionPort(OwnedHandle);
282
283impl IoCompletionPort {
284 pub fn new() -> Self {
285 unsafe {
286 let handle = CreateIoCompletionPort(INVALID_HANDLE_VALUE, null_mut(), 0, 0);
287 if handle.is_null() {
288 panic!("oom allocating completion port");
289 }
290 Self(OwnedHandle::from_raw_handle(handle))
291 }
292 }
293
294 pub fn get(&self, entries: &mut [OVERLAPPED_ENTRY], timeout: Option<Duration>) -> usize {
295 unsafe {
296 let mut n = 0;
297 if GetQueuedCompletionStatusEx(
298 self.0.as_raw_handle(),
299 entries.as_mut_ptr(),
300 entries.len().try_into().expect("too many entries"),
301 &mut n,
302 timeout
303 .map(|t| t.as_millis().try_into().unwrap_or(INFINITE - 1))
304 .unwrap_or(INFINITE),
305 false.into(),
306 ) != 0
307 {
308 n as usize
309 } else {
310 assert!(timeout.is_some());
312 0
313 }
314 }
315 }
316
317 #[expect(clippy::not_unsafe_ptr_arg_deref)]
320 pub fn post(&self, bytes: u32, key: usize, overlapped: *mut OVERLAPPED) {
321 unsafe {
322 if PostQueuedCompletionStatus(self.0.as_raw_handle(), bytes, key, overlapped) == 0 {
323 panic!("oom posting completion port");
324 }
325 }
326 }
327
328 pub unsafe fn associate(&self, handle: RawHandle, key: usize) -> Result<()> {
332 if unsafe { CreateIoCompletionPort(handle, self.0.as_raw_handle(), key, 0).is_null() } {
333 return Err(Error::last_os_error());
334 }
335 Ok(())
336 }
337}
338
339impl From<OwnedHandle> for IoCompletionPort {
340 fn from(handle: OwnedHandle) -> Self {
341 Self(handle)
342 }
343}
344
345impl AsHandle for IoCompletionPort {
346 fn as_handle(&self) -> BorrowedHandle<'_> {
347 self.0.as_handle()
348 }
349}
350
351impl From<IoCompletionPort> for OwnedHandle {
352 fn from(value: IoCompletionPort) -> OwnedHandle {
353 value.0
354 }
355}
356
357pub unsafe fn set_file_completion_notification_modes(handle: RawHandle, flags: u8) -> Result<()> {
364 if unsafe { SetFileCompletionNotificationModes(handle, flags) } == 0 {
366 return Err(Error::last_os_error());
367 }
368 Ok(())
369}
370
371pub unsafe fn disassociate_completion_port(handle: RawHandle) -> Result<()> {
377 let mut info = FILE_COMPLETION_INFORMATION {
378 Port: null_mut(),
379 Key: null_mut(),
380 };
381 let mut iosb = IO_STATUS_BLOCK::default();
382 unsafe {
384 chk_status(NtSetInformationFile(
385 handle,
386 &mut iosb,
387 std::ptr::from_mut::<FILE_COMPLETION_INFORMATION>(&mut info).cast(),
388 size_of_val(&info) as u32,
389 FileReplaceCompletionInformation,
390 ))?;
391 }
392 Ok(())
393}
394
395#[derive(Debug)]
398pub struct WaitPacket(OwnedHandle);
399
400impl WaitPacket {
401 pub fn new() -> Result<Self> {
403 unsafe {
404 let mut handle = null_mut();
405 chk_status(NtCreateWaitCompletionPacket(&mut handle, 1, null_mut()))?;
406 Ok(Self(OwnedHandle::from_raw_handle(handle)))
407 }
408 }
409
410 pub unsafe fn associate(
422 &self,
423 iocp: &IoCompletionPort,
424 handle: RawHandle,
425 key: usize,
426 apc: usize,
427 status: i32,
428 information: usize,
429 ) -> bool {
430 unsafe {
432 let mut already_signaled = 0;
433 chk_status(NtAssociateWaitCompletionPacket(
434 self.0.as_raw_handle(),
435 iocp.as_handle().as_raw_handle(),
436 handle,
437 key as *mut c_void,
438 apc as *mut c_void,
439 status,
440 information,
441 &mut already_signaled,
442 ))
443 .expect("failed to associate wait completion packet");
444 already_signaled != 0
445 }
446 }
447
448 pub fn cancel(&self, remove_signaled_packet: bool) -> bool {
453 match unsafe {
454 NtCancelWaitCompletionPacket(self.0.as_raw_handle(), remove_signaled_packet.into())
455 } {
456 ntstatus::STATUS_SUCCESS => true,
457 STATUS_PENDING => false,
458 ntstatus::STATUS_CANCELLED => false,
459 s => panic!(
460 "unexpected failure in NtCancelWaitCompletionPacket: {:?}",
461 chk_status(s).unwrap_err()
462 ),
463 }
464 }
465}
466
467impl AsHandle for WaitPacket {
468 fn as_handle(&self) -> BorrowedHandle<'_> {
469 self.0.as_handle()
470 }
471}
472
473#[repr(transparent)]
476pub struct UnicodeString(UNICODE_STRING);
477
478unsafe impl Send for UnicodeString {}
481unsafe impl Sync for UnicodeString {}
482
483#[derive(Debug)]
484pub struct StringTooLong;
485
486impl UnicodeString {
487 pub fn new(s: &[u16]) -> std::result::Result<Self, StringTooLong> {
488 let byte_count: u16 = (s.len() * 2).try_into().map_err(|_| StringTooLong)?;
489 unsafe {
492 let buf = RtlAllocateHeap(GetProcessHeap(), 0, byte_count.into()).cast::<u16>();
493 assert!(!buf.is_null(), "out of memory");
494 std::ptr::copy(s.as_ptr(), buf, s.len());
495 Ok(Self(UNICODE_STRING {
496 Length: byte_count,
497 MaximumLength: byte_count,
498 Buffer: buf,
499 }))
500 }
501 }
502
503 pub fn empty() -> Self {
504 Self(unsafe { zeroed() })
505 }
506
507 pub fn is_empty(&self) -> bool {
508 self.0.Buffer.is_null()
509 }
510
511 pub fn as_ptr(&self) -> *const UNICODE_STRING {
512 &self.0
513 }
514
515 pub fn as_mut_ptr(&mut self) -> *mut UNICODE_STRING {
516 &mut self.0
517 }
518
519 pub fn into_raw(mut self) -> UNICODE_STRING {
520 let raw = self.0;
521 self.0.Length = 0;
522 self.0.MaximumLength = 0;
523 self.0.Buffer = null_mut();
524 raw
525 }
526
527 pub fn as_slice(&self) -> &[u16] {
528 let buffer = NonNull::new(self.0.Buffer).unwrap_or_else(NonNull::dangling);
529 unsafe { std::slice::from_raw_parts(buffer.as_ptr(), self.0.Length as usize / 2) }
530 }
531
532 pub fn as_mut_slice(&mut self) -> &mut [u16] {
533 let buffer = NonNull::new(self.0.Buffer).unwrap_or_else(NonNull::dangling);
534 unsafe { std::slice::from_raw_parts_mut(buffer.as_ptr(), self.0.Length as usize / 2) }
535 }
536}
537
538impl Drop for UnicodeString {
539 fn drop(&mut self) {
540 unsafe {
541 RtlFreeUnicodeString(&mut self.0);
542 }
543 }
544}
545
546impl<'a> TryFrom<&'a OsStr> for UnicodeString {
547 type Error = StringTooLong;
548 fn try_from(value: &'a OsStr) -> std::result::Result<Self, Self::Error> {
549 let value16: Vec<_> = value.encode_wide().collect();
551 Self::new(&value16)
552 }
553}
554
555impl<'a> TryFrom<&'a str> for UnicodeString {
556 type Error = StringTooLong;
557 fn try_from(value: &'a str) -> std::result::Result<Self, Self::Error> {
558 Self::try_from(OsStr::new(value))
559 }
560}
561
562impl TryFrom<String> for UnicodeString {
563 type Error = StringTooLong;
564 fn try_from(value: String) -> std::result::Result<Self, Self::Error> {
565 Self::try_from(OsStr::new(&value))
566 }
567}
568
569impl<'a> TryFrom<&'a Path> for UnicodeString {
570 type Error = StringTooLong;
571 fn try_from(value: &'a Path) -> std::result::Result<Self, Self::Error> {
572 Self::try_from(OsStr::new(value))
573 }
574}
575
576#[repr(transparent)]
577#[derive(Copy, Clone)]
578pub struct UnicodeStringRef<'a>(UNICODE_STRING, PhantomData<&'a [u16]>);
579
580impl<'a> UnicodeStringRef<'a> {
581 pub fn new(s: &'a [u16]) -> Option<Self> {
582 let len: u16 = (s.len() * 2).try_into().ok()?;
583 Some(Self(
584 UNICODE_STRING {
585 Length: len,
586 MaximumLength: len,
587 Buffer: s.as_ptr().cast_mut(),
588 },
589 PhantomData,
590 ))
591 }
592
593 pub fn empty() -> Self {
594 Self(unsafe { zeroed() }, PhantomData)
595 }
596
597 pub fn is_empty(&self) -> bool {
598 self.0.Buffer.is_null()
599 }
600
601 pub fn as_ptr(&self) -> *const UNICODE_STRING {
602 &self.0
603 }
604
605 pub fn as_mut_ptr(&mut self) -> *mut UNICODE_STRING {
606 &mut self.0
607 }
608
609 pub fn as_slice(&self) -> &[u16] {
610 let buffer = NonNull::new(self.0.Buffer).unwrap_or_else(NonNull::dangling);
611 unsafe { std::slice::from_raw_parts(buffer.as_ptr(), self.0.Length as usize / 2) }
612 }
613}
614
615pub trait AsUnicodeStringRef {
616 fn as_unicode_string_ref(&self) -> &UnicodeStringRef<'_>;
617}
618
619impl<T: AsUnicodeStringRef> AsUnicodeStringRef for &T {
620 fn as_unicode_string_ref(&self) -> &UnicodeStringRef<'_> {
621 (*self).as_unicode_string_ref()
622 }
623}
624
625impl AsUnicodeStringRef for UnicodeString {
626 fn as_unicode_string_ref(&self) -> &UnicodeStringRef<'_> {
627 unsafe { std::mem::transmute(&self.0) }
631 }
632}
633
634impl AsUnicodeStringRef for UnicodeStringRef<'_> {
635 fn as_unicode_string_ref(&self) -> &UnicodeStringRef<'_> {
636 self
637 }
638}
639
640impl AsRef<windows::Win32::Foundation::UNICODE_STRING> for UnicodeStringRef<'_> {
641 fn as_ref(&self) -> &windows::Win32::Foundation::UNICODE_STRING {
642 unsafe { std::mem::transmute(&self.0) }
645 }
646}
647
648impl<'a> TryFrom<&'a Utf16Str> for UnicodeStringRef<'a> {
649 type Error = StringTooLong;
650
651 fn try_from(value: &'a Utf16Str) -> std::result::Result<Self, Self::Error> {
652 UnicodeStringRef::new(value.as_slice()).ok_or(StringTooLong)
653 }
654}
655
656#[repr(transparent)]
658pub struct AnsiStringRef<'a>(ANSI_STRING, PhantomData<&'a [u8]>);
659
660impl<'a> AnsiStringRef<'a> {
661 pub fn new(s: &'a [u8]) -> Option<Self> {
665 let len: u16 = s.len().try_into().ok()?;
666 Some(Self(
667 ANSI_STRING {
668 Length: len,
669 MaximumLength: len,
670 Buffer: s.as_ptr() as *mut i8,
671 },
672 PhantomData,
673 ))
674 }
675
676 pub fn empty() -> Self {
678 Self(unsafe { zeroed() }, PhantomData)
679 }
680
681 pub fn is_empty(&self) -> bool {
683 self.0.Buffer.is_null()
684 }
685
686 pub fn as_ptr(&self) -> *const ANSI_STRING {
688 &self.0
689 }
690
691 pub fn as_mut_ptr(&mut self) -> *mut ANSI_STRING {
693 &mut self.0
694 }
695
696 pub fn as_slice(&self) -> &[u8] {
698 let buffer = NonNull::new(self.0.Buffer.cast::<u8>()).unwrap_or_else(NonNull::dangling);
699 unsafe { std::slice::from_raw_parts(buffer.as_ptr(), self.0.Length as usize) }
700 }
701}
702
703impl AsRef<ANSI_STRING> for AnsiStringRef<'_> {
704 fn as_ref(&self) -> &ANSI_STRING {
705 &self.0
706 }
707}
708
709pub fn status_to_error(status: i32) -> Error {
710 Error::from_raw_os_error(unsafe { RtlNtStatusToDosErrorNoTeb(status) } as i32)
711}
712
713pub fn chk_status(status: i32) -> Result<i32> {
714 if status >= 0 {
715 Ok(status)
716 } else {
717 Err(status_to_error(status))
718 }
719}
720
721pub fn dos_to_nt_path<P: AsRef<Path>>(path: P) -> Result<UnicodeString> {
722 let path16 = U16CString::from_os_str(path.as_ref().as_os_str())
723 .map_err(|_| Error::from_raw_os_error(ERROR_BAD_PATHNAME as i32))?;
724 let mut pathu = UnicodeString::empty();
725 unsafe {
726 chk_status(RtlDosPathNameToNtPathName_U_WithStatus(
727 path16.as_ptr().cast_mut(),
728 pathu.as_mut_ptr(),
729 null_mut(),
730 null_mut(),
731 ))?;
732 }
733 Ok(pathu)
734}
735
736#[repr(transparent)]
738pub struct ObjectAttributes<'a> {
739 attributes: ntdef::OBJECT_ATTRIBUTES,
740 phantom: PhantomData<&'a ()>,
741}
742
743impl Default for ObjectAttributes<'_> {
744 fn default() -> Self {
745 Self::new()
746 }
747}
748
749impl<'a> ObjectAttributes<'a> {
750 pub fn new() -> Self {
753 Self {
754 attributes: ntdef::OBJECT_ATTRIBUTES {
755 Length: size_of::<ntdef::OBJECT_ATTRIBUTES>() as u32,
756 RootDirectory: null_mut(),
757 ObjectName: null_mut(),
758 Attributes: 0,
759 SecurityDescriptor: null_mut(),
760 SecurityQualityOfService: null_mut(),
761 },
762 phantom: PhantomData,
763 }
764 }
765
766 pub fn name<P>(&mut self, name: &'a P) -> &mut Self
768 where
769 P: AsUnicodeStringRef,
770 {
771 self.attributes.ObjectName = name.as_unicode_string_ref().as_ptr().cast_mut();
772 self
773 }
774
775 pub fn root(&mut self, root: BorrowedHandle<'a>) -> &mut Self {
777 self.attributes.RootDirectory = root.as_raw_handle();
778 self
779 }
780
781 pub fn attributes(&mut self, attributes: u32) -> &mut Self {
783 self.attributes.Attributes = attributes;
784 self
785 }
786
787 pub fn security_descriptor(&mut self, sd: &'a SecurityDescriptor) -> &mut Self {
789 self.attributes.SecurityDescriptor = sd.as_ptr();
790 self
791 }
792
793 pub fn as_ptr(&self) -> *mut ntdef::OBJECT_ATTRIBUTES {
795 std::ptr::from_ref(&self.attributes).cast_mut()
796 }
797}
798
799impl AsRef<windows::Wdk::Foundation::OBJECT_ATTRIBUTES> for ObjectAttributes<'_> {
800 fn as_ref(&self) -> &windows::Wdk::Foundation::OBJECT_ATTRIBUTES {
801 unsafe { std::mem::transmute(&self.attributes) }
804 }
805}
806
807pub fn open_object_directory(obj_attr: &ObjectAttributes<'_>, access: u32) -> Result<OwnedHandle> {
808 unsafe {
810 let mut handle = null_mut();
811 chk_status(NtOpenDirectoryObject(
812 &mut handle,
813 access,
814 obj_attr.as_ptr(),
815 ))?;
816 Ok(OwnedHandle::from_raw_handle(handle))
817 }
818}
819
820pub fn create_object_directory(
821 obj_attr: &ObjectAttributes<'_>,
822 access: u32,
823) -> Result<OwnedHandle> {
824 unsafe {
826 let mut handle = null_mut();
827 chk_status(NtCreateDirectoryObject(
828 &mut handle,
829 access,
830 obj_attr.as_ptr(),
831 ))?;
832 Ok(OwnedHandle::from_raw_handle(handle))
833 }
834}
835
836pub struct RtlHeapBox<T: ?Sized> {
839 value: NonNull<T>,
840}
841
842impl<T> RtlHeapBox<T> {
843 pub unsafe fn from_raw(value: *mut T) -> Self {
856 Self {
857 value: NonNull::new(value).unwrap(),
858 }
859 }
860
861 pub fn as_ptr(&self) -> *const T {
863 self.value.as_ptr()
864 }
865}
866
867impl<T> std::ops::Deref for RtlHeapBox<T> {
868 type Target = T;
869 fn deref(&self) -> &Self::Target {
870 unsafe { self.value.as_ref() }
872 }
873}
874
875impl<T> std::ops::DerefMut for RtlHeapBox<T> {
876 fn deref_mut(&mut self) -> &mut T {
877 unsafe { self.value.as_mut() }
879 }
880}
881
882impl<T> AsRef<T> for RtlHeapBox<T> {
883 fn as_ref(&self) -> &T {
884 unsafe { self.value.as_ref() }
886 }
887}
888
889impl<T> AsMut<T> for RtlHeapBox<T> {
890 fn as_mut(&mut self) -> &mut T {
891 unsafe { self.value.as_mut() }
893 }
894}
895
896impl<T: ?Sized> Drop for RtlHeapBox<T> {
897 fn drop(&mut self) {
898 unsafe {
901 ntrtl::RtlFreeHeap(GetProcessHeap(), 0, self.value.as_ptr().cast::<c_void>());
902 }
903 }
904}
905
906pub struct RtlHeapBuffer {
909 buffer: RtlHeapBox<u8>,
910 size: usize,
911}
912
913impl RtlHeapBuffer {
914 pub unsafe fn from_raw(buffer: *mut u8, size: usize) -> RtlHeapBuffer {
922 Self {
923 buffer: unsafe { RtlHeapBox::from_raw(buffer) },
926 size,
927 }
928 }
929}
930
931impl std::ops::Deref for RtlHeapBuffer {
932 type Target = [u8];
933 fn deref(&self) -> &Self::Target {
934 unsafe { std::slice::from_raw_parts(self.buffer.as_ptr(), self.size) }
936 }
937}
938
939#[repr(transparent)]
944#[derive(Default, Debug)]
945pub struct Overlapped(UnsafeCell<OVERLAPPED>);
946
947impl Overlapped {
948 pub fn new() -> Self {
949 Default::default()
950 }
951
952 pub fn set_offset(&mut self, offset: i64) {
954 let overlapped = self.0.get_mut();
955 unsafe {
957 overlapped.u.s_mut().Offset = offset as u32;
958 overlapped.u.s_mut().OffsetHigh = (offset >> 32) as u32;
959 }
960 }
961
962 pub fn set_event(&mut self, event: RawHandle) {
963 self.0.get_mut().hEvent = event;
964 }
965
966 pub fn as_ptr(&self) -> *mut OVERLAPPED {
967 self.0.get()
968 }
969
970 pub fn io_status(&self) -> Option<(NTSTATUS, usize)> {
972 let overlapped = self.0.get();
973 let internal = unsafe { &*addr_of!((*overlapped).Internal).cast::<AtomicUsize>() };
977 let status = internal.load(Ordering::Acquire) as NTSTATUS;
978 if status != STATUS_PENDING {
979 let information = unsafe { (*self.0.get()).InternalHigh };
981 Some((status, information))
982 } else {
983 None
984 }
985 }
986}
987
988unsafe impl Send for Overlapped {}
992unsafe impl Sync for Overlapped {}
994
995#[macro_export]
996macro_rules! delayload {
997 {$dll:literal {$($($idents:ident)+ ($($params:ident : $types:ty),* $(,)?) -> $result:ty;)*}} => {
998 fn get_module() -> Result<::winapi::shared::minwindef::HINSTANCE, u32> {
999 use ::std::ptr::null_mut;
1000 use ::std::sync::atomic::{AtomicPtr, Ordering};
1001 use ::winapi::{
1002 um::{
1003 errhandlingapi::GetLastError,
1004 libloaderapi::{FreeLibrary, LoadLibraryA},
1005 },
1006 };
1007
1008 static MODULE: AtomicPtr<::winapi::shared::minwindef::HINSTANCE__> = AtomicPtr::new(null_mut());
1009 let mut module = MODULE.load(Ordering::Relaxed);
1010 if module.is_null() {
1011 module = unsafe { LoadLibraryA(concat!($dll, "\0").as_ptr() as *const i8) };
1012 if module.is_null() {
1013 return Err(unsafe { GetLastError() });
1014 }
1015 let old_module = MODULE.swap(module, Ordering::Relaxed);
1016 if !old_module.is_null() {
1017 unsafe { FreeLibrary(old_module) };
1018 }
1019 }
1020 Ok(module)
1021 }
1022
1023 $(
1024 $crate::delayload! { @func $($idents)* ($($params:$types),*) -> $result }
1025 )*
1026 };
1027
1028 (@func pub fn $name:ident($($params:ident : $types:ty),* $(,)?) -> $result:ty) => {
1029 #[expect(non_snake_case, clippy::too_many_arguments, clippy::diverging_sub_expression)]
1030 pub unsafe fn $name($($params: $types,)*) -> $result {
1031 $crate::delayload!(@body $name($($params : $types),*) -> $result)
1032 }
1033 };
1034
1035 (@func fn $name:ident($($params:ident : $types:ty),* $(,)?) -> $result:ty) => {
1036 #[expect(non_snake_case, clippy::diverging_sub_expression)]
1037 unsafe fn $name($($params: $types,)*) -> $result {
1038 $crate::delayload!(@body $name($($params : $types),*) -> $result)
1039 }
1040 };
1041
1042 (@body $name:ident($($params:ident : $types:ty),* $(,)?) -> $result:ty) => {
1043 {
1044 use ::winapi::{
1045 shared::winerror::ERROR_PROC_NOT_FOUND,
1046 um::libloaderapi::GetProcAddress,
1047 };
1048 use ::std::concat;
1049 use ::std::sync::atomic::{AtomicUsize, Ordering};
1050
1051 static FNCELL: AtomicUsize = AtomicUsize::new(0);
1052 let mut fnval = FNCELL.load(Ordering::Relaxed);
1053 if fnval == 0 {
1054 #[allow(unreachable_code)]
1055 match get_module() {
1056 Ok(module) => {
1057 fnval = GetProcAddress(
1058 module,
1059 concat!(stringify!($name), "\0").as_ptr() as *const i8)
1060 as usize;
1061 }
1062 Err(e) => return $crate::delayload!(@result_from_win32(($result), e)),
1063 }
1064 if fnval == 0 {
1065 fnval = 1;
1066 }
1067 FNCELL.store(fnval, Ordering::Relaxed);
1068 }
1069 if fnval == 1 {
1070 #[allow(unreachable_code)]
1071 return $crate::delayload!(@result_from_win32(($result), ERROR_PROC_NOT_FOUND));
1072 }
1073 type FnType = unsafe extern "stdcall" fn($($params: $types,)*) -> $result;
1074 let fnptr: FnType = ::std::mem::transmute(fnval);
1075 fnptr($($params,)*)
1076 }
1077 };
1078
1079 (@result_from_win32((i32), $val:expr)) => { ::winapi::shared::winerror::HRESULT_FROM_WIN32($val) };
1080 (@result_from_win32((u32), $val:expr)) => { $val };
1081 (@result_from_win32((DWORD), $val:expr)) => { $val };
1082 (@result_from_win32((HRESULT), $val:expr)) => { ::winapi::shared::winerror::HRESULT_FROM_WIN32($val) };
1083 (@result_from_win32(($t:tt), $val:expr)) => { panic!("could not load: {}", $val) };
1084}
1085
1086pub fn close_stdout() -> Result<()> {
1088 let new_stdout = File::open("nul")?;
1089 let stdout = io::stdout();
1090 let _locked = stdout.lock();
1092 let old_handle = stdout.as_raw_handle();
1093 unsafe {
1095 if SetStdHandle(STD_OUTPUT_HANDLE, new_stdout.into_raw_handle()) == 0 {
1096 panic!("failed to set handle");
1097 }
1098 }
1099 drop(_locked);
1100 unsafe {
1101 CloseHandle(old_handle);
1103 }
1104
1105 Ok(())
1106}
1107
1108pub fn disable_hard_error_dialog() {
1110 unsafe {
1112 SetErrorMode(GetErrorMode() | SEM_FAILCRITICALERRORS);
1113 }
1114}
1115
1116#[cfg(test)]
1117mod tests {
1118 use super::*;
1119
1120 #[test]
1121 fn test_dos_to_nt_path() {
1122 let pathu = dos_to_nt_path("c:\\foo").unwrap();
1123 assert!(
1124 pathu
1125 .as_slice()
1126 .iter()
1127 .copied()
1128 .eq("\\??\\c:\\foo".encode_utf16())
1129 );
1130 }
1131
1132 #[test]
1133 fn test_alloc_unicode_string() {
1134 let s: UnicodeString = "abc".try_into().unwrap();
1135 assert!(s.as_slice().iter().copied().eq("abc".encode_utf16()));
1136 }
1137}