1#![cfg(target_os = "windows")]
12#![forbid(warnings)]
13#![forbid(future_incompatible)]
14#![deny(unused)]
15#![forbid(box_pointers)]
16#![forbid(missing_copy_implementations)]
17#![forbid(missing_debug_implementations)]
18#![forbid(missing_docs)]
19#![forbid(trivial_casts)]
20#![forbid(trivial_numeric_casts)]
21#![forbid(unsafe_code)]
22#![forbid(unused_import_braces)]
23#![deny(unused_qualifications)]
24#![forbid(unused_results)]
25#![forbid(variant_size_differences)]
26#![cfg_attr(feature = "cargo-clippy", forbid(clippy))]
27#![cfg_attr(feature = "cargo-clippy", forbid(clippy_pedantic))]
28#![cfg_attr(feature = "cargo-clippy", forbid(clippy_complexity))]
29#![cfg_attr(feature = "cargo-clippy", deny(clippy_correctness))]
30#![cfg_attr(feature = "cargo-clippy", forbid(clippy_perf))]
31#![cfg_attr(feature = "cargo-clippy", forbid(clippy_style))]
32
33#[macro_use]
34extern crate serde_derive;
35extern crate winreg;
36
37use std::env;
38use std::ffi::OsStr;
39use std::io::{self, ErrorKind};
40use std::path::{Path, PathBuf};
41use winreg::enums::{KEY_WOW64_32KEY, HKEY_LOCAL_MACHINE, KEY_QUERY_VALUE};
42use winreg::RegKey;
43
44const V10_0_REG_KEY: &str = r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0";
45const V8_1A_REG_KEY: &str = r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v8.1A";
46const V8_1_REG_KEY: &str = r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v8.1";
47const V8_0A_REG_KEY: &str = r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v8.0A";
48const V8_0_REG_KEY: &str = r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v8.0";
49const V7_1A_REG_KEY: &str = r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v7.1A";
50const V7_1_REG_KEY: &str = r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v7.1";
51const V7_0A_REG_KEY: &str = r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v7.0a";
52const V7_0_REG_KEY: &str = r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v7.0";
53const V6_1A_REG_KEY: &str = r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v6.1a";
54const V6_1_REG_KEY: &str = r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v6.1";
55const V6_0A_REG_KEY: &str = r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v6.0a";
56const V6_0_REG_KEY: &str = r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v6.0";
57
58#[derive(Clone, Copy, Debug, Eq, PartialEq)]
59pub enum SdkVersion {
65 Any,
70 Env,
72 V10_0,
74 V8_1,
76 V8_0,
78 V7_1,
80 V7_0,
82 V6_1,
84 V6_0,
86}
87
88#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
89#[serde(rename_all = "PascalCase")]
90pub struct SdkInfo {
92 installation_folder: PathBuf,
93 product_name: Option<String>,
94 product_version: String,
95}
96
97impl SdkInfo {
98 pub fn find(version: SdkVersion) -> io::Result<Option<Self>> {
104 match version {
105 SdkVersion::Any => {
106 use SdkVersion::*;
107 let vers = [Env, V10_0, V8_1, V8_0, V7_1, V7_0, V6_1, V6_0];
108 for res in vers.iter().map(|v| Self::find(*v)) {
109 match res {
110 Ok(None) => (),
111 _ => return res,
112 }
113 }
114 Ok(None)
115 }
116 SdkVersion::Env => Ok(Self::query_env()),
117 SdkVersion::V10_0 => Self::query_reg(V10_0_REG_KEY),
118 SdkVersion::V8_1 => Self::find_double_release((V8_1A_REG_KEY, V8_1_REG_KEY)),
119 SdkVersion::V8_0 => Self::find_double_release((V8_0A_REG_KEY, V8_0_REG_KEY)),
120 SdkVersion::V7_1 => Self::find_double_release((V7_1A_REG_KEY, V7_1_REG_KEY)),
121 SdkVersion::V7_0 => Self::find_double_release((V7_0A_REG_KEY, V7_0_REG_KEY)),
122 SdkVersion::V6_1 => Self::find_double_release((V6_1A_REG_KEY, V6_1_REG_KEY)),
123 SdkVersion::V6_0 => Self::find_double_release((V6_0A_REG_KEY, V6_0_REG_KEY)),
124 }
125 }
126
127 fn find_double_release(keys: (&str, &str)) -> io::Result<Option<Self>> {
128 let res = Self::query_reg(keys.0);
129 match res {
130 Ok(None) => Self::query_reg(keys.1),
131 _ => res,
132 }
133 }
134
135 fn query_env() -> Option<Self> {
137 env::var_os("WindowsSdkDir")
138 .and_then(|install_dir| {
139 env::var_os("WindowsSdkVersion").map(|version| (install_dir, version))
140 })
141 .map(|(install_dir, version)| {
142 let ver = version
143 .into_string()
144 .map(|s| {
145 s.split(r".0\")
146 .next()
147 .expect("`str::split` failed")
148 .to_owned()
149 })
150 .expect("`WindowsSdkVersion` was not valid UTF-8");
151 Self {
152 installation_folder: Path::new(&install_dir).to_owned(),
153 product_name: None,
154 product_version: ver,
155 }
156 })
157 }
158
159 fn query_reg<P: AsRef<OsStr>>(subkey: P) -> io::Result<Option<Self>> {
160 RegKey::predef(HKEY_LOCAL_MACHINE)
161 .open_subkey_with_flags(subkey, KEY_QUERY_VALUE | KEY_WOW64_32KEY)
162 .map(|key| {
163 if let Ok(info) = key.decode() {
165 Some(info)
166 } else {
167 None
168 }
169 })
170 .or_else(|e| {
171 if e.kind() == ErrorKind::NotFound {
172 Ok(None)
173 } else {
174 Err(e)
175 }
176 })
177 }
178
179 pub fn installation_folder(&self) -> &Path {
181 &self.installation_folder
182 }
183
184 pub fn product_name(&self) -> Option<&str> {
186 self.product_name.as_ref().map(|s| s.as_ref())
187 }
188
189 pub fn product_version(&self) -> &str {
191 &self.product_version
192 }
193}
194
195#[cfg(test)]
196mod tests {
197 use {SdkInfo, SdkVersion};
198
199 #[test]
200 fn any() {
201 let _ = SdkInfo::find(SdkVersion::Any)
202 .expect("could not retrieve Windows SDK info from registry")
203 .expect("Windows SDK is not installed");
204 }
205
206 #[test]
207 fn winsdk_env() {
208 let _ = SdkInfo::find(SdkVersion::Env)
209 .unwrap_or_else(|_| unreachable!())
210 .expect("environment does not specify a Windows SDK installation");
211 }
212
213 #[test]
214 fn winsdk_10_0() {
215 let _ = SdkInfo::find(SdkVersion::V10_0)
216 .expect("could not retrieve Windows 10 SDK info from registry")
217 .expect("Windows 10 SDK is not installed");
218 }
219
220 #[test]
221 fn winsdk_8_1() {
222 let _ = SdkInfo::find(SdkVersion::V8_1)
223 .expect("could not retrieve Windows 8.1 SDK info from registry")
224 .expect("Windows 8.1 SDK is not installed");
225 }
226}