1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
// Copyright (c) Mike Grier.
//! Capture a Windows thread's ambient state and apply it on another thread.
//!
//! Some Windows behaviour is not a parameter of the call you make; it is
//! ambient state hanging off the calling thread. An impersonation token decides
//! whose access rights an open is checked against, and even which drive letters
//! resolve. The thread error mode decides whether a hard device error raises a
//! modal dialog. WOW64 filesystem redirection decides which of two directories a
//! 32-bit process actually reaches. None of it travels with work handed to
//! another thread.
//!
//! That matters most when the other thread is shared. A thread-pool worker
//! inherits none of the submitter's ambient state: measured, `OpenThreadToken`
//! on a worker returns `ERROR_NO_TOKEN` while the submitting thread genuinely
//! held a token, and the worker's error mode is `0`, meaning the critical-error
//! handler is enabled and an absent removable drive can put a modal dialog on
//! process-shared infrastructure. Explicit capture is therefore necessary rather
//! than merely prudent.
//!
//! # Scope
//!
//! This crate carries thread-scoped ambient state that changes what a Win32 call
//! does. It does not carry call parameters, does not open files, and does not
//! know what any particular Windows operation is.
//!
//! # Two sets, because the aspects do not relate to the caller the same way
//!
//! Aspects that can be read off the calling thread are **captured**, and which
//! of them to collect is chosen by the caller. Aspects that cannot be read --
//! WOW64 redirection has no getter at all, and I/O priority has no documented
//! one -- are **declared** instead: the caller states the value it wants
//! installed. A declared aspect has nothing to collect, so it is not part of the
//! capture set; left unspecified, it leaves the target thread's own value alone.
//!
//! # This crate holds no policy
//!
//! Every aspect is offered for capture *and* for explicit declaration, and no
//! combination is privileged. A consumer running on shared threads will want to
//! force the dialog-suppressing error-mode bits; a consumer with a private
//! thread, where a modal dialog is its own problem and nobody else's, is
//! entitled to the opposite choice. Both compose that policy from the primitives
//! here rather than finding it already decided.
//!
//! # Example
//!
//! Capture on the submitting thread, where a failure is still the caller's to
//! see, then reconstruct the context on a worker that inherited none of it:
//!
//! ```
//! use std::thread;
//!
//! use windows_thread_ambient_sys::declared::MemoryPriority;
//! use windows_thread_ambient_sys::{AmbientState, CaptureSet, Declared};
//!
//! // Captured here, on the submitting thread, where a failure is still ours to
//! // see. Declared aspects are stated rather than read from anything.
//! let state = AmbientState::capture(CaptureSet::DEFAULT)?
//! .with_declared(Declared::none().with_memory_priority(MemoryPriority::Low));
//!
//! let applied = thread::spawn(move || {
//! // Guards apply outermost-first and release in exact reverse, with
//! // impersonation innermost because its window is the narrowest.
//! state.with_applied(|| "ran as the submitter")
//! })
//! .join()
//! .expect("the worker did not panic")?;
//!
//! assert_eq!(*applied.value(), "ran as the submitter");
//!
//! // A restore failure for the reported aspects -- the error mode, the
//! // declared aspects, the transaction -- does not discard the operation's
//! // value; it arrives alongside it, so a caller can retire a contaminated
//! // thread without losing what the work produced.
//! assert!(applied.restore().is_clean());
//! # Ok::<(), Box<dyn std::error::Error>>(())
//! ```
//!
//! Impersonation is deliberately *not* among those reported aspects: its restore
//! is fail-fast, so a failure panics instead of being reported, and a panic
//! inside a thread-pool callback aborts the process rather than failing one
//! operation. That is the intended trade rather than an oversight, and it is the
//! one property to weigh before adopting this crate on shared workers;
//! [`state`] gives the reasoning in full.
//!
//! A consumer that wants to *override* the error mode rather than transplant it
//! -- forcing the dialog-suppressing bits on a shared worker -- leaves
//! [`CaptureSet::ERROR_MODE`] out of its capture set and wraps the call in its
//! own [`ThreadErrorMode::apply`] guard, which then sits outermost, exactly
//! where the ordering puts it.
pub use ;
pub use Captured;
pub use Declared;
pub use ;
/// Compiles the README's examples, so a contract change breaks the build rather
/// than silently teaching the old answer.
;
pub use ThreadErrorMode;
pub use ImpersonationToken;