diff --git a/CMakeLists.txt b/CMakeLists.txt index f57d67655a..8b653c41c6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -193,8 +193,6 @@ set(ZIG_CPP_SOURCES "${CMAKE_SOURCE_DIR}/src/zig_clang_driver.cpp" "${CMAKE_SOURCE_DIR}/src/zig_clang_cc1_main.cpp" "${CMAKE_SOURCE_DIR}/src/zig_clang_cc1as_main.cpp" - # https://github.com/ziglang/zig/issues/6363 - "${CMAKE_SOURCE_DIR}/src/windows_sdk.cpp" ) # Needed because we use cmake, not the zig build system, to build zig2.o. set(ZIG_STAGE2_SOURCES diff --git a/build.zig b/build.zig index 1a129babc6..9e6e863867 100644 --- a/build.zig +++ b/build.zig @@ -927,8 +927,6 @@ const zig_cpp_sources = [_][]const u8{ "src/zig_clang_driver.cpp", "src/zig_clang_cc1_main.cpp", "src/zig_clang_cc1as_main.cpp", - // https://github.com/ziglang/zig/issues/6363 - "src/windows_sdk.cpp", }; const clang_libs = [_][]const u8{ diff --git a/lib/std/os/windows/advapi32.zig b/lib/std/os/windows/advapi32.zig index bace7ce850..3419fdc95c 100644 --- a/lib/std/os/windows/advapi32.zig +++ b/lib/std/os/windows/advapi32.zig @@ -33,3 +33,41 @@ pub extern "advapi32" fn RegCloseKey(hKey: HKEY) callconv(WINAPI) LSTATUS; // http://msdn.microsoft.com/en-us/library/windows/desktop/aa387694.aspx */ pub extern "advapi32" fn SystemFunction036(output: [*]u8, length: ULONG) callconv(WINAPI) BOOL; pub const RtlGenRandom = SystemFunction036; + +pub const RRF = struct { + pub const RT_ANY: DWORD = 0x0000ffff; + + pub const RT_DWORD: DWORD = 0x00000018; + pub const RT_QWORD: DWORD = 0x00000048; + + pub const RT_REG_BINARY: DWORD = 0x00000008; + pub const RT_REG_DWORD: DWORD = 0x00000010; + pub const RT_REG_EXPAND_SZ: DWORD = 0x00000004; + pub const RT_REG_MULTI_SZ: DWORD = 0x00000020; + pub const RT_REG_NONE: DWORD = 0x00000001; + pub const RT_REG_QWORD: DWORD = 0x00000040; + pub const RT_REG_SZ: DWORD = 0x00000002; + + pub const NOEXPAND: DWORD = 0x10000000; + pub const ZEROONFAILURE: DWORD = 0x20000000; + pub const SUBKEY_WOW6464KEY: DWORD = 0x00010000; + pub const SUBKEY_WOW6432KEY: DWORD = 0x00020000; +}; + +pub extern "advapi32" fn RegGetValueW( + hkey: HKEY, + lpSubKey: LPCWSTR, + lpValue: LPCWSTR, + dwFlags: DWORD, + pdwType: ?*DWORD, + pvData: ?*anyopaque, + pcbData: ?*DWORD, +) callconv(WINAPI) LSTATUS; + +pub extern "advapi32" fn RegLoadAppKeyW( + lpFile: LPCWSTR, + phkResult: *HKEY, + samDesired: REGSAM, + dwOptions: DWORD, + reserved: DWORD, +) callconv(WINAPI) LSTATUS; diff --git a/src/libc_installation.zig b/src/libc_installation.zig index 355c3bad8d..8466087e15 100644 --- a/src/libc_installation.zig +++ b/src/libc_installation.zig @@ -186,21 +186,19 @@ pub const LibCInstallation = struct { } else if (is_windows) { if (!build_options.have_llvm) return error.WindowsSdkNotFound; - var sdk: *ZigWindowsSDK = undefined; - switch (ZigWindowsSDK.find(&sdk)) { - .None => { - defer sdk.free(); - try self.findNativeMsvcIncludeDir(args, sdk); - try self.findNativeMsvcLibDir(args, sdk); - try self.findNativeKernel32LibDir(args, sdk); - try self.findNativeIncludeDirWindows(args, sdk); - try self.findNativeCrtDirWindows(args, sdk); - }, - .OutOfMemory => return error.OutOfMemory, - .NotFound => return error.WindowsSdkNotFound, - .PathTooLong => return error.WindowsSdkNotFound, - } + var sdk: ZigWindowsSDK = ZigWindowsSDK.find(args.allocator) catch |err| switch (err) { + error.NotFound => return error.WindowsSdkNotFound, + error.PathTooLong => return error.WindowsSdkNotFound, + error.OutOfMemory => return error.OutOfMemory, + }; + defer sdk.free(args.allocator); + + try self.findNativeMsvcIncludeDir(args, &sdk); + try self.findNativeMsvcLibDir(args, &sdk); + try self.findNativeKernel32LibDir(args, &sdk); + try self.findNativeIncludeDirWindows(args, &sdk); + try self.findNativeCrtDirWindows(args, &sdk); } else if (is_haiku) { try self.findNativeIncludeDirPosix(args); try self.findNativeCrtBeginDirHaiku(args); @@ -512,8 +510,7 @@ pub const LibCInstallation = struct { ) FindError!void { const allocator = args.allocator; - const msvc_lib_dir_ptr = sdk.msvc_lib_dir_ptr orelse return error.LibCStdLibHeaderNotFound; - const msvc_lib_dir = msvc_lib_dir_ptr[0..sdk.msvc_lib_dir_len]; + const msvc_lib_dir = sdk.msvc_lib_dir orelse return error.LibCStdLibHeaderNotFound; const up1 = fs.path.dirname(msvc_lib_dir) orelse return error.LibCStdLibHeaderNotFound; const up2 = fs.path.dirname(up1) orelse return error.LibCStdLibHeaderNotFound; @@ -544,8 +541,8 @@ pub const LibCInstallation = struct { sdk: *ZigWindowsSDK, ) FindError!void { const allocator = args.allocator; - const msvc_lib_dir_ptr = sdk.msvc_lib_dir_ptr orelse return error.LibCRuntimeNotFound; - self.msvc_lib_dir = try allocator.dupeZ(u8, msvc_lib_dir_ptr[0..sdk.msvc_lib_dir_len]); + const msvc_lib_dir = sdk.msvc_lib_dir orelse return error.LibCRuntimeNotFound; + self.msvc_lib_dir = try allocator.dupe(u8, msvc_lib_dir); } }; @@ -657,23 +654,19 @@ const Search = struct { fn fillSearch(search_buf: *[2]Search, sdk: *ZigWindowsSDK) []Search { var search_end: usize = 0; - if (sdk.path10_ptr) |path10_ptr| { - if (sdk.version10_ptr) |version10_ptr| { - search_buf[search_end] = Search{ - .path = path10_ptr[0..sdk.path10_len], - .version = version10_ptr[0..sdk.version10_len], - }; - search_end += 1; - } + if (sdk.windows10sdk) |windows10sdk| { + search_buf[search_end] = .{ + .path = windows10sdk.path, + .version = windows10sdk.version, + }; + search_end += 1; } - if (sdk.path81_ptr) |path81_ptr| { - if (sdk.version81_ptr) |version81_ptr| { - search_buf[search_end] = Search{ - .path = path81_ptr[0..sdk.path81_len], - .version = version81_ptr[0..sdk.version81_len], - }; - search_end += 1; - } + if (sdk.windows81sdk) |windows81sdk| { + search_buf[search_end] = .{ + .path = windows81sdk.path, + .version = windows81sdk.version, + }; + search_end += 1; } return search_buf[0..search_end]; } diff --git a/src/windows_com.hpp b/src/windows_com.hpp deleted file mode 100644 index 35b46c9ce2..0000000000 --- a/src/windows_com.hpp +++ /dev/null @@ -1,909 +0,0 @@ -// The MIT License(MIT) -// Copyright(C) Microsoft Corporation.All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files(the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions : -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. -// - -#pragma once - -// Windows headers -#include -#include -#include -#include - -// Standard headers -#include - -// COM support header files -#include - -// Constants -// -#ifndef E_NOTFOUND -#define E_NOTFOUND HRESULT_FROM_WIN32(ERROR_NOT_FOUND) -#endif - -#ifndef E_FILENOTFOUND -#define E_FILENOTFOUND HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) -#endif - -#ifndef E_NOTSUPPORTED -#define E_NOTSUPPORTED HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED) -#endif - -// Enumerations -// -/// -/// The state of an instance. -/// -enum InstanceState -{ - /// - /// The instance state has not been determined. - /// - eNone = 0, - - /// - /// The instance installation path exists. - /// - eLocal = 1, - - /// - /// A product is registered to the instance. - /// - eRegistered = 2, - - /// - /// No reboot is required for the instance. - /// - eNoRebootRequired = 4, - - /// - /// No errors were reported for the instance. - /// - eNoErrors = 8, - - /// - /// The instance represents a complete install. - /// - eComplete = UINT_MAX, -}; - -// Forward interface declarations -// -#ifndef __ISetupInstance_FWD_DEFINED__ -#define __ISetupInstance_FWD_DEFINED__ -typedef struct ISetupInstance ISetupInstance; -#endif - -#ifndef __ISetupInstance2_FWD_DEFINED__ -#define __ISetupInstance2_FWD_DEFINED__ -typedef struct ISetupInstance2 ISetupInstance2; -#endif - -#ifndef __ISetupInstanceCatalog_FWD_DEFINED__ -#define __ISetupInstanceCatalog_FWD_DEFINED__ -typedef struct ISetupInstanceCatalog ISetupInstanceCatalog; -#endif - -#ifndef __ISetupLocalizedProperties_FWD_DEFINED__ -#define __ISetupLocalizedProperties_FWD_DEFINED__ -typedef struct ISetupLocalizedProperties ISetupLocalizedProperties; -#endif - -#ifndef __IEnumSetupInstances_FWD_DEFINED__ -#define __IEnumSetupInstances_FWD_DEFINED__ -typedef struct IEnumSetupInstances IEnumSetupInstances; -#endif - -#ifndef __ISetupConfiguration_FWD_DEFINED__ -#define __ISetupConfiguration_FWD_DEFINED__ -typedef struct ISetupConfiguration ISetupConfiguration; -#endif - -#ifndef __ISetupConfiguration2_FWD_DEFINED__ -#define __ISetupConfiguration2_FWD_DEFINED__ -typedef struct ISetupConfiguration2 ISetupConfiguration2; -#endif - -#ifndef __ISetupPackageReference_FWD_DEFINED__ -#define __ISetupPackageReference_FWD_DEFINED__ -typedef struct ISetupPackageReference ISetupPackageReference; -#endif - -#ifndef __ISetupHelper_FWD_DEFINED__ -#define __ISetupHelper_FWD_DEFINED__ -typedef struct ISetupHelper ISetupHelper; -#endif - -#ifndef __ISetupErrorState_FWD_DEFINED__ -#define __ISetupErrorState_FWD_DEFINED__ -typedef struct ISetupErrorState ISetupErrorState; -#endif - -#ifndef __ISetupErrorState2_FWD_DEFINED__ -#define __ISetupErrorState2_FWD_DEFINED__ -typedef struct ISetupErrorState2 ISetupErrorState2; -#endif - -#ifndef __ISetupFailedPackageReference_FWD_DEFINED__ -#define __ISetupFailedPackageReference_FWD_DEFINED__ -typedef struct ISetupFailedPackageReference ISetupFailedPackageReference; -#endif - -#ifndef __ISetupFailedPackageReference2_FWD_DEFINED__ -#define __ISetupFailedPackageReference2_FWD_DEFINED__ -typedef struct ISetupFailedPackageReference2 ISetupFailedPackageReference2; -#endif - -#ifndef __ISetupPropertyStore_FWD_DEFINED__ -#define __ISetupPropertyStore_FWD_DEFINED__ -typedef struct ISetupPropertyStore ISetupPropertyStore; -#endif - -#ifndef __ISetupLocalizedPropertyStore_FWD_DEFINED__ -#define __ISetupLocalizedPropertyStore_FWD_DEFINED__ -typedef struct ISetupLocalizedPropertyStore ISetupLocalizedPropertyStore; -#endif - -// Forward class declarations -// -#ifndef __SetupConfiguration_FWD_DEFINED__ -#define __SetupConfiguration_FWD_DEFINED__ - -#ifdef __cplusplus -typedef class SetupConfiguration SetupConfiguration; -#endif - -#endif - -#ifndef _MSC_VER -#define _Deref_out_opt_ -#endif - -#ifdef __cplusplus -extern "C" { -#endif - - // Interface definitions - // - EXTERN_C const IID IID_ISetupInstance; - -#if defined(__cplusplus) && !defined(CINTERFACE) - /// - /// Information about an instance of a product. - /// - struct DECLSPEC_UUID("B41463C3-8866-43B5-BC33-2B0676F7F42E") DECLSPEC_NOVTABLE ISetupInstance : public IUnknown - { - /// - /// Gets the instance identifier (should match the name of the parent instance directory). - /// - /// The instance identifier. - /// Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist. - STDMETHOD(GetInstanceId)( - _Out_ BSTR* pbstrInstanceId - ) = 0; - - /// - /// Gets the local date and time when the installation was originally installed. - /// - /// The local date and time when the installation was originally installed. - /// 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. - STDMETHOD(GetInstallDate)( - _Out_ LPFILETIME pInstallDate - ) = 0; - - /// - /// Gets the unique name of the installation, often indicating the branch and other information used for telemetry. - /// - /// The unique name of the installation, often indicating the branch and other information used for telemetry. - /// 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. - STDMETHOD(GetInstallationName)( - _Out_ BSTR* pbstrInstallationName - ) = 0; - - /// - /// Gets the path to the installation root of the product. - /// - /// The path to the installation root of the product. - /// 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. - STDMETHOD(GetInstallationPath)( - _Out_ BSTR* pbstrInstallationPath - ) = 0; - - /// - /// Gets the version of the product installed in this instance. - /// - /// The version of the product installed in this instance. - /// 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. - STDMETHOD(GetInstallationVersion)( - _Out_ BSTR* pbstrInstallationVersion - ) = 0; - - /// - /// Gets the display name (title) of the product installed in this instance. - /// - /// The LCID for the display name. - /// The display name (title) of the product installed in this instance. - /// 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. - STDMETHOD(GetDisplayName)( - _In_ LCID lcid, - _Out_ BSTR* pbstrDisplayName - ) = 0; - - /// - /// Gets the description of the product installed in this instance. - /// - /// The LCID for the description. - /// The description of the product installed in this instance. - /// 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. - STDMETHOD(GetDescription)( - _In_ LCID lcid, - _Out_ BSTR* pbstrDescription - ) = 0; - - /// - /// Resolves the optional relative path to the root path of the instance. - /// - /// A relative path within the instance to resolve, or NULL to get the root path. - /// 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. - /// 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. - STDMETHOD(ResolvePath)( - _In_opt_z_ LPCOLESTR pwszRelativePath, - _Out_ BSTR* pbstrAbsolutePath - ) = 0; - }; -#endif - - EXTERN_C const IID IID_ISetupInstance2; - -#if defined(__cplusplus) && !defined(CINTERFACE) - /// - /// Information about an instance of a product. - /// - struct DECLSPEC_UUID("89143C9A-05AF-49B0-B717-72E218A2185C") DECLSPEC_NOVTABLE ISetupInstance2 : public ISetupInstance - { - /// - /// Gets the state of the instance. - /// - /// The state of the instance. - /// Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist. - STDMETHOD(GetState)( - _Out_ InstanceState* pState - ) = 0; - - /// - /// Gets an array of package references registered to the instance. - /// - /// Pointer to an array of . - /// 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. - STDMETHOD(GetPackages)( - _Out_ LPSAFEARRAY* ppsaPackages - ) = 0; - - /// - /// Gets a pointer to the that represents the registered product. - /// - /// Pointer to an instance of . This may be NULL if does not return . - /// 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. - STDMETHOD(GetProduct)( - _Outptr_result_maybenull_ ISetupPackageReference** ppPackage - ) = 0; - - /// - /// Gets the relative path to the product application, if available. - /// - /// The relative path to the product application, if available. - /// Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist. - STDMETHOD(GetProductPath)( - _Outptr_result_maybenull_ BSTR* pbstrProductPath - ) = 0; - - /// - /// Gets the error state of the instance, if available. - /// - /// The error state of the instance, if available. - /// Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist. - STDMETHOD(GetErrors)( - _Outptr_result_maybenull_ ISetupErrorState** ppErrorState - ) = 0; - - /// - /// Gets a value indicating whether the instance can be launched. - /// - /// Whether the instance can be launched. - /// Standard HRESULT indicating success or failure. - /// - /// An instance could have had errors during install but still be launched. Some features may not work correctly, but others will. - /// - STDMETHOD(IsLaunchable)( - _Out_ VARIANT_BOOL* pfIsLaunchable - ) = 0; - - /// - /// Gets a value indicating whether the instance is complete. - /// - /// Whether the instance is complete. - /// Standard HRESULT indicating success or failure. - /// - /// An instance is complete if it had no errors during install, resume, or repair. - /// - STDMETHOD(IsComplete)( - _Out_ VARIANT_BOOL* pfIsComplete - ) = 0; - - /// - /// Gets product-specific properties. - /// - /// A pointer to an instance of . This may be NULL if no properties are defined. - /// Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist. - STDMETHOD(GetProperties)( - _Outptr_result_maybenull_ ISetupPropertyStore** ppProperties - ) = 0; - - /// - /// Gets the directory path to the setup engine that installed the instance. - /// - /// The directory path to the setup engine that installed the instance. - /// Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist. - STDMETHOD(GetEnginePath)( - _Outptr_result_maybenull_ BSTR* pbstrEnginePath - ) = 0; - }; -#endif - - EXTERN_C const IID IID_ISetupInstanceCatalog; - -#if defined(__cplusplus) && !defined(CINTERFACE) - /// - /// Information about a catalog used to install an instance. - /// - struct DECLSPEC_UUID("9AD8E40F-39A2-40F1-BF64-0A6C50DD9EEB") DECLSPEC_NOVTABLE ISetupInstanceCatalog : public IUnknown - { - /// - /// Gets catalog information properties. - /// - /// A pointer to an instance of . - /// 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. - STDMETHOD(GetCatalogInfo)( - _Out_ ISetupPropertyStore** ppCatalogInfo - ) = 0; - - /// - /// Gets a value indicating whether the catalog is a prerelease. - /// - /// Whether the catalog for the instance is a prerelease version. - /// 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. - STDMETHOD(IsPrerelease)( - _Out_ VARIANT_BOOL* pfIsPrerelease - ) = 0; - }; -#endif - - EXTERN_C const IID IID_ISetupLocalizedProperties; - -#if defined(__cplusplus) && !defined(CINTERFACE) - /// - /// Provides localized properties of an instance of a product. - /// - struct DECLSPEC_UUID("F4BD7382-FE27-4AB4-B974-9905B2A148B0") DECLSPEC_NOVTABLE ISetupLocalizedProperties : public IUnknown - { - /// - /// Gets localized product-specific properties. - /// - /// A pointer to an instance of . This may be NULL if no properties are defined. - /// Standard HRESULT indicating success or failure. - STDMETHOD(GetLocalizedProperties)( - _Outptr_result_maybenull_ ISetupLocalizedPropertyStore** ppLocalizedProperties - ) = 0; - - /// - /// Gets localized channel-specific properties. - /// - /// A pointer to an instance of . This may be NULL if no channel properties are defined. - /// Standard HRESULT indicating success or failure. - STDMETHOD(GetLocalizedChannelProperties)( - _Outptr_result_maybenull_ ISetupLocalizedPropertyStore** ppLocalizedChannelProperties - ) = 0; - }; -#endif - - EXTERN_C const IID IID_IEnumSetupInstances; - -#if defined(__cplusplus) && !defined(CINTERFACE) - -#ifdef __GNUC__ - __CRT_UUID_DECL(IEnumSetupInstances, 0x6380BCFF, 0x41D3, 0x4B2E, 0x8B, 0x2E, 0xBF, 0x8A, 0x68, 0x10, 0xC8, 0x48); -#endif - - /// - /// An enumerator of installed objects. - /// - struct DECLSPEC_UUID("6380BCFF-41D3-4B2E-8B2E-BF8A6810C848") DECLSPEC_NOVTABLE IEnumSetupInstances : public IUnknown - { - /// - /// Retrieves the next set of product instances in the enumeration sequence. - /// - /// The number of product instances to retrieve. - /// A pointer to an array of . - /// A pointer to the number of product instances retrieved. If is 1 this parameter may be NULL. - /// S_OK if the number of elements were fetched, S_FALSE if nothing was fetched (at end of enumeration), E_INVALIDARG if is greater than 1 and pceltFetched is NULL, or E_OUTOFMEMORY if an could not be allocated. - STDMETHOD(Next)( - _In_ ULONG celt, - _Out_writes_to_(celt, *pceltFetched) ISetupInstance** rgelt, - _Out_opt_ _Deref_out_range_(0, celt) ULONG* pceltFetched - ) = 0; - - /// - /// Skips the next set of product instances in the enumeration sequence. - /// - /// The number of product instances to skip. - /// S_OK if the number of elements could be skipped; otherwise, S_FALSE; - STDMETHOD(Skip)( - _In_ ULONG celt - ) = 0; - - /// - /// Resets the enumeration sequence to the beginning. - /// - /// Always returns S_OK; - STDMETHOD(Reset)(void) = 0; - - /// - /// 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. - /// - /// A pointer to a pointer to a new interface. If the method fails, this parameter is undefined. - /// S_OK if a clone was returned; otherwise, E_OUTOFMEMORY. - STDMETHOD(Clone)( - _Deref_out_opt_ IEnumSetupInstances** ppenum - ) = 0; - }; -#endif - - EXTERN_C const IID IID_ISetupConfiguration; - -#if defined(__cplusplus) && !defined(CINTERFACE) - -#ifdef __GNUC__ - __CRT_UUID_DECL(ISetupConfiguration, 0x42843719, 0xDB4C, 0x46C2, 0x8E, 0x7C, 0x64, 0xF1, 0x81, 0x6E, 0xFD, 0x5B); -#endif - - /// - /// Gets information about product instances installed on the machine. - /// - struct DECLSPEC_UUID("42843719-DB4C-46C2-8E7C-64F1816EFD5B") DECLSPEC_NOVTABLE ISetupConfiguration : public IUnknown - { - /// - /// Enumerates all launchable product instances installed. - /// - /// An enumeration of completed, installed product instances. - /// Standard HRESULT indicating success or failure. - STDMETHOD(EnumInstances)( - _Out_ IEnumSetupInstances** ppEnumInstances - ) = 0; - - /// - /// Gets the instance for the current process path. - /// - /// The instance for the current process path. - /// - /// The instance for the current process path, or E_NOTFOUND if not found. - /// The may indicate the instance is invalid. - /// - /// - /// The returned instance may not be launchable. - /// - STDMETHOD(GetInstanceForCurrentProcess)( - _Out_ ISetupInstance** ppInstance - ) = 0; - - /// - /// Gets the instance for the given path. - /// - /// The instance for the given path. - /// - /// The instance for the given path, or E_NOTFOUND if not found. - /// The may indicate the instance is invalid. - /// - /// - /// The returned instance may not be launchable. - /// - STDMETHOD(GetInstanceForPath)( - _In_z_ LPCWSTR wzPath, - _Out_ ISetupInstance** ppInstance - ) = 0; - }; -#endif - - EXTERN_C const IID IID_ISetupConfiguration2; - -#if defined(__cplusplus) && !defined(CINTERFACE) - /// - /// Gets information about product instances. - /// - struct DECLSPEC_UUID("26AAB78C-4A60-49D6-AF3B-3C35BC93365D") DECLSPEC_NOVTABLE ISetupConfiguration2 : public ISetupConfiguration - { - /// - /// Enumerates all product instances. - /// - /// An enumeration of all product instances. - /// Standard HRESULT indicating success or failure. - STDMETHOD(EnumAllInstances)( - _Out_ IEnumSetupInstances** ppEnumInstances - ) = 0; - }; -#endif - - EXTERN_C const IID IID_ISetupPackageReference; - -#if defined(__cplusplus) && !defined(CINTERFACE) - /// - /// A reference to a package. - /// - struct DECLSPEC_UUID("da8d8a16-b2b6-4487-a2f1-594ccccd6bf5") DECLSPEC_NOVTABLE ISetupPackageReference : public IUnknown - { - /// - /// Gets the general package identifier. - /// - /// The general package identifier. - /// Standard HRESULT indicating success or failure. - STDMETHOD(GetId)( - _Out_ BSTR* pbstrId - ) = 0; - - /// - /// Gets the version of the package. - /// - /// The version of the package. - /// Standard HRESULT indicating success or failure. - STDMETHOD(GetVersion)( - _Out_ BSTR* pbstrVersion - ) = 0; - - /// - /// Gets the target process architecture of the package. - /// - /// The target process architecture of the package. - /// Standard HRESULT indicating success or failure. - STDMETHOD(GetChip)( - _Out_ BSTR* pbstrChip - ) = 0; - - /// - /// Gets the language and optional region identifier. - /// - /// The language and optional region identifier. - /// Standard HRESULT indicating success or failure. - STDMETHOD(GetLanguage)( - _Out_ BSTR* pbstrLanguage - ) = 0; - - /// - /// Gets the build branch of the package. - /// - /// The build branch of the package. - /// Standard HRESULT indicating success or failure. - STDMETHOD(GetBranch)( - _Out_ BSTR* pbstrBranch - ) = 0; - - /// - /// Gets the type of the package. - /// - /// The type of the package. - /// Standard HRESULT indicating success or failure. - STDMETHOD(GetType)( - _Out_ BSTR* pbstrType - ) = 0; - - /// - /// Gets the unique identifier consisting of all defined tokens. - /// - /// The unique identifier consisting of all defined tokens. - /// Standard HRESULT indicating success or failure, including E_UNEXPECTED if no Id was defined (required). - STDMETHOD(GetUniqueId)( - _Out_ BSTR* pbstrUniqueId - ) = 0; - - /// - /// Gets a value indicating whether the package refers to an external extension. - /// - /// A value indicating whether the package refers to an external extension. - /// Standard HRESULT indicating success or failure, including E_UNEXPECTED if no Id was defined (required). - STDMETHOD(GetIsExtension)( - _Out_ VARIANT_BOOL* pfIsExtension - ) = 0; - }; -#endif - - EXTERN_C const IID IID_ISetupHelper; - -#if defined(__cplusplus) && !defined(CINTERFACE) - /// - /// Helper functions. - /// - /// - /// You can query for this interface from the class. - /// - struct DECLSPEC_UUID("42b21b78-6192-463e-87bf-d577838f1d5c") DECLSPEC_NOVTABLE ISetupHelper : public IUnknown - { - /// - /// Parses a dotted quad version string into a 64-bit unsigned integer. - /// - /// The dotted quad version string to parse, e.g. 1.2.3.4. - /// A 64-bit unsigned integer representing the version. You can compare this to other versions. - /// Standard HRESULT indicating success or failure, including E_INVALIDARG if the version is not valid. - STDMETHOD(ParseVersion)( - _In_ LPCOLESTR pwszVersion, - _Out_ PULONGLONG pullVersion - ) = 0; - - /// - /// Parses a dotted quad version string into a 64-bit unsigned integer. - /// - /// The string containing 1 or 2 dotted quad version strings to parse, e.g. [1.0,) that means 1.0.0.0 or newer. - /// A 64-bit unsigned integer representing the minimum version, which may be 0. You can compare this to other versions. - /// A 64-bit unsigned integer representing the maximum version, which may be MAXULONGLONG. You can compare this to other versions. - /// Standard HRESULT indicating success or failure, including E_INVALIDARG if the version range is not valid. - STDMETHOD(ParseVersionRange)( - _In_ LPCOLESTR pwszVersionRange, - _Out_ PULONGLONG pullMinVersion, - _Out_ PULONGLONG pullMaxVersion - ) = 0; - }; -#endif - - EXTERN_C const IID IID_ISetupErrorState; - -#if defined(__cplusplus) && !defined(CINTERFACE) - /// - /// Information about the error state of an instance. - /// - struct DECLSPEC_UUID("46DCCD94-A287-476A-851E-DFBC2FFDBC20") DECLSPEC_NOVTABLE ISetupErrorState : public IUnknown - { - /// - /// Gets an array of failed package references. - /// - /// Pointer to an array of , if packages have failed. - /// Standard HRESULT indicating success or failure. - STDMETHOD(GetFailedPackages)( - _Outptr_result_maybenull_ LPSAFEARRAY* ppsaFailedPackages - ) = 0; - - /// - /// Gets an array of skipped package references. - /// - /// Pointer to an array of , if packages have been skipped. - /// Standard HRESULT indicating success or failure. - STDMETHOD(GetSkippedPackages)( - _Outptr_result_maybenull_ LPSAFEARRAY* ppsaSkippedPackages - ) = 0; - }; -#endif - - EXTERN_C const IID IID_ISetupErrorState2; - -#if defined(__cplusplus) && !defined(CINTERFACE) - /// - /// Information about the error state of an instance. - /// - struct DECLSPEC_UUID("9871385B-CA69-48F2-BC1F-7A37CBF0B1EF") DECLSPEC_NOVTABLE ISetupErrorState2 : public ISetupErrorState - { - /// - /// Gets the path to the error log. - /// - /// The path to the error log. - /// Standard HRESULT indicating success or failure. - STDMETHOD(GetErrorLogFilePath)( - _Outptr_result_maybenull_ BSTR* pbstrErrorLogFilePath - ) = 0; - - /// - /// Gets the path to the main setup log. - /// - /// The path to the main setup log. - /// Standard HRESULT indicating success or failure. - STDMETHOD(GetLogFilePath)( - _Outptr_result_maybenull_ BSTR* pbstrLogFilePath - ) = 0; - }; -#endif - - EXTERN_C const IID IID_ISetupFailedPackageReference; - -#if defined(__cplusplus) && !defined(CINTERFACE) - /// - /// A reference to a failed package. - /// - struct DECLSPEC_UUID("E73559CD-7003-4022-B134-27DC650B280F") DECLSPEC_NOVTABLE ISetupFailedPackageReference : public ISetupPackageReference - { - }; - -#endif - - EXTERN_C const IID IID_ISetupFailedPackageReference2; - -#if defined(__cplusplus) && !defined(CINTERFACE) - /// - /// A reference to a failed package. - /// - struct DECLSPEC_UUID("0FAD873E-E874-42E3-B268-4FE2F096B9CA") DECLSPEC_NOVTABLE ISetupFailedPackageReference2 : public ISetupFailedPackageReference - { - /// - /// Gets the path to the optional package log. - /// - /// The path to the optional package log. - /// Standard HRESULT indicating success or failure. - STDMETHOD(GetLogFilePath)( - _Outptr_result_maybenull_ BSTR* pbstrLogFilePath - ) = 0; - - /// - /// Gets the description of the package failure. - /// - /// The description of the package failure. - /// Standard HRESULT indicating success or failure. - STDMETHOD(GetDescription)( - _Outptr_result_maybenull_ BSTR* pbstrDescription - ) = 0; - - /// - /// Gets the signature to use for feedback reporting. - /// - /// The signature to use for feedback reporting. - /// Standard HRESULT indicating success or failure. - STDMETHOD(GetSignature)( - _Outptr_result_maybenull_ BSTR* pbstrSignature - ) = 0; - - /// - /// Gets the array of details for this package failure. - /// - /// Pointer to an array of details as BSTRs. - /// Standard HRESULT indicating success or failure. - STDMETHOD(GetDetails)( - _Out_ LPSAFEARRAY* ppsaDetails - ) = 0; - - /// - /// Gets an array of packages affected by this package failure. - /// - /// Pointer to an array of for packages affected by this package failure. This may be NULL. - /// Standard HRESULT indicating success or failure. - STDMETHOD(GetAffectedPackages)( - _Out_ LPSAFEARRAY* ppsaAffectedPackages - ) = 0; - }; - -#endif - - EXTERN_C const IID IID_ISetupPropertyStore; - -#if defined(__cplusplus) && !defined(CINTERFACE) - /// - /// Provides named properties. - /// - /// - /// You can get this from an , , or derivative. - /// - struct DECLSPEC_UUID("C601C175-A3BE-44BC-91F6-4568D230FC83") DECLSPEC_NOVTABLE ISetupPropertyStore : public IUnknown - { - /// - /// Gets an array of property names in this property store. - /// - /// Pointer to an array of property names as BSTRs. - /// Standard HRESULT indicating success or failure. - STDMETHOD(GetNames)( - _Out_ LPSAFEARRAY* ppsaNames - ) = 0; - - /// - /// Gets the value of a named property in this property store. - /// - /// The name of the property to get. - /// The value of the property. - /// 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. - STDMETHOD(GetValue)( - _In_ LPCOLESTR pwszName, - _Out_ LPVARIANT pvtValue - ) = 0; - }; - -#endif - - EXTERN_C const IID IID_ISetupLocalizedPropertyStore; - -#if defined(__cplusplus) && !defined(CINTERFACE) - /// - /// Provides localized named properties. - /// - /// - /// You can get this from an . - /// - struct DECLSPEC_UUID("5BB53126-E0D5-43DF-80F1-6B161E5C6F6C") DECLSPEC_NOVTABLE ISetupLocalizedPropertyStore : public IUnknown - { - /// - /// Gets an array of property names in this property store. - /// - /// The LCID for the property names. - /// Pointer to an array of property names as BSTRs. - /// Standard HRESULT indicating success or failure. - STDMETHOD(GetNames)( - _In_ LCID lcid, - _Out_ LPSAFEARRAY* ppsaNames - ) = 0; - - /// - /// Gets the value of a named property in this property store. - /// - /// The name of the property to get. - /// The LCID for the property. - /// The value of the property. - /// 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. - STDMETHOD(GetValue)( - _In_ LPCOLESTR pwszName, - _In_ LCID lcid, - _Out_ LPVARIANT pvtValue - ) = 0; - }; - -#endif - - // Class declarations - // - EXTERN_C const CLSID CLSID_SetupConfiguration; - -#ifdef __cplusplus - -#ifdef __GNUC__ - __CRT_UUID_DECL(SetupConfiguration, 0x177F0C4A, 0x1CD3, 0x4DE7, 0xA3, 0x2C, 0x71, 0xDB, 0xBB, 0x9F, 0xA3, 0x6D); -#endif - - /// - /// This class implements , , and . - /// - class DECLSPEC_UUID("177F0C4A-1CD3-4DE7-A32C-71DBBB9FA36D") SetupConfiguration; -#endif - // Function declarations - // - /// - /// Gets an that provides information about product instances installed on the machine. - /// - /// The that provides information about product instances installed on the machine. - /// Reserved for future use. - /// Standard HRESULT indicating success or failure. - STDMETHODIMP GetSetupConfiguration( - _Out_ ISetupConfiguration** ppConfiguration, - _Reserved_ LPVOID pReserved - ); - -#ifdef __cplusplus -} -#endif - -_COM_SMARTPTR_TYPEDEF(ISetupInstance, __uuidof(ISetupInstance)); -_COM_SMARTPTR_TYPEDEF(ISetupInstance2, __uuidof(ISetupInstance2)); -_COM_SMARTPTR_TYPEDEF(IEnumSetupInstances, __uuidof(IEnumSetupInstances)); -_COM_SMARTPTR_TYPEDEF(ISetupConfiguration, __uuidof(ISetupConfiguration)); -_COM_SMARTPTR_TYPEDEF(ISetupConfiguration2, __uuidof(ISetupConfiguration2)); -_COM_SMARTPTR_TYPEDEF(ISetupHelper, __uuidof(ISetupHelper)); -_COM_SMARTPTR_TYPEDEF(ISetupPackageReference, __uuidof(ISetupPackageReference)); -_COM_SMARTPTR_TYPEDEF(ISetupPropertyStore, __uuidof(ISetupPropertyStore)); -_COM_SMARTPTR_TYPEDEF(ISetupInstanceCatalog, __uuidof(ISetupInstanceCatalog)); diff --git a/src/windows_sdk.cpp b/src/windows_sdk.cpp deleted file mode 100644 index 6bc1ed7e01..0000000000 --- a/src/windows_sdk.cpp +++ /dev/null @@ -1,387 +0,0 @@ -/* - * Copyright (c) 2018 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#include "windows_sdk.h" - -#if defined(_WIN32) - -#include "windows_com.hpp" -#include -#include - -const char *ZIG_WINDOWS_KIT_REG_KEY = "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots"; - -struct ZigWindowsSDKPrivate { - ZigWindowsSDK base; -}; - -enum NativeArch { - NativeArchArm, - NativeArchx86, - NativeArchx86_64, - NativeArchAarch64, -}; - -#if defined(_M_ARM) || defined(__arm_) -static const NativeArch native_arch = NativeArchArm; -#elif defined(_M_IX86) || defined(__i386__) -static const NativeArch native_arch = NativeArchx86; -#elif defined(_M_X64) || defined(__x86_64__) -static const NativeArch native_arch = NativeArchx86_64; -#elif defined(_M_ARM64) || defined(__aarch64__) -static const NativeArch native_arch = NativeArchAarch64; -#else -#error unsupported architecture -#endif - -void zig_free_windows_sdk(struct ZigWindowsSDK *sdk) { - if (sdk == nullptr) { - return; - } - free((void*)sdk->path10_ptr); - free((void*)sdk->version10_ptr); - free((void*)sdk->path81_ptr); - free((void*)sdk->version81_ptr); - free((void*)sdk->msvc_lib_dir_ptr); -} - -static ZigFindWindowsSdkError find_msvc_lib_dir(ZigWindowsSDKPrivate *priv) { - //COM Smart Pointers requires explicit scope - { - HRESULT rc = CoInitializeEx(NULL, COINIT_MULTITHREADED); - if (rc != S_OK && rc != S_FALSE) { - goto com_done; - } - - //This COM class is installed when a VS2017 - ISetupConfigurationPtr setup_config; - rc = setup_config.CreateInstance(__uuidof(SetupConfiguration)); - if (rc != S_OK) { - goto com_done; - } - - IEnumSetupInstancesPtr all_instances; - rc = setup_config->EnumInstances(&all_instances); - if (rc != S_OK) { - goto com_done; - } - - ISetupInstance* curr_instance; - ULONG found_inst; - while ((rc = all_instances->Next(1, &curr_instance, &found_inst) == S_OK)) { - BSTR bstr_inst_path; - rc = curr_instance->GetInstallationPath(&bstr_inst_path); - if (rc != S_OK) { - goto com_done; - } - //BSTRs are UTF-16 encoded, so we need to convert the string & adjust the length - //TODO call an actual function to do this - UINT bstr_path_len = *((UINT*)bstr_inst_path - 1); - ULONG tmp_path_len = bstr_path_len / 2 + 1; - char* conv_path = (char*)bstr_inst_path; - // TODO don't use alloca - char *tmp_path = (char*)alloca(tmp_path_len); - memset(tmp_path, 0, tmp_path_len); - uint32_t c = 0; - for (uint32_t i = 0; i < bstr_path_len; i += 2) { - tmp_path[c] = conv_path[i]; - ++c; - assert(c != tmp_path_len); - } - char output_path[4096]; - output_path[0] = 0; - char *out_append_ptr = output_path; - - out_append_ptr += sprintf(out_append_ptr, "%s\\", tmp_path); - - char tmp_buf[4096]; - sprintf(tmp_buf, "%s%s", output_path, "VC\\Auxiliary\\Build\\Microsoft.VCToolsVersion.default.txt"); - FILE* tools_file = fopen(tmp_buf, "rb"); - if (!tools_file) { - goto com_done; - } - memset(tmp_path, 0, tmp_path_len); - fgets(tmp_path, tmp_path_len, tools_file); - strtok(tmp_path, " \r\n"); - fclose(tools_file); - out_append_ptr += sprintf(out_append_ptr, "VC\\Tools\\MSVC\\%s\\lib\\", tmp_path); - switch (native_arch) { - case NativeArchx86: - out_append_ptr += sprintf(out_append_ptr, "x86\\"); - break; - case NativeArchx86_64: - out_append_ptr += sprintf(out_append_ptr, "x64\\"); - break; - case NativeArchArm: - out_append_ptr += sprintf(out_append_ptr, "arm\\"); - break; - case NativeArchAarch64: - out_append_ptr += sprintf(out_append_ptr, "arm64\\"); - break; - } - sprintf(tmp_buf, "%s%s", output_path, "vcruntime.lib"); - - if (GetFileAttributesA(tmp_buf) != INVALID_FILE_ATTRIBUTES) { - priv->base.msvc_lib_dir_ptr = strdup(output_path); - if (priv->base.msvc_lib_dir_ptr == nullptr) { - return ZigFindWindowsSdkErrorOutOfMemory; - } - priv->base.msvc_lib_dir_len = strlen(priv->base.msvc_lib_dir_ptr); - return ZigFindWindowsSdkErrorNone; - } - } - } - -com_done:; - HKEY key; - HRESULT rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7", 0, - KEY_QUERY_VALUE | KEY_WOW64_32KEY, &key); - if (rc != ERROR_SUCCESS) { - return ZigFindWindowsSdkErrorNotFound; - } - - DWORD dw_type = 0; - DWORD cb_data = 0; - rc = RegQueryValueEx(key, "14.0", NULL, &dw_type, NULL, &cb_data); - if ((rc == ERROR_FILE_NOT_FOUND) || (REG_SZ != dw_type)) { - return ZigFindWindowsSdkErrorNotFound; - } - - char tmp_buf[4096]; - - RegQueryValueExA(key, "14.0", NULL, NULL, (LPBYTE)tmp_buf, &cb_data); - // RegQueryValueExA returns the length of the string INCLUDING the null terminator - char *tmp_buf_append_ptr = tmp_buf + (cb_data - 1); - tmp_buf_append_ptr += sprintf(tmp_buf_append_ptr, "VC\\Lib\\"); - switch (native_arch) { - case NativeArchx86: - //x86 is in the root of the Lib folder - break; - case NativeArchx86_64: - tmp_buf_append_ptr += sprintf(tmp_buf_append_ptr, "amd64\\"); - break; - case NativeArchArm: - tmp_buf_append_ptr += sprintf(tmp_buf_append_ptr, "arm\\"); - break; - case NativeArchAarch64: - tmp_buf_append_ptr += sprintf(tmp_buf_append_ptr, "arm64\\"); - break; - } - - char *output_path = strdup(tmp_buf); - if (output_path == nullptr) { - return ZigFindWindowsSdkErrorOutOfMemory; - } - - tmp_buf_append_ptr += sprintf(tmp_buf_append_ptr, "vcruntime.lib"); - - if (GetFileAttributesA(tmp_buf) != INVALID_FILE_ATTRIBUTES) { - priv->base.msvc_lib_dir_ptr = output_path; - priv->base.msvc_lib_dir_len = strlen(output_path); - return ZigFindWindowsSdkErrorNone; - } else { - free(output_path); - return ZigFindWindowsSdkErrorNotFound; - } -} - -static ZigFindWindowsSdkError find_10_version(ZigWindowsSDKPrivate *priv) { - if (priv->base.path10_ptr == nullptr) { - return ZigFindWindowsSdkErrorNone; - } - - char reg_query[MAX_PATH] = { 0 }; - int n = snprintf(reg_query, MAX_PATH, "%s\\%s.0\\Installed Options", ZIG_WINDOWS_KIT_REG_KEY, priv->base.version10_ptr); - if (n < 0 || n >= MAX_PATH) { - return ZigFindWindowsSdkErrorPathTooLong; - } - - HKEY options_key; - HRESULT rc; - rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, reg_query, 0, - KEY_QUERY_VALUE | KEY_WOW64_32KEY | KEY_ENUMERATE_SUB_KEYS, &options_key); - if (rc != ERROR_SUCCESS) { - return ZigFindWindowsSdkErrorNotFound; - } - - const char *option_name = nullptr; - switch (native_arch) { - case NativeArchArm: - option_name = "OptionId.DesktopCPParm"; - break; - case NativeArchAarch64: - option_name = "OptionId.DesktopCPParm64"; - break; - case NativeArchx86_64: - option_name = "OptionId.DesktopCPPx64"; - break; - case NativeArchx86: - option_name = "OptionId.DesktopCPPx86"; - break; - default: - return ZigFindWindowsSdkErrorNotFound; - } - - DWORD val_sz = sizeof(DWORD); - DWORD reg_val = 0; - DWORD type = REG_DWORD; - rc = RegQueryValueEx(options_key, option_name, NULL, &type, (LPBYTE)®_val, &val_sz); - if (rc != ERROR_SUCCESS || reg_val != 1) { - return ZigFindWindowsSdkErrorNotFound; - } - return ZigFindWindowsSdkErrorNone; -} - -static ZigFindWindowsSdkError find_81_version(ZigWindowsSDKPrivate *priv) { - if (priv->base.path81_ptr == nullptr) { - return ZigFindWindowsSdkErrorNone; - } - - char sdk_lib_dir[4096]; - int n = snprintf(sdk_lib_dir, 4096, "%s\\Lib\\winv*", priv->base.path81_ptr); - if (n < 0 || n >= 4096) { - return ZigFindWindowsSdkErrorPathTooLong; - } - - // enumerate files in sdk path looking for latest version - WIN32_FIND_DATA ffd; - HANDLE hFind = FindFirstFileA(sdk_lib_dir, &ffd); - if (hFind == INVALID_HANDLE_VALUE) { - return ZigFindWindowsSdkErrorNotFound; - } - int v0 = 0, v1 = 0; - for (;;) { - if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - int c0 = 0, c1 = 0; - sscanf(ffd.cFileName, "winv%d.%d", &c0, &c1); - - if ( (c0 > v0) || (c0 == v0 && c1 > v1) ) { - v0 = c0, v1 = c1; - free((void*)priv->base.version81_ptr); - priv->base.version81_ptr = strdup(ffd.cFileName); - if (priv->base.version81_ptr == nullptr) { - FindClose(hFind); - return ZigFindWindowsSdkErrorOutOfMemory; - } - } - } - if (FindNextFile(hFind, &ffd) == 0) { - FindClose(hFind); - break; - } - } - priv->base.version81_len = strlen(priv->base.version81_ptr); - return ZigFindWindowsSdkErrorNone; -} - -ZigFindWindowsSdkError zig_find_windows_sdk(struct ZigWindowsSDK **out_sdk) { - ZigWindowsSDKPrivate *priv = (ZigWindowsSDKPrivate*)calloc(1, sizeof(ZigWindowsSDKPrivate)); - if (priv == nullptr) { - return ZigFindWindowsSdkErrorOutOfMemory; - } - - HRESULT rc; - - //note(dimenus): If this key doesn't exist, neither the Win 8 SDK nor the Win 10 SDK is installed - HKEY roots_key; - rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, ZIG_WINDOWS_KIT_REG_KEY, 0, - KEY_QUERY_VALUE | KEY_WOW64_32KEY | KEY_ENUMERATE_SUB_KEYS, &roots_key); - if (rc != ERROR_SUCCESS) { - zig_free_windows_sdk(&priv->base); - return ZigFindWindowsSdkErrorNotFound; - } - - { - HKEY v10_key; - rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v10.0", 0, - KEY_QUERY_VALUE | KEY_WOW64_32KEY | KEY_ENUMERATE_SUB_KEYS, &v10_key); - if (rc != ERROR_SUCCESS) { - goto find_win10_sdk_done; - } - - DWORD tmp_buf_len = MAX_PATH; - priv->base.path10_ptr = (const char *)calloc(tmp_buf_len, 1); - if (priv->base.path10_ptr == nullptr) { - zig_free_windows_sdk(&priv->base); - return ZigFindWindowsSdkErrorOutOfMemory; - } - rc = RegQueryValueEx(v10_key, "InstallationFolder", NULL, NULL, (LPBYTE)priv->base.path10_ptr, &tmp_buf_len); - if (rc == ERROR_SUCCESS) { - priv->base.path10_len = tmp_buf_len - 1; - if (priv->base.path10_ptr[priv->base.path10_len - 1] == '\\') { - priv->base.path10_len -= 1; - } - } else { - free((void*)priv->base.path10_ptr); - priv->base.path10_ptr = nullptr; - } - - priv->base.version10_ptr = (const char*)calloc(tmp_buf_len, 1); - rc = RegQueryValueEx(v10_key, "ProductVersion", NULL, NULL, (LPBYTE)priv->base.version10_ptr, &tmp_buf_len); - if (rc == ERROR_SUCCESS) { - snprintf((char*)priv->base.version10_ptr, MAX_PATH, "%s.0", priv->base.version10_ptr); - priv->base.version10_len = tmp_buf_len - 1 + 2; // note(dimenus): Microsoft doesn't include the .0 in the ProductVersion key.... - } else { - free((void*)priv->base.version10_ptr); - priv->base.version10_ptr = nullptr; - } - } - find_win10_sdk_done: - { - DWORD tmp_buf_len = MAX_PATH; - priv->base.path81_ptr = (const char *)calloc(tmp_buf_len, 1); - if (priv->base.path81_ptr == nullptr) { - zig_free_windows_sdk(&priv->base); - return ZigFindWindowsSdkErrorOutOfMemory; - } - rc = RegQueryValueEx(roots_key, "KitsRoot81", NULL, NULL, (LPBYTE)priv->base.path81_ptr, &tmp_buf_len); - if (rc == ERROR_SUCCESS) { - priv->base.path81_len = tmp_buf_len - 1; - if (priv->base.path81_ptr[priv->base.path81_len - 1] == '\\') { - priv->base.path81_len -= 1; - } - } else { - free((void*)priv->base.path81_ptr); - priv->base.path81_ptr = nullptr; - } - } - - { - ZigFindWindowsSdkError err = find_10_version(priv); - if (err == ZigFindWindowsSdkErrorOutOfMemory) { - zig_free_windows_sdk(&priv->base); - return err; - } - } - { - ZigFindWindowsSdkError err = find_81_version(priv); - if (err == ZigFindWindowsSdkErrorOutOfMemory) { - zig_free_windows_sdk(&priv->base); - return err; - } - } - - { - ZigFindWindowsSdkError err = find_msvc_lib_dir(priv); - if (err == ZigFindWindowsSdkErrorOutOfMemory) { - zig_free_windows_sdk(&priv->base); - return err; - } - } - - *out_sdk = &priv->base; - return ZigFindWindowsSdkErrorNone; -} - -#else - -void zig_free_windows_sdk(struct ZigWindowsSDK *sdk) {} -ZigFindWindowsSdkError zig_find_windows_sdk(struct ZigWindowsSDK **out_sdk) { - return ZigFindWindowsSdkErrorNotFound; -} - -#endif diff --git a/src/windows_sdk.h b/src/windows_sdk.h deleted file mode 100644 index a4707b3de0..0000000000 --- a/src/windows_sdk.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2018 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#ifndef ZIG_WINDOWS_SDK_H -#define ZIG_WINDOWS_SDK_H - -#ifdef __cplusplus -#define ZIG_EXTERN_C extern "C" -#else -#define ZIG_EXTERN_C -#endif - -#include - -// ABI warning - src/windows_sdk.zig -struct ZigWindowsSDK { - const char *path10_ptr; - size_t path10_len; - - const char *version10_ptr; - size_t version10_len; - - const char *path81_ptr; - size_t path81_len; - - const char *version81_ptr; - size_t version81_len; - - const char *msvc_lib_dir_ptr; - size_t msvc_lib_dir_len; -}; - -// ABI warning - src/windows_sdk.zig -enum ZigFindWindowsSdkError { - ZigFindWindowsSdkErrorNone, - ZigFindWindowsSdkErrorOutOfMemory, - ZigFindWindowsSdkErrorNotFound, - ZigFindWindowsSdkErrorPathTooLong, -}; - -// ABI warning - src/windows_sdk.zig -ZIG_EXTERN_C enum ZigFindWindowsSdkError zig_find_windows_sdk(struct ZigWindowsSDK **out_sdk); - -// ABI warning - src/windows_sdk.zig -ZIG_EXTERN_C void zig_free_windows_sdk(struct ZigWindowsSDK *sdk); - -#endif diff --git a/src/windows_sdk.zig b/src/windows_sdk.zig index 3ff53dc2e1..42b3b57b4f 100644 --- a/src/windows_sdk.zig +++ b/src/windows_sdk.zig @@ -1,27 +1,755 @@ -// C API bindings for src/windows_sdk.h +const std = @import("std"); +const builtin = @import("builtin"); -pub const ZigWindowsSDK = extern struct { - path10_ptr: ?[*]const u8, - path10_len: usize, - version10_ptr: ?[*]const u8, - version10_len: usize, - path81_ptr: ?[*]const u8, - path81_len: usize, - version81_ptr: ?[*]const u8, - version81_len: usize, - msvc_lib_dir_ptr: ?[*]const u8, - msvc_lib_dir_len: usize, +const windows = std.os.windows; +const RRF = windows.advapi32.RRF; - pub const find = zig_find_windows_sdk; - pub const free = zig_free_windows_sdk; +const WINDOWS_KIT_REG_KEY = "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots"; - pub const FindError = enum(c_int) { - None, - OutOfMemory, - NotFound, - PathTooLong, - }; +// https://learn.microsoft.com/en-us/windows/win32/msi/productversion +const version_major_minor_max_length = "255.255".len; +// note(bratishkaerik): i think ProductVersion in registry (created by Visual Studio installer) also follows this rule +const product_version_max_length = version_major_minor_max_length + ".65535".len; - extern fn zig_find_windows_sdk(out_sdk: **ZigWindowsSDK) FindError; - extern fn zig_free_windows_sdk(sdk: *ZigWindowsSDK) void; +/// Iterates via `iterator` and collects all folders with names starting with `optional_prefix` +/// and similar to SemVer. Returns slice of folder names sorted in descending order. +/// Caller owns result. +fn iterateAndFilterBySemVer(iterator: *std.fs.IterableDir.Iterator, allocator: std.mem.Allocator, comptime optional_prefix: ?[]const u8) error{ OutOfMemory, VersionNotFound }![][]const u8 { + var dirs_filtered_list = std.ArrayList([]const u8).init(allocator); + errdefer { + for (dirs_filtered_list.items) |filtered_dir| allocator.free(filtered_dir); + dirs_filtered_list.deinit(); + } + + var normalized_name_buf: [std.fs.MAX_NAME_BYTES + ".0+build.0".len]u8 = undefined; + var normalized_name_fbs = std.io.fixedBufferStream(&normalized_name_buf); + const normalized_name_w = normalized_name_fbs.writer(); + iterate_folder: while (true) : (normalized_name_fbs.reset()) { + const maybe_entry = iterator.next() catch continue :iterate_folder; + const entry = maybe_entry orelse break :iterate_folder; + + if (entry.kind != .directory) + continue :iterate_folder; + + // invalidated on next iteration + const subfolder_name = blk: { + if (comptime optional_prefix) |prefix| { + if (!std.mem.startsWith(u8, entry.name, prefix)) continue :iterate_folder; + break :blk entry.name[prefix.len..]; + } else break :blk entry.name; + }; + + { // check if subfolder name looks similar to SemVer + switch (std.mem.count(u8, subfolder_name, ".")) { + 0 => normalized_name_w.print("{s}.0.0+build.0", .{subfolder_name}) catch unreachable, // 17 => 17.0.0+build.0 + 1 => if (std.mem.indexOfScalar(u8, subfolder_name, '_')) |underscore_pos| blk: { // 17.0_9e9cbb98 => 17.0.1+build.9e9cbb98 + var subfolder_name_tmp_copy_buf: [std.fs.MAX_NAME_BYTES]u8 = undefined; + const subfolder_name_tmp_copy = subfolder_name_tmp_copy_buf[0..subfolder_name.len]; + @memcpy(subfolder_name_tmp_copy, subfolder_name); + + subfolder_name_tmp_copy[underscore_pos] = '.'; // 17.0_9e9cbb98 => 17.0.9e9cbb98 + var subfolder_name_parts = std.mem.splitScalar(u8, subfolder_name_tmp_copy, '.'); // [ 17, 0, 9e9cbb98 ] + + const first = subfolder_name_parts.first(); // 17 + const second = subfolder_name_parts.next().?; // 0 + const third = subfolder_name_parts.rest(); // 9e9cbb98 + + break :blk normalized_name_w.print("{s}.{s}.1+build.{s}", .{ first, second, third }) catch unreachable; // [ 17, 0, 9e9cbb98 ] => 17.0.1+build.9e9cbb98 + } else normalized_name_w.print("{s}.0+build.0", .{subfolder_name}) catch unreachable, // 17.0 => 17.0.0+build.0 + else => normalized_name_w.print("{s}+build.0", .{subfolder_name}) catch unreachable, // 17.0.0 => 17.0.0+build.0 + } + const subfolder_name_normalized: []const u8 = normalized_name_fbs.getWritten(); + const sem_ver = std.SemanticVersion.parse(subfolder_name_normalized); + _ = sem_ver catch continue :iterate_folder; + } + // entry.name passed check + + const subfolder_name_allocated = try allocator.dupe(u8, subfolder_name); + errdefer allocator.free(subfolder_name_allocated); + try dirs_filtered_list.append(subfolder_name_allocated); + } + + var dirs_filtered_slice = try dirs_filtered_list.toOwnedSlice(); + // Keep in mind that order of these names is not guaranteed by Windows, + // so we cannot just reverse or "while (popOrNull())" this ArrayList. + std.mem.sortUnstable([]const u8, dirs_filtered_slice, {}, struct { + fn desc(_: void, lhs: []const u8, rhs: []const u8) bool { + return std.mem.order(u8, lhs, rhs) == .gt; + } + }.desc); + return dirs_filtered_slice; +} + +const RegistryUtf8 = struct { + key: windows.HKEY, + + /// Assert that `key` is valid UTF-8 string + pub fn openKey(key: []const u8) error{KeyNotFound}!RegistryUtf8 { + const key_utf16le: [:0]const u16 = key_utf16le: { + var key_utf16le_buf: [RegistryUtf16Le.key_name_max_len]u16 = undefined; + const key_utf16le_len: usize = std.unicode.utf8ToUtf16Le(key_utf16le_buf[0..], key) catch |err| switch (err) { + error.InvalidUtf8 => unreachable, + }; + key_utf16le_buf[key_utf16le_len] = 0; + break :key_utf16le key_utf16le_buf[0..key_utf16le_len :0]; + }; + + const registry_utf16le = try RegistryUtf16Le.openKey(key_utf16le); + return RegistryUtf8{ .key = registry_utf16le.key }; + } + + /// Closes key, after that usage is invalid + pub fn closeKey(self: *const RegistryUtf8) void { + const return_code_int: windows.HRESULT = windows.advapi32.RegCloseKey(self.key); + const return_code: windows.Win32Error = @enumFromInt(return_code_int); + switch (return_code) { + .SUCCESS => {}, + else => {}, + } + } + + /// Get string from registry. + /// Caller owns result. + pub fn getString(self: *const RegistryUtf8, allocator: std.mem.Allocator, subkey: []const u8, value_name: []const u8) error{ OutOfMemory, ValueNameNotFound, NotAString, StringNotFound }![]u8 { + const subkey_utf16le: [:0]const u16 = subkey_utf16le: { + var subkey_utf16le_buf: [RegistryUtf16Le.key_name_max_len]u16 = undefined; + const subkey_utf16le_len: usize = std.unicode.utf8ToUtf16Le(subkey_utf16le_buf[0..], subkey) catch unreachable; + subkey_utf16le_buf[subkey_utf16le_len] = 0; + break :subkey_utf16le subkey_utf16le_buf[0..subkey_utf16le_len :0]; + }; + + const value_name_utf16le: [:0]const u16 = value_name_utf16le: { + var value_name_utf16le_buf: [RegistryUtf16Le.value_name_max_len]u16 = undefined; + const value_name_utf16le_len: usize = std.unicode.utf8ToUtf16Le(value_name_utf16le_buf[0..], value_name) catch unreachable; + value_name_utf16le_buf[value_name_utf16le_len] = 0; + break :value_name_utf16le value_name_utf16le_buf[0..value_name_utf16le_len :0]; + }; + + const registry_utf16le = RegistryUtf16Le{ .key = self.key }; + const value_utf16le = try registry_utf16le.getString(allocator, subkey_utf16le, value_name_utf16le); + defer allocator.free(value_utf16le); + + var value_utf8: []u8 = std.unicode.utf16leToUtf8Alloc(allocator, value_utf16le) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + else => return error.StringNotFound, + }; + errdefer allocator.free(value_utf8); + + return value_utf8; + } + + /// Get DWORD (u32) from registry. + pub fn getDword(self: *const RegistryUtf8, subkey: []const u8, value_name: []const u8) error{ ValueNameNotFound, NotADword, DwordTooLong, DwordNotFound }!u32 { + const subkey_utf16le: [:0]const u16 = subkey_utf16le: { + var subkey_utf16le_buf: [RegistryUtf16Le.key_name_max_len]u16 = undefined; + const subkey_utf16le_len: usize = std.unicode.utf8ToUtf16Le(subkey_utf16le_buf[0..], subkey) catch unreachable; + subkey_utf16le_buf[subkey_utf16le_len] = 0; + break :subkey_utf16le subkey_utf16le_buf[0..subkey_utf16le_len :0]; + }; + + const value_name_utf16le: [:0]const u16 = value_name_utf16le: { + var value_name_utf16le_buf: [RegistryUtf16Le.value_name_max_len]u16 = undefined; + const value_name_utf16le_len: usize = std.unicode.utf8ToUtf16Le(value_name_utf16le_buf[0..], value_name) catch unreachable; + value_name_utf16le_buf[value_name_utf16le_len] = 0; + break :value_name_utf16le value_name_utf16le_buf[0..value_name_utf16le_len :0]; + }; + + const registry_utf16le = RegistryUtf16Le{ .key = self.key }; + return try registry_utf16le.getDword(subkey_utf16le, value_name_utf16le); + } + + /// Under private space with flags: + /// KEY_QUERY_VALUE and KEY_ENUMERATE_SUB_KEYS. + /// After finishing work, call `closeKey`. + pub fn loadFromPath(absolute_path: []const u8) error{KeyNotFound}!RegistryUtf8 { + const absolute_path_utf16le: [:0]const u16 = absolute_path_utf16le: { + var absolute_path_utf16le_buf: [RegistryUtf16Le.value_name_max_len]u16 = undefined; + const absolute_path_utf16le_len: usize = std.unicode.utf8ToUtf16Le(absolute_path_utf16le_buf[0..], absolute_path) catch unreachable; + absolute_path_utf16le_buf[absolute_path_utf16le_len] = 0; + break :absolute_path_utf16le absolute_path_utf16le_buf[0..absolute_path_utf16le_len :0]; + }; + + const registry_utf16le = try RegistryUtf16Le.loadFromPath(absolute_path_utf16le); + return RegistryUtf8{ .key = registry_utf16le.key }; + } +}; + +const RegistryUtf16Le = struct { + key: windows.HKEY, + + /// Includes root key (f.e. HKEY_LOCAL_MACHINE). + /// https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry-element-size-limits + pub const key_name_max_len = 255; + /// In Unicode characters. + /// https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry-element-size-limits + pub const value_name_max_len = 16_383; + + /// Under HKEY_LOCAL_MACHINE with flags: + /// KEY_QUERY_VALUE, KEY_WOW64_32KEY, and KEY_ENUMERATE_SUB_KEYS. + /// After finishing work, call `closeKey`. + fn openKey(key_utf16le: [:0]const u16) error{KeyNotFound}!RegistryUtf16Le { + var key: windows.HKEY = undefined; + const return_code_int: windows.HRESULT = windows.advapi32.RegOpenKeyExW( + windows.HKEY_LOCAL_MACHINE, + key_utf16le, + 0, + windows.KEY_QUERY_VALUE | windows.KEY_WOW64_32KEY | windows.KEY_ENUMERATE_SUB_KEYS, + &key, + ); + const return_code: windows.Win32Error = @enumFromInt(return_code_int); + switch (return_code) { + .SUCCESS => {}, + .FILE_NOT_FOUND => return error.KeyNotFound, + + else => return error.KeyNotFound, + } + return RegistryUtf16Le{ .key = key }; + } + + /// Closes key, after that usage is invalid + fn closeKey(self: *const RegistryUtf16Le) void { + const return_code_int: windows.HRESULT = windows.advapi32.RegCloseKey(self.key); + const return_code: windows.Win32Error = @enumFromInt(return_code_int); + switch (return_code) { + .SUCCESS => {}, + else => {}, + } + } + + /// Get string ([:0]const u16) from registry. + 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 { + var actual_type: windows.ULONG = undefined; + + // Calculating length to allocate + var value_utf16le_buf_size: u32 = 0; // in bytes, including any terminating NUL character or characters. + var return_code_int: windows.HRESULT = windows.advapi32.RegGetValueW( + self.key, + subkey_utf16le, + value_name_utf16le, + RRF.RT_REG_SZ, + &actual_type, + null, + &value_utf16le_buf_size, + ); + + // Check returned code and type + var return_code: windows.Win32Error = @enumFromInt(return_code_int); + switch (return_code) { + .SUCCESS => std.debug.assert(value_utf16le_buf_size != 0), + .MORE_DATA => unreachable, // We are only reading length + .FILE_NOT_FOUND => return error.ValueNameNotFound, + .INVALID_PARAMETER => unreachable, // We didn't combine RRF.SUBKEY_WOW6464KEY and RRF.SUBKEY_WOW6432KEY + else => return error.StringNotFound, + } + switch (actual_type) { + windows.REG.SZ => {}, + else => return error.NotAString, + } + + var value_utf16le_buf: []u16 = try allocator.alloc(u16, std.math.divCeil(u32, value_utf16le_buf_size, 2) catch unreachable); + errdefer allocator.free(value_utf16le_buf); + + return_code_int = windows.advapi32.RegGetValueW( + self.key, + subkey_utf16le, + value_name_utf16le, + RRF.RT_REG_SZ, + &actual_type, + value_utf16le_buf.ptr, + &value_utf16le_buf_size, + ); + + // Check returned code and (just in case) type again. + return_code = @enumFromInt(return_code_int); + switch (return_code) { + .SUCCESS => {}, + .MORE_DATA => unreachable, // Calculated first time length should be enough, even overestimated + .FILE_NOT_FOUND => return error.ValueNameNotFound, + .INVALID_PARAMETER => unreachable, // We didn't combine RRF.SUBKEY_WOW6464KEY and RRF.SUBKEY_WOW6432KEY + else => return error.StringNotFound, + } + switch (actual_type) { + windows.REG.SZ => {}, + else => return error.NotAString, + } + + const value_utf16le: []const u16 = value_utf16le: { + // note(bratishkaerik): somehow returned value in `buf_len` is overestimated by Windows and contains extra space + // we will just search for zero termination and forget length + // Windows sure is strange + const value_utf16le_overestimated: [*:0]const u16 = @ptrCast(value_utf16le_buf.ptr); + break :value_utf16le std.mem.span(value_utf16le_overestimated); + }; + + _ = allocator.resize(value_utf16le_buf, value_utf16le.len); + return value_utf16le; + } + + /// Get DWORD (u32) from registry. + fn getDword(self: *const RegistryUtf16Le, subkey_utf16le: [:0]const u16, value_name_utf16le: [:0]const u16) error{ ValueNameNotFound, NotADword, DwordTooLong, DwordNotFound }!u32 { + var actual_type: windows.ULONG = undefined; + var reg_size: u32 = @sizeOf(u32); + var reg_value: u32 = 0; + + const return_code_int: windows.HRESULT = windows.advapi32.RegGetValueW( + self.key, + subkey_utf16le, + value_name_utf16le, + RRF.RT_REG_DWORD, + &actual_type, + ®_value, + ®_size, + ); + const return_code: windows.Win32Error = @enumFromInt(return_code_int); + switch (return_code) { + .SUCCESS => {}, + .MORE_DATA => return error.DwordTooLong, + .FILE_NOT_FOUND => return error.ValueNameNotFound, + .INVALID_PARAMETER => unreachable, // We didn't combine RRF.SUBKEY_WOW6464KEY and RRF.SUBKEY_WOW6432KEY + else => return error.DwordNotFound, + } + + switch (actual_type) { + windows.REG.DWORD => {}, + else => return error.NotADword, + } + + return reg_value; + } + + /// Under private space with flags: + /// KEY_QUERY_VALUE and KEY_ENUMERATE_SUB_KEYS. + /// After finishing work, call `closeKey`. + fn loadFromPath(absolute_path_as_utf16le: [:0]const u16) error{KeyNotFound}!RegistryUtf16Le { + var key: windows.HKEY = undefined; + + const return_code_int: windows.HRESULT = std.os.windows.advapi32.RegLoadAppKeyW( + absolute_path_as_utf16le, + &key, + windows.KEY_QUERY_VALUE | windows.KEY_ENUMERATE_SUB_KEYS, + 0, + 0, + ); + const return_code: windows.Win32Error = @enumFromInt(return_code_int); + switch (return_code) { + .SUCCESS => {}, + else => return error.KeyNotFound, + } + + return RegistryUtf16Le{ .key = key }; + } +}; + +pub const Windows10Sdk = struct { + path: []const u8, + version: []const u8, + + /// Find path and version of Windows 10 SDK. + /// Caller owns the result's fields. + /// After finishing work, call `free(allocator)`. + fn find(allocator: std.mem.Allocator) error{ OutOfMemory, Windows10SdkNotFound, PathTooLong, VersionTooLong }!Windows10Sdk { + const v10_key = RegistryUtf8.openKey("SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v10.0") catch |err| switch (err) { + error.KeyNotFound => return error.Windows10SdkNotFound, + }; + defer v10_key.closeKey(); + + const path: []const u8 = path10: { + var path_maybe_with_trailing_slash = v10_key.getString(allocator, "", "InstallationFolder") catch |err| switch (err) { + error.NotAString => return error.Windows10SdkNotFound, + error.ValueNameNotFound => return error.Windows10SdkNotFound, + error.StringNotFound => return error.Windows10SdkNotFound, + + error.OutOfMemory => return error.OutOfMemory, + }; + + if (path_maybe_with_trailing_slash.len > std.fs.MAX_PATH_BYTES or !std.fs.path.isAbsolute(path_maybe_with_trailing_slash)) { + allocator.free(path_maybe_with_trailing_slash); + return error.PathTooLong; + } + + var path = std.ArrayList(u8).fromOwnedSlice(allocator, path_maybe_with_trailing_slash); + errdefer path.deinit(); + + // String might contain trailing slash, so trim it here + if (path.items.len > "C:\\".len and path.getLast() == '\\') _ = path.pop(); + + const path_without_trailing_slash = try path.toOwnedSlice(); + break :path10 path_without_trailing_slash; + }; + errdefer allocator.free(path); + + const version: []const u8 = version10: { + + // note(dimenus): Microsoft doesn't include the .0 in the ProductVersion key.... + var version_without_0 = v10_key.getString(allocator, "", "ProductVersion") catch |err| switch (err) { + error.NotAString => return error.Windows10SdkNotFound, + error.ValueNameNotFound => return error.Windows10SdkNotFound, + error.StringNotFound => return error.Windows10SdkNotFound, + + error.OutOfMemory => return error.OutOfMemory, + }; + if (version_without_0.len + ".0".len > product_version_max_length) { + allocator.free(version_without_0); + return error.VersionTooLong; + } + + var version = std.ArrayList(u8).fromOwnedSlice(allocator, version_without_0); + errdefer version.deinit(); + + try version.appendSlice(".0"); + + const version_with_0 = try version.toOwnedSlice(); + break :version10 version_with_0; + }; + errdefer allocator.free(version); + + return Windows10Sdk{ .path = path, .version = version }; + } + + /// Check whether this version is enumerated in registry. + fn isValidVersion(windows10sdk: *const Windows10Sdk) bool { + var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; + const reg_query_as_utf8 = std.fmt.bufPrint(buf[0..], "{s}\\{s}\\Installed Options", .{ WINDOWS_KIT_REG_KEY, windows10sdk.version }) catch |err| switch (err) { + error.NoSpaceLeft => return false, + }; + + const options_key = RegistryUtf8.openKey(reg_query_as_utf8) catch |err| switch (err) { + error.KeyNotFound => return false, + }; + defer options_key.closeKey(); + + const option_name = comptime switch (builtin.target.cpu.arch) { + .arm, .armeb => "OptionId.DesktopCPParm", + .aarch64 => "OptionId.DesktopCPParm64", + .x86_64 => "OptionId.DesktopCPPx64", + .x86 => "OptionId.DesktopCPPx86", + else => |tag| @compileError("Windows 10 SDK cannot be detected on architecture " ++ tag), + }; + + const reg_value = options_key.getDword("", option_name) catch return false; + return (reg_value == 1); + } + + fn free(self: *const Windows10Sdk, allocator: std.mem.Allocator) void { + allocator.free(self.path); + allocator.free(self.version); + } +}; + +pub const Windows81Sdk = struct { + path: []const u8, + version: []const u8, + + /// Find path and version of Windows 8.1 SDK. + /// Caller owns the result's fields. + /// After finishing work, call `free(allocator)`. + fn find(allocator: std.mem.Allocator, roots_key: *const RegistryUtf8) error{ OutOfMemory, Windows81SdkNotFound, PathTooLong, VersionTooLong }!Windows81Sdk { + const path: []const u8 = path81: { + var path_maybe_with_trailing_slash = roots_key.getString(allocator, "", "KitsRoot81") catch |err| switch (err) { + error.NotAString => return error.Windows81SdkNotFound, + error.ValueNameNotFound => return error.Windows81SdkNotFound, + error.StringNotFound => return error.Windows81SdkNotFound, + + error.OutOfMemory => return error.OutOfMemory, + }; + if (path_maybe_with_trailing_slash.len > std.fs.MAX_PATH_BYTES or !std.fs.path.isAbsolute(path_maybe_with_trailing_slash)) { + allocator.free(path_maybe_with_trailing_slash); + return error.PathTooLong; + } + + var path = std.ArrayList(u8).fromOwnedSlice(allocator, path_maybe_with_trailing_slash); + errdefer path.deinit(); + + // String might contain trailing slash, so trim it here + if (path.items.len > "C:\\".len and path.getLast() == '\\') _ = path.pop(); + + const path_without_trailing_slash = try path.toOwnedSlice(); + break :path81 path_without_trailing_slash; + }; + errdefer allocator.free(path); + + const version: []const u8 = version81: { + var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; + const sdk_lib_dir_path = std.fmt.bufPrint(buf[0..], "{s}\\Lib\\", .{path}) catch |err| switch (err) { + error.NoSpaceLeft => return error.PathTooLong, + }; + if (!std.fs.path.isAbsolute(sdk_lib_dir_path)) return error.Windows81SdkNotFound; + + // enumerate files in sdk path looking for latest version + var sdk_lib_dir = std.fs.openIterableDirAbsolute(sdk_lib_dir_path, .{}) catch |err| switch (err) { + error.NameTooLong => return error.PathTooLong, + else => return error.Windows81SdkNotFound, + }; + defer sdk_lib_dir.close(); + + var iterator = sdk_lib_dir.iterate(); + const versions = iterateAndFilterBySemVer(&iterator, allocator, "winv") catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.VersionNotFound => return error.Windows81SdkNotFound, + }; + defer { + for (versions) |version| allocator.free(version); + allocator.free(versions); + } + const latest_version = try allocator.dupe(u8, versions[0]); + break :version81 latest_version; + }; + errdefer allocator.free(version); + + return Windows81Sdk{ .path = path, .version = version }; + } + + fn free(self: *const Windows81Sdk, allocator: std.mem.Allocator) void { + allocator.free(self.path); + allocator.free(self.version); + } +}; + +pub const ZigWindowsSDK = struct { + windows10sdk: ?Windows10Sdk, + windows81sdk: ?Windows81Sdk, + msvc_lib_dir: ?[]const u8, + + /// Find path and version of Windows 10 SDK and Windows 8.1 SDK, and find path to MSVC's `lib/` directory. + /// Caller owns the result's fields. + /// After finishing work, call `free(allocator)`. + pub fn find(allocator: std.mem.Allocator) error{ OutOfMemory, NotFound, PathTooLong }!ZigWindowsSDK { + if (builtin.os.tag != .windows) return error.NotFound; + + //note(dimenus): If this key doesn't exist, neither the Win 8 SDK nor the Win 10 SDK is installed + const roots_key = RegistryUtf8.openKey(WINDOWS_KIT_REG_KEY) catch |err| switch (err) { + error.KeyNotFound => return error.NotFound, + }; + defer roots_key.closeKey(); + + const windows10sdk: ?Windows10Sdk = blk: { + const windows10sdk = Windows10Sdk.find(allocator) catch |err| switch (err) { + error.Windows10SdkNotFound, + error.PathTooLong, + error.VersionTooLong, + => break :blk null, + error.OutOfMemory => return error.OutOfMemory, + }; + const is_valid_version = windows10sdk.isValidVersion(); + if (!is_valid_version) break :blk null; + break :blk windows10sdk; + }; + errdefer if (windows10sdk) |*w| w.free(allocator); + + const windows81sdk: ?Windows81Sdk = blk: { + const windows81sdk = Windows81Sdk.find(allocator, &roots_key) catch |err| switch (err) { + error.Windows81SdkNotFound => break :blk null, + error.PathTooLong => break :blk null, + error.VersionTooLong => break :blk null, + error.OutOfMemory => return error.OutOfMemory, + }; + // no check + break :blk windows81sdk; + }; + errdefer if (windows81sdk) |*w| w.free(allocator); + + const msvc_lib_dir: ?[]const u8 = MsvcLibDir.find(allocator) catch |err| switch (err) { + error.MsvcLibDirNotFound => null, + error.PathTooLong => null, + error.OutOfMemory => return error.OutOfMemory, + }; + errdefer allocator.free(msvc_lib_dir); + + return ZigWindowsSDK{ + .windows10sdk = windows10sdk, + .windows81sdk = windows81sdk, + .msvc_lib_dir = msvc_lib_dir, + }; + } + + pub fn free(self: *const ZigWindowsSDK, allocator: std.mem.Allocator) void { + if (self.windows10sdk) |*w10sdk| { + w10sdk.free(allocator); + } + if (self.windows81sdk) |*w81sdk| { + w81sdk.free(allocator); + } + if (self.msvc_lib_dir) |msvc_lib_dir| { + allocator.free(msvc_lib_dir); + } + } +}; + +const MsvcLibDir = struct { + // 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 + fn findViaRegistry(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }![]const u8 { + + // %localappdata%\Microsoft\VisualStudio\ + // %appdata%\Local\Microsoft\VisualStudio\ + const visualstudio_folder_path = std.fs.getAppDataDir(allocator, "Microsoft\\VisualStudio\\") catch return error.PathNotFound; + defer allocator.free(visualstudio_folder_path); + + const vs_versions: []const []const u8 = vs_versions: { + if (!std.fs.path.isAbsolute(visualstudio_folder_path)) return error.PathNotFound; + // enumerate folders that contain `privateregistry.bin`, looking for all versions + // f.i. %localappdata%\Microsoft\VisualStudio\17.0_9e9cbb98\ + var visualstudio_folder = std.fs.openIterableDirAbsolute(visualstudio_folder_path, .{}) catch return error.PathNotFound; + defer visualstudio_folder.close(); + + var iterator = visualstudio_folder.iterate(); + const versions = iterateAndFilterBySemVer(&iterator, allocator, null) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.VersionNotFound => return error.PathNotFound, + }; + break :vs_versions versions; + }; + defer { + for (vs_versions) |vs_version| allocator.free(vs_version); + allocator.free(vs_versions); + } + var config_subkey_buf: [RegistryUtf16Le.key_name_max_len * 2]u8 = undefined; + const source_directories: []const u8 = source_directories: for (vs_versions) |vs_version| { + const privateregistry_absolute_path = std.fs.path.join(allocator, &.{ visualstudio_folder_path, vs_version, "privateregistry.bin" }) catch continue; + defer allocator.free(privateregistry_absolute_path); + if (!std.fs.path.isAbsolute(privateregistry_absolute_path)) continue; + + const visualstudio_registry = RegistryUtf8.loadFromPath(privateregistry_absolute_path) catch continue; + defer visualstudio_registry.closeKey(); + + const config_subkey = std.fmt.bufPrint(config_subkey_buf[0..], "Software\\Microsoft\\VisualStudio\\{s}_Config", .{vs_version}) catch unreachable; + + var source_directories_value = visualstudio_registry.getString(allocator, config_subkey, "Source Directories") catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + else => continue, + }; + 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 + allocator.free(source_directories_value); + continue; + } + + break :source_directories source_directories_value; + } else return error.PathNotFound; + defer allocator.free(source_directories); + + var source_directories_splitted = std.mem.splitScalar(u8, source_directories, ';'); + + const msvc_dir: []const u8 = msvc_dir: { + var msvc_include_dir_maybe_with_trailing_slash = try allocator.dupe(u8, source_directories_splitted.first()); + + 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)) { + allocator.free(msvc_include_dir_maybe_with_trailing_slash); + return error.PathNotFound; + } + + var msvc_dir = std.ArrayList(u8).fromOwnedSlice(allocator, msvc_include_dir_maybe_with_trailing_slash); + errdefer msvc_dir.deinit(); + + // String might contain trailing slash, so trim it here + if (msvc_dir.items.len > "C:\\".len and msvc_dir.getLast() == '\\') _ = msvc_dir.pop(); + + // Remove `\include` at the end of path + if (std.mem.endsWith(u8, msvc_dir.items, "\\include")) { + msvc_dir.shrinkRetainingCapacity(msvc_dir.items.len - "\\include".len); + } + + const folder_with_arch = "\\Lib\\" ++ comptime switch (builtin.target.cpu.arch) { + .x86 => "x86", + .x86_64 => "x64", + .arm, .armeb => "arm", + .aarch64 => "arm64", + else => |tag| @compileError("MSVC lib dir cannot be detected on architecture " ++ tag), + }; + + try msvc_dir.appendSlice(folder_with_arch); + const msvc_dir_with_arch = try msvc_dir.toOwnedSlice(); + break :msvc_dir msvc_dir_with_arch; + }; + errdefer allocator.free(msvc_dir); + + return msvc_dir; + } + + fn findViaVs7Key(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }![]const u8 { + var base_path: std.ArrayList(u8) = base_path: { + try_env: { + var env_map = std.process.getEnvMap(allocator) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + else => break :try_env, + }; + defer env_map.deinit(); + + if (env_map.get("VS140COMNTOOLS")) |VS140COMNTOOLS| { + if (VS140COMNTOOLS.len < "C:\\Common7\\Tools".len) break :try_env; + if (!std.fs.path.isAbsolute(VS140COMNTOOLS)) break :try_env; + var list = std.ArrayList(u8).init(allocator); + errdefer list.deinit(); + + try list.appendSlice(VS140COMNTOOLS); // C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\Tools + // String might contain trailing slash, so trim it here + if (list.items.len > "C:\\".len and list.getLast() == '\\') _ = list.pop(); + list.shrinkRetainingCapacity(list.items.len - "\\Common7\\Tools".len); // C:\Program Files (x86)\Microsoft Visual Studio 14.0 + break :base_path list; + } + } + + const vs7_key = RegistryUtf8.openKey("SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7") catch return error.PathNotFound; + defer vs7_key.closeKey(); + try_vs7_key: { + var path_maybe_with_trailing_slash = vs7_key.getString(allocator, "", "14.0") catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + else => break :try_vs7_key, + }; + + if (path_maybe_with_trailing_slash.len > std.fs.MAX_PATH_BYTES or !std.fs.path.isAbsolute(path_maybe_with_trailing_slash)) { + allocator.free(path_maybe_with_trailing_slash); + break :try_vs7_key; + } + + var path = std.ArrayList(u8).fromOwnedSlice(allocator, path_maybe_with_trailing_slash); + errdefer path.deinit(); + + // String might contain trailing slash, so trim it here + if (path.items.len > "C:\\".len and path.getLast() == '\\') _ = path.pop(); + break :base_path path; + } + return error.PathNotFound; + }; + errdefer base_path.deinit(); + + const folder_with_arch = "\\VC\\lib\\" ++ comptime switch (builtin.target.cpu.arch) { + .x86 => "", //x86 is in the root of the Lib folder + .x86_64 => "amd64", + .arm, .armeb => "arm", + .aarch64 => "arm64", + else => |tag| @compileError("MSVC lib dir cannot be detected on architecture " ++ tag), + }; + try base_path.appendSlice(folder_with_arch); + + const full_path = try base_path.toOwnedSlice(); + return full_path; + } + + /// Find path to MSVC's `lib/` directory. + /// Caller owns the result. + pub fn find(allocator: std.mem.Allocator) error{ OutOfMemory, MsvcLibDirNotFound, PathTooLong }![]const u8 { + const full_path = MsvcLibDir.findViaRegistry(allocator) catch |err1| switch (err1) { + error.OutOfMemory => return error.OutOfMemory, + error.PathNotFound => MsvcLibDir.findViaVs7Key(allocator) catch |err2| switch (err2) { + error.OutOfMemory => return error.OutOfMemory, + error.PathNotFound => return error.MsvcLibDirNotFound, + }, + }; + errdefer allocator.free(full_path); + std.debug.assert(std.fs.path.isAbsolute(full_path)); // should be already handled in `findVia*` + + var dir = std.fs.openDirAbsolute(full_path, .{}) catch |err| switch (err) { + error.NameTooLong => return error.PathTooLong, + else => return error.MsvcLibDirNotFound, + }; + defer dir.close(); + + const stat = dir.statFile("vcruntime.lib") catch |err| switch (err) { + error.NameTooLong => return error.PathTooLong, + else => return error.MsvcLibDirNotFound, + }; + if (stat.kind != .file) + return error.MsvcLibDirNotFound; + + return full_path; + } };