11#pragma once
22
3+ #include < assert.h>
34#include < array>
45#include < limits>
56#include < stdint.h>
1415#include < Windows.h>
1516#include < shlwapi.h>
1617#include < XmlLite.h>
17- #include " cmd_reader_windows.h"
1818
19- namespace xlang ::cmd
19+ namespace cppwinrt
2020{
21+ struct registry_key
22+ {
23+ HKEY handle{};
24+
25+ registry_key (registry_key const &) = delete ;
26+ registry_key& operator =(registry_key const &) = delete ;
27+
28+ ~registry_key () noexcept
29+ {
30+ if (handle)
31+ {
32+ RegCloseKey (handle);
33+ }
34+ }
35+ };
36+
37+ template <typename T>
38+ struct com_ptr
39+ {
40+ T* ptr{};
41+
42+ com_ptr (com_ptr const &) = delete ;
43+ com_ptr& operator =(com_ptr const &) = delete ;
44+
45+ com_ptr () noexcept = default ;
46+
47+ ~com_ptr () noexcept
48+ {
49+ if (ptr)
50+ {
51+ ptr->Release ();
52+ }
53+ }
54+
55+ auto operator ->() const noexcept
56+ {
57+ return ptr;
58+ }
59+ };
60+
61+ static void check_xml (HRESULT result)
62+ {
63+ if (result < 0 )
64+ {
65+ throw std::invalid_argument (" Could not read the Windows SDK's Platform.xml" );
66+ }
67+ }
68+
69+ inline void add_files_from_xml (
70+ std::set<std::string>& files,
71+ std::string const & sdk_version,
72+ std::filesystem::path const & xml_path,
73+ std::filesystem::path const & sdk_path)
74+ {
75+ com_ptr<IStream> stream;
76+
77+ check_xml (SHCreateStreamOnFileW (
78+ xml_path.c_str (),
79+ STGM_READ, &stream.ptr ));
80+
81+ com_ptr<IXmlReader> reader;
82+
83+ check_xml (CreateXmlReader (
84+ __uuidof (IXmlReader),
85+ reinterpret_cast <void **>(&reader.ptr ),
86+ nullptr ));
87+
88+ check_xml (reader->SetInput (stream.ptr ));
89+ XmlNodeType node_type = XmlNodeType_None;
90+
91+ while (S_OK == reader->Read (&node_type))
92+ {
93+ if (node_type != XmlNodeType_Element)
94+ {
95+ continue ;
96+ }
97+
98+ wchar_t const * value{ nullptr };
99+ check_xml (reader->GetLocalName (&value, nullptr ));
100+
101+ if (0 != wcscmp (value, L" ApiContract" ))
102+ {
103+ continue ;
104+ }
105+
106+ auto path = sdk_path;
107+ path /= L" References" ;
108+ path /= sdk_version;
109+
110+ check_xml (reader->MoveToAttributeByName (L" name" , nullptr ));
111+ check_xml (reader->GetValue (&value, nullptr ));
112+ path /= value;
113+
114+ check_xml (reader->MoveToAttributeByName (L" version" , nullptr ));
115+ check_xml (reader->GetValue (&value, nullptr ));
116+ path /= value;
117+
118+ check_xml (reader->MoveToAttributeByName (L" name" , nullptr ));
119+ check_xml (reader->GetValue (&value, nullptr ));
120+ path /= value;
121+
122+ path += L" .winmd" ;
123+ files.insert (path.string ());
124+ }
125+ }
126+
127+ inline registry_key open_sdk ()
128+ {
129+ HKEY key;
130+
131+ if (0 != RegOpenKeyExW (
132+ HKEY_LOCAL_MACHINE,
133+ L" SOFTWARE\\ Microsoft\\ Windows Kits\\ Installed Roots" ,
134+ 0 ,
135+ KEY_READ,
136+ &key))
137+ {
138+ throw std::invalid_argument (" Could not find the Windows SDK in the registry" );
139+ }
140+
141+ return { key };
142+ }
143+
144+ inline std::filesystem::path get_sdk_path ()
145+ {
146+ auto key = open_sdk ();
147+
148+ DWORD path_size = 0 ;
149+
150+ if (0 != RegQueryValueExW (
151+ key.handle ,
152+ L" KitsRoot10" ,
153+ nullptr ,
154+ nullptr ,
155+ nullptr ,
156+ &path_size))
157+ {
158+ throw std::invalid_argument (" Could not find the Windows SDK path in the registry" );
159+ }
160+
161+ std::wstring root ((path_size / sizeof (wchar_t )) - 1 , L' ?' );
162+
163+ RegQueryValueExW (
164+ key.handle ,
165+ L" KitsRoot10" ,
166+ nullptr ,
167+ nullptr ,
168+ reinterpret_cast <BYTE*>(root.data ()),
169+ &path_size);
170+
171+ return root;
172+ }
173+
174+ inline std::string get_module_path ()
175+ {
176+ std::string path (100 , ' ?' );
177+ DWORD actual_size{};
178+
179+ while (true )
180+ {
181+ actual_size = GetModuleFileNameA (nullptr , path.data (), 1 + static_cast <uint32_t >(path.size ()));
182+
183+ if (actual_size < 1 + path.size ())
184+ {
185+ path.resize (actual_size);
186+ break ;
187+ }
188+ else
189+ {
190+ path.resize (path.size () * 2 , ' ?' );
191+ }
192+ }
193+
194+ return path;
195+ }
196+
197+ inline std::string get_sdk_version ()
198+ {
199+ auto module_path = get_module_path ();
200+ std::regex rx (R"( ((\d+)\.(\d+)\.(\d+)\.(\d+)))" );
201+ std::cmatch match;
202+ auto sdk_path = get_sdk_path ();
203+
204+ if (std::regex_search (module_path.c_str (), match, rx))
205+ {
206+ auto path = sdk_path / " Platforms\\ UAP" / match[1 ].str () / " Platform.xml" ;
207+
208+ if (std::filesystem::exists (path))
209+ {
210+ return match[1 ].str ();
211+ }
212+ }
213+
214+ auto key = open_sdk ();
215+ uint32_t index{};
216+ std::array<char , 100 > subkey;
217+ std::array<unsigned long , 4 > version_parts{};
218+ std::string result;
219+
220+ while (0 == RegEnumKeyA (key.handle , index++, subkey.data (), static_cast <uint32_t >(subkey.size ())))
221+ {
222+ if (!std::regex_match (subkey.data (), match, rx))
223+ {
224+ continue ;
225+ }
226+
227+ auto path = sdk_path / " Platforms\\ UAP" / match[1 ].str () / " Platform.xml" ;
228+ if (!std::filesystem::exists (path))
229+ {
230+ continue ;
231+ }
232+
233+ char * next_part = subkey.data ();
234+ bool force_newer = false ;
235+
236+ for (size_t i = 0 ; ; ++i)
237+ {
238+ auto version_part = strtoul (next_part, &next_part, 10 );
239+
240+ if ((version_part < version_parts[i]) && !force_newer)
241+ {
242+ break ;
243+ }
244+ else if (version_part > version_parts[i])
245+ {
246+ // E.g. ensure something like '2.1' is considered newer than '1.2'
247+ force_newer = true ;
248+ }
249+
250+ version_parts[i] = version_part;
251+
252+ if (i == std::size (version_parts) - 1 )
253+ {
254+ result = subkey.data ();
255+ break ;
256+ }
257+
258+ if (!next_part)
259+ {
260+ break ;
261+ }
262+
263+ ++next_part;
264+ }
265+ }
266+
267+ if (result.empty ())
268+ {
269+ throw std::invalid_argument (" Could not find the Windows SDK" );
270+ }
271+
272+ return result;
273+ }
274+
21275 [[noreturn]] inline void throw_invalid (std::string const & message)
22276 {
23277 throw std::invalid_argument (message);
@@ -47,14 +301,14 @@ namespace xlang::cmd
47301 template <typename C, typename V, size_t numOptions>
48302 reader (C const argc, V const argv, const option (& options)[numOptions])
49303 {
50- #ifdef XLANG_DEBUG
304+ #ifdef _DEBUG
51305 {
52306 std::set<std::string_view> unique;
53307
54308 for (auto && option : options)
55309 {
56310 // If this assertion fails it means there are duplicate options.
57- XLANG_ASSERT (unique.insert (option.name ).second );
311+ assert (unique.insert (option.name ).second );
58312 }
59313 }
60314#endif
@@ -180,7 +434,7 @@ namespace xlang::cmd
180434
181435 if (path == " sdk" || path == " sdk+" )
182436 {
183- sdk_version = impl:: get_sdk_version ();
437+ sdk_version = get_sdk_version ();
184438 }
185439 else
186440 {
@@ -195,13 +449,13 @@ namespace xlang::cmd
195449
196450 if (!sdk_version.empty ())
197451 {
198- auto sdk_path = impl:: get_sdk_path ();
452+ auto sdk_path = get_sdk_path ();
199453 auto xml_path = sdk_path;
200454 xml_path /= L" Platforms\\ UAP" ;
201455 xml_path /= sdk_version;
202456 xml_path /= L" Platform.xml" ;
203457
204- impl:: add_files_from_xml (files, sdk_version, xml_path, sdk_path);
458+ add_files_from_xml (files, sdk_version, xml_path, sdk_path);
205459
206460 if (path.back () != ' +' )
207461 {
@@ -213,7 +467,7 @@ namespace xlang::cmd
213467 xml_path = item.path () / sdk_version;
214468 xml_path /= L" SDKManifest.xml" ;
215469
216- impl:: add_files_from_xml (files, sdk_version, xml_path, sdk_path);
470+ add_files_from_xml (files, sdk_version, xml_path, sdk_path);
217471 }
218472
219473 continue ;
0 commit comments