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
118
//! "compatibility layer",用于支持较早版本的 Windows
//!
//! 标准库使用一些 Windows API 函数,这些 Windows API 函数在 Windows 的较早版本中不存在。
//! (请注意,Rust 支持的最早的 Windows 版本是 Windows 7 (client) 和 Windows Server 2008 (服务器)。) 该模块实现了一种延迟的 DLL 导入绑定形式,使用 `GetModuleHandle` 和 `GetProcAddress` 在运行时查找 DLL 入口点。
//!
//!
//! 此实现使用静态初始化来查找 DLL 入口点。CRT (C 运行时) 在调用 `main` (对于二进制文件) 和调用 `DllMain` (对于 DLL) 之前执行静态初始化程序。
//! 这是查找 DLL 导入的理想时间,因为我们保证没有其他线程会尝试调用这些入口点。
//! 因此,我们可以查找导入并将其存储在 `static mut` 字段中,而无需进行任何同步。
//!
//! 这还有一个额外的优势: 因为 DLL 导入查找是在模块初始化时发生的,所以这些查找的开销是确定性的,并且从实际上调用 DLL 导入的代码路径中删除了。
//! 也就是说,调用 DLL 导入时,不会发生不可预测的 "cache miss"。
//! 对于受益于可预测的延迟的应用程序,这是一个好处。这也消除了热路径中的比较和分支。
//!
//! 当前,标准库仅使用少量动态 DLL 导入。如果该数目显着增加,则在初始化时执行所有查找的成本可能会变得很高。
//!
//! [CRT 初始化](https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-initialization?view=msvc-160) 中记录了向 CRT 注册静态初始化的机制。
//! 它通过在 `.CRT$XCU` 部分添加一个符号来起作用。
//! 链接器会建立一个包含所有静态初始化函数的表。
//! 然后,CRT 启动代码将迭代该表,并调用每个初始化函数。
//!
//! # **WARNING!!*
//! 静态初始化函数运行的环境受到严格限制。静态初始值设定项可以安全执行的操作有很多限制。静态初始化函数 **不得** 执行以下任何一项操作 (此列表并不全面) :
//! * 触摸其他静态初始化所使用的任何其他静态字段,因为未定义静态初始化程序运行的顺序。
//! * 调用 `LoadLibrary` 或任何其他获得 DLL 加载程序锁的函数。
//! * 调用任何接触 (global) 静态状态的 Rust 函数或 CRT 函数。
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!

macro_rules! compat_fn {
    ($module:literal: $(
        $(#[$meta:meta])*
        pub fn $symbol:ident($($argname:ident: $argtype:ty),*) -> $rettype:ty $fallback_body:block
    )*) => ($(
        $(#[$meta])*
        pub mod $symbol {
            #[allow(unused_imports)]
            use super::*;
            use crate::mem;

            type F = unsafe extern "system" fn($($argtype),*) -> $rettype;

            /// 指向 DLL 导入或后备函数。
            ///
            /// 该静态可以是普通的,非同步的静态静态,因为我们保证在 CRT 初始化期间所有写操作都将完成,并且所有读取都在 CRT 初始化之后进行。
            ///
            ///
            static mut PTR: Option<F> = None;

            /// 这个符号使 CRT 可以找到 `init` 函数并调用它。
            /// 之所以标记为 `#[used]`,是因为否则 Rust 会假定未使用它,并将其删除。
            ///
            #[used]
            #[link_section = ".CRT$XCU"]
            static INIT_TABLE_ENTRY: unsafe extern "C" fn() = init;

            unsafe extern "C" fn init() {
                // 这里没有锁定。该代码在输入 main() 之前执行,并且保证是单线程的。
                //
                // 不要在此函数中做任何有趣或复杂的事情! 如果那些 Rust 函数或 CRT 函数接触到任何状态,则不要调用任何 Rust 函数或 CRT 函数,因为此函数在初始化时运行。
                // 例如,请勿进行任何动态分配,请勿调用 LoadLibrary 等。
                //
                //
                //
                let module_name: *const u8 = concat!($module, "\0").as_ptr();
                let symbol_name: *const u8 = concat!(stringify!($symbol), "\0").as_ptr();
                let module_handle = $crate::sys::c::GetModuleHandleA(module_name as *const i8);
                if !module_handle.is_null() {
                    match $crate::sys::c::GetProcAddress(module_handle, symbol_name as *const i8) as usize {
                        0 => {}
                        n => {
                            PTR = Some(mem::transmute::<usize, F>(n));
                        }
                    }
                }
            }

            #[allow(dead_code)]
            pub fn option() -> Option<F> {
                unsafe { PTR }
            }

            #[allow(dead_code)]
            pub unsafe fn call($($argname: $argtype),*) -> $rettype {
                if let Some(ptr) = PTR {
                    ptr($($argname),*)
                } else {
                    $fallback_body
                }
            }
        }

        $(#[$meta])*
        pub use $symbol::call as $symbol;
    )*)
}