WindowsPath.h#

Fully qualified name: carb/extras/WindowsPath.h

File members: carb/extras/WindowsPath.h

// SPDX-FileCopyrightText: Copyright (c) 2018-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: LicenseRef-NvidiaProprietary
//
// NVIDIA CORPORATION, its affiliates and licensors retain all intellectual
// property and proprietary rights in and to this material, related
// documentation and any modifications thereto. Any use, reproduction,
// disclosure or distribution of this material and related documentation
// without an express license agreement from NVIDIA CORPORATION or
// its affiliates is strictly prohibited.

#pragma once

#include "../Defines.h"

#if CARB_PLATFORM_WINDOWS || defined(DOXYGEN_BUILD)

#    include "../CarbWindows.h"
#    include "../Error.h"
#    include "../../omni/Expected.h"
#    include "Unicode.h"

#    include <algorithm>
#    include <string>

namespace carb::extras
{

constexpr cpp::wzstring_view kLongPathPrefix(LR"(\\?\)");

namespace detail
{
template <class StringType>
StringType convertWindowsToCarbonitePath(cpp::wstring_view pathW)
{
    if (pathW.starts_with(kLongPathPrefix))
        pathW.remove_prefix(kLongPathPrefix.size());
    auto path = convertWideStringToUtf8<StringType>(pathW);
    std::replace(path.begin(), path.end(), '\\', '/');
    return path;
}
} // namespace detail

std::wstring convertCarboniteToWindowsPath(cpp::string_view path);

std::wstring convertCarboniteToWindowsPath(cpp::unbounded_string path);

std::string convertWindowsToCarbonitePath(cpp::wstring_view pathW);

std::string convertWindowsToCarbonitePath(cpp::unbounded_wstring path);

std::wstring fixWindowsPathPrefix(std::wstring pathW);

std::wstring getWindowsCanonicalPath(const std::wstring& pathW);

std::wstring getWindowsFullPath(const std::wstring& pathW);

void adjustWindowsDllSearchPaths();

//
// Implementations
//

inline std::wstring convertCarboniteToWindowsPath(cpp::string_view path)
{
    auto pathW = convertUtf8StringToWide<std::wstring>(path);
    std::replace(pathW.begin(), pathW.end(), L'/', L'\\');
    const bool hasPrefix = pathW.compare(0, kLongPathPrefix.size(), kLongPathPrefix) == 0;
    if (!hasPrefix && pathW.size() >= CARBWIN_MAX_PATH)
        return extras::join(kLongPathPrefix, pathW);
    if (hasPrefix && pathW.size() < CARBWIN_MAX_PATH)
        pathW.erase(0, kLongPathPrefix.size());
    return pathW;
}

inline std::wstring convertCarboniteToWindowsPath(cpp::unbounded_string path)
{
    return convertCarboniteToWindowsPath(cpp::string_view(cpp::unsafe_length, path));
}

inline std::string convertWindowsToCarbonitePath(cpp::wstring_view pathW)
{
    return detail::convertWindowsToCarbonitePath<std::string>(pathW);
}

inline std::string convertWindowsToCarbonitePath(cpp::unbounded_wstring path)
{
    return convertWindowsToCarbonitePath(cpp::wstring_view(cpp::unsafe_length, path));
}

inline std::wstring fixWindowsPathPrefix(std::wstring pathW)
{
    const bool hasPrefix = pathW.compare(0, kLongPathPrefix.size(), kLongPathPrefix) == 0;
    if (!hasPrefix && pathW.size() >= CARBWIN_MAX_PATH)
        return join(kLongPathPrefix, std::move(pathW));
    if (hasPrefix && pathW.size() < CARBWIN_MAX_PATH)
        pathW.erase(0, kLongPathPrefix.size());
    return pathW;
}

inline omni::expected<std::wstring, HRESULT> getWindowsCanonicalPathEx(cpp::wzstring_view path)
{
    wchar_t* canonical = nullptr;
    if (auto hr = PathAllocCanonicalize(path.c_str(), CARBWIN_PATHCCH_ALLOW_LONG_PATHS, &canonical);
        !CARBWIN_SUCCEEDED(hr))
    {
        return omni::unexpected(hr);
    }
    std::wstring result(canonical);
    LocalFree(canonical);
    return result;
}

inline std::wstring getWindowsCanonicalPath(const std::wstring& pathW)
{
    wchar_t* canonical = nullptr;
    auto hr = PathAllocCanonicalize(pathW.c_str(), CARBWIN_PATHCCH_ALLOW_LONG_PATHS, &canonical);
    if (CARBWIN_SUCCEEDED(hr))
    {
        std::wstring result = canonical;
        LocalFree(canonical);
        return result;
    }

    (void)carb::detail::setError(
        ErrorCode::kFail, omni::string{ omni::formatted, "PathAllocCanonicalize failed with HRESULT 0x%08x", hr });
    return pathW;
}

inline std::wstring getWindowsFullPath(const std::wstring& pathW)
{
    // Retrieve the size
    DWORD size = GetFullPathNameW(pathW.c_str(), 0, nullptr, nullptr);
    if (size != 0)
    {
        std::wstring fullPathName(size - 1, '\0');
        size = GetFullPathNameW(pathW.c_str(), size, &fullPathName[0], nullptr);
        if (size)
        {
            // Assert if the Win32 API lied to us. Note that sometimes it does use less than asked for.
            CARB_ASSERT(size <= fullPathName.size());
            fullPathName.resize(size);
            return fullPathName;
        }
    }

    ErrorApi::setFromWinApiErrorCode();
    return pathW;
}

inline void adjustWindowsDllSearchPaths()
{
    // MSDN:
    // https://docs.microsoft.com/en-us/windows/desktop/api/libloaderapi/nf-libloaderapi-setdefaultdlldirectories
    // LOAD_LIBRARY_SEARCH_DEFAULT_DIRS
    // This value is a combination of LOAD_LIBRARY_SEARCH_APPLICATION_DIR, LOAD_LIBRARY_SEARCH_SYSTEM32, and
    // LOAD_LIBRARY_SEARCH_USER_DIRS.
    SetDefaultDllDirectories(CARBWIN_LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
}

} // namespace carb::extras

#endif