mirror of
https://github.com/ziglang/zig.git
synced 2025-02-08 21:50:33 +00:00
Merge pull request #15657 from BratishkaErik/windows-sdk-finder-port-to-zig
src/windows_sdk.cpp: port to Zig
This commit is contained in:
commit
77b96231a6
@ -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
|
||||
|
@ -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{
|
||||
|
@ -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;
|
||||
|
@ -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];
|
||||
}
|
||||
|
@ -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 <windows.h>
|
||||
#include <fcntl.h>
|
||||
#include <io.h>
|
||||
#include <shellapi.h>
|
||||
|
||||
// Standard headers
|
||||
#include <stdio.h>
|
||||
|
||||
// COM support header files
|
||||
#include <comdef.h>
|
||||
|
||||
// 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
|
||||
//
|
||||
/// <summary>
|
||||
/// The state of an instance.
|
||||
/// </summary>
|
||||
enum InstanceState
|
||||
{
|
||||
/// <summary>
|
||||
/// The instance state has not been determined.
|
||||
/// </summary>
|
||||
eNone = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The instance installation path exists.
|
||||
/// </summary>
|
||||
eLocal = 1,
|
||||
|
||||
/// <summary>
|
||||
/// A product is registered to the instance.
|
||||
/// </summary>
|
||||
eRegistered = 2,
|
||||
|
||||
/// <summary>
|
||||
/// No reboot is required for the instance.
|
||||
/// </summary>
|
||||
eNoRebootRequired = 4,
|
||||
|
||||
/// <summary>
|
||||
/// No errors were reported for the instance.
|
||||
/// </summary>
|
||||
eNoErrors = 8,
|
||||
|
||||
/// <summary>
|
||||
/// The instance represents a complete install.
|
||||
/// </summary>
|
||||
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)
|
||||
/// <summary>
|
||||
/// Information about an instance of a product.
|
||||
/// </summary>
|
||||
struct DECLSPEC_UUID("B41463C3-8866-43B5-BC33-2B0676F7F42E") DECLSPEC_NOVTABLE ISetupInstance : public IUnknown
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the instance identifier (should match the name of the parent instance directory).
|
||||
/// </summary>
|
||||
/// <param name="pbstrInstanceId">The instance identifier.</param>
|
||||
/// <returns>Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist.</returns>
|
||||
STDMETHOD(GetInstanceId)(
|
||||
_Out_ BSTR* pbstrInstanceId
|
||||
) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the local date and time when the installation was originally installed.
|
||||
/// </summary>
|
||||
/// <param name="pInstallDate">The local date and time when the installation was originally installed.</param>
|
||||
/// <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>
|
||||
STDMETHOD(GetInstallDate)(
|
||||
_Out_ LPFILETIME pInstallDate
|
||||
) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the unique name of the installation, often indicating the branch and other information used for telemetry.
|
||||
/// </summary>
|
||||
/// <param name="pbstrInstallationName">The unique name of the installation, often indicating the branch and other information used for telemetry.</param>
|
||||
/// <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>
|
||||
STDMETHOD(GetInstallationName)(
|
||||
_Out_ BSTR* pbstrInstallationName
|
||||
) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path to the installation root of the product.
|
||||
/// </summary>
|
||||
/// <param name="pbstrInstallationPath">The path to the installation root of the product.</param>
|
||||
/// <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>
|
||||
STDMETHOD(GetInstallationPath)(
|
||||
_Out_ BSTR* pbstrInstallationPath
|
||||
) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the version of the product installed in this instance.
|
||||
/// </summary>
|
||||
/// <param name="pbstrInstallationVersion">The version of the product installed in this instance.</param>
|
||||
/// <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>
|
||||
STDMETHOD(GetInstallationVersion)(
|
||||
_Out_ BSTR* pbstrInstallationVersion
|
||||
) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the display name (title) of the product installed in this instance.
|
||||
/// </summary>
|
||||
/// <param name="lcid">The LCID for the display name.</param>
|
||||
/// <param name="pbstrDisplayName">The display name (title) of the product installed in this instance.</param>
|
||||
/// <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>
|
||||
STDMETHOD(GetDisplayName)(
|
||||
_In_ LCID lcid,
|
||||
_Out_ BSTR* pbstrDisplayName
|
||||
) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the description of the product installed in this instance.
|
||||
/// </summary>
|
||||
/// <param name="lcid">The LCID for the description.</param>
|
||||
/// <param name="pbstrDescription">The description of the product installed in this instance.</param>
|
||||
/// <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>
|
||||
STDMETHOD(GetDescription)(
|
||||
_In_ LCID lcid,
|
||||
_Out_ BSTR* pbstrDescription
|
||||
) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the optional relative path to the root path of the instance.
|
||||
/// </summary>
|
||||
/// <param name="pwszRelativePath">A relative path within the instance to resolve, or NULL to get the root path.</param>
|
||||
/// <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>
|
||||
/// <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>
|
||||
STDMETHOD(ResolvePath)(
|
||||
_In_opt_z_ LPCOLESTR pwszRelativePath,
|
||||
_Out_ BSTR* pbstrAbsolutePath
|
||||
) = 0;
|
||||
};
|
||||
#endif
|
||||
|
||||
EXTERN_C const IID IID_ISetupInstance2;
|
||||
|
||||
#if defined(__cplusplus) && !defined(CINTERFACE)
|
||||
/// <summary>
|
||||
/// Information about an instance of a product.
|
||||
/// </summary>
|
||||
struct DECLSPEC_UUID("89143C9A-05AF-49B0-B717-72E218A2185C") DECLSPEC_NOVTABLE ISetupInstance2 : public ISetupInstance
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the state of the instance.
|
||||
/// </summary>
|
||||
/// <param name="pState">The state of the instance.</param>
|
||||
/// <returns>Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist.</returns>
|
||||
STDMETHOD(GetState)(
|
||||
_Out_ InstanceState* pState
|
||||
) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets an array of package references registered to the instance.
|
||||
/// </summary>
|
||||
/// <param name="ppsaPackages">Pointer to an array of <see cref="ISetupPackageReference"/>.</param>
|
||||
/// <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>
|
||||
STDMETHOD(GetPackages)(
|
||||
_Out_ LPSAFEARRAY* ppsaPackages
|
||||
) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a pointer to the <see cref="ISetupPackageReference"/> that represents the registered product.
|
||||
/// </summary>
|
||||
/// <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>
|
||||
/// <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>
|
||||
STDMETHOD(GetProduct)(
|
||||
_Outptr_result_maybenull_ ISetupPackageReference** ppPackage
|
||||
) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the relative path to the product application, if available.
|
||||
/// </summary>
|
||||
/// <param name="pbstrProductPath">The relative path to the product application, if available.</param>
|
||||
/// <returns>Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist.</returns>
|
||||
STDMETHOD(GetProductPath)(
|
||||
_Outptr_result_maybenull_ BSTR* pbstrProductPath
|
||||
) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the error state of the instance, if available.
|
||||
/// </summary>
|
||||
/// <param name="pErrorState">The error state of the instance, if available.</param>
|
||||
/// <returns>Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist.</returns>
|
||||
STDMETHOD(GetErrors)(
|
||||
_Outptr_result_maybenull_ ISetupErrorState** ppErrorState
|
||||
) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the instance can be launched.
|
||||
/// </summary>
|
||||
/// <param name="pfIsLaunchable">Whether the instance can be launched.</param>
|
||||
/// <returns>Standard HRESULT indicating success or failure.</returns>
|
||||
/// <remarks>
|
||||
/// An instance could have had errors during install but still be launched. Some features may not work correctly, but others will.
|
||||
/// </remarks>
|
||||
STDMETHOD(IsLaunchable)(
|
||||
_Out_ VARIANT_BOOL* pfIsLaunchable
|
||||
) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the instance is complete.
|
||||
/// </summary>
|
||||
/// <param name="pfIsLaunchable">Whether the instance is complete.</param>
|
||||
/// <returns>Standard HRESULT indicating success or failure.</returns>
|
||||
/// <remarks>
|
||||
/// An instance is complete if it had no errors during install, resume, or repair.
|
||||
/// </remarks>
|
||||
STDMETHOD(IsComplete)(
|
||||
_Out_ VARIANT_BOOL* pfIsComplete
|
||||
) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets product-specific properties.
|
||||
/// </summary>
|
||||
/// <param name="ppProperties">A pointer to an instance of <see cref="ISetupPropertyStore"/>. This may be NULL if no properties are defined.</param>
|
||||
/// <returns>Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist.</returns>
|
||||
STDMETHOD(GetProperties)(
|
||||
_Outptr_result_maybenull_ ISetupPropertyStore** ppProperties
|
||||
) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the directory path to the setup engine that installed the instance.
|
||||
/// </summary>
|
||||
/// <param name="pbstrEnginePath">The directory path to the setup engine that installed the instance.</param>
|
||||
/// <returns>Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist.</returns>
|
||||
STDMETHOD(GetEnginePath)(
|
||||
_Outptr_result_maybenull_ BSTR* pbstrEnginePath
|
||||
) = 0;
|
||||
};
|
||||
#endif
|
||||
|
||||
EXTERN_C const IID IID_ISetupInstanceCatalog;
|
||||
|
||||
#if defined(__cplusplus) && !defined(CINTERFACE)
|
||||
/// <summary>
|
||||
/// Information about a catalog used to install an instance.
|
||||
/// </summary>
|
||||
struct DECLSPEC_UUID("9AD8E40F-39A2-40F1-BF64-0A6C50DD9EEB") DECLSPEC_NOVTABLE ISetupInstanceCatalog : public IUnknown
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets catalog information properties.
|
||||
/// </summary>
|
||||
/// <param name="ppCatalogInfo">A pointer to an instance of <see cref="ISetupPropertyStore"/>.</param>
|
||||
/// <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>
|
||||
STDMETHOD(GetCatalogInfo)(
|
||||
_Out_ ISetupPropertyStore** ppCatalogInfo
|
||||
) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the catalog is a prerelease.
|
||||
/// </summary>
|
||||
/// <param name="pfIsPrerelease">Whether the catalog for the instance is a prerelease version.</param>
|
||||
/// <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>
|
||||
STDMETHOD(IsPrerelease)(
|
||||
_Out_ VARIANT_BOOL* pfIsPrerelease
|
||||
) = 0;
|
||||
};
|
||||
#endif
|
||||
|
||||
EXTERN_C const IID IID_ISetupLocalizedProperties;
|
||||
|
||||
#if defined(__cplusplus) && !defined(CINTERFACE)
|
||||
/// <summary>
|
||||
/// Provides localized properties of an instance of a product.
|
||||
/// </summary>
|
||||
struct DECLSPEC_UUID("F4BD7382-FE27-4AB4-B974-9905B2A148B0") DECLSPEC_NOVTABLE ISetupLocalizedProperties : public IUnknown
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets localized product-specific properties.
|
||||
/// </summary>
|
||||
/// <param name="ppLocalizedProperties">A pointer to an instance of <see cref="ISetupLocalizedPropertyStore"/>. This may be NULL if no properties are defined.</param>
|
||||
/// <returns>Standard HRESULT indicating success or failure.</returns>
|
||||
STDMETHOD(GetLocalizedProperties)(
|
||||
_Outptr_result_maybenull_ ISetupLocalizedPropertyStore** ppLocalizedProperties
|
||||
) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets localized channel-specific properties.
|
||||
/// </summary>
|
||||
/// <param name="ppLocalizedChannelProperties">A pointer to an instance of <see cref="ISetupLocalizedPropertyStore"/>. This may be NULL if no channel properties are defined.</param>
|
||||
/// <returns>Standard HRESULT indicating success or failure.</returns>
|
||||
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
|
||||
|
||||
/// <summary>
|
||||
/// An enumerator of installed <see cref="ISetupInstance"/> objects.
|
||||
/// </summary>
|
||||
struct DECLSPEC_UUID("6380BCFF-41D3-4B2E-8B2E-BF8A6810C848") DECLSPEC_NOVTABLE IEnumSetupInstances : public IUnknown
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves the next set of product instances in the enumeration sequence.
|
||||
/// </summary>
|
||||
/// <param name="celt">The number of product instances to retrieve.</param>
|
||||
/// <param name="rgelt">A pointer to an array of <see cref="ISetupInstance"/>.</param>
|
||||
/// <param name="pceltFetched">A pointer to the number of product instances retrieved. If <paramref name="celt"/> is 1 this parameter may be NULL.</param>
|
||||
/// <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>
|
||||
STDMETHOD(Next)(
|
||||
_In_ ULONG celt,
|
||||
_Out_writes_to_(celt, *pceltFetched) ISetupInstance** rgelt,
|
||||
_Out_opt_ _Deref_out_range_(0, celt) ULONG* pceltFetched
|
||||
) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Skips the next set of product instances in the enumeration sequence.
|
||||
/// </summary>
|
||||
/// <param name="celt">The number of product instances to skip.</param>
|
||||
/// <returns>S_OK if the number of elements could be skipped; otherwise, S_FALSE;</returns>
|
||||
STDMETHOD(Skip)(
|
||||
_In_ ULONG celt
|
||||
) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Resets the enumeration sequence to the beginning.
|
||||
/// </summary>
|
||||
/// <returns>Always returns S_OK;</returns>
|
||||
STDMETHOD(Reset)(void) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="ppenum">A pointer to a pointer to a new <see cref="IEnumSetupInstances"/> interface. If the method fails, this parameter is undefined.</param>
|
||||
/// <returns>S_OK if a clone was returned; otherwise, E_OUTOFMEMORY.</returns>
|
||||
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
|
||||
|
||||
/// <summary>
|
||||
/// Gets information about product instances installed on the machine.
|
||||
/// </summary>
|
||||
struct DECLSPEC_UUID("42843719-DB4C-46C2-8E7C-64F1816EFD5B") DECLSPEC_NOVTABLE ISetupConfiguration : public IUnknown
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumerates all launchable product instances installed.
|
||||
/// </summary>
|
||||
/// <param name="ppEnumInstances">An enumeration of completed, installed product instances.</param>
|
||||
/// <returns>Standard HRESULT indicating success or failure.</returns>
|
||||
STDMETHOD(EnumInstances)(
|
||||
_Out_ IEnumSetupInstances** ppEnumInstances
|
||||
) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the instance for the current process path.
|
||||
/// </summary>
|
||||
/// <param name="ppInstance">The instance for the current process path.</param>
|
||||
/// <returns>
|
||||
/// The instance for the current process path, or E_NOTFOUND if not found.
|
||||
/// The <see cref="ISetupInstance::GetState"/> may indicate the instance is invalid.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// The returned instance may not be launchable.
|
||||
/// </remarks>
|
||||
STDMETHOD(GetInstanceForCurrentProcess)(
|
||||
_Out_ ISetupInstance** ppInstance
|
||||
) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the instance for the given path.
|
||||
/// </summary>
|
||||
/// <param name="ppInstance">The instance for the given path.</param>
|
||||
/// <returns>
|
||||
/// The instance for the given path, or E_NOTFOUND if not found.
|
||||
/// The <see cref="ISetupInstance::GetState"/> may indicate the instance is invalid.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// The returned instance may not be launchable.
|
||||
/// </remarks>
|
||||
STDMETHOD(GetInstanceForPath)(
|
||||
_In_z_ LPCWSTR wzPath,
|
||||
_Out_ ISetupInstance** ppInstance
|
||||
) = 0;
|
||||
};
|
||||
#endif
|
||||
|
||||
EXTERN_C const IID IID_ISetupConfiguration2;
|
||||
|
||||
#if defined(__cplusplus) && !defined(CINTERFACE)
|
||||
/// <summary>
|
||||
/// Gets information about product instances.
|
||||
/// </summary>
|
||||
struct DECLSPEC_UUID("26AAB78C-4A60-49D6-AF3B-3C35BC93365D") DECLSPEC_NOVTABLE ISetupConfiguration2 : public ISetupConfiguration
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumerates all product instances.
|
||||
/// </summary>
|
||||
/// <param name="ppEnumInstances">An enumeration of all product instances.</param>
|
||||
/// <returns>Standard HRESULT indicating success or failure.</returns>
|
||||
STDMETHOD(EnumAllInstances)(
|
||||
_Out_ IEnumSetupInstances** ppEnumInstances
|
||||
) = 0;
|
||||
};
|
||||
#endif
|
||||
|
||||
EXTERN_C const IID IID_ISetupPackageReference;
|
||||
|
||||
#if defined(__cplusplus) && !defined(CINTERFACE)
|
||||
/// <summary>
|
||||
/// A reference to a package.
|
||||
/// </summary>
|
||||
struct DECLSPEC_UUID("da8d8a16-b2b6-4487-a2f1-594ccccd6bf5") DECLSPEC_NOVTABLE ISetupPackageReference : public IUnknown
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the general package identifier.
|
||||
/// </summary>
|
||||
/// <param name="pbstrId">The general package identifier.</param>
|
||||
/// <returns>Standard HRESULT indicating success or failure.</returns>
|
||||
STDMETHOD(GetId)(
|
||||
_Out_ BSTR* pbstrId
|
||||
) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the version of the package.
|
||||
/// </summary>
|
||||
/// <param name="pbstrVersion">The version of the package.</param>
|
||||
/// <returns>Standard HRESULT indicating success or failure.</returns>
|
||||
STDMETHOD(GetVersion)(
|
||||
_Out_ BSTR* pbstrVersion
|
||||
) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the target process architecture of the package.
|
||||
/// </summary>
|
||||
/// <param name="pbstrChip">The target process architecture of the package.</param>
|
||||
/// <returns>Standard HRESULT indicating success or failure.</returns>
|
||||
STDMETHOD(GetChip)(
|
||||
_Out_ BSTR* pbstrChip
|
||||
) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the language and optional region identifier.
|
||||
/// </summary>
|
||||
/// <param name="pbstrLanguage">The language and optional region identifier.</param>
|
||||
/// <returns>Standard HRESULT indicating success or failure.</returns>
|
||||
STDMETHOD(GetLanguage)(
|
||||
_Out_ BSTR* pbstrLanguage
|
||||
) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the build branch of the package.
|
||||
/// </summary>
|
||||
/// <param name="pbstrBranch">The build branch of the package.</param>
|
||||
/// <returns>Standard HRESULT indicating success or failure.</returns>
|
||||
STDMETHOD(GetBranch)(
|
||||
_Out_ BSTR* pbstrBranch
|
||||
) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the package.
|
||||
/// </summary>
|
||||
/// <param name="pbstrType">The type of the package.</param>
|
||||
/// <returns>Standard HRESULT indicating success or failure.</returns>
|
||||
STDMETHOD(GetType)(
|
||||
_Out_ BSTR* pbstrType
|
||||
) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the unique identifier consisting of all defined tokens.
|
||||
/// </summary>
|
||||
/// <param name="pbstrUniqueId">The unique identifier consisting of all defined tokens.</param>
|
||||
/// <returns>Standard HRESULT indicating success or failure, including E_UNEXPECTED if no Id was defined (required).</returns>
|
||||
STDMETHOD(GetUniqueId)(
|
||||
_Out_ BSTR* pbstrUniqueId
|
||||
) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the package refers to an external extension.
|
||||
/// </summary>
|
||||
/// <param name="pfIsExtension">A value indicating whether the package refers to an external extension.</param>
|
||||
/// <returns>Standard HRESULT indicating success or failure, including E_UNEXPECTED if no Id was defined (required).</returns>
|
||||
STDMETHOD(GetIsExtension)(
|
||||
_Out_ VARIANT_BOOL* pfIsExtension
|
||||
) = 0;
|
||||
};
|
||||
#endif
|
||||
|
||||
EXTERN_C const IID IID_ISetupHelper;
|
||||
|
||||
#if defined(__cplusplus) && !defined(CINTERFACE)
|
||||
/// <summary>
|
||||
/// Helper functions.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// You can query for this interface from the <see cref="SetupConfiguration"/> class.
|
||||
/// </remarks>
|
||||
struct DECLSPEC_UUID("42b21b78-6192-463e-87bf-d577838f1d5c") DECLSPEC_NOVTABLE ISetupHelper : public IUnknown
|
||||
{
|
||||
/// <summary>
|
||||
/// Parses a dotted quad version string into a 64-bit unsigned integer.
|
||||
/// </summary>
|
||||
/// <param name="pwszVersion">The dotted quad version string to parse, e.g. 1.2.3.4.</param>
|
||||
/// <param name="pullVersion">A 64-bit unsigned integer representing the version. You can compare this to other versions.</param>
|
||||
/// <returns>Standard HRESULT indicating success or failure, including E_INVALIDARG if the version is not valid.</returns>
|
||||
STDMETHOD(ParseVersion)(
|
||||
_In_ LPCOLESTR pwszVersion,
|
||||
_Out_ PULONGLONG pullVersion
|
||||
) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Parses a dotted quad version string into a 64-bit unsigned integer.
|
||||
/// </summary>
|
||||
/// <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>
|
||||
/// <param name="pullMinVersion">A 64-bit unsigned integer representing the minimum version, which may be 0. You can compare this to other versions.</param>
|
||||
/// <param name="pullMaxVersion">A 64-bit unsigned integer representing the maximum version, which may be MAXULONGLONG. You can compare this to other versions.</param>
|
||||
/// <returns>Standard HRESULT indicating success or failure, including E_INVALIDARG if the version range is not valid.</returns>
|
||||
STDMETHOD(ParseVersionRange)(
|
||||
_In_ LPCOLESTR pwszVersionRange,
|
||||
_Out_ PULONGLONG pullMinVersion,
|
||||
_Out_ PULONGLONG pullMaxVersion
|
||||
) = 0;
|
||||
};
|
||||
#endif
|
||||
|
||||
EXTERN_C const IID IID_ISetupErrorState;
|
||||
|
||||
#if defined(__cplusplus) && !defined(CINTERFACE)
|
||||
/// <summary>
|
||||
/// Information about the error state of an instance.
|
||||
/// </summary>
|
||||
struct DECLSPEC_UUID("46DCCD94-A287-476A-851E-DFBC2FFDBC20") DECLSPEC_NOVTABLE ISetupErrorState : public IUnknown
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets an array of failed package references.
|
||||
/// </summary>
|
||||
/// <param name="ppsaPackages">Pointer to an array of <see cref="ISetupFailedPackageReference"/>, if packages have failed.</param>
|
||||
/// <returns>Standard HRESULT indicating success or failure.</returns>
|
||||
STDMETHOD(GetFailedPackages)(
|
||||
_Outptr_result_maybenull_ LPSAFEARRAY* ppsaFailedPackages
|
||||
) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets an array of skipped package references.
|
||||
/// </summary>
|
||||
/// <param name="ppsaPackages">Pointer to an array of <see cref="ISetupPackageReference"/>, if packages have been skipped.</param>
|
||||
/// <returns>Standard HRESULT indicating success or failure.</returns>
|
||||
STDMETHOD(GetSkippedPackages)(
|
||||
_Outptr_result_maybenull_ LPSAFEARRAY* ppsaSkippedPackages
|
||||
) = 0;
|
||||
};
|
||||
#endif
|
||||
|
||||
EXTERN_C const IID IID_ISetupErrorState2;
|
||||
|
||||
#if defined(__cplusplus) && !defined(CINTERFACE)
|
||||
/// <summary>
|
||||
/// Information about the error state of an instance.
|
||||
/// </summary>
|
||||
struct DECLSPEC_UUID("9871385B-CA69-48F2-BC1F-7A37CBF0B1EF") DECLSPEC_NOVTABLE ISetupErrorState2 : public ISetupErrorState
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the path to the error log.
|
||||
/// </summary>
|
||||
/// <param name="pbstrChip">The path to the error log.</param>
|
||||
/// <returns>Standard HRESULT indicating success or failure.</returns>
|
||||
STDMETHOD(GetErrorLogFilePath)(
|
||||
_Outptr_result_maybenull_ BSTR* pbstrErrorLogFilePath
|
||||
) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path to the main setup log.
|
||||
/// </summary>
|
||||
/// <param name="pbstrChip">The path to the main setup log.</param>
|
||||
/// <returns>Standard HRESULT indicating success or failure.</returns>
|
||||
STDMETHOD(GetLogFilePath)(
|
||||
_Outptr_result_maybenull_ BSTR* pbstrLogFilePath
|
||||
) = 0;
|
||||
};
|
||||
#endif
|
||||
|
||||
EXTERN_C const IID IID_ISetupFailedPackageReference;
|
||||
|
||||
#if defined(__cplusplus) && !defined(CINTERFACE)
|
||||
/// <summary>
|
||||
/// A reference to a failed package.
|
||||
/// </summary>
|
||||
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)
|
||||
/// <summary>
|
||||
/// A reference to a failed package.
|
||||
/// </summary>
|
||||
struct DECLSPEC_UUID("0FAD873E-E874-42E3-B268-4FE2F096B9CA") DECLSPEC_NOVTABLE ISetupFailedPackageReference2 : public ISetupFailedPackageReference
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the path to the optional package log.
|
||||
/// </summary>
|
||||
/// <param name="pbstrId">The path to the optional package log.</param>
|
||||
/// <returns>Standard HRESULT indicating success or failure.</returns>
|
||||
STDMETHOD(GetLogFilePath)(
|
||||
_Outptr_result_maybenull_ BSTR* pbstrLogFilePath
|
||||
) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the description of the package failure.
|
||||
/// </summary>
|
||||
/// <param name="pbstrId">The description of the package failure.</param>
|
||||
/// <returns>Standard HRESULT indicating success or failure.</returns>
|
||||
STDMETHOD(GetDescription)(
|
||||
_Outptr_result_maybenull_ BSTR* pbstrDescription
|
||||
) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the signature to use for feedback reporting.
|
||||
/// </summary>
|
||||
/// <param name="pbstrId">The signature to use for feedback reporting.</param>
|
||||
/// <returns>Standard HRESULT indicating success or failure.</returns>
|
||||
STDMETHOD(GetSignature)(
|
||||
_Outptr_result_maybenull_ BSTR* pbstrSignature
|
||||
) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the array of details for this package failure.
|
||||
/// </summary>
|
||||
/// <param name="ppsaDetails">Pointer to an array of details as BSTRs.</param>
|
||||
/// <returns>Standard HRESULT indicating success or failure.</returns>
|
||||
STDMETHOD(GetDetails)(
|
||||
_Out_ LPSAFEARRAY* ppsaDetails
|
||||
) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets an array of packages affected by this package failure.
|
||||
/// </summary>
|
||||
/// <param name="ppsaPackages">Pointer to an array of <see cref="ISetupPackageReference"/> for packages affected by this package failure. This may be NULL.</param>
|
||||
/// <returns>Standard HRESULT indicating success or failure.</returns>
|
||||
STDMETHOD(GetAffectedPackages)(
|
||||
_Out_ LPSAFEARRAY* ppsaAffectedPackages
|
||||
) = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
EXTERN_C const IID IID_ISetupPropertyStore;
|
||||
|
||||
#if defined(__cplusplus) && !defined(CINTERFACE)
|
||||
/// <summary>
|
||||
/// Provides named properties.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// You can get this from an <see cref="ISetupInstance"/>, <see cref="ISetupPackageReference"/>, or derivative.
|
||||
/// </remarks>
|
||||
struct DECLSPEC_UUID("C601C175-A3BE-44BC-91F6-4568D230FC83") DECLSPEC_NOVTABLE ISetupPropertyStore : public IUnknown
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets an array of property names in this property store.
|
||||
/// </summary>
|
||||
/// <param name="ppsaNames">Pointer to an array of property names as BSTRs.</param>
|
||||
/// <returns>Standard HRESULT indicating success or failure.</returns>
|
||||
STDMETHOD(GetNames)(
|
||||
_Out_ LPSAFEARRAY* ppsaNames
|
||||
) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of a named property in this property store.
|
||||
/// </summary>
|
||||
/// <param name="pwszName">The name of the property to get.</param>
|
||||
/// <param name="pvtValue">The value of the property.</param>
|
||||
/// <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>
|
||||
STDMETHOD(GetValue)(
|
||||
_In_ LPCOLESTR pwszName,
|
||||
_Out_ LPVARIANT pvtValue
|
||||
) = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
EXTERN_C const IID IID_ISetupLocalizedPropertyStore;
|
||||
|
||||
#if defined(__cplusplus) && !defined(CINTERFACE)
|
||||
/// <summary>
|
||||
/// Provides localized named properties.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// You can get this from an <see cref="ISetupLocalizedProperties"/>.
|
||||
/// </remarks>
|
||||
struct DECLSPEC_UUID("5BB53126-E0D5-43DF-80F1-6B161E5C6F6C") DECLSPEC_NOVTABLE ISetupLocalizedPropertyStore : public IUnknown
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets an array of property names in this property store.
|
||||
/// </summary>
|
||||
/// <param name="lcid">The LCID for the property names.</param>
|
||||
/// <param name="ppsaNames">Pointer to an array of property names as BSTRs.</param>
|
||||
/// <returns>Standard HRESULT indicating success or failure.</returns>
|
||||
STDMETHOD(GetNames)(
|
||||
_In_ LCID lcid,
|
||||
_Out_ LPSAFEARRAY* ppsaNames
|
||||
) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of a named property in this property store.
|
||||
/// </summary>
|
||||
/// <param name="pwszName">The name of the property to get.</param>
|
||||
/// <param name="lcid">The LCID for the property.</param>
|
||||
/// <param name="pvtValue">The value of the property.</param>
|
||||
/// <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>
|
||||
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
|
||||
|
||||
/// <summary>
|
||||
/// This class implements <see cref="ISetupConfiguration"/>, <see cref="ISetupConfiguration2"/>, and <see cref="ISetupHelper"/>.
|
||||
/// </summary>
|
||||
class DECLSPEC_UUID("177F0C4A-1CD3-4DE7-A32C-71DBBB9FA36D") SetupConfiguration;
|
||||
#endif
|
||||
// Function declarations
|
||||
//
|
||||
/// <summary>
|
||||
/// Gets an <see cref="ISetupConfiguration"/> that provides information about product instances installed on the machine.
|
||||
/// </summary>
|
||||
/// <param name="ppConfiguration">The <see cref="ISetupConfiguration"/> that provides information about product instances installed on the machine.</param>
|
||||
/// <param name="pReserved">Reserved for future use.</param>
|
||||
/// <returns>Standard HRESULT indicating success or failure.</returns>
|
||||
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));
|
@ -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 <inttypes.h>
|
||||
#include <assert.h>
|
||||
|
||||
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
|
@ -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 <stddef.h>
|
||||
|
||||
// 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
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user