authorgravatar for bratishkaerik@getgoogleoff.meEric Joldasov <bratishkaerik@getgoogleoff.me> 2023-07-18 20:54:37+06:00
committergravatar for bratishkaerik@getgoogleoff.meEric Joldasov <bratishkaerik@getgoogleoff.me> 2023-07-23 17:10:57+06:00
log38d10ee4d0ae6f3ea24a1e5e951127b27e979297
treecad969a92afec50b431350fe646907521b76c528
parent664ecdfb8e0df58912e8a7b445e8390370b28ccd
signaturelock-open Commit is signed but in an unrecognized format.

src/windows_sdk.cpp: port to Zig

Signed-off-by: Eric Joldasov <bratishkaerik@getgoogleoff.me>

7 files changed, 782 insertions(+), 1412 deletions(-)

CMakeLists.txt-2
...@@ -193,8 +193,6 @@ set(ZIG_CPP_SOURCES...@@ -193,8 +193,6 @@ set(ZIG_CPP_SOURCES
193 "${CMAKE_SOURCE_DIR}/src/zig_clang_driver.cpp"193 "${CMAKE_SOURCE_DIR}/src/zig_clang_driver.cpp"
194 "${CMAKE_SOURCE_DIR}/src/zig_clang_cc1_main.cpp"194 "${CMAKE_SOURCE_DIR}/src/zig_clang_cc1_main.cpp"
195 "${CMAKE_SOURCE_DIR}/src/zig_clang_cc1as_main.cpp"195 "${CMAKE_SOURCE_DIR}/src/zig_clang_cc1as_main.cpp"
196 # https://github.com/ziglang/zig/issues/6363
197 "${CMAKE_SOURCE_DIR}/src/windows_sdk.cpp"
198)196)
199# Needed because we use cmake, not the zig build system, to build zig2.o.197# Needed because we use cmake, not the zig build system, to build zig2.o.
200set(ZIG_STAGE2_SOURCES198set(ZIG_STAGE2_SOURCES
build.zig-2
...@@ -926,8 +926,6 @@ const zig_cpp_sources = [_][]const u8{...@@ -926,8 +926,6 @@ const zig_cpp_sources = [_][]const u8{
926 "src/zig_clang_driver.cpp",926 "src/zig_clang_driver.cpp",
927 "src/zig_clang_cc1_main.cpp",927 "src/zig_clang_cc1_main.cpp",
928 "src/zig_clang_cc1as_main.cpp",928 "src/zig_clang_cc1as_main.cpp",
929 // https://github.com/ziglang/zig/issues/6363
930 "src/windows_sdk.cpp",
931};929};
932930
933const clang_libs = [_][]const u8{931const clang_libs = [_][]const u8{
src/libc_installation.zig+28-35
...@@ -186,21 +186,19 @@ pub const LibCInstallation = struct {...@@ -186,21 +186,19 @@ pub const LibCInstallation = struct {
186 } else if (is_windows) {186 } else if (is_windows) {
187 if (!build_options.have_llvm)187 if (!build_options.have_llvm)
188 return error.WindowsSdkNotFound;188 return error.WindowsSdkNotFound;
189 var sdk: *ZigWindowsSDK = undefined;189
190 switch (ZigWindowsSDK.find(&sdk)) {190 var sdk: ZigWindowsSDK = ZigWindowsSDK.find(args.allocator) catch |err| switch (err) {
191 .None => {191 error.NotFound => return error.WindowsSdkNotFound,
192 defer sdk.free();192 error.PathTooLong => return error.WindowsSdkNotFound,
193193 error.OutOfMemory => return error.OutOfMemory,
194 try self.findNativeMsvcIncludeDir(args, sdk);194 };
195 try self.findNativeMsvcLibDir(args, sdk);195 defer sdk.free(args.allocator);
196 try self.findNativeKernel32LibDir(args, sdk);196
197 try self.findNativeIncludeDirWindows(args, sdk);197 try self.findNativeMsvcIncludeDir(args, &sdk);
198 try self.findNativeCrtDirWindows(args, sdk);198 try self.findNativeMsvcLibDir(args, &sdk);
199 },199 try self.findNativeKernel32LibDir(args, &sdk);
200 .OutOfMemory => return error.OutOfMemory,200 try self.findNativeIncludeDirWindows(args, &sdk);
201 .NotFound => return error.WindowsSdkNotFound,201 try self.findNativeCrtDirWindows(args, &sdk);
202 .PathTooLong => return error.WindowsSdkNotFound,
203 }
204 } else if (is_haiku) {202 } else if (is_haiku) {
205 try self.findNativeIncludeDirPosix(args);203 try self.findNativeIncludeDirPosix(args);
206 try self.findNativeCrtBeginDirHaiku(args);204 try self.findNativeCrtBeginDirHaiku(args);
...@@ -512,8 +510,7 @@ pub const LibCInstallation = struct {...@@ -512,8 +510,7 @@ pub const LibCInstallation = struct {
512 ) FindError!void {510 ) FindError!void {
513 const allocator = args.allocator;511 const allocator = args.allocator;
514512
515 const msvc_lib_dir_ptr = sdk.msvc_lib_dir_ptr orelse return error.LibCStdLibHeaderNotFound;513 const msvc_lib_dir = sdk.msvc_lib_dir orelse return error.LibCStdLibHeaderNotFound;
516 const msvc_lib_dir = msvc_lib_dir_ptr[0..sdk.msvc_lib_dir_len];
517 const up1 = fs.path.dirname(msvc_lib_dir) orelse return error.LibCStdLibHeaderNotFound;514 const up1 = fs.path.dirname(msvc_lib_dir) orelse return error.LibCStdLibHeaderNotFound;
518 const up2 = fs.path.dirname(up1) orelse return error.LibCStdLibHeaderNotFound;515 const up2 = fs.path.dirname(up1) orelse return error.LibCStdLibHeaderNotFound;
519516
...@@ -544,8 +541,8 @@ pub const LibCInstallation = struct {...@@ -544,8 +541,8 @@ pub const LibCInstallation = struct {
544 sdk: *ZigWindowsSDK,541 sdk: *ZigWindowsSDK,
545 ) FindError!void {542 ) FindError!void {
546 const allocator = args.allocator;543 const allocator = args.allocator;
547 const msvc_lib_dir_ptr = sdk.msvc_lib_dir_ptr orelse return error.LibCRuntimeNotFound;544 const msvc_lib_dir = sdk.msvc_lib_dir orelse return error.LibCRuntimeNotFound;
548 self.msvc_lib_dir = try allocator.dupeZ(u8, msvc_lib_dir_ptr[0..sdk.msvc_lib_dir_len]);545 self.msvc_lib_dir = try allocator.dupe(u8, msvc_lib_dir);
549 }546 }
550};547};
551548
...@@ -657,23 +654,19 @@ const Search = struct {...@@ -657,23 +654,19 @@ const Search = struct {
657654
658fn fillSearch(search_buf: *[2]Search, sdk: *ZigWindowsSDK) []Search {655fn fillSearch(search_buf: *[2]Search, sdk: *ZigWindowsSDK) []Search {
659 var search_end: usize = 0;656 var search_end: usize = 0;
660 if (sdk.path10_ptr) |path10_ptr| {657 if (sdk.windows10sdk) |windows10sdk| {
661 if (sdk.version10_ptr) |version10_ptr| {658 search_buf[search_end] = .{
662 search_buf[search_end] = Search{659 .path = windows10sdk.path,
663 .path = path10_ptr[0..sdk.path10_len],660 .version = windows10sdk.version,
664 .version = version10_ptr[0..sdk.version10_len],661 };
665 };662 search_end += 1;
666 search_end += 1;
667 }
668 }663 }
669 if (sdk.path81_ptr) |path81_ptr| {664 if (sdk.windows81sdk) |windows81sdk| {
670 if (sdk.version81_ptr) |version81_ptr| {665 search_buf[search_end] = .{
671 search_buf[search_end] = Search{666 .path = windows81sdk.path,
672 .path = path81_ptr[0..sdk.path81_len],667 .version = windows81sdk.version,
673 .version = version81_ptr[0..sdk.version81_len],668 };
674 };669 search_end += 1;
675 search_end += 1;
676 }
677 }670 }
678 return search_buf[0..search_end];671 return search_buf[0..search_end];
679}672}
src/windows_com.hpp deleted-909
...@@ -1,909 +0,0 @@
1// The MIT License(MIT)
2// Copyright(C) Microsoft Corporation.All rights reserved.
3//
4// Permission is hereby granted, free of charge, to any person obtaining a copy
5// of this software and associated documentation files(the "Software"), to deal
6// in the Software without restriction, including without limitation the rights
7// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
8// copies of the Software, and to permit persons to whom the Software is
9// furnished to do so, subject to the following conditions :
10//
11// The above copyright notice and this permission notice shall be included in
12// all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
17// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20// IN THE SOFTWARE.
21//
22
23#pragma once
24
25// Windows headers
26#include <windows.h>
27#include <fcntl.h>
28#include <io.h>
29#include <shellapi.h>
30
31// Standard headers
32#include <stdio.h>
33
34// COM support header files
35#include <comdef.h>
36
37// Constants
38//
39#ifndef E_NOTFOUND
40#define E_NOTFOUND HRESULT_FROM_WIN32(ERROR_NOT_FOUND)
41#endif
42
43#ifndef E_FILENOTFOUND
44#define E_FILENOTFOUND HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)
45#endif
46
47#ifndef E_NOTSUPPORTED
48#define E_NOTSUPPORTED HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)
49#endif
50
51// Enumerations
52//
53/// <summary>
54/// The state of an instance.
55/// </summary>
56enum InstanceState
57{
58 /// <summary>
59 /// The instance state has not been determined.
60 /// </summary>
61 eNone = 0,
62
63 /// <summary>
64 /// The instance installation path exists.
65 /// </summary>
66 eLocal = 1,
67
68 /// <summary>
69 /// A product is registered to the instance.
70 /// </summary>
71 eRegistered = 2,
72
73 /// <summary>
74 /// No reboot is required for the instance.
75 /// </summary>
76 eNoRebootRequired = 4,
77
78 /// <summary>
79 /// No errors were reported for the instance.
80 /// </summary>
81 eNoErrors = 8,
82
83 /// <summary>
84 /// The instance represents a complete install.
85 /// </summary>
86 eComplete = UINT_MAX,
87};
88
89// Forward interface declarations
90//
91#ifndef __ISetupInstance_FWD_DEFINED__
92#define __ISetupInstance_FWD_DEFINED__
93typedef struct ISetupInstance ISetupInstance;
94#endif
95
96#ifndef __ISetupInstance2_FWD_DEFINED__
97#define __ISetupInstance2_FWD_DEFINED__
98typedef struct ISetupInstance2 ISetupInstance2;
99#endif
100
101#ifndef __ISetupInstanceCatalog_FWD_DEFINED__
102#define __ISetupInstanceCatalog_FWD_DEFINED__
103typedef struct ISetupInstanceCatalog ISetupInstanceCatalog;
104#endif
105
106#ifndef __ISetupLocalizedProperties_FWD_DEFINED__
107#define __ISetupLocalizedProperties_FWD_DEFINED__
108typedef struct ISetupLocalizedProperties ISetupLocalizedProperties;
109#endif
110
111#ifndef __IEnumSetupInstances_FWD_DEFINED__
112#define __IEnumSetupInstances_FWD_DEFINED__
113typedef struct IEnumSetupInstances IEnumSetupInstances;
114#endif
115
116#ifndef __ISetupConfiguration_FWD_DEFINED__
117#define __ISetupConfiguration_FWD_DEFINED__
118typedef struct ISetupConfiguration ISetupConfiguration;
119#endif
120
121#ifndef __ISetupConfiguration2_FWD_DEFINED__
122#define __ISetupConfiguration2_FWD_DEFINED__
123typedef struct ISetupConfiguration2 ISetupConfiguration2;
124#endif
125
126#ifndef __ISetupPackageReference_FWD_DEFINED__
127#define __ISetupPackageReference_FWD_DEFINED__
128typedef struct ISetupPackageReference ISetupPackageReference;
129#endif
130
131#ifndef __ISetupHelper_FWD_DEFINED__
132#define __ISetupHelper_FWD_DEFINED__
133typedef struct ISetupHelper ISetupHelper;
134#endif
135
136#ifndef __ISetupErrorState_FWD_DEFINED__
137#define __ISetupErrorState_FWD_DEFINED__
138typedef struct ISetupErrorState ISetupErrorState;
139#endif
140
141#ifndef __ISetupErrorState2_FWD_DEFINED__
142#define __ISetupErrorState2_FWD_DEFINED__
143typedef struct ISetupErrorState2 ISetupErrorState2;
144#endif
145
146#ifndef __ISetupFailedPackageReference_FWD_DEFINED__
147#define __ISetupFailedPackageReference_FWD_DEFINED__
148typedef struct ISetupFailedPackageReference ISetupFailedPackageReference;
149#endif
150
151#ifndef __ISetupFailedPackageReference2_FWD_DEFINED__
152#define __ISetupFailedPackageReference2_FWD_DEFINED__
153typedef struct ISetupFailedPackageReference2 ISetupFailedPackageReference2;
154#endif
155
156#ifndef __ISetupPropertyStore_FWD_DEFINED__
157#define __ISetupPropertyStore_FWD_DEFINED__
158typedef struct ISetupPropertyStore ISetupPropertyStore;
159#endif
160
161#ifndef __ISetupLocalizedPropertyStore_FWD_DEFINED__
162#define __ISetupLocalizedPropertyStore_FWD_DEFINED__
163typedef struct ISetupLocalizedPropertyStore ISetupLocalizedPropertyStore;
164#endif
165
166// Forward class declarations
167//
168#ifndef __SetupConfiguration_FWD_DEFINED__
169#define __SetupConfiguration_FWD_DEFINED__
170
171#ifdef __cplusplus
172typedef class SetupConfiguration SetupConfiguration;
173#endif
174
175#endif
176
177#ifndef _MSC_VER
178#define _Deref_out_opt_
179#endif
180
181#ifdef __cplusplus
182extern "C" {
183#endif
184
185 // Interface definitions
186 //
187 EXTERN_C const IID IID_ISetupInstance;
188
189#if defined(__cplusplus) && !defined(CINTERFACE)
190 /// <summary>
191 /// Information about an instance of a product.
192 /// </summary>
193 struct DECLSPEC_UUID("B41463C3-8866-43B5-BC33-2B0676F7F42E") DECLSPEC_NOVTABLE ISetupInstance : public IUnknown
194 {
195 /// <summary>
196 /// Gets the instance identifier (should match the name of the parent instance directory).
197 /// </summary>
198 /// <param name="pbstrInstanceId">The instance identifier.</param>
199 /// <returns>Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist.</returns>
200 STDMETHOD(GetInstanceId)(
201 _Out_ BSTR* pbstrInstanceId
202 ) = 0;
203
204 /// <summary>
205 /// Gets the local date and time when the installation was originally installed.
206 /// </summary>
207 /// <param name="pInstallDate">The local date and time when the installation was originally installed.</param>
208 /// <returns>Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist and E_NOTFOUND if the property is not defined.</returns>
209 STDMETHOD(GetInstallDate)(
210 _Out_ LPFILETIME pInstallDate
211 ) = 0;
212
213 /// <summary>
214 /// Gets the unique name of the installation, often indicating the branch and other information used for telemetry.
215 /// </summary>
216 /// <param name="pbstrInstallationName">The unique name of the installation, often indicating the branch and other information used for telemetry.</param>
217 /// <returns>Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist and E_NOTFOUND if the property is not defined.</returns>
218 STDMETHOD(GetInstallationName)(
219 _Out_ BSTR* pbstrInstallationName
220 ) = 0;
221
222 /// <summary>
223 /// Gets the path to the installation root of the product.
224 /// </summary>
225 /// <param name="pbstrInstallationPath">The path to the installation root of the product.</param>
226 /// <returns>Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist and E_NOTFOUND if the property is not defined.</returns>
227 STDMETHOD(GetInstallationPath)(
228 _Out_ BSTR* pbstrInstallationPath
229 ) = 0;
230
231 /// <summary>
232 /// Gets the version of the product installed in this instance.
233 /// </summary>
234 /// <param name="pbstrInstallationVersion">The version of the product installed in this instance.</param>
235 /// <returns>Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist and E_NOTFOUND if the property is not defined.</returns>
236 STDMETHOD(GetInstallationVersion)(
237 _Out_ BSTR* pbstrInstallationVersion
238 ) = 0;
239
240 /// <summary>
241 /// Gets the display name (title) of the product installed in this instance.
242 /// </summary>
243 /// <param name="lcid">The LCID for the display name.</param>
244 /// <param name="pbstrDisplayName">The display name (title) of the product installed in this instance.</param>
245 /// <returns>Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist and E_NOTFOUND if the property is not defined.</returns>
246 STDMETHOD(GetDisplayName)(
247 _In_ LCID lcid,
248 _Out_ BSTR* pbstrDisplayName
249 ) = 0;
250
251 /// <summary>
252 /// Gets the description of the product installed in this instance.
253 /// </summary>
254 /// <param name="lcid">The LCID for the description.</param>
255 /// <param name="pbstrDescription">The description of the product installed in this instance.</param>
256 /// <returns>Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist and E_NOTFOUND if the property is not defined.</returns>
257 STDMETHOD(GetDescription)(
258 _In_ LCID lcid,
259 _Out_ BSTR* pbstrDescription
260 ) = 0;
261
262 /// <summary>
263 /// Resolves the optional relative path to the root path of the instance.
264 /// </summary>
265 /// <param name="pwszRelativePath">A relative path within the instance to resolve, or NULL to get the root path.</param>
266 /// <param name="pbstrAbsolutePath">The full path to the optional relative path within the instance. If the relative path is NULL, the root path will always terminate in a backslash.</param>
267 /// <returns>Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist and E_NOTFOUND if the property is not defined.</returns>
268 STDMETHOD(ResolvePath)(
269 _In_opt_z_ LPCOLESTR pwszRelativePath,
270 _Out_ BSTR* pbstrAbsolutePath
271 ) = 0;
272 };
273#endif
274
275 EXTERN_C const IID IID_ISetupInstance2;
276
277#if defined(__cplusplus) && !defined(CINTERFACE)
278 /// <summary>
279 /// Information about an instance of a product.
280 /// </summary>
281 struct DECLSPEC_UUID("89143C9A-05AF-49B0-B717-72E218A2185C") DECLSPEC_NOVTABLE ISetupInstance2 : public ISetupInstance
282 {
283 /// <summary>
284 /// Gets the state of the instance.
285 /// </summary>
286 /// <param name="pState">The state of the instance.</param>
287 /// <returns>Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist.</returns>
288 STDMETHOD(GetState)(
289 _Out_ InstanceState* pState
290 ) = 0;
291
292 /// <summary>
293 /// Gets an array of package references registered to the instance.
294 /// </summary>
295 /// <param name="ppsaPackages">Pointer to an array of <see cref="ISetupPackageReference"/>.</param>
296 /// <returns>Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist and E_NOTFOUND if the packages property is not defined.</returns>
297 STDMETHOD(GetPackages)(
298 _Out_ LPSAFEARRAY* ppsaPackages
299 ) = 0;
300
301 /// <summary>
302 /// Gets a pointer to the <see cref="ISetupPackageReference"/> that represents the registered product.
303 /// </summary>
304 /// <param name="ppPackage">Pointer to an instance of <see cref="ISetupPackageReference"/>. This may be NULL if <see cref="GetState"/> does not return <see cref="eComplete"/>.</param>
305 /// <returns>Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist and E_NOTFOUND if the packages property is not defined.</returns>
306 STDMETHOD(GetProduct)(
307 _Outptr_result_maybenull_ ISetupPackageReference** ppPackage
308 ) = 0;
309
310 /// <summary>
311 /// Gets the relative path to the product application, if available.
312 /// </summary>
313 /// <param name="pbstrProductPath">The relative path to the product application, if available.</param>
314 /// <returns>Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist.</returns>
315 STDMETHOD(GetProductPath)(
316 _Outptr_result_maybenull_ BSTR* pbstrProductPath
317 ) = 0;
318
319 /// <summary>
320 /// Gets the error state of the instance, if available.
321 /// </summary>
322 /// <param name="pErrorState">The error state of the instance, if available.</param>
323 /// <returns>Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist.</returns>
324 STDMETHOD(GetErrors)(
325 _Outptr_result_maybenull_ ISetupErrorState** ppErrorState
326 ) = 0;
327
328 /// <summary>
329 /// Gets a value indicating whether the instance can be launched.
330 /// </summary>
331 /// <param name="pfIsLaunchable">Whether the instance can be launched.</param>
332 /// <returns>Standard HRESULT indicating success or failure.</returns>
333 /// <remarks>
334 /// An instance could have had errors during install but still be launched. Some features may not work correctly, but others will.
335 /// </remarks>
336 STDMETHOD(IsLaunchable)(
337 _Out_ VARIANT_BOOL* pfIsLaunchable
338 ) = 0;
339
340 /// <summary>
341 /// Gets a value indicating whether the instance is complete.
342 /// </summary>
343 /// <param name="pfIsLaunchable">Whether the instance is complete.</param>
344 /// <returns>Standard HRESULT indicating success or failure.</returns>
345 /// <remarks>
346 /// An instance is complete if it had no errors during install, resume, or repair.
347 /// </remarks>
348 STDMETHOD(IsComplete)(
349 _Out_ VARIANT_BOOL* pfIsComplete
350 ) = 0;
351
352 /// <summary>
353 /// Gets product-specific properties.
354 /// </summary>
355 /// <param name="ppProperties">A pointer to an instance of <see cref="ISetupPropertyStore"/>. This may be NULL if no properties are defined.</param>
356 /// <returns>Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist.</returns>
357 STDMETHOD(GetProperties)(
358 _Outptr_result_maybenull_ ISetupPropertyStore** ppProperties
359 ) = 0;
360
361 /// <summary>
362 /// Gets the directory path to the setup engine that installed the instance.
363 /// </summary>
364 /// <param name="pbstrEnginePath">The directory path to the setup engine that installed the instance.</param>
365 /// <returns>Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist.</returns>
366 STDMETHOD(GetEnginePath)(
367 _Outptr_result_maybenull_ BSTR* pbstrEnginePath
368 ) = 0;
369 };
370#endif
371
372 EXTERN_C const IID IID_ISetupInstanceCatalog;
373
374#if defined(__cplusplus) && !defined(CINTERFACE)
375 /// <summary>
376 /// Information about a catalog used to install an instance.
377 /// </summary>
378 struct DECLSPEC_UUID("9AD8E40F-39A2-40F1-BF64-0A6C50DD9EEB") DECLSPEC_NOVTABLE ISetupInstanceCatalog : public IUnknown
379 {
380 /// <summary>
381 /// Gets catalog information properties.
382 /// </summary>
383 /// <param name="ppCatalogInfo">A pointer to an instance of <see cref="ISetupPropertyStore"/>.</param>
384 /// <returns>Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist and E_NOTFOUND if the property does not exist.</returns>
385 STDMETHOD(GetCatalogInfo)(
386 _Out_ ISetupPropertyStore** ppCatalogInfo
387 ) = 0;
388
389 /// <summary>
390 /// Gets a value indicating whether the catalog is a prerelease.
391 /// </summary>
392 /// <param name="pfIsPrerelease">Whether the catalog for the instance is a prerelease version.</param>
393 /// <returns>Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist and E_NOTFOUND if the property does not exist.</returns>
394 STDMETHOD(IsPrerelease)(
395 _Out_ VARIANT_BOOL* pfIsPrerelease
396 ) = 0;
397 };
398#endif
399
400 EXTERN_C const IID IID_ISetupLocalizedProperties;
401
402#if defined(__cplusplus) && !defined(CINTERFACE)
403 /// <summary>
404 /// Provides localized properties of an instance of a product.
405 /// </summary>
406 struct DECLSPEC_UUID("F4BD7382-FE27-4AB4-B974-9905B2A148B0") DECLSPEC_NOVTABLE ISetupLocalizedProperties : public IUnknown
407 {
408 /// <summary>
409 /// Gets localized product-specific properties.
410 /// </summary>
411 /// <param name="ppLocalizedProperties">A pointer to an instance of <see cref="ISetupLocalizedPropertyStore"/>. This may be NULL if no properties are defined.</param>
412 /// <returns>Standard HRESULT indicating success or failure.</returns>
413 STDMETHOD(GetLocalizedProperties)(
414 _Outptr_result_maybenull_ ISetupLocalizedPropertyStore** ppLocalizedProperties
415 ) = 0;
416
417 /// <summary>
418 /// Gets localized channel-specific properties.
419 /// </summary>
420 /// <param name="ppLocalizedChannelProperties">A pointer to an instance of <see cref="ISetupLocalizedPropertyStore"/>. This may be NULL if no channel properties are defined.</param>
421 /// <returns>Standard HRESULT indicating success or failure.</returns>
422 STDMETHOD(GetLocalizedChannelProperties)(
423 _Outptr_result_maybenull_ ISetupLocalizedPropertyStore** ppLocalizedChannelProperties
424 ) = 0;
425 };
426#endif
427
428 EXTERN_C const IID IID_IEnumSetupInstances;
429
430#if defined(__cplusplus) && !defined(CINTERFACE)
431
432#ifdef __GNUC__
433 __CRT_UUID_DECL(IEnumSetupInstances, 0x6380BCFF, 0x41D3, 0x4B2E, 0x8B, 0x2E, 0xBF, 0x8A, 0x68, 0x10, 0xC8, 0x48);
434#endif
435
436 /// <summary>
437 /// An enumerator of installed <see cref="ISetupInstance"/> objects.
438 /// </summary>
439 struct DECLSPEC_UUID("6380BCFF-41D3-4B2E-8B2E-BF8A6810C848") DECLSPEC_NOVTABLE IEnumSetupInstances : public IUnknown
440 {
441 /// <summary>
442 /// Retrieves the next set of product instances in the enumeration sequence.
443 /// </summary>
444 /// <param name="celt">The number of product instances to retrieve.</param>
445 /// <param name="rgelt">A pointer to an array of <see cref="ISetupInstance"/>.</param>
446 /// <param name="pceltFetched">A pointer to the number of product instances retrieved. If <paramref name="celt"/> is 1 this parameter may be NULL.</param>
447 /// <returns>S_OK if the number of elements were fetched, S_FALSE if nothing was fetched (at end of enumeration), E_INVALIDARG if <paramref name="celt"/> is greater than 1 and pceltFetched is NULL, or E_OUTOFMEMORY if an <see cref="ISetupInstance"/> could not be allocated.</returns>
448 STDMETHOD(Next)(
449 _In_ ULONG celt,
450 _Out_writes_to_(celt, *pceltFetched) ISetupInstance** rgelt,
451 _Out_opt_ _Deref_out_range_(0, celt) ULONG* pceltFetched
452 ) = 0;
453
454 /// <summary>
455 /// Skips the next set of product instances in the enumeration sequence.
456 /// </summary>
457 /// <param name="celt">The number of product instances to skip.</param>
458 /// <returns>S_OK if the number of elements could be skipped; otherwise, S_FALSE;</returns>
459 STDMETHOD(Skip)(
460 _In_ ULONG celt
461 ) = 0;
462
463 /// <summary>
464 /// Resets the enumeration sequence to the beginning.
465 /// </summary>
466 /// <returns>Always returns S_OK;</returns>
467 STDMETHOD(Reset)(void) = 0;
468
469 /// <summary>
470 /// Creates a new enumeration object in the same state as the current enumeration object: the new object points to the same place in the enumeration sequence.
471 /// </summary>
472 /// <param name="ppenum">A pointer to a pointer to a new <see cref="IEnumSetupInstances"/> interface. If the method fails, this parameter is undefined.</param>
473 /// <returns>S_OK if a clone was returned; otherwise, E_OUTOFMEMORY.</returns>
474 STDMETHOD(Clone)(
475 _Deref_out_opt_ IEnumSetupInstances** ppenum
476 ) = 0;
477 };
478#endif
479
480 EXTERN_C const IID IID_ISetupConfiguration;
481
482#if defined(__cplusplus) && !defined(CINTERFACE)
483
484#ifdef __GNUC__
485 __CRT_UUID_DECL(ISetupConfiguration, 0x42843719, 0xDB4C, 0x46C2, 0x8E, 0x7C, 0x64, 0xF1, 0x81, 0x6E, 0xFD, 0x5B);
486#endif
487
488 /// <summary>
489 /// Gets information about product instances installed on the machine.
490 /// </summary>
491 struct DECLSPEC_UUID("42843719-DB4C-46C2-8E7C-64F1816EFD5B") DECLSPEC_NOVTABLE ISetupConfiguration : public IUnknown
492 {
493 /// <summary>
494 /// Enumerates all launchable product instances installed.
495 /// </summary>
496 /// <param name="ppEnumInstances">An enumeration of completed, installed product instances.</param>
497 /// <returns>Standard HRESULT indicating success or failure.</returns>
498 STDMETHOD(EnumInstances)(
499 _Out_ IEnumSetupInstances** ppEnumInstances
500 ) = 0;
501
502 /// <summary>
503 /// Gets the instance for the current process path.
504 /// </summary>
505 /// <param name="ppInstance">The instance for the current process path.</param>
506 /// <returns>
507 /// The instance for the current process path, or E_NOTFOUND if not found.
508 /// The <see cref="ISetupInstance::GetState"/> may indicate the instance is invalid.
509 /// </returns>
510 /// <remarks>
511 /// The returned instance may not be launchable.
512 /// </remarks>
513 STDMETHOD(GetInstanceForCurrentProcess)(
514 _Out_ ISetupInstance** ppInstance
515 ) = 0;
516
517 /// <summary>
518 /// Gets the instance for the given path.
519 /// </summary>
520 /// <param name="ppInstance">The instance for the given path.</param>
521 /// <returns>
522 /// The instance for the given path, or E_NOTFOUND if not found.
523 /// The <see cref="ISetupInstance::GetState"/> may indicate the instance is invalid.
524 /// </returns>
525 /// <remarks>
526 /// The returned instance may not be launchable.
527 /// </remarks>
528 STDMETHOD(GetInstanceForPath)(
529 _In_z_ LPCWSTR wzPath,
530 _Out_ ISetupInstance** ppInstance
531 ) = 0;
532 };
533#endif
534
535 EXTERN_C const IID IID_ISetupConfiguration2;
536
537#if defined(__cplusplus) && !defined(CINTERFACE)
538 /// <summary>
539 /// Gets information about product instances.
540 /// </summary>
541 struct DECLSPEC_UUID("26AAB78C-4A60-49D6-AF3B-3C35BC93365D") DECLSPEC_NOVTABLE ISetupConfiguration2 : public ISetupConfiguration
542 {
543 /// <summary>
544 /// Enumerates all product instances.
545 /// </summary>
546 /// <param name="ppEnumInstances">An enumeration of all product instances.</param>
547 /// <returns>Standard HRESULT indicating success or failure.</returns>
548 STDMETHOD(EnumAllInstances)(
549 _Out_ IEnumSetupInstances** ppEnumInstances
550 ) = 0;
551 };
552#endif
553
554 EXTERN_C const IID IID_ISetupPackageReference;
555
556#if defined(__cplusplus) && !defined(CINTERFACE)
557 /// <summary>
558 /// A reference to a package.
559 /// </summary>
560 struct DECLSPEC_UUID("da8d8a16-b2b6-4487-a2f1-594ccccd6bf5") DECLSPEC_NOVTABLE ISetupPackageReference : public IUnknown
561 {
562 /// <summary>
563 /// Gets the general package identifier.
564 /// </summary>
565 /// <param name="pbstrId">The general package identifier.</param>
566 /// <returns>Standard HRESULT indicating success or failure.</returns>
567 STDMETHOD(GetId)(
568 _Out_ BSTR* pbstrId
569 ) = 0;
570
571 /// <summary>
572 /// Gets the version of the package.
573 /// </summary>
574 /// <param name="pbstrVersion">The version of the package.</param>
575 /// <returns>Standard HRESULT indicating success or failure.</returns>
576 STDMETHOD(GetVersion)(
577 _Out_ BSTR* pbstrVersion
578 ) = 0;
579
580 /// <summary>
581 /// Gets the target process architecture of the package.
582 /// </summary>
583 /// <param name="pbstrChip">The target process architecture of the package.</param>
584 /// <returns>Standard HRESULT indicating success or failure.</returns>
585 STDMETHOD(GetChip)(
586 _Out_ BSTR* pbstrChip
587 ) = 0;
588
589 /// <summary>
590 /// Gets the language and optional region identifier.
591 /// </summary>
592 /// <param name="pbstrLanguage">The language and optional region identifier.</param>
593 /// <returns>Standard HRESULT indicating success or failure.</returns>
594 STDMETHOD(GetLanguage)(
595 _Out_ BSTR* pbstrLanguage
596 ) = 0;
597
598 /// <summary>
599 /// Gets the build branch of the package.
600 /// </summary>
601 /// <param name="pbstrBranch">The build branch of the package.</param>
602 /// <returns>Standard HRESULT indicating success or failure.</returns>
603 STDMETHOD(GetBranch)(
604 _Out_ BSTR* pbstrBranch
605 ) = 0;
606
607 /// <summary>
608 /// Gets the type of the package.
609 /// </summary>
610 /// <param name="pbstrType">The type of the package.</param>
611 /// <returns>Standard HRESULT indicating success or failure.</returns>
612 STDMETHOD(GetType)(
613 _Out_ BSTR* pbstrType
614 ) = 0;
615
616 /// <summary>
617 /// Gets the unique identifier consisting of all defined tokens.
618 /// </summary>
619 /// <param name="pbstrUniqueId">The unique identifier consisting of all defined tokens.</param>
620 /// <returns>Standard HRESULT indicating success or failure, including E_UNEXPECTED if no Id was defined (required).</returns>
621 STDMETHOD(GetUniqueId)(
622 _Out_ BSTR* pbstrUniqueId
623 ) = 0;
624
625 /// <summary>
626 /// Gets a value indicating whether the package refers to an external extension.
627 /// </summary>
628 /// <param name="pfIsExtension">A value indicating whether the package refers to an external extension.</param>
629 /// <returns>Standard HRESULT indicating success or failure, including E_UNEXPECTED if no Id was defined (required).</returns>
630 STDMETHOD(GetIsExtension)(
631 _Out_ VARIANT_BOOL* pfIsExtension
632 ) = 0;
633 };
634#endif
635
636 EXTERN_C const IID IID_ISetupHelper;
637
638#if defined(__cplusplus) && !defined(CINTERFACE)
639 /// <summary>
640 /// Helper functions.
641 /// </summary>
642 /// <remarks>
643 /// You can query for this interface from the <see cref="SetupConfiguration"/> class.
644 /// </remarks>
645 struct DECLSPEC_UUID("42b21b78-6192-463e-87bf-d577838f1d5c") DECLSPEC_NOVTABLE ISetupHelper : public IUnknown
646 {
647 /// <summary>
648 /// Parses a dotted quad version string into a 64-bit unsigned integer.
649 /// </summary>
650 /// <param name="pwszVersion">The dotted quad version string to parse, e.g. 1.2.3.4.</param>
651 /// <param name="pullVersion">A 64-bit unsigned integer representing the version. You can compare this to other versions.</param>
652 /// <returns>Standard HRESULT indicating success or failure, including E_INVALIDARG if the version is not valid.</returns>
653 STDMETHOD(ParseVersion)(
654 _In_ LPCOLESTR pwszVersion,
655 _Out_ PULONGLONG pullVersion
656 ) = 0;
657
658 /// <summary>
659 /// Parses a dotted quad version string into a 64-bit unsigned integer.
660 /// </summary>
661 /// <param name="pwszVersionRange">The string containing 1 or 2 dotted quad version strings to parse, e.g. [1.0,) that means 1.0.0.0 or newer.</param>
662 /// <param name="pullMinVersion">A 64-bit unsigned integer representing the minimum version, which may be 0. You can compare this to other versions.</param>
663 /// <param name="pullMaxVersion">A 64-bit unsigned integer representing the maximum version, which may be MAXULONGLONG. You can compare this to other versions.</param>
664 /// <returns>Standard HRESULT indicating success or failure, including E_INVALIDARG if the version range is not valid.</returns>
665 STDMETHOD(ParseVersionRange)(
666 _In_ LPCOLESTR pwszVersionRange,
667 _Out_ PULONGLONG pullMinVersion,
668 _Out_ PULONGLONG pullMaxVersion
669 ) = 0;
670 };
671#endif
672
673 EXTERN_C const IID IID_ISetupErrorState;
674
675#if defined(__cplusplus) && !defined(CINTERFACE)
676 /// <summary>
677 /// Information about the error state of an instance.
678 /// </summary>
679 struct DECLSPEC_UUID("46DCCD94-A287-476A-851E-DFBC2FFDBC20") DECLSPEC_NOVTABLE ISetupErrorState : public IUnknown
680 {
681 /// <summary>
682 /// Gets an array of failed package references.
683 /// </summary>
684 /// <param name="ppsaPackages">Pointer to an array of <see cref="ISetupFailedPackageReference"/>, if packages have failed.</param>
685 /// <returns>Standard HRESULT indicating success or failure.</returns>
686 STDMETHOD(GetFailedPackages)(
687 _Outptr_result_maybenull_ LPSAFEARRAY* ppsaFailedPackages
688 ) = 0;
689
690 /// <summary>
691 /// Gets an array of skipped package references.
692 /// </summary>
693 /// <param name="ppsaPackages">Pointer to an array of <see cref="ISetupPackageReference"/>, if packages have been skipped.</param>
694 /// <returns>Standard HRESULT indicating success or failure.</returns>
695 STDMETHOD(GetSkippedPackages)(
696 _Outptr_result_maybenull_ LPSAFEARRAY* ppsaSkippedPackages
697 ) = 0;
698 };
699#endif
700
701 EXTERN_C const IID IID_ISetupErrorState2;
702
703#if defined(__cplusplus) && !defined(CINTERFACE)
704 /// <summary>
705 /// Information about the error state of an instance.
706 /// </summary>
707 struct DECLSPEC_UUID("9871385B-CA69-48F2-BC1F-7A37CBF0B1EF") DECLSPEC_NOVTABLE ISetupErrorState2 : public ISetupErrorState
708 {
709 /// <summary>
710 /// Gets the path to the error log.
711 /// </summary>
712 /// <param name="pbstrChip">The path to the error log.</param>
713 /// <returns>Standard HRESULT indicating success or failure.</returns>
714 STDMETHOD(GetErrorLogFilePath)(
715 _Outptr_result_maybenull_ BSTR* pbstrErrorLogFilePath
716 ) = 0;
717
718 /// <summary>
719 /// Gets the path to the main setup log.
720 /// </summary>
721 /// <param name="pbstrChip">The path to the main setup log.</param>
722 /// <returns>Standard HRESULT indicating success or failure.</returns>
723 STDMETHOD(GetLogFilePath)(
724 _Outptr_result_maybenull_ BSTR* pbstrLogFilePath
725 ) = 0;
726 };
727#endif
728
729 EXTERN_C const IID IID_ISetupFailedPackageReference;
730
731#if defined(__cplusplus) && !defined(CINTERFACE)
732 /// <summary>
733 /// A reference to a failed package.
734 /// </summary>
735 struct DECLSPEC_UUID("E73559CD-7003-4022-B134-27DC650B280F") DECLSPEC_NOVTABLE ISetupFailedPackageReference : public ISetupPackageReference
736 {
737 };
738
739#endif
740
741 EXTERN_C const IID IID_ISetupFailedPackageReference2;
742
743#if defined(__cplusplus) && !defined(CINTERFACE)
744 /// <summary>
745 /// A reference to a failed package.
746 /// </summary>
747 struct DECLSPEC_UUID("0FAD873E-E874-42E3-B268-4FE2F096B9CA") DECLSPEC_NOVTABLE ISetupFailedPackageReference2 : public ISetupFailedPackageReference
748 {
749 /// <summary>
750 /// Gets the path to the optional package log.
751 /// </summary>
752 /// <param name="pbstrId">The path to the optional package log.</param>
753 /// <returns>Standard HRESULT indicating success or failure.</returns>
754 STDMETHOD(GetLogFilePath)(
755 _Outptr_result_maybenull_ BSTR* pbstrLogFilePath
756 ) = 0;
757
758 /// <summary>
759 /// Gets the description of the package failure.
760 /// </summary>
761 /// <param name="pbstrId">The description of the package failure.</param>
762 /// <returns>Standard HRESULT indicating success or failure.</returns>
763 STDMETHOD(GetDescription)(
764 _Outptr_result_maybenull_ BSTR* pbstrDescription
765 ) = 0;
766
767 /// <summary>
768 /// Gets the signature to use for feedback reporting.
769 /// </summary>
770 /// <param name="pbstrId">The signature to use for feedback reporting.</param>
771 /// <returns>Standard HRESULT indicating success or failure.</returns>
772 STDMETHOD(GetSignature)(
773 _Outptr_result_maybenull_ BSTR* pbstrSignature
774 ) = 0;
775
776 /// <summary>
777 /// Gets the array of details for this package failure.
778 /// </summary>
779 /// <param name="ppsaDetails">Pointer to an array of details as BSTRs.</param>
780 /// <returns>Standard HRESULT indicating success or failure.</returns>
781 STDMETHOD(GetDetails)(
782 _Out_ LPSAFEARRAY* ppsaDetails
783 ) = 0;
784
785 /// <summary>
786 /// Gets an array of packages affected by this package failure.
787 /// </summary>
788 /// <param name="ppsaPackages">Pointer to an array of <see cref="ISetupPackageReference"/> for packages affected by this package failure. This may be NULL.</param>
789 /// <returns>Standard HRESULT indicating success or failure.</returns>
790 STDMETHOD(GetAffectedPackages)(
791 _Out_ LPSAFEARRAY* ppsaAffectedPackages
792 ) = 0;
793 };
794
795#endif
796
797 EXTERN_C const IID IID_ISetupPropertyStore;
798
799#if defined(__cplusplus) && !defined(CINTERFACE)
800 /// <summary>
801 /// Provides named properties.
802 /// </summary>
803 /// <remarks>
804 /// You can get this from an <see cref="ISetupInstance"/>, <see cref="ISetupPackageReference"/>, or derivative.
805 /// </remarks>
806 struct DECLSPEC_UUID("C601C175-A3BE-44BC-91F6-4568D230FC83") DECLSPEC_NOVTABLE ISetupPropertyStore : public IUnknown
807 {
808 /// <summary>
809 /// Gets an array of property names in this property store.
810 /// </summary>
811 /// <param name="ppsaNames">Pointer to an array of property names as BSTRs.</param>
812 /// <returns>Standard HRESULT indicating success or failure.</returns>
813 STDMETHOD(GetNames)(
814 _Out_ LPSAFEARRAY* ppsaNames
815 ) = 0;
816
817 /// <summary>
818 /// Gets the value of a named property in this property store.
819 /// </summary>
820 /// <param name="pwszName">The name of the property to get.</param>
821 /// <param name="pvtValue">The value of the property.</param>
822 /// <returns>Standard HRESULT indicating success or failure, including E_NOTFOUND if the property is not defined or E_NOTSUPPORTED if the property type is not supported.</returns>
823 STDMETHOD(GetValue)(
824 _In_ LPCOLESTR pwszName,
825 _Out_ LPVARIANT pvtValue
826 ) = 0;
827 };
828
829#endif
830
831 EXTERN_C const IID IID_ISetupLocalizedPropertyStore;
832
833#if defined(__cplusplus) && !defined(CINTERFACE)
834 /// <summary>
835 /// Provides localized named properties.
836 /// </summary>
837 /// <remarks>
838 /// You can get this from an <see cref="ISetupLocalizedProperties"/>.
839 /// </remarks>
840 struct DECLSPEC_UUID("5BB53126-E0D5-43DF-80F1-6B161E5C6F6C") DECLSPEC_NOVTABLE ISetupLocalizedPropertyStore : public IUnknown
841 {
842 /// <summary>
843 /// Gets an array of property names in this property store.
844 /// </summary>
845 /// <param name="lcid">The LCID for the property names.</param>
846 /// <param name="ppsaNames">Pointer to an array of property names as BSTRs.</param>
847 /// <returns>Standard HRESULT indicating success or failure.</returns>
848 STDMETHOD(GetNames)(
849 _In_ LCID lcid,
850 _Out_ LPSAFEARRAY* ppsaNames
851 ) = 0;
852
853 /// <summary>
854 /// Gets the value of a named property in this property store.
855 /// </summary>
856 /// <param name="pwszName">The name of the property to get.</param>
857 /// <param name="lcid">The LCID for the property.</param>
858 /// <param name="pvtValue">The value of the property.</param>
859 /// <returns>Standard HRESULT indicating success or failure, including E_NOTFOUND if the property is not defined or E_NOTSUPPORTED if the property type is not supported.</returns>
860 STDMETHOD(GetValue)(
861 _In_ LPCOLESTR pwszName,
862 _In_ LCID lcid,
863 _Out_ LPVARIANT pvtValue
864 ) = 0;
865 };
866
867#endif
868
869 // Class declarations
870 //
871 EXTERN_C const CLSID CLSID_SetupConfiguration;
872
873#ifdef __cplusplus
874
875#ifdef __GNUC__
876 __CRT_UUID_DECL(SetupConfiguration, 0x177F0C4A, 0x1CD3, 0x4DE7, 0xA3, 0x2C, 0x71, 0xDB, 0xBB, 0x9F, 0xA3, 0x6D);
877#endif
878
879 /// <summary>
880 /// This class implements <see cref="ISetupConfiguration"/>, <see cref="ISetupConfiguration2"/>, and <see cref="ISetupHelper"/>.
881 /// </summary>
882 class DECLSPEC_UUID("177F0C4A-1CD3-4DE7-A32C-71DBBB9FA36D") SetupConfiguration;
883#endif
884 // Function declarations
885 //
886 /// <summary>
887 /// Gets an <see cref="ISetupConfiguration"/> that provides information about product instances installed on the machine.
888 /// </summary>
889 /// <param name="ppConfiguration">The <see cref="ISetupConfiguration"/> that provides information about product instances installed on the machine.</param>
890 /// <param name="pReserved">Reserved for future use.</param>
891 /// <returns>Standard HRESULT indicating success or failure.</returns>
892 STDMETHODIMP GetSetupConfiguration(
893 _Out_ ISetupConfiguration** ppConfiguration,
894 _Reserved_ LPVOID pReserved
895 );
896
897#ifdef __cplusplus
898}
899#endif
900
901_COM_SMARTPTR_TYPEDEF(ISetupInstance, __uuidof(ISetupInstance));
902_COM_SMARTPTR_TYPEDEF(ISetupInstance2, __uuidof(ISetupInstance2));
903_COM_SMARTPTR_TYPEDEF(IEnumSetupInstances, __uuidof(IEnumSetupInstances));
904_COM_SMARTPTR_TYPEDEF(ISetupConfiguration, __uuidof(ISetupConfiguration));
905_COM_SMARTPTR_TYPEDEF(ISetupConfiguration2, __uuidof(ISetupConfiguration2));
906_COM_SMARTPTR_TYPEDEF(ISetupHelper, __uuidof(ISetupHelper));
907_COM_SMARTPTR_TYPEDEF(ISetupPackageReference, __uuidof(ISetupPackageReference));
908_COM_SMARTPTR_TYPEDEF(ISetupPropertyStore, __uuidof(ISetupPropertyStore));
909_COM_SMARTPTR_TYPEDEF(ISetupInstanceCatalog, __uuidof(ISetupInstanceCatalog));
src/windows_sdk.cpp deleted-387
...@@ -1,387 +0,0 @@
1/*
2 * Copyright (c) 2018 Andrew Kelley
3 *
4 * This file is part of zig, which is MIT licensed.
5 * See http://opensource.org/licenses/MIT
6 */
7
8#include "windows_sdk.h"
9
10#if defined(_WIN32)
11
12#include "windows_com.hpp"
13#include <inttypes.h>
14#include <assert.h>
15
16const char *ZIG_WINDOWS_KIT_REG_KEY = "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots";
17
18struct ZigWindowsSDKPrivate {
19 ZigWindowsSDK base;
20};
21
22enum NativeArch {
23 NativeArchArm,
24 NativeArchx86,
25 NativeArchx86_64,
26 NativeArchAarch64,
27};
28
29#if defined(_M_ARM) || defined(__arm_)
30static const NativeArch native_arch = NativeArchArm;
31#elif defined(_M_IX86) || defined(__i386__)
32static const NativeArch native_arch = NativeArchx86;
33#elif defined(_M_X64) || defined(__x86_64__)
34static const NativeArch native_arch = NativeArchx86_64;
35#elif defined(_M_ARM64) || defined(__aarch64__)
36static const NativeArch native_arch = NativeArchAarch64;
37#else
38#error unsupported architecture
39#endif
40
41void zig_free_windows_sdk(struct ZigWindowsSDK *sdk) {
42 if (sdk == nullptr) {
43 return;
44 }
45 free((void*)sdk->path10_ptr);
46 free((void*)sdk->version10_ptr);
47 free((void*)sdk->path81_ptr);
48 free((void*)sdk->version81_ptr);
49 free((void*)sdk->msvc_lib_dir_ptr);
50}
51
52static ZigFindWindowsSdkError find_msvc_lib_dir(ZigWindowsSDKPrivate *priv) {
53 //COM Smart Pointers requires explicit scope
54 {
55 HRESULT rc = CoInitializeEx(NULL, COINIT_MULTITHREADED);
56 if (rc != S_OK && rc != S_FALSE) {
57 goto com_done;
58 }
59
60 //This COM class is installed when a VS2017
61 ISetupConfigurationPtr setup_config;
62 rc = setup_config.CreateInstance(__uuidof(SetupConfiguration));
63 if (rc != S_OK) {
64 goto com_done;
65 }
66
67 IEnumSetupInstancesPtr all_instances;
68 rc = setup_config->EnumInstances(&all_instances);
69 if (rc != S_OK) {
70 goto com_done;
71 }
72
73 ISetupInstance* curr_instance;
74 ULONG found_inst;
75 while ((rc = all_instances->Next(1, &curr_instance, &found_inst) == S_OK)) {
76 BSTR bstr_inst_path;
77 rc = curr_instance->GetInstallationPath(&bstr_inst_path);
78 if (rc != S_OK) {
79 goto com_done;
80 }
81 //BSTRs are UTF-16 encoded, so we need to convert the string & adjust the length
82 //TODO call an actual function to do this
83 UINT bstr_path_len = *((UINT*)bstr_inst_path - 1);
84 ULONG tmp_path_len = bstr_path_len / 2 + 1;
85 char* conv_path = (char*)bstr_inst_path;
86 // TODO don't use alloca
87 char *tmp_path = (char*)alloca(tmp_path_len);
88 memset(tmp_path, 0, tmp_path_len);
89 uint32_t c = 0;
90 for (uint32_t i = 0; i < bstr_path_len; i += 2) {
91 tmp_path[c] = conv_path[i];
92 ++c;
93 assert(c != tmp_path_len);
94 }
95 char output_path[4096];
96 output_path[0] = 0;
97 char *out_append_ptr = output_path;
98
99 out_append_ptr += sprintf(out_append_ptr, "%s\\", tmp_path);
100
101 char tmp_buf[4096];
102 sprintf(tmp_buf, "%s%s", output_path, "VC\\Auxiliary\\Build\\Microsoft.VCToolsVersion.default.txt");
103 FILE* tools_file = fopen(tmp_buf, "rb");
104 if (!tools_file) {
105 goto com_done;
106 }
107 memset(tmp_path, 0, tmp_path_len);
108 fgets(tmp_path, tmp_path_len, tools_file);
109 strtok(tmp_path, " \r\n");
110 fclose(tools_file);
111 out_append_ptr += sprintf(out_append_ptr, "VC\\Tools\\MSVC\\%s\\lib\\", tmp_path);
112 switch (native_arch) {
113 case NativeArchx86:
114 out_append_ptr += sprintf(out_append_ptr, "x86\\");
115 break;
116 case NativeArchx86_64:
117 out_append_ptr += sprintf(out_append_ptr, "x64\\");
118 break;
119 case NativeArchArm:
120 out_append_ptr += sprintf(out_append_ptr, "arm\\");
121 break;
122 case NativeArchAarch64:
123 out_append_ptr += sprintf(out_append_ptr, "arm64\\");
124 break;
125 }
126 sprintf(tmp_buf, "%s%s", output_path, "vcruntime.lib");
127
128 if (GetFileAttributesA(tmp_buf) != INVALID_FILE_ATTRIBUTES) {
129 priv->base.msvc_lib_dir_ptr = strdup(output_path);
130 if (priv->base.msvc_lib_dir_ptr == nullptr) {
131 return ZigFindWindowsSdkErrorOutOfMemory;
132 }
133 priv->base.msvc_lib_dir_len = strlen(priv->base.msvc_lib_dir_ptr);
134 return ZigFindWindowsSdkErrorNone;
135 }
136 }
137 }
138
139com_done:;
140 HKEY key;
141 HRESULT rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7", 0,
142 KEY_QUERY_VALUE | KEY_WOW64_32KEY, &key);
143 if (rc != ERROR_SUCCESS) {
144 return ZigFindWindowsSdkErrorNotFound;
145 }
146
147 DWORD dw_type = 0;
148 DWORD cb_data = 0;
149 rc = RegQueryValueEx(key, "14.0", NULL, &dw_type, NULL, &cb_data);
150 if ((rc == ERROR_FILE_NOT_FOUND) || (REG_SZ != dw_type)) {
151 return ZigFindWindowsSdkErrorNotFound;
152 }
153
154 char tmp_buf[4096];
155
156 RegQueryValueExA(key, "14.0", NULL, NULL, (LPBYTE)tmp_buf, &cb_data);
157 // RegQueryValueExA returns the length of the string INCLUDING the null terminator
158 char *tmp_buf_append_ptr = tmp_buf + (cb_data - 1);
159 tmp_buf_append_ptr += sprintf(tmp_buf_append_ptr, "VC\\Lib\\");
160 switch (native_arch) {
161 case NativeArchx86:
162 //x86 is in the root of the Lib folder
163 break;
164 case NativeArchx86_64:
165 tmp_buf_append_ptr += sprintf(tmp_buf_append_ptr, "amd64\\");
166 break;
167 case NativeArchArm:
168 tmp_buf_append_ptr += sprintf(tmp_buf_append_ptr, "arm\\");
169 break;
170 case NativeArchAarch64:
171 tmp_buf_append_ptr += sprintf(tmp_buf_append_ptr, "arm64\\");
172 break;
173 }
174
175 char *output_path = strdup(tmp_buf);
176 if (output_path == nullptr) {
177 return ZigFindWindowsSdkErrorOutOfMemory;
178 }
179
180 tmp_buf_append_ptr += sprintf(tmp_buf_append_ptr, "vcruntime.lib");
181
182 if (GetFileAttributesA(tmp_buf) != INVALID_FILE_ATTRIBUTES) {
183 priv->base.msvc_lib_dir_ptr = output_path;
184 priv->base.msvc_lib_dir_len = strlen(output_path);
185 return ZigFindWindowsSdkErrorNone;
186 } else {
187 free(output_path);
188 return ZigFindWindowsSdkErrorNotFound;
189 }
190}
191
192static ZigFindWindowsSdkError find_10_version(ZigWindowsSDKPrivate *priv) {
193 if (priv->base.path10_ptr == nullptr) {
194 return ZigFindWindowsSdkErrorNone;
195 }
196
197 char reg_query[MAX_PATH] = { 0 };
198 int n = snprintf(reg_query, MAX_PATH, "%s\\%s.0\\Installed Options", ZIG_WINDOWS_KIT_REG_KEY, priv->base.version10_ptr);
199 if (n < 0 || n >= MAX_PATH) {
200 return ZigFindWindowsSdkErrorPathTooLong;
201 }
202
203 HKEY options_key;
204 HRESULT rc;
205 rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, reg_query, 0,
206 KEY_QUERY_VALUE | KEY_WOW64_32KEY | KEY_ENUMERATE_SUB_KEYS, &options_key);
207 if (rc != ERROR_SUCCESS) {
208 return ZigFindWindowsSdkErrorNotFound;
209 }
210
211 const char *option_name = nullptr;
212 switch (native_arch) {
213 case NativeArchArm:
214 option_name = "OptionId.DesktopCPParm";
215 break;
216 case NativeArchAarch64:
217 option_name = "OptionId.DesktopCPParm64";
218 break;
219 case NativeArchx86_64:
220 option_name = "OptionId.DesktopCPPx64";
221 break;
222 case NativeArchx86:
223 option_name = "OptionId.DesktopCPPx86";
224 break;
225 default:
226 return ZigFindWindowsSdkErrorNotFound;
227 }
228
229 DWORD val_sz = sizeof(DWORD);
230 DWORD reg_val = 0;
231 DWORD type = REG_DWORD;
232 rc = RegQueryValueEx(options_key, option_name, NULL, &type, (LPBYTE)&reg_val, &val_sz);
233 if (rc != ERROR_SUCCESS || reg_val != 1) {
234 return ZigFindWindowsSdkErrorNotFound;
235 }
236 return ZigFindWindowsSdkErrorNone;
237}
238
239static ZigFindWindowsSdkError find_81_version(ZigWindowsSDKPrivate *priv) {
240 if (priv->base.path81_ptr == nullptr) {
241 return ZigFindWindowsSdkErrorNone;
242 }
243
244 char sdk_lib_dir[4096];
245 int n = snprintf(sdk_lib_dir, 4096, "%s\\Lib\\winv*", priv->base.path81_ptr);
246 if (n < 0 || n >= 4096) {
247 return ZigFindWindowsSdkErrorPathTooLong;
248 }
249
250 // enumerate files in sdk path looking for latest version
251 WIN32_FIND_DATA ffd;
252 HANDLE hFind = FindFirstFileA(sdk_lib_dir, &ffd);
253 if (hFind == INVALID_HANDLE_VALUE) {
254 return ZigFindWindowsSdkErrorNotFound;
255 }
256 int v0 = 0, v1 = 0;
257 for (;;) {
258 if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
259 int c0 = 0, c1 = 0;
260 sscanf(ffd.cFileName, "winv%d.%d", &c0, &c1);
261
262 if ( (c0 > v0) || (c0 == v0 && c1 > v1) ) {
263 v0 = c0, v1 = c1;
264 free((void*)priv->base.version81_ptr);
265 priv->base.version81_ptr = strdup(ffd.cFileName);
266 if (priv->base.version81_ptr == nullptr) {
267 FindClose(hFind);
268 return ZigFindWindowsSdkErrorOutOfMemory;
269 }
270 }
271 }
272 if (FindNextFile(hFind, &ffd) == 0) {
273 FindClose(hFind);
274 break;
275 }
276 }
277 priv->base.version81_len = strlen(priv->base.version81_ptr);
278 return ZigFindWindowsSdkErrorNone;
279}
280
281ZigFindWindowsSdkError zig_find_windows_sdk(struct ZigWindowsSDK **out_sdk) {
282 ZigWindowsSDKPrivate *priv = (ZigWindowsSDKPrivate*)calloc(1, sizeof(ZigWindowsSDKPrivate));
283 if (priv == nullptr) {
284 return ZigFindWindowsSdkErrorOutOfMemory;
285 }
286
287 HRESULT rc;
288
289 //note(dimenus): If this key doesn't exist, neither the Win 8 SDK nor the Win 10 SDK is installed
290 HKEY roots_key;
291 rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, ZIG_WINDOWS_KIT_REG_KEY, 0,
292 KEY_QUERY_VALUE | KEY_WOW64_32KEY | KEY_ENUMERATE_SUB_KEYS, &roots_key);
293 if (rc != ERROR_SUCCESS) {
294 zig_free_windows_sdk(&priv->base);
295 return ZigFindWindowsSdkErrorNotFound;
296 }
297
298 {
299 HKEY v10_key;
300 rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v10.0", 0,
301 KEY_QUERY_VALUE | KEY_WOW64_32KEY | KEY_ENUMERATE_SUB_KEYS, &v10_key);
302 if (rc != ERROR_SUCCESS) {
303 goto find_win10_sdk_done;
304 }
305
306 DWORD tmp_buf_len = MAX_PATH;
307 priv->base.path10_ptr = (const char *)calloc(tmp_buf_len, 1);
308 if (priv->base.path10_ptr == nullptr) {
309 zig_free_windows_sdk(&priv->base);
310 return ZigFindWindowsSdkErrorOutOfMemory;
311 }
312 rc = RegQueryValueEx(v10_key, "InstallationFolder", NULL, NULL, (LPBYTE)priv->base.path10_ptr, &tmp_buf_len);
313 if (rc == ERROR_SUCCESS) {
314 priv->base.path10_len = tmp_buf_len - 1;
315 if (priv->base.path10_ptr[priv->base.path10_len - 1] == '\\') {
316 priv->base.path10_len -= 1;
317 }
318 } else {
319 free((void*)priv->base.path10_ptr);
320 priv->base.path10_ptr = nullptr;
321 }
322
323 priv->base.version10_ptr = (const char*)calloc(tmp_buf_len, 1);
324 rc = RegQueryValueEx(v10_key, "ProductVersion", NULL, NULL, (LPBYTE)priv->base.version10_ptr, &tmp_buf_len);
325 if (rc == ERROR_SUCCESS) {
326 snprintf((char*)priv->base.version10_ptr, MAX_PATH, "%s.0", priv->base.version10_ptr);
327 priv->base.version10_len = tmp_buf_len - 1 + 2; // note(dimenus): Microsoft doesn't include the .0 in the ProductVersion key....
328 } else {
329 free((void*)priv->base.version10_ptr);
330 priv->base.version10_ptr = nullptr;
331 }
332 }
333 find_win10_sdk_done:
334 {
335 DWORD tmp_buf_len = MAX_PATH;
336 priv->base.path81_ptr = (const char *)calloc(tmp_buf_len, 1);
337 if (priv->base.path81_ptr == nullptr) {
338 zig_free_windows_sdk(&priv->base);
339 return ZigFindWindowsSdkErrorOutOfMemory;
340 }
341 rc = RegQueryValueEx(roots_key, "KitsRoot81", NULL, NULL, (LPBYTE)priv->base.path81_ptr, &tmp_buf_len);
342 if (rc == ERROR_SUCCESS) {
343 priv->base.path81_len = tmp_buf_len - 1;
344 if (priv->base.path81_ptr[priv->base.path81_len - 1] == '\\') {
345 priv->base.path81_len -= 1;
346 }
347 } else {
348 free((void*)priv->base.path81_ptr);
349 priv->base.path81_ptr = nullptr;
350 }
351 }
352
353 {
354 ZigFindWindowsSdkError err = find_10_version(priv);
355 if (err == ZigFindWindowsSdkErrorOutOfMemory) {
356 zig_free_windows_sdk(&priv->base);
357 return err;
358 }
359 }
360 {
361 ZigFindWindowsSdkError err = find_81_version(priv);
362 if (err == ZigFindWindowsSdkErrorOutOfMemory) {
363 zig_free_windows_sdk(&priv->base);
364 return err;
365 }
366 }
367
368 {
369 ZigFindWindowsSdkError err = find_msvc_lib_dir(priv);
370 if (err == ZigFindWindowsSdkErrorOutOfMemory) {
371 zig_free_windows_sdk(&priv->base);
372 return err;
373 }
374 }
375
376 *out_sdk = &priv->base;
377 return ZigFindWindowsSdkErrorNone;
378}
379
380#else
381
382void zig_free_windows_sdk(struct ZigWindowsSDK *sdk) {}
383ZigFindWindowsSdkError zig_find_windows_sdk(struct ZigWindowsSDK **out_sdk) {
384 return ZigFindWindowsSdkErrorNotFound;
385}
386
387#endif
src/windows_sdk.h deleted-51
...@@ -1,51 +0,0 @@
1/*
2 * Copyright (c) 2018 Andrew Kelley
3 *
4 * This file is part of zig, which is MIT licensed.
5 * See http://opensource.org/licenses/MIT
6 */
7
8#ifndef ZIG_WINDOWS_SDK_H
9#define ZIG_WINDOWS_SDK_H
10
11#ifdef __cplusplus
12#define ZIG_EXTERN_C extern "C"
13#else
14#define ZIG_EXTERN_C
15#endif
16
17#include <stddef.h>
18
19// ABI warning - src/windows_sdk.zig
20struct ZigWindowsSDK {
21 const char *path10_ptr;
22 size_t path10_len;
23
24 const char *version10_ptr;
25 size_t version10_len;
26
27 const char *path81_ptr;
28 size_t path81_len;
29
30 const char *version81_ptr;
31 size_t version81_len;
32
33 const char *msvc_lib_dir_ptr;
34 size_t msvc_lib_dir_len;
35};
36
37// ABI warning - src/windows_sdk.zig
38enum ZigFindWindowsSdkError {
39 ZigFindWindowsSdkErrorNone,
40 ZigFindWindowsSdkErrorOutOfMemory,
41 ZigFindWindowsSdkErrorNotFound,
42 ZigFindWindowsSdkErrorPathTooLong,
43};
44
45// ABI warning - src/windows_sdk.zig
46ZIG_EXTERN_C enum ZigFindWindowsSdkError zig_find_windows_sdk(struct ZigWindowsSDK **out_sdk);
47
48// ABI warning - src/windows_sdk.zig
49ZIG_EXTERN_C void zig_free_windows_sdk(struct ZigWindowsSDK *sdk);
50
51#endif
src/windows_sdk.zig+754-26
...@@ -1,27 +1,755 @@...@@ -1,27 +1,755 @@
1// C API bindings for src/windows_sdk.h1const std = @import("std");
22const builtin = @import("builtin");
3pub const ZigWindowsSDK = extern struct {3
4 path10_ptr: ?[*]const u8,4const windows = std.os.windows;
5 path10_len: usize,5const RRF = windows.advapi32.RRF;
6 version10_ptr: ?[*]const u8,6
7 version10_len: usize,7const WINDOWS_KIT_REG_KEY = "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots";
8 path81_ptr: ?[*]const u8,8
9 path81_len: usize,9// https://learn.microsoft.com/en-us/windows/win32/msi/productversion
10 version81_ptr: ?[*]const u8,10const version_major_minor_max_length = "255.255".len;
11 version81_len: usize,11// note(bratishkaerik): i think ProductVersion in registry (created by Visual Studio installer) also follows this rule
12 msvc_lib_dir_ptr: ?[*]const u8,12const product_version_max_length = version_major_minor_max_length + ".65535".len;
13 msvc_lib_dir_len: usize,13
1414/// Iterates via `iterator` and collects all folders with names starting with `optional_prefix`
15 pub const find = zig_find_windows_sdk;15/// and similar to SemVer. Returns slice of folder names sorted in descending order.
16 pub const free = zig_free_windows_sdk;16/// Caller owns result.
1717fn iterateAndFilterBySemVer(iterator: *std.fs.IterableDir.Iterator, allocator: std.mem.Allocator, comptime optional_prefix: ?[]const u8) error{ OutOfMemory, VersionNotFound }![][]const u8 {
18 pub const FindError = enum(c_int) {18 var dirs_filtered_list = std.ArrayList([]const u8).init(allocator);
19 None,19 errdefer {
20 OutOfMemory,20 for (dirs_filtered_list.items) |filtered_dir| allocator.free(filtered_dir);
21 NotFound,21 dirs_filtered_list.deinit();
22 PathTooLong,22 }
23 };23
2424 var normalized_name_buf: [std.fs.MAX_NAME_BYTES + ".0+build.0".len]u8 = undefined;
25 extern fn zig_find_windows_sdk(out_sdk: **ZigWindowsSDK) FindError;25 var normalized_name_fbs = std.io.fixedBufferStream(&normalized_name_buf);
26 extern fn zig_free_windows_sdk(sdk: *ZigWindowsSDK) void;26 const normalized_name_w = normalized_name_fbs.writer();
27 iterate_folder: while (true) : (normalized_name_fbs.reset()) {
28 const maybe_entry = iterator.next() catch continue :iterate_folder;
29 const entry = maybe_entry orelse break :iterate_folder;
30
31 if (entry.kind != .directory)
32 continue :iterate_folder;
33
34 // invalidated on next iteration
35 const subfolder_name = blk: {
36 if (comptime optional_prefix) |prefix| {
37 if (!std.mem.startsWith(u8, entry.name, prefix)) continue :iterate_folder;
38 break :blk entry.name[prefix.len..];
39 } else break :blk entry.name;
40 };
41
42 { // check if subfolder name looks similar to SemVer
43 switch (std.mem.count(u8, subfolder_name, ".")) {
44 0 => normalized_name_w.print("{s}.0.0+build.0", .{subfolder_name}) catch unreachable, // 17 => 17.0.0+build.0
45 1 => if (std.mem.indexOfScalar(u8, subfolder_name, '_')) |underscore_pos| blk: { // 17.0_9e9cbb98 => 17.0.1+build.9e9cbb98
46 var subfolder_name_tmp_copy_buf: [std.fs.MAX_NAME_BYTES]u8 = undefined;
47 const subfolder_name_tmp_copy = subfolder_name_tmp_copy_buf[0..subfolder_name.len];
48 @memcpy(subfolder_name_tmp_copy, subfolder_name);
49
50 subfolder_name_tmp_copy[underscore_pos] = '.'; // 17.0_9e9cbb98 => 17.0.9e9cbb98
51 var subfolder_name_parts = std.mem.splitScalar(u8, subfolder_name_tmp_copy, '.'); // [ 17, 0, 9e9cbb98 ]
52
53 const first = subfolder_name_parts.first(); // 17
54 const second = subfolder_name_parts.next().?; // 0
55 const third = subfolder_name_parts.rest(); // 9e9cbb98
56
57 break :blk normalized_name_w.print("{s}.{s}.1+build.{s}", .{ first, second, third }) catch unreachable; // [ 17, 0, 9e9cbb98 ] => 17.0.1+build.9e9cbb98
58 } else normalized_name_w.print("{s}.0+build.0", .{subfolder_name}) catch unreachable, // 17.0 => 17.0.0+build.0
59 else => normalized_name_w.print("{s}+build.0", .{subfolder_name}) catch unreachable, // 17.0.0 => 17.0.0+build.0
60 }
61 const subfolder_name_normalized: []const u8 = normalized_name_fbs.getWritten();
62 const sem_ver = std.SemanticVersion.parse(subfolder_name_normalized);
63 _ = sem_ver catch continue :iterate_folder;
64 }
65 // entry.name passed check
66
67 const subfolder_name_allocated = try allocator.dupe(u8, subfolder_name);
68 errdefer allocator.free(subfolder_name_allocated);
69 try dirs_filtered_list.append(subfolder_name_allocated);
70 }
71
72 var dirs_filtered_slice = try dirs_filtered_list.toOwnedSlice();
73 // Keep in mind that order of these names is not guaranteed by Windows,
74 // so we cannot just reverse or "while (popOrNull())" this ArrayList.
75 std.mem.sortUnstable([]const u8, dirs_filtered_slice, {}, struct {
76 fn desc(_: void, lhs: []const u8, rhs: []const u8) bool {
77 return std.mem.order(u8, lhs, rhs) == .gt;
78 }
79 }.desc);
80 return dirs_filtered_slice;
81}
82
83const RegistryUtf8 = struct {
84 key: windows.HKEY,
85
86 /// Assert that `key` is valid UTF-8 string
87 pub fn openKey(key: []const u8) error{KeyNotFound}!RegistryUtf8 {
88 const key_utf16le: [:0]const u16 = key_utf16le: {
89 var key_utf16le_buf: [RegistryUtf16Le.key_name_max_len]u16 = undefined;
90 const key_utf16le_len: usize = std.unicode.utf8ToUtf16Le(key_utf16le_buf[0..], key) catch |err| switch (err) {
91 error.InvalidUtf8 => unreachable,
92 };
93 key_utf16le_buf[key_utf16le_len] = 0;
94 break :key_utf16le key_utf16le_buf[0..key_utf16le_len :0];
95 };
96
97 const registry_utf16le = try RegistryUtf16Le.openKey(key_utf16le);
98 return RegistryUtf8{ .key = registry_utf16le.key };
99 }
100
101 /// Closes key, after that usage is invalid
102 pub fn closeKey(self: *const RegistryUtf8) void {
103 const return_code_int: windows.HRESULT = windows.advapi32.RegCloseKey(self.key);
104 const return_code: windows.Win32Error = @enumFromInt(return_code_int);
105 switch (return_code) {
106 .SUCCESS => {},
107 else => {},
108 }
109 }
110
111 /// Get string from registry.
112 /// Caller owns result.
113 pub fn getString(self: *const RegistryUtf8, allocator: std.mem.Allocator, subkey: []const u8, value_name: []const u8) error{ OutOfMemory, ValueNameNotFound, NotAString, StringNotFound }![]u8 {
114 const subkey_utf16le: [:0]const u16 = subkey_utf16le: {
115 var subkey_utf16le_buf: [RegistryUtf16Le.key_name_max_len]u16 = undefined;
116 const subkey_utf16le_len: usize = std.unicode.utf8ToUtf16Le(subkey_utf16le_buf[0..], subkey) catch unreachable;
117 subkey_utf16le_buf[subkey_utf16le_len] = 0;
118 break :subkey_utf16le subkey_utf16le_buf[0..subkey_utf16le_len :0];
119 };
120
121 const value_name_utf16le: [:0]const u16 = value_name_utf16le: {
122 var value_name_utf16le_buf: [RegistryUtf16Le.value_name_max_len]u16 = undefined;
123 const value_name_utf16le_len: usize = std.unicode.utf8ToUtf16Le(value_name_utf16le_buf[0..], value_name) catch unreachable;
124 value_name_utf16le_buf[value_name_utf16le_len] = 0;
125 break :value_name_utf16le value_name_utf16le_buf[0..value_name_utf16le_len :0];
126 };
127
128 const registry_utf16le = RegistryUtf16Le{ .key = self.key };
129 const value_utf16le = try registry_utf16le.getString(allocator, subkey_utf16le, value_name_utf16le);
130 defer allocator.free(value_utf16le);
131
132 var value_utf8: []u8 = std.unicode.utf16leToUtf8Alloc(allocator, value_utf16le) catch |err| switch (err) {
133 error.OutOfMemory => return error.OutOfMemory,
134 else => return error.StringNotFound,
135 };
136 errdefer allocator.free(value_utf8);
137
138 return value_utf8;
139 }
140
141 /// Get DWORD (u32) from registry.
142 pub fn getDword(self: *const RegistryUtf8, subkey: []const u8, value_name: []const u8) error{ ValueNameNotFound, NotADword, DwordTooLong, DwordNotFound }!u32 {
143 const subkey_utf16le: [:0]const u16 = subkey_utf16le: {
144 var subkey_utf16le_buf: [RegistryUtf16Le.key_name_max_len]u16 = undefined;
145 const subkey_utf16le_len: usize = std.unicode.utf8ToUtf16Le(subkey_utf16le_buf[0..], subkey) catch unreachable;
146 subkey_utf16le_buf[subkey_utf16le_len] = 0;
147 break :subkey_utf16le subkey_utf16le_buf[0..subkey_utf16le_len :0];
148 };
149
150 const value_name_utf16le: [:0]const u16 = value_name_utf16le: {
151 var value_name_utf16le_buf: [RegistryUtf16Le.value_name_max_len]u16 = undefined;
152 const value_name_utf16le_len: usize = std.unicode.utf8ToUtf16Le(value_name_utf16le_buf[0..], value_name) catch unreachable;
153 value_name_utf16le_buf[value_name_utf16le_len] = 0;
154 break :value_name_utf16le value_name_utf16le_buf[0..value_name_utf16le_len :0];
155 };
156
157 const registry_utf16le = RegistryUtf16Le{ .key = self.key };
158 return try registry_utf16le.getDword(subkey_utf16le, value_name_utf16le);
159 }
160
161 /// Under private space with flags:
162 /// KEY_QUERY_VALUE and KEY_ENUMERATE_SUB_KEYS.
163 /// After finishing work, call `closeKey`.
164 pub fn loadFromPath(absolute_path: []const u8) error{KeyNotFound}!RegistryUtf8 {
165 const absolute_path_utf16le: [:0]const u16 = absolute_path_utf16le: {
166 var absolute_path_utf16le_buf: [RegistryUtf16Le.value_name_max_len]u16 = undefined;
167 const absolute_path_utf16le_len: usize = std.unicode.utf8ToUtf16Le(absolute_path_utf16le_buf[0..], absolute_path) catch unreachable;
168 absolute_path_utf16le_buf[absolute_path_utf16le_len] = 0;
169 break :absolute_path_utf16le absolute_path_utf16le_buf[0..absolute_path_utf16le_len :0];
170 };
171
172 const registry_utf16le = try RegistryUtf16Le.loadFromPath(absolute_path_utf16le);
173 return RegistryUtf8{ .key = registry_utf16le.key };
174 }
175};
176
177const RegistryUtf16Le = struct {
178 key: windows.HKEY,
179
180 /// Includes root key (f.e. HKEY_LOCAL_MACHINE).
181 /// https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry-element-size-limits
182 pub const key_name_max_len = 255;
183 /// In Unicode characters.
184 /// https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry-element-size-limits
185 pub const value_name_max_len = 16_383;
186
187 /// Under HKEY_LOCAL_MACHINE with flags:
188 /// KEY_QUERY_VALUE, KEY_WOW64_32KEY, and KEY_ENUMERATE_SUB_KEYS.
189 /// After finishing work, call `closeKey`.
190 fn openKey(key_utf16le: [:0]const u16) error{KeyNotFound}!RegistryUtf16Le {
191 var key: windows.HKEY = undefined;
192 const return_code_int: windows.HRESULT = windows.advapi32.RegOpenKeyExW(
193 windows.HKEY_LOCAL_MACHINE,
194 key_utf16le,
195 0,
196 windows.KEY_QUERY_VALUE | windows.KEY_WOW64_32KEY | windows.KEY_ENUMERATE_SUB_KEYS,
197 &key,
198 );
199 const return_code: windows.Win32Error = @enumFromInt(return_code_int);
200 switch (return_code) {
201 .SUCCESS => {},
202 .FILE_NOT_FOUND => return error.KeyNotFound,
203
204 else => return error.KeyNotFound,
205 }
206 return RegistryUtf16Le{ .key = key };
207 }
208
209 /// Closes key, after that usage is invalid
210 fn closeKey(self: *const RegistryUtf16Le) void {
211 const return_code_int: windows.HRESULT = windows.advapi32.RegCloseKey(self.key);
212 const return_code: windows.Win32Error = @enumFromInt(return_code_int);
213 switch (return_code) {
214 .SUCCESS => {},
215 else => {},
216 }
217 }
218
219 /// Get string ([:0]const u16) from registry.
220 fn getString(self: *const RegistryUtf16Le, allocator: std.mem.Allocator, subkey_utf16le: [:0]const u16, value_name_utf16le: [:0]const u16) error{ OutOfMemory, ValueNameNotFound, NotAString, StringNotFound }![]const u16 {
221 var actual_type: windows.ULONG = undefined;
222
223 // Calculating length to allocate
224 var value_utf16le_buf_size: u32 = 0; // in bytes, including any terminating NUL character or characters.
225 var return_code_int: windows.HRESULT = windows.advapi32.RegGetValueW(
226 self.key,
227 subkey_utf16le,
228 value_name_utf16le,
229 RRF.RT_REG_SZ,
230 &actual_type,
231 null,
232 &value_utf16le_buf_size,
233 );
234
235 // Check returned code and type
236 var return_code: windows.Win32Error = @enumFromInt(return_code_int);
237 switch (return_code) {
238 .SUCCESS => std.debug.assert(value_utf16le_buf_size != 0),
239 .MORE_DATA => unreachable, // We are only reading length
240 .FILE_NOT_FOUND => return error.ValueNameNotFound,
241 .INVALID_PARAMETER => unreachable, // We didn't combine RRF.SUBKEY_WOW6464KEY and RRF.SUBKEY_WOW6432KEY
242 else => return error.StringNotFound,
243 }
244 switch (actual_type) {
245 windows.REG.SZ => {},
246 else => return error.NotAString,
247 }
248
249 var value_utf16le_buf: []u16 = try allocator.alloc(u16, std.math.divCeil(u32, value_utf16le_buf_size, 2) catch unreachable);
250 errdefer allocator.free(value_utf16le_buf);
251
252 return_code_int = windows.advapi32.RegGetValueW(
253 self.key,
254 subkey_utf16le,
255 value_name_utf16le,
256 RRF.RT_REG_SZ,
257 &actual_type,
258 value_utf16le_buf.ptr,
259 &value_utf16le_buf_size,
260 );
261
262 // Check returned code and (just in case) type again.
263 return_code = @enumFromInt(return_code_int);
264 switch (return_code) {
265 .SUCCESS => {},
266 .MORE_DATA => unreachable, // Calculated first time length should be enough, even overestimated
267 .FILE_NOT_FOUND => return error.ValueNameNotFound,
268 .INVALID_PARAMETER => unreachable, // We didn't combine RRF.SUBKEY_WOW6464KEY and RRF.SUBKEY_WOW6432KEY
269 else => return error.StringNotFound,
270 }
271 switch (actual_type) {
272 windows.REG.SZ => {},
273 else => return error.NotAString,
274 }
275
276 const value_utf16le: []const u16 = value_utf16le: {
277 // note(bratishkaerik): somehow returned value in `buf_len` is overestimated by Windows and contains extra space
278 // we will just search for zero termination and forget length
279 // Windows sure is strange
280 const value_utf16le_overestimated: [*:0]const u16 = @ptrCast(value_utf16le_buf.ptr);
281 break :value_utf16le std.mem.span(value_utf16le_overestimated);
282 };
283
284 _ = allocator.resize(value_utf16le_buf, value_utf16le.len);
285 return value_utf16le;
286 }
287
288 /// Get DWORD (u32) from registry.
289 fn getDword(self: *const RegistryUtf16Le, subkey_utf16le: [:0]const u16, value_name_utf16le: [:0]const u16) error{ ValueNameNotFound, NotADword, DwordTooLong, DwordNotFound }!u32 {
290 var actual_type: windows.ULONG = undefined;
291 var reg_size: u32 = @sizeOf(u32);
292 var reg_value: u32 = 0;
293
294 const return_code_int: windows.HRESULT = windows.advapi32.RegGetValueW(
295 self.key,
296 subkey_utf16le,
297 value_name_utf16le,
298 RRF.RT_REG_DWORD,
299 &actual_type,
300 &reg_value,
301 &reg_size,
302 );
303 const return_code: windows.Win32Error = @enumFromInt(return_code_int);
304 switch (return_code) {
305 .SUCCESS => {},
306 .MORE_DATA => return error.DwordTooLong,
307 .FILE_NOT_FOUND => return error.ValueNameNotFound,
308 .INVALID_PARAMETER => unreachable, // We didn't combine RRF.SUBKEY_WOW6464KEY and RRF.SUBKEY_WOW6432KEY
309 else => return error.DwordNotFound,
310 }
311
312 switch (actual_type) {
313 windows.REG.DWORD => {},
314 else => return error.NotADword,
315 }
316
317 return reg_value;
318 }
319
320 /// Under private space with flags:
321 /// KEY_QUERY_VALUE and KEY_ENUMERATE_SUB_KEYS.
322 /// After finishing work, call `closeKey`.
323 fn loadFromPath(absolute_path_as_utf16le: [:0]const u16) error{KeyNotFound}!RegistryUtf16Le {
324 var key: windows.HKEY = undefined;
325
326 const return_code_int: windows.HRESULT = std.os.windows.advapi32.RegLoadAppKeyW(
327 absolute_path_as_utf16le,
328 &key,
329 windows.KEY_QUERY_VALUE | windows.KEY_ENUMERATE_SUB_KEYS,
330 0,
331 0,
332 );
333 const return_code: windows.Win32Error = @enumFromInt(return_code_int);
334 switch (return_code) {
335 .SUCCESS => {},
336 else => return error.KeyNotFound,
337 }
338
339 return RegistryUtf16Le{ .key = key };
340 }
341};
342
343pub const Windows10Sdk = struct {
344 path: []const u8,
345 version: []const u8,
346
347 /// Find path and version of Windows 10 SDK.
348 /// Caller owns the result's fields.
349 /// After finishing work, call `free(allocator)`.
350 fn find(allocator: std.mem.Allocator) error{ OutOfMemory, Windows10SdkNotFound, PathTooLong, VersionTooLong }!Windows10Sdk {
351 const v10_key = RegistryUtf8.openKey("SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v10.0") catch |err| switch (err) {
352 error.KeyNotFound => return error.Windows10SdkNotFound,
353 };
354 defer v10_key.closeKey();
355
356 const path: []const u8 = path10: {
357 var path_maybe_with_trailing_slash = v10_key.getString(allocator, "", "InstallationFolder") catch |err| switch (err) {
358 error.NotAString => return error.Windows10SdkNotFound,
359 error.ValueNameNotFound => return error.Windows10SdkNotFound,
360 error.StringNotFound => return error.Windows10SdkNotFound,
361
362 error.OutOfMemory => return error.OutOfMemory,
363 };
364
365 if (path_maybe_with_trailing_slash.len > std.fs.MAX_PATH_BYTES or !std.fs.path.isAbsolute(path_maybe_with_trailing_slash)) {
366 allocator.free(path_maybe_with_trailing_slash);
367 return error.PathTooLong;
368 }
369
370 var path = std.ArrayList(u8).fromOwnedSlice(allocator, path_maybe_with_trailing_slash);
371 errdefer path.deinit();
372
373 // String might contain trailing slash, so trim it here
374 if (path.items.len > "C:\\".len and path.getLast() == '\\') _ = path.pop();
375
376 const path_without_trailing_slash = try path.toOwnedSlice();
377 break :path10 path_without_trailing_slash;
378 };
379 errdefer allocator.free(path);
380
381 const version: []const u8 = version10: {
382
383 // note(dimenus): Microsoft doesn't include the .0 in the ProductVersion key....
384 var version_without_0 = v10_key.getString(allocator, "", "ProductVersion") catch |err| switch (err) {
385 error.NotAString => return error.Windows10SdkNotFound,
386 error.ValueNameNotFound => return error.Windows10SdkNotFound,
387 error.StringNotFound => return error.Windows10SdkNotFound,
388
389 error.OutOfMemory => return error.OutOfMemory,
390 };
391 if (version_without_0.len + ".0".len > product_version_max_length) {
392 allocator.free(version_without_0);
393 return error.VersionTooLong;
394 }
395
396 var version = std.ArrayList(u8).fromOwnedSlice(allocator, version_without_0);
397 errdefer version.deinit();
398
399 try version.appendSlice(".0");
400
401 const version_with_0 = try version.toOwnedSlice();
402 break :version10 version_with_0;
403 };
404 errdefer allocator.free(version);
405
406 return Windows10Sdk{ .path = path, .version = version };
407 }
408
409 /// Check whether this version is enumerated in registry.
410 fn isValidVersion(windows10sdk: *const Windows10Sdk) bool {
411 var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
412 const reg_query_as_utf8 = std.fmt.bufPrint(buf[0..], "{s}\\{s}\\Installed Options", .{ WINDOWS_KIT_REG_KEY, windows10sdk.version }) catch |err| switch (err) {
413 error.NoSpaceLeft => return false,
414 };
415
416 const options_key = RegistryUtf8.openKey(reg_query_as_utf8) catch |err| switch (err) {
417 error.KeyNotFound => return false,
418 };
419 defer options_key.closeKey();
420
421 const option_name = comptime switch (builtin.target.cpu.arch) {
422 .arm, .armeb => "OptionId.DesktopCPParm",
423 .aarch64 => "OptionId.DesktopCPParm64",
424 .x86_64 => "OptionId.DesktopCPPx64",
425 .x86 => "OptionId.DesktopCPPx86",
426 else => |tag| @compileError("Windows 10 SDK cannot be detected on architecture " ++ tag),
427 };
428
429 const reg_value = options_key.getDword("", option_name) catch return false;
430 return (reg_value == 1);
431 }
432
433 fn free(self: *const Windows10Sdk, allocator: std.mem.Allocator) void {
434 allocator.free(self.path);
435 allocator.free(self.version);
436 }
437};
438
439pub const Windows81Sdk = struct {
440 path: []const u8,
441 version: []const u8,
442
443 /// Find path and version of Windows 8.1 SDK.
444 /// Caller owns the result's fields.
445 /// After finishing work, call `free(allocator)`.
446 fn find(allocator: std.mem.Allocator, roots_key: *const RegistryUtf8) error{ OutOfMemory, Windows81SdkNotFound, PathTooLong, VersionTooLong }!Windows81Sdk {
447 const path: []const u8 = path81: {
448 var path_maybe_with_trailing_slash = roots_key.getString(allocator, "", "KitsRoot81") catch |err| switch (err) {
449 error.NotAString => return error.Windows81SdkNotFound,
450 error.ValueNameNotFound => return error.Windows81SdkNotFound,
451 error.StringNotFound => return error.Windows81SdkNotFound,
452
453 error.OutOfMemory => return error.OutOfMemory,
454 };
455 if (path_maybe_with_trailing_slash.len > std.fs.MAX_PATH_BYTES or !std.fs.path.isAbsolute(path_maybe_with_trailing_slash)) {
456 allocator.free(path_maybe_with_trailing_slash);
457 return error.PathTooLong;
458 }
459
460 var path = std.ArrayList(u8).fromOwnedSlice(allocator, path_maybe_with_trailing_slash);
461 errdefer path.deinit();
462
463 // String might contain trailing slash, so trim it here
464 if (path.items.len > "C:\\".len and path.getLast() == '\\') _ = path.pop();
465
466 const path_without_trailing_slash = try path.toOwnedSlice();
467 break :path81 path_without_trailing_slash;
468 };
469 errdefer allocator.free(path);
470
471 const version: []const u8 = version81: {
472 var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
473 const sdk_lib_dir_path = std.fmt.bufPrint(buf[0..], "{s}\\Lib\\", .{path}) catch |err| switch (err) {
474 error.NoSpaceLeft => return error.PathTooLong,
475 };
476 if (!std.fs.path.isAbsolute(sdk_lib_dir_path)) return error.Windows81SdkNotFound;
477
478 // enumerate files in sdk path looking for latest version
479 var sdk_lib_dir = std.fs.openIterableDirAbsolute(sdk_lib_dir_path, .{}) catch |err| switch (err) {
480 error.NameTooLong => return error.PathTooLong,
481 else => return error.Windows81SdkNotFound,
482 };
483 defer sdk_lib_dir.close();
484
485 var iterator = sdk_lib_dir.iterate();
486 const versions = iterateAndFilterBySemVer(&iterator, allocator, "winv") catch |err| switch (err) {
487 error.OutOfMemory => return error.OutOfMemory,
488 error.VersionNotFound => return error.Windows81SdkNotFound,
489 };
490 defer {
491 for (versions) |version| allocator.free(version);
492 allocator.free(versions);
493 }
494 const latest_version = try allocator.dupe(u8, versions[0]);
495 break :version81 latest_version;
496 };
497 errdefer allocator.free(version);
498
499 return Windows81Sdk{ .path = path, .version = version };
500 }
501
502 fn free(self: *const Windows81Sdk, allocator: std.mem.Allocator) void {
503 allocator.free(self.path);
504 allocator.free(self.version);
505 }
506};
507
508pub const ZigWindowsSDK = struct {
509 windows10sdk: ?Windows10Sdk,
510 windows81sdk: ?Windows81Sdk,
511 msvc_lib_dir: ?[]const u8,
512
513 /// Find path and version of Windows 10 SDK and Windows 8.1 SDK, and find path to MSVC's `lib/` directory.
514 /// Caller owns the result's fields.
515 /// After finishing work, call `free(allocator)`.
516 pub fn find(allocator: std.mem.Allocator) error{ OutOfMemory, NotFound, PathTooLong }!ZigWindowsSDK {
517 if (builtin.os.tag != .windows) return error.NotFound;
518
519 //note(dimenus): If this key doesn't exist, neither the Win 8 SDK nor the Win 10 SDK is installed
520 const roots_key = RegistryUtf8.openKey(WINDOWS_KIT_REG_KEY) catch |err| switch (err) {
521 error.KeyNotFound => return error.NotFound,
522 };
523 defer roots_key.closeKey();
524
525 const windows10sdk: ?Windows10Sdk = blk: {
526 const windows10sdk = Windows10Sdk.find(allocator) catch |err| switch (err) {
527 error.Windows10SdkNotFound,
528 error.PathTooLong,
529 error.VersionTooLong,
530 => break :blk null,
531 error.OutOfMemory => return error.OutOfMemory,
532 };
533 const is_valid_version = windows10sdk.isValidVersion();
534 if (!is_valid_version) break :blk null;
535 break :blk windows10sdk;
536 };
537 errdefer if (windows10sdk) |*w| w.free(allocator);
538
539 const windows81sdk: ?Windows81Sdk = blk: {
540 const windows81sdk = Windows81Sdk.find(allocator, &roots_key) catch |err| switch (err) {
541 error.Windows81SdkNotFound => break :blk null,
542 error.PathTooLong => break :blk null,
543 error.VersionTooLong => break :blk null,
544 error.OutOfMemory => return error.OutOfMemory,
545 };
546 // no check
547 break :blk windows81sdk;
548 };
549 errdefer if (windows81sdk) |*w| w.free(allocator);
550
551 const msvc_lib_dir: ?[]const u8 = MsvcLibDir.find(allocator) catch |err| switch (err) {
552 error.MsvcLibDirNotFound => null,
553 error.PathTooLong => null,
554 error.OutOfMemory => return error.OutOfMemory,
555 };
556 errdefer allocator.free(msvc_lib_dir);
557
558 return ZigWindowsSDK{
559 .windows10sdk = windows10sdk,
560 .windows81sdk = windows81sdk,
561 .msvc_lib_dir = msvc_lib_dir,
562 };
563 }
564
565 pub fn free(self: *const ZigWindowsSDK, allocator: std.mem.Allocator) void {
566 if (self.windows10sdk) |*w10sdk| {
567 w10sdk.free(allocator);
568 }
569 if (self.windows81sdk) |*w81sdk| {
570 w81sdk.free(allocator);
571 }
572 if (self.msvc_lib_dir) |msvc_lib_dir| {
573 allocator.free(msvc_lib_dir);
574 }
575 }
576};
577
578const MsvcLibDir = struct {
579 // https://learn.microsoft.com/en-us/visualstudio/install/tools-for-managing-visual-studio-instances?view=vs-2022#editing-the-registry-for-a-visual-studio-instance
580 fn findViaRegistry(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }![]const u8 {
581
582 // %localappdata%\Microsoft\VisualStudio\
583 // %appdata%\Local\Microsoft\VisualStudio\
584 const visualstudio_folder_path = std.fs.getAppDataDir(allocator, "Microsoft\\VisualStudio\\") catch return error.PathNotFound;
585 defer allocator.free(visualstudio_folder_path);
586
587 const vs_versions: []const []const u8 = vs_versions: {
588 if (!std.fs.path.isAbsolute(visualstudio_folder_path)) return error.PathNotFound;
589 // enumerate folders that contain `privateregistry.bin`, looking for all versions
590 // f.i. %localappdata%\Microsoft\VisualStudio\17.0_9e9cbb98\
591 var visualstudio_folder = std.fs.openIterableDirAbsolute(visualstudio_folder_path, .{}) catch return error.PathNotFound;
592 defer visualstudio_folder.close();
593
594 var iterator = visualstudio_folder.iterate();
595 const versions = iterateAndFilterBySemVer(&iterator, allocator, null) catch |err| switch (err) {
596 error.OutOfMemory => return error.OutOfMemory,
597 error.VersionNotFound => return error.PathNotFound,
598 };
599 break :vs_versions versions;
600 };
601 defer {
602 for (vs_versions) |vs_version| allocator.free(vs_version);
603 allocator.free(vs_versions);
604 }
605 var config_subkey_buf: [RegistryUtf16Le.key_name_max_len * 2]u8 = undefined;
606 const source_directories: []const u8 = source_directories: for (vs_versions) |vs_version| {
607 const privateregistry_absolute_path = std.fs.path.join(allocator, &.{ visualstudio_folder_path, vs_version, "privateregistry.bin" }) catch continue;
608 defer allocator.free(privateregistry_absolute_path);
609 if (!std.fs.path.isAbsolute(privateregistry_absolute_path)) continue;
610
611 const visualstudio_registry = RegistryUtf8.loadFromPath(privateregistry_absolute_path) catch continue;
612 defer visualstudio_registry.closeKey();
613
614 const config_subkey = std.fmt.bufPrint(config_subkey_buf[0..], "Software\\Microsoft\\VisualStudio\\{s}_Config", .{vs_version}) catch unreachable;
615
616 var source_directories_value = visualstudio_registry.getString(allocator, config_subkey, "Source Directories") catch |err| switch (err) {
617 error.OutOfMemory => return error.OutOfMemory,
618 else => continue,
619 };
620 if (source_directories_value.len > (std.fs.MAX_PATH_BYTES * 30)) { // note(bratishkaerik): guessing from the fact that on my computer it has 15 pathes and at least some of them are not of max length
621 allocator.free(source_directories_value);
622 continue;
623 }
624
625 break :source_directories source_directories_value;
626 } else return error.PathNotFound;
627 defer allocator.free(source_directories);
628
629 var source_directories_splitted = std.mem.splitScalar(u8, source_directories, ';');
630
631 const msvc_dir: []const u8 = msvc_dir: {
632 var msvc_include_dir_maybe_with_trailing_slash = try allocator.dupe(u8, source_directories_splitted.first());
633
634 if (msvc_include_dir_maybe_with_trailing_slash.len > std.fs.MAX_PATH_BYTES or !std.fs.path.isAbsolute(msvc_include_dir_maybe_with_trailing_slash)) {
635 allocator.free(msvc_include_dir_maybe_with_trailing_slash);
636 return error.PathNotFound;
637 }
638
639 var msvc_dir = std.ArrayList(u8).fromOwnedSlice(allocator, msvc_include_dir_maybe_with_trailing_slash);
640 errdefer msvc_dir.deinit();
641
642 // String might contain trailing slash, so trim it here
643 if (msvc_dir.items.len > "C:\\".len and msvc_dir.getLast() == '\\') _ = msvc_dir.pop();
644
645 // Remove `\include` at the end of path
646 if (std.mem.endsWith(u8, msvc_dir.items, "\\include")) {
647 msvc_dir.shrinkRetainingCapacity(msvc_dir.items.len - "\\include".len);
648 }
649
650 const folder_with_arch = "\\Lib\\" ++ comptime switch (builtin.target.cpu.arch) {
651 .x86 => "x86",
652 .x86_64 => "x64",
653 .arm, .armeb => "arm",
654 .aarch64 => "arm64",
655 else => |tag| @compileError("MSVC lib dir cannot be detected on architecture " ++ tag),
656 };
657
658 try msvc_dir.appendSlice(folder_with_arch);
659 const msvc_dir_with_arch = try msvc_dir.toOwnedSlice();
660 break :msvc_dir msvc_dir_with_arch;
661 };
662 errdefer allocator.free(msvc_dir);
663
664 return msvc_dir;
665 }
666
667 fn findViaVs7Key(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }![]const u8 {
668 var base_path: std.ArrayList(u8) = base_path: {
669 try_env: {
670 var env_map = std.process.getEnvMap(allocator) catch |err| switch (err) {
671 error.OutOfMemory => return error.OutOfMemory,
672 else => break :try_env,
673 };
674 defer env_map.deinit();
675
676 if (env_map.get("VS140COMNTOOLS")) |VS140COMNTOOLS| {
677 if (VS140COMNTOOLS.len < "C:\\Common7\\Tools".len) break :try_env;
678 if (!std.fs.path.isAbsolute(VS140COMNTOOLS)) break :try_env;
679 var list = std.ArrayList(u8).init(allocator);
680 errdefer list.deinit();
681
682 try list.appendSlice(VS140COMNTOOLS); // C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\Tools
683 // String might contain trailing slash, so trim it here
684 if (list.items.len > "C:\\".len and list.getLast() == '\\') _ = list.pop();
685 list.shrinkRetainingCapacity(list.items.len - "\\Common7\\Tools".len); // C:\Program Files (x86)\Microsoft Visual Studio 14.0
686 break :base_path list;
687 }
688 }
689
690 const vs7_key = RegistryUtf8.openKey("SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7") catch return error.PathNotFound;
691 defer vs7_key.closeKey();
692 try_vs7_key: {
693 var path_maybe_with_trailing_slash = vs7_key.getString(allocator, "", "14.0") catch |err| switch (err) {
694 error.OutOfMemory => return error.OutOfMemory,
695 else => break :try_vs7_key,
696 };
697
698 if (path_maybe_with_trailing_slash.len > std.fs.MAX_PATH_BYTES or !std.fs.path.isAbsolute(path_maybe_with_trailing_slash)) {
699 allocator.free(path_maybe_with_trailing_slash);
700 break :try_vs7_key;
701 }
702
703 var path = std.ArrayList(u8).fromOwnedSlice(allocator, path_maybe_with_trailing_slash);
704 errdefer path.deinit();
705
706 // String might contain trailing slash, so trim it here
707 if (path.items.len > "C:\\".len and path.getLast() == '\\') _ = path.pop();
708 break :base_path path;
709 }
710 return error.PathNotFound;
711 };
712 errdefer base_path.deinit();
713
714 const folder_with_arch = "\\VC\\lib\\" ++ comptime switch (builtin.target.cpu.arch) {
715 .x86 => "", //x86 is in the root of the Lib folder
716 .x86_64 => "amd64",
717 .arm, .armeb => "arm",
718 .aarch64 => "arm64",
719 else => |tag| @compileError("MSVC lib dir cannot be detected on architecture " ++ tag),
720 };
721 try base_path.appendSlice(folder_with_arch);
722
723 const full_path = try base_path.toOwnedSlice();
724 return full_path;
725 }
726
727 /// Find path to MSVC's `lib/` directory.
728 /// Caller owns the result.
729 pub fn find(allocator: std.mem.Allocator) error{ OutOfMemory, MsvcLibDirNotFound, PathTooLong }![]const u8 {
730 const full_path = MsvcLibDir.findViaRegistry(allocator) catch |err1| switch (err1) {
731 error.OutOfMemory => return error.OutOfMemory,
732 error.PathNotFound => MsvcLibDir.findViaVs7Key(allocator) catch |err2| switch (err2) {
733 error.OutOfMemory => return error.OutOfMemory,
734 error.PathNotFound => return error.MsvcLibDirNotFound,
735 },
736 };
737 errdefer allocator.free(full_path);
738 std.debug.assert(std.fs.path.isAbsolute(full_path)); // should be already handled in `findVia*`
739
740 var dir = std.fs.openDirAbsolute(full_path, .{}) catch |err| switch (err) {
741 error.NameTooLong => return error.PathTooLong,
742 else => return error.MsvcLibDirNotFound,
743 };
744 defer dir.close();
745
746 const stat = dir.statFile("vcruntime.lib") catch |err| switch (err) {
747 error.NameTooLong => return error.PathTooLong,
748 else => return error.MsvcLibDirNotFound,
749 };
750 if (stat.kind != .file)
751 return error.MsvcLibDirNotFound;
752
753 return full_path;
754 }
27};755};