|
| 1 | +use std::ffi::{c_void, OsStr}; |
| 2 | +use std::os::windows::ffi::OsStrExt; |
| 3 | +use std::sync::Once; |
| 4 | + |
| 5 | +/// Windows API types |
| 6 | +type BOOL = i32; |
| 7 | +type HANDLE = *mut c_void; |
| 8 | +type USHORT = u16; |
| 9 | + |
| 10 | +const FALSE: BOOL = 0; |
| 11 | +const IMAGE_FILE_MACHINE_I386: USHORT = 0x014c; |
| 12 | +const IMAGE_FILE_MACHINE_AMD64: USHORT = 0x8664; |
| 13 | +const IMAGE_FILE_MACHINE_ARM64: USHORT = 0xAA64; |
| 14 | + |
| 15 | +#[link(name = "kernel32")] |
| 16 | +extern "system" { |
| 17 | + fn GetCurrentProcess() -> HANDLE; |
| 18 | + fn GetModuleHandleW(lpModuleName: *const u16) -> HANDLE; |
| 19 | + fn GetProcAddress(hModule: HANDLE, lpProcName: *const u8) -> *mut c_void; |
| 20 | + fn IsWow64Process(hProcess: HANDLE, wow64Process: *mut BOOL) -> BOOL; |
| 21 | +} |
| 22 | + |
| 23 | +/// Signature of IsWow64Process2 |
| 24 | +type FnIsWow64Process2 = unsafe extern "system" fn( |
| 25 | + HANDLE, |
| 26 | + *mut USHORT, |
| 27 | + *mut USHORT, |
| 28 | +) -> BOOL; |
| 29 | + |
| 30 | +static INIT: Once = Once::new(); |
| 31 | +static mut FN_ISWOW64PROCESS2: Option<FnIsWow64Process2> = None; |
| 32 | + |
| 33 | +unsafe fn init_iswow64process2() { |
| 34 | + // Load kernel32.dll |
| 35 | + let wide_name: Vec<u16> = OsStr::new("kernel32.dll") |
| 36 | + .encode_wide() |
| 37 | + .chain(std::iter::once(0)) |
| 38 | + .collect(); |
| 39 | + let module = GetModuleHandleW(wide_name.as_ptr()); |
| 40 | + if module.is_null() { |
| 41 | + return; |
| 42 | + } |
| 43 | + |
| 44 | + // Look up IsWow64Process2 |
| 45 | + let proc_name = b"IsWow64Process2\0"; |
| 46 | + let ptr_fn = GetProcAddress(module, proc_name.as_ptr()); |
| 47 | + if !ptr_fn.is_null() { |
| 48 | + FN_ISWOW64PROCESS2 = Some(std::mem::transmute(ptr_fn)); |
| 49 | + } |
| 50 | +} |
| 51 | + |
| 52 | +/// Returns a string representing the **native system architecture**, e.g. |
| 53 | +/// `"x86_64"`, `"aarch64"`, `"x86"`, or `"unknown"`. |
| 54 | +pub fn get_native_arch() -> &'static str { |
| 55 | + unsafe { |
| 56 | + INIT.call_once(|| { init_iswow64process2() }); |
| 57 | + |
| 58 | + let hproc = GetCurrentProcess(); |
| 59 | + |
| 60 | + // Try IsWow64Process2 if available |
| 61 | + if let Some(f) = FN_ISWOW64PROCESS2 { |
| 62 | + let mut proc_machine: USHORT = 0; |
| 63 | + let mut native_machine: USHORT = 0; |
| 64 | + let ok = f(hproc, &mut proc_machine, &mut native_machine); |
| 65 | + if ok != FALSE { |
| 66 | + return match native_machine { |
| 67 | + IMAGE_FILE_MACHINE_AMD64 => "x86_64", |
| 68 | + IMAGE_FILE_MACHINE_ARM64 => "aarch64", |
| 69 | + IMAGE_FILE_MACHINE_I386 => "x86", |
| 70 | + _ => "unknown", |
| 71 | + }; |
| 72 | + } |
| 73 | + } |
| 74 | + |
| 75 | + // Fallback: use IsWow64Process |
| 76 | + let mut is_wow64: BOOL = 0; |
| 77 | + let ok = IsWow64Process(hproc, &mut is_wow64); |
| 78 | + if ok != FALSE { |
| 79 | + // We can only distinguish 32-bit vs 64-bit, not ARM64 vs x86_64 |
| 80 | + if is_wow64 != 0 { |
| 81 | + return "x86_64"; // assume 64-bit host when under WOW64 |
| 82 | + } else { |
| 83 | + #[cfg(target_arch = "x86_64")] |
| 84 | + { return "x86_64"; } |
| 85 | + #[cfg(target_arch = "aarch64")] |
| 86 | + { return "aarch64"; } |
| 87 | + #[cfg(target_arch = "x86")] |
| 88 | + { return "x86"; } |
| 89 | + #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "x86")))] |
| 90 | + return "unknown"; |
| 91 | + } |
| 92 | + } |
| 93 | + |
| 94 | + // Final fallback if everything fails |
| 95 | + #[cfg(target_arch = "x86_64")] |
| 96 | + { "x86_64" } |
| 97 | + #[cfg(target_arch = "aarch64")] |
| 98 | + { "aarch64" } |
| 99 | + #[cfg(target_arch = "x86")] |
| 100 | + { "x86" } |
| 101 | + #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "x86")))] |
| 102 | + { "unknown" } |
| 103 | + } |
| 104 | +} |
0 commit comments