From 0a3fac73ec9ac1c60d8f8645786ac72727851e08 Mon Sep 17 00:00:00 2001 From: Blue Date: Thu, 20 Nov 2025 09:57:39 -0800 Subject: [PATCH 1/2] Merge latest master into feature/wsl-for-apps --- .clang-format | 1 + CMakeLists.txt | 5 +- distributions/DistributionInfo.json | 8 +-- localization/strings/es-ES/Resources.resw | 4 +- localization/strings/nl-NL/Resources.resw | 2 +- src/linux/init/SecCompDispatcher.cpp | 2 + src/linux/init/config.cpp | 4 +- src/linux/init/init.cpp | 8 ++- src/linux/init/waitablevalue.h | 8 +++ src/linux/netlinkutil/IpNeighborManager.cpp | 24 ++++++++ src/linux/netlinkutil/Packet.h | 8 +++ src/windows/common/WslClient.cpp | 4 +- src/windows/common/WslCoreConfig.cpp | 8 ++- src/windows/common/socket.cpp | 2 + src/windows/common/svccomm.cpp | 8 ++- src/windows/service/exe/LxssInstance.cpp | 4 +- src/windows/service/exe/LxssUserSession.cpp | 12 +++- src/windows/service/exe/WslCoreInstance.cpp | 8 ++- src/windows/service/exe/WslCoreVm.cpp | 53 ++++++++++++----- .../wslsettings/Controls/OOBEContent.xaml | 2 +- .../wslsettings/Controls/OOBEContent.xaml.cs | 35 +++++++++++- .../wslsettings/Helpers/RuntimeHelper.cs | 37 ++++++++++++ src/windows/wslsettings/LibWsl.cs | 4 +- .../wslsettings/Views/Settings/AboutPage.xaml | 14 +++-- .../Views/Settings/DeveloperPage.xaml | 12 ++-- .../Views/Settings/DeveloperPage.xaml.cs | 9 +++ .../Views/Settings/FileSystemPage.xaml | 4 +- .../Views/Settings/FileSystemPage.xaml.cs | 18 ++++++ .../Views/Settings/MemAndProcPage.xaml | 16 +++--- .../Views/Settings/MemAndProcPage.xaml.cs | 10 ++++ .../Views/Settings/NetworkingPage.xaml | 8 +-- .../Views/Settings/NetworkingPage.xaml.cs | 8 +++ .../Views/Settings/OptionalFeaturesPage.xaml | 4 +- .../Settings/OptionalFeaturesPage.xaml.cs | 8 +++ .../wslsettings/Windows/OOBEWindow.xaml | 4 +- .../wslsettings/Windows/OOBEWindow.xaml.cs | 57 +++++++++++++++++-- test/windows/DrvFsTests.cpp | 2 +- test/windows/UnitTests.cpp | 34 ++++++----- tools/FormatSource.ps1.in | 15 +++-- 39 files changed, 380 insertions(+), 94 deletions(-) diff --git a/.clang-format b/.clang-format index 840920154..040609f55 100644 --- a/.clang-format +++ b/.clang-format @@ -58,6 +58,7 @@ IncludeCategories: - Regex: '^"(stdafx.h|pch.h|precomp.h)"$' Priority: -1 IndentCaseLabels: false +InsertBraces: true IndentWidth: 4 IndentWrappedFunctionNames: false KeepEmptyLinesAtTheStartOfBlocks: true diff --git a/CMakeLists.txt b/CMakeLists.txt index e66a48936..3f0ec82ec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -267,15 +267,16 @@ else() endif() # Determine the Visual Studio installation directory which contains LLVM tools +# N.B. The version is set to VS2022 to ensure local runs match pipeline behavior execute_process( - COMMAND "${VSWHERE_SOURCE_DIR}/vswhere.exe" -latest -products * -property installationPath + COMMAND "${VSWHERE_SOURCE_DIR}/vswhere.exe" -version "[17.0,18.0)" -products * -property installationPath OUTPUT_VARIABLE VS_INSTALL_DIR OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND_ERROR_IS_FATAL ANY ) if (NOT VS_INSTALL_DIR) - message(FATAL_ERROR "Could not determine Visual Studio installation directory.") + message(FATAL_ERROR "Could not determine Visual Studio 2022 installation directory.") endif() if("${CMAKE_HOST_SYSTEM_PROCESSOR}" STREQUAL "AMD64") diff --git a/distributions/DistributionInfo.json b/distributions/DistributionInfo.json index 6c2a7b127..3e3265232 100644 --- a/distributions/DistributionInfo.json +++ b/distributions/DistributionInfo.json @@ -125,12 +125,12 @@ "FriendlyName": "AlmaLinux OS 9", "Default": false, "Amd64Url": { - "Url": "https://github.com/AlmaLinux/wsl-images/releases/download/v9.6.20250522.0/AlmaLinux-9.6_x64_20250522.0.wsl", - "Sha256": "e0f6acf1ce5aff80f2e60d3d8191c0a3857720149a7e5521b96e11777cdd2779" + "Url": "https://github.com/AlmaLinux/wsl-images/releases/download/v9.7.20251119.0/AlmaLinux-9.7_x64_20251119.0.wsl", + "Sha256": "0a6588f4f723fcb3edbc37dd3e3e13be8ffe0a5027e47513e3d4d2a4451794e7" }, "Arm64Url": { - "Url": "https://github.com/AlmaLinux/wsl-images/releases/download/v9.6.20250522.0/AlmaLinux-9.6_ARM64_20250522.0.wsl", - "Sha256": "58d1cc623c4570307825036460e51a406bdcf53b4b5a4148cf644f5f68ce48dd" + "Url": "https://github.com/AlmaLinux/wsl-images/releases/download/v9.7.20251119.0/AlmaLinux-9.7_ARM64_20251119.0.wsl", + "Sha256": "b3e2632efe029a81db1ae7a914ea3b7e9e6e20d8c71c158270c331e6fea39bf8" } }, { diff --git a/localization/strings/es-ES/Resources.resw b/localization/strings/es-ES/Resources.resw index 8f764d915..6fcebecf7 100644 --- a/localization/strings/es-ES/Resources.resw +++ b/localization/strings/es-ES/Resources.resw @@ -537,7 +537,7 @@ Argumentos para administrar distribuciones en el Subsistema de Windows para Linu --export <Distro> <FileName> [Options] Exporta la distribución a un archivo tar. - El nombre de archivo puede ser: para stdout. + El nombre de archivo puede ser - para stdout. Opciones: --format <Format> @@ -545,7 +545,7 @@ Argumentos para administrar distribuciones en el Subsistema de Windows para Linu --import <Distro> <InstallLocation> <FileName> [Options] Importa el archivo tar especificado como una nueva distribución. - El nombre de archivo puede ser : para stdin. + El nombre de archivo puede ser - para stdin. Opciones: --version <Version> diff --git a/localization/strings/nl-NL/Resources.resw b/localization/strings/nl-NL/Resources.resw index f6fc34f06..08092264e 100644 --- a/localization/strings/nl-NL/Resources.resw +++ b/localization/strings/nl-NL/Resources.resw @@ -606,7 +606,7 @@ Argumenten voor het beheren van distributies in het Windows-subsysteem voor Linu "}{Locked="--quiet,"}{Locked="--verbose,"}{Locked="--online,"}{Locked="--install'"}{Locked="--set-default,"}{Locked="--set-version "}{Locked="--terminate,"}{Locked="--unregister "}Command line arguments, file names and string inserts should not be translated - WSL-versiie: {} + WSL-versie: {} Kernelversie: {} WSLg-versie: {} MSRDC-versie: {} diff --git a/src/linux/init/SecCompDispatcher.cpp b/src/linux/init/SecCompDispatcher.cpp index 7dc7be3aa..98a81c3bd 100644 --- a/src/linux/init/SecCompDispatcher.cpp +++ b/src/linux/init/SecCompDispatcher.cpp @@ -77,7 +77,9 @@ void SecCompDispatcher::Run() for (;;) { if (!wait_for_fd(m_notifyFd.get(), POLLIN)) + { break; + } // Clear the buffers to make the 5.15 kernel happy. notification_buffer.clear(); diff --git a/src/linux/init/config.cpp b/src/linux/init/config.cpp index f36d319d9..e6f60cb08 100644 --- a/src/linux/init/config.cpp +++ b/src/linux/init/config.cpp @@ -955,6 +955,7 @@ try // if (Config.InitPid.has_value()) + { try { std::string LinkPath = std::format(WSL_INTEROP_SOCKET_FORMAT, WSL_TEMP_FOLDER, 1, WSL_INTEROP_SOCKET); @@ -963,7 +964,8 @@ try LOG_ERROR("symlink({}, {}) failed {}", InteropServer.Path(), LinkPath.c_str(), errno); } } - CATCH_LOG() + CATCH_LOG() + } UtilCreateWorkerThread( "Interop", [InteropChannel = std::move(InteropChannel), InteropServer = std::move(InteropServer), Elevated, &Config]() mutable { diff --git a/src/linux/init/init.cpp b/src/linux/init/init.cpp index 77495eb5e..0611dda72 100644 --- a/src/linux/init/init.cpp +++ b/src/linux/init/init.cpp @@ -221,6 +221,7 @@ int WslEntryPoint(int Argc, char* Argv[]) { // Handle the special case for import result messages, everything else is sent to the binfmt interpreter. if (Pid == 1 && strcmp(BaseName, "init") == 0 && Argc == 3 && strcmp(Argv[1], LX_INIT_IMPORT_MESSAGE_ARG) == 0) + { try { wsl::shared::MessageWriter message; @@ -230,7 +231,8 @@ int WslEntryPoint(int Argc, char* Argv[]) read(STDIN_FILENO, buffer, sizeof(buffer)); exit(0); } - CATCH_RETURN_ERRNO() + CATCH_RETURN_ERRNO() + } ExitCode = CreateNtProcess(Argc - 1, &Argv[1]); } @@ -327,6 +329,10 @@ int GenerateSystemdUnits(int Argc, char** Argv) File.reset(); } + // Mask systemd-networkd-wait-online.service since WSL always ensures that networking is configured during boot. + // That unit can cause systemd boot timeouts since WSL's network interface is unmanaged by systemd. + THROW_LAST_ERROR_IF(symlink("/dev/null", std::format("{}/systemd-networkd-wait-online.service", installPath).c_str()) < 0); + // Only create the wslg unit if both enabled in wsl.conf, and if the wslg folder actually exists. if (enableGuiApps && access("/mnt/wslg/runtime-dir", F_OK) == 0) { diff --git a/src/linux/init/waitablevalue.h b/src/linux/init/waitablevalue.h index 9b1bd5874..4f297e03d 100644 --- a/src/linux/init/waitablevalue.h +++ b/src/linux/init/waitablevalue.h @@ -25,7 +25,9 @@ struct WaitableValue { std::unique_lock lck(m_mtx); while (m_value.has_value()) + { m_cv.wait(lck); + } m_value = value; m_cv.notify_all(); } @@ -39,7 +41,9 @@ struct WaitableValue { std::unique_lock lck(m_mtx); while (!m_value.has_value()) + { m_cv.wait(lck); + } auto return_value = m_value.value(); m_value.reset(); m_cv.notify_all(); @@ -57,8 +61,12 @@ struct WaitableValue { std::unique_lock lck(m_mtx); while (!m_value.has_value()) + { if (m_cv.wait_for(lck, timeout) == std::cv_status::timeout) + { return std::nullopt; + } + } auto return_value = m_value.value(); m_value.reset(); m_cv.notify_all(); diff --git a/src/linux/netlinkutil/IpNeighborManager.cpp b/src/linux/netlinkutil/IpNeighborManager.cpp index 4f293e0c6..4aa2b2ee0 100644 --- a/src/linux/netlinkutil/IpNeighborManager.cpp +++ b/src/linux/netlinkutil/IpNeighborManager.cpp @@ -65,23 +65,41 @@ bool ParseArpReply(const T& ArpReply, uint16_t ProtocolType, const Neighbor& Sou Target.ipAddress.ConvertToBytes(TargetIp.data()); if (ArpReply.Destination != Source.macAddress) + { return false; + } if (ArpReply.EthernetType != htons(ETH_P_ARP)) + { return false; + } if (ArpReply.HardwareType != htons(ARPHRD_ETHER)) + { return false; + } if (ArpReply.ProtocolType != htons(ProtocolType)) + { return false; + } if (ArpReply.HardwareAddressLength != sizeof(ArpReply.SenderHardwareAddress)) + { return false; + } if (ArpReply.ProtocolAddressLength != sizeof(ArpReply.SenderIpAddress)) + { return false; + } if (ArpReply.Operation != htons(ARPOP_REPLY)) + { return false; + } if (ArpReply.TargetHardwareAddress != Source.macAddress) + { return false; + } if (ArpReply.TargetIpAddress != SourceIp) + { return false; + } Target.macAddress = ArpReply.SenderHardwareAddress; return true; @@ -129,7 +147,9 @@ bool IpNeighborManager::PerformNeighborDiscovery(Neighbor& Local, Neighbor& Neig while (std::chrono::steady_clock::now() < expiry) { if (!wait_for_read(packet_socket.get(), std::chrono::duration_cast(expiry - std::chrono::steady_clock::now()))) + { continue; + } int bytes_read = Syscall(read, packet_socket.get(), &ArpReply, ArpPacketSize); if (bytes_read != ArpPacketSize) { @@ -138,12 +158,16 @@ bool IpNeighborManager::PerformNeighborDiscovery(Neighbor& Local, Neighbor& Neig if (Local.getFamily() == AF_INET) { if (ParseArpReply(ArpReply.IPv4, ETH_P_IP, Local, Neighbor)) + { return true; + } } else { if (ParseArpReply(ArpReply.IPv6, ETH_P_IPV6, Local, Neighbor)) + { return true; + } } } } diff --git a/src/linux/netlinkutil/Packet.h b/src/linux/netlinkutil/Packet.h index 863fe1a86..d1724a4e3 100644 --- a/src/linux/netlinkutil/Packet.h +++ b/src/linux/netlinkutil/Packet.h @@ -31,10 +31,14 @@ class Packet bool adjust_head(long count) { if ((count + data_offset) < 0) + { return false; + } if ((count + data_offset) > data_end_offset) + { return false; + } data_offset += count; return true; @@ -43,9 +47,13 @@ class Packet bool adjust_tail(long count) { if ((count + data_end_offset) < data_offset) + { return false; + } if ((count + data_end_offset) > Buffer.size()) + { Buffer.resize(count + data_end_offset); + } data_end_offset += count; return true; diff --git a/src/windows/common/WslClient.cpp b/src/windows/common/WslClient.cpp index f97ce30ef..b84ca8d61 100644 --- a/src/windows/common/WslClient.cpp +++ b/src/windows/common/WslClient.cpp @@ -2033,6 +2033,7 @@ int wsl::windows::common::WslClient::Main(_In_ LPCWSTR commandLine) // Print error messages for failures. if (FAILED(result)) + { try { std::wstring errorString{}; @@ -2086,7 +2087,8 @@ int wsl::windows::common::WslClient::Main(_In_ LPCWSTR commandLine) } } } - CATCH_LOG() + CATCH_LOG() + } if (g_promptBeforeExit) { diff --git a/src/windows/common/WslCoreConfig.cpp b/src/windows/common/WslCoreConfig.cpp index f4e39d228..ee8b780aa 100644 --- a/src/windows/common/WslCoreConfig.cpp +++ b/src/windows/common/WslCoreConfig.cpp @@ -400,6 +400,7 @@ void wsl::core::Config::Initialize(_In_opt_ HANDLE UserToken) // Load NAT configuration from the registry. if (NetworkingMode == wsl::core::NetworkingMode::Nat) + { try { const auto machineKey = wsl::windows::common::registry::OpenLxssMachineKey(); @@ -410,10 +411,12 @@ void wsl::core::Config::Initialize(_In_opt_ HANDLE UserToken) const auto userKey = wsl::windows::common::registry::OpenLxssUserKey(); NatIpAddress = wsl::windows::common::registry::ReadString(userKey.get(), nullptr, c_natIpAddress, L""); } - CATCH_LOG() + CATCH_LOG() + } // Due to an issue with Global Secure Access Client, do not use DNS tunneling if the service is present. if (EnableDnsTunneling) + { try { // Open a handle to the service control manager and check if the inbox service is registered. @@ -438,7 +441,8 @@ void wsl::core::Config::Initialize(_In_opt_ HANDLE UserToken) } } } - CATCH_LOG() + CATCH_LOG() + } // Ensure that settings are consistent (disable features that require other features that are not present). if (EnableSafeMode) diff --git a/src/windows/common/socket.cpp b/src/windows/common/socket.cpp index 50fe0ae61..edc49aaed 100644 --- a/src/windows/common/socket.cpp +++ b/src/windows/common/socket.cpp @@ -105,6 +105,7 @@ int wsl::windows::common::socket::ReceiveNoThrow( Overlapped.hEvent = OverlappedEvent.get(); DWORD BytesReturned{}; if (WSARecv(Socket, &VectorBuffer, 1, &BytesReturned, &Flags, &Overlapped, nullptr) != 0) + { try { BytesReturned = SOCKET_ERROR; @@ -117,6 +118,7 @@ int wsl::windows::common::socket::ReceiveNoThrow( // Receive will call GetLastError to look for the error code SetLastError(wil::ResultFromCaughtException()); } + } return BytesReturned; } diff --git a/src/windows/common/svccomm.cpp b/src/windows/common/svccomm.cpp index 919031078..4f33f4b6a 100644 --- a/src/windows/common/svccomm.cpp +++ b/src/windows/common/svccomm.cpp @@ -378,11 +378,13 @@ wsl::windows::common::SvcComm::LaunchProcess( // if ((WI_IsFlagSet(LaunchFlags, LXSS_LAUNCH_FLAG_ENABLE_INTEROP)) && (ServerPortHandle)) + { try { InitializeInterop(ServerPortHandle.get(), DistributionId); } - CATCH_LOG() + CATCH_LOG() + } ServerPortHandle.reset(); @@ -473,11 +475,13 @@ wsl::windows::common::SvcComm::LaunchProcess( // if (WI_IsFlagSet(LaunchFlags, LXSS_LAUNCH_FLAG_ENABLE_INTEROP)) + { try { SpawnWslHost(InteropSocket.get(), DistributionId, &InstanceId); } - CATCH_LOG() + CATCH_LOG() + } // // Begin reading messages from the utility vm. diff --git a/src/windows/service/exe/LxssInstance.cpp b/src/windows/service/exe/LxssInstance.cpp index 2700366e5..3f4a3001a 100644 --- a/src/windows/service/exe/LxssInstance.cpp +++ b/src/windows/service/exe/LxssInstance.cpp @@ -346,6 +346,7 @@ bool LxssInstance::RequestStop(_In_ bool Force) // Send the message to the init daemon to check if the instance can be terminated. bool shutdown = true; if (m_InitMessagePort) + { try { auto lock = m_InitMessagePort->Lock(); @@ -358,7 +359,8 @@ bool LxssInstance::RequestStop(_In_ bool Force) m_InitMessagePort->Receive(&terminateResponse, sizeof(terminateResponse)); shutdown = terminateResponse.Result; } - CATCH_LOG() + CATCH_LOG() + } return shutdown; } diff --git a/src/windows/service/exe/LxssUserSession.cpp b/src/windows/service/exe/LxssUserSession.cpp index d0dcc04da..2e5611c1b 100644 --- a/src/windows/service/exe/LxssUserSession.cpp +++ b/src/windows/service/exe/LxssUserSession.cpp @@ -2952,11 +2952,13 @@ void LxssUserSessionImpl::_DeleteDistributionLockHeld(_In_ const LXSS_DISTRO_CON if (PathFileExistsW(Configuration.VhdFilePath.c_str())) { if (m_utilityVm) + { try { m_utilityVm->EjectVhd(Configuration.VhdFilePath.c_str()); } - CATCH_LOG() + CATCH_LOG() + } if (WI_IsFlagSet(Flags, LXSS_DELETE_DISTRO_FLAGS_VHD)) { @@ -2997,13 +2999,15 @@ void LxssUserSessionImpl::_DeleteDistributionLockHeld(_In_ const LXSS_DISTRO_CON // Remove start menu shortcuts for WSLg applications. if (WI_IsFlagSet(Flags, LXSS_DELETE_DISTRO_FLAGS_WSLG_SHORTCUTS)) + { try { const auto dllPath = wsl::windows::common::wslutil::GetBasePath() / WSLG_TS_PLUGIN_DLL; static LxssDynamicFunction removeAppProvider(dllPath.c_str(), "RemoveAppProvider"); LOG_IF_FAILED(removeAppProvider(Configuration.Name.c_str())); } - CATCH_LOG() + CATCH_LOG() + } // If the basepath is empty, delete it. try @@ -3059,11 +3063,13 @@ std::vector LxssUserSessionImpl::_EnumerateDistributio // Ensure that the default distribution is still valid. if (!orphanedDistributions.empty()) + { try { _GetDefaultDistro(LxssKey); } - CATCH_LOG() + CATCH_LOG() + } return distributions; } diff --git a/src/windows/service/exe/WslCoreInstance.cpp b/src/windows/service/exe/WslCoreInstance.cpp index 213d2cc22..74fe3168d 100644 --- a/src/windows/service/exe/WslCoreInstance.cpp +++ b/src/windows/service/exe/WslCoreInstance.cpp @@ -412,13 +412,15 @@ void WslCoreInstance::Initialize() // Launch the interop server with the user's token. if (response.InteropPort != LX_INIT_UTILITY_VM_INVALID_PORT) + { try { const wil::unique_socket socket{wsl::windows::common::hvsocket::Connect(m_runtimeId, response.InteropPort)}; wil::unique_handle info{wsl::windows::common::helpers::LaunchInteropServer( nullptr, reinterpret_cast(socket.get()), nullptr, nullptr, &m_runtimeId, m_userToken.get())}; } - CATCH_LOG() + CATCH_LOG() + } // Initialization was successful. m_initialized = true; @@ -463,6 +465,7 @@ bool WslCoreInstance::RequestStop(_In_ bool Force) bool shutdown = true; std::lock_guard lock(m_lock); if (m_initChannel) + { try { LX_INIT_TERMINATE_INSTANCE terminateMessage{}; @@ -477,7 +480,8 @@ bool WslCoreInstance::RequestStop(_In_ bool Force) shutdown = message->Result; } } - CATCH_LOG() + CATCH_LOG() + } return shutdown; } diff --git a/src/windows/service/exe/WslCoreVm.cpp b/src/windows/service/exe/WslCoreVm.cpp index a85f1c7f2..9f6f7d411 100644 --- a/src/windows/service/exe/WslCoreVm.cpp +++ b/src/windows/service/exe/WslCoreVm.cpp @@ -274,18 +274,20 @@ void WslCoreVm::Initialize(const GUID& VmId, const wil::shared_handle& UserToken // N.B. wslhost.exe is launched at medium integrity level and its lifetime // is tied to the lifetime of the utility VM. if (m_vmConfig.EnableDebugConsole || !m_vmConfig.DebugConsoleLogFile.empty()) + { try { m_vmConfig.EnableDebugConsole = true; m_comPipe0 = wsl::windows::common::helpers::GetUniquePipeName(); } - CATCH_LOG() + CATCH_LOG() + } // If the system supports virtio console serial ports, use dmesg capture for telemetry and/or debug output. // Legacy serial is much slower, so this is not enabled without virtio console support. - auto enableVirtioSerial = m_vmConfig.EnableVirtio && helpers::IsVirtioSerialConsoleSupported(); - m_vmConfig.EnableDebugShell &= enableVirtioSerial; - if (enableVirtioSerial) + m_vmConfig.EnableDebugShell &= helpers::IsVirtioSerialConsoleSupported(); + if (m_vmConfig.EnableDebugShell) + { try { bool enableTelemetry = TraceLoggingProviderEnabled(g_hTraceLoggingProvider, WINEVENT_LEVEL_INFO, 0); @@ -302,9 +304,11 @@ void WslCoreVm::Initialize(const GUID& VmId, const wil::shared_handle& UserToken // Initialize the guest telemetry logger. m_gnsTelemetryLogger = GuestTelemetryLogger::Create(VmId, m_vmExitEvent); } - CATCH_LOG() + CATCH_LOG() + } if (m_vmConfig.EnableDebugConsole) + { try { // If specified, create a file to log the debug console output. @@ -321,7 +325,8 @@ void WslCoreVm::Initialize(const GUID& VmId, const wil::shared_handle& UserToken wsl::windows::common::helpers::LaunchDebugConsole( m_comPipe0.c_str(), !!m_dmesgCollector, m_restrictedToken.get(), logFile ? logFile.get() : nullptr, !m_vmConfig.EnableTelemetry); } - CATCH_LOG() + CATCH_LOG() + } // Create the utility VM and store the runtime ID. std::wstring json = GenerateConfigJson(); @@ -393,12 +398,14 @@ void WslCoreVm::Initialize(const GUID& VmId, const wil::shared_handle& UserToken THROW_IF_FAILED(wil::ExpandEnvironmentStringsW(L"%SystemRoot%\\System32\\lxss\\lib", path)); if (wsl::windows::common::filesystem::FileExists(path.c_str())) + { try { addShare(TEXT(LXSS_GPU_INBOX_LIB_SHARE), path.c_str()); m_enableInboxGpuLibs = true; } - CATCH_LOG() + CATCH_LOG() + } #ifdef WSL_GPU_LIB_PATH @@ -475,6 +482,7 @@ void WslCoreVm::Initialize(const GUID& VmId, const wil::shared_handle& UserToken // the user does not have write access. ULONG swapLun = ULONG_MAX; if ((m_systemDistroDeviceId != ULONG_MAX) && (m_vmConfig.SwapSizeBytes > 0)) + { try { { @@ -517,7 +525,8 @@ void WslCoreVm::Initialize(const GUID& VmId, const wil::shared_handle& UserToken swapLun = AttachDiskLockHeld(m_vmConfig.SwapFilePath.c_str(), DiskType::VHD, MountFlags::None, {}, false, m_userToken.get()); } - CATCH_LOG() + CATCH_LOG() + } // Validate that the requesting network mode is supported. // @@ -770,11 +779,13 @@ WslCoreVm::~WslCoreVm() noexcept // If the notification did not arrive within the timeout, the VM is // forcefully terminated. if (forcedTerminate) + { try { wsl::windows::common::hcs::TerminateComputeSystem(m_system.get()); } - CATCH_LOG() + CATCH_LOG() + } } m_vmExitEvent.wait(UTILITY_VM_TERMINATE_TIMEOUT); @@ -833,33 +844,40 @@ WslCoreVm::~WslCoreVm() noexcept } if (WI_IsFlagSet(Entry.second.Flags, DiskStateFlags::AccessGranted)) + { try { wsl::windows::common::hcs::RevokeVmAccess(m_machineId.c_str(), Entry.first.Path.c_str()); } - CATCH_LOG() + CATCH_LOG() + } }); // Delete the swap vhd if one was created. if (m_swapFileCreated) + { try { const auto runAsUser = wil::impersonate_token(m_userToken.get()); LOG_IF_WIN32_BOOL_FALSE(DeleteFileW(m_vmConfig.SwapFilePath.c_str())); } - CATCH_LOG() + CATCH_LOG() + } // Delete the temp folder if it was created. if (m_tempDirectoryCreated) + { try { const auto runAsUser = wil::impersonate_token(m_userToken.get()); wil::RemoveDirectoryRecursive(m_tempPath.c_str()); } - CATCH_LOG() + CATCH_LOG() + } // Delete the mstsc.exe local devices key if one was created. if (m_localDevicesKeyCreated) + { try { const auto runAsUser = wil::impersonate_token(m_userToken.get()); @@ -867,7 +885,8 @@ WslCoreVm::~WslCoreVm() noexcept const auto key = wsl::windows::common::registry::CreateKey(userKey.get(), c_localDevicesKey, KEY_SET_VALUE); THROW_IF_WIN32_ERROR(::RegDeleteKeyValueW(key.get(), nullptr, m_machineId.c_str())); } - CATCH_LOG() + CATCH_LOG() + } WSL_LOG("TerminateVmStop"); } @@ -1583,6 +1602,7 @@ std::wstring WslCoreVm::GenerateConfigJson() // N.B. This is done because arm64 and some older amd64 processors do not support nested virtualization. // Nested virtualization not supported on Windows 10. if (m_vmConfig.EnableNestedVirtualization) + { try { std::vector processorFeatures{}; @@ -1600,7 +1620,8 @@ std::wstring WslCoreVm::GenerateConfigJson() EMIT_USER_WARNING(wsl::shared::Localization::MessageNestedVirtualizationNotSupported()); } } - CATCH_LOG() + CATCH_LOG() + } #ifdef _AMD64_ @@ -1843,12 +1864,14 @@ void WslCoreVm::InitializeGuest() if (LXSS_ENABLE_GUI_APPS()) { if (m_vmConfig.EnableVirtio) + { try { MountSharedMemoryDevice(c_virtiofsClassId, L"wslg", L"wslg", WSLG_SHARED_MEMORY_SIZE_MB); m_sharedMemoryRoot = std::format(L"WSL\\{}\\wslg", m_machineId); } - CATCH_LOG() + CATCH_LOG() + } try { diff --git a/src/windows/wslsettings/Controls/OOBEContent.xaml b/src/windows/wslsettings/Controls/OOBEContent.xaml index 670a4aad5..e4c6721fb 100644 --- a/src/windows/wslsettings/Controls/OOBEContent.xaml +++ b/src/windows/wslsettings/Controls/OOBEContent.xaml @@ -19,7 +19,7 @@ diff --git a/src/windows/wslsettings/Controls/OOBEContent.xaml.cs b/src/windows/wslsettings/Controls/OOBEContent.xaml.cs index a017b0a56..90f37dce8 100644 --- a/src/windows/wslsettings/Controls/OOBEContent.xaml.cs +++ b/src/windows/wslsettings/Controls/OOBEContent.xaml.cs @@ -1,15 +1,46 @@ -// Copyright (c) Microsoft Corporation +// Copyright (C) Microsoft Corporation. All rights reserved. using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; +using Windows.UI.ViewManagement; namespace WslSettings.Controls { public sealed partial class OOBEContent : UserControl { + // Constants for hero image height calculations + private const double BaseImageHeight = 280.0; + private const double MinimumImageHeight = 200.0; + + private static readonly UISettings Settings = new UISettings(); + public OOBEContent() { this.InitializeComponent(); + + // Set initial hero image height based on current text scaling + UpdateHeroImageHeight(); + + // Subscribe to text scale factor changes for dynamic updates + Settings.TextScaleFactorChanged += OnTextScaleFactorChanged; + + // Ensure event cleanup when control is unloaded + this.Unloaded += (s, e) => Settings.TextScaleFactorChanged -= OnTextScaleFactorChanged; + } + + private void UpdateHeroImageHeight() + { + double textScaleFactor = Settings.TextScaleFactor; + + // Reduce image height when text scaling increases to preserve content space + // Use inverse relationship: as text gets larger, image gets proportionally smaller + HeroImageHeight = Math.Max(BaseImageHeight / textScaleFactor, MinimumImageHeight); + } + + private void OnTextScaleFactorChanged(UISettings sender, object args) + { + // Update hero image height when text scaling changes at runtime + this.DispatcherQueue.TryEnqueue(() => UpdateHeroImageHeight()); } public string Title @@ -46,6 +77,6 @@ public object PageContent public static readonly DependencyProperty DescriptionProperty = DependencyProperty.Register("Description", typeof(string), typeof(OOBEContent), new PropertyMetadata(default(string))); public static readonly DependencyProperty HeroImageProperty = DependencyProperty.Register("HeroImage", typeof(string), typeof(OOBEContent), new PropertyMetadata(default(string))); public static readonly DependencyProperty PageContentProperty = DependencyProperty.Register("PageContent", typeof(object), typeof(OOBEContent), new PropertyMetadata(new Grid())); - public static readonly DependencyProperty HeroImageHeightProperty = DependencyProperty.Register("HeroImageHeight", typeof(double), typeof(OOBEContent), new PropertyMetadata(280.0)); + public static readonly DependencyProperty HeroImageHeightProperty = DependencyProperty.Register("HeroImageHeight", typeof(double), typeof(OOBEContent), new PropertyMetadata(BaseImageHeight)); } } \ No newline at end of file diff --git a/src/windows/wslsettings/Helpers/RuntimeHelper.cs b/src/windows/wslsettings/Helpers/RuntimeHelper.cs index 5a3592a4b..58e33deec 100644 --- a/src/windows/wslsettings/Helpers/RuntimeHelper.cs +++ b/src/windows/wslsettings/Helpers/RuntimeHelper.cs @@ -1,7 +1,9 @@ // Copyright (C) Microsoft Corporation. All rights reserved. +using CommunityToolkit.WinUI.Controls; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Input; +using System.Diagnostics; using System.Reflection; using System.Runtime.InteropServices; using System.Text; @@ -47,4 +49,39 @@ public static void TryMoveFocusPreviousControl(Button? button) FindNextElementOptions fneo = new() { SearchRoot = button.XamlRoot.Content }; FocusManager.TryMoveFocus(FocusNavigationDirection.Previous, fneo); } + + public static void SetupSettingsExpanderFocusManagement(Microsoft.UI.Xaml.FrameworkElement expander, Microsoft.UI.Xaml.Controls.Control firstFocusableElement) + { + if (expander is CommunityToolkit.WinUI.Controls.SettingsExpander settingsExpander) + { + settingsExpander.RegisterPropertyChangedCallback(CommunityToolkit.WinUI.Controls.SettingsExpander.IsExpandedProperty, (sender, dp) => + { + if (sender is CommunityToolkit.WinUI.Controls.SettingsExpander se && se.IsExpanded) + { + System.EventHandler? layoutHandler = null; + layoutHandler = (s, e) => + { + se.LayoutUpdated -= layoutHandler; + firstFocusableElement.Focus(Microsoft.UI.Xaml.FocusState.Keyboard); + }; + + se.LayoutUpdated += layoutHandler; + } + }); + } + } + + public static void SetupExpanderFocusManagementByName(Microsoft.UI.Xaml.FrameworkElement parent, string expanderName, string textBoxName) + { + var expander = parent.FindName(expanderName) as Microsoft.UI.Xaml.FrameworkElement; + var textBox = parent.FindName(textBoxName) as Microsoft.UI.Xaml.Controls.Control; + + Debug.Assert(expander != null, $"Expander '{expanderName}' not found"); + Debug.Assert(textBox != null, $"TextBox '{textBoxName}' not found"); + + if (expander != null && textBox != null) + { + SetupSettingsExpanderFocusManagement(expander, textBox); + } + } } \ No newline at end of file diff --git a/src/windows/wslsettings/LibWsl.cs b/src/windows/wslsettings/LibWsl.cs index 4037772fe..cd151fdf4 100644 --- a/src/windows/wslsettings/LibWsl.cs +++ b/src/windows/wslsettings/LibWsl.cs @@ -80,7 +80,7 @@ internal static void __RecordNativeToManagedMapping(IntPtr native, global::LibWs internal static bool __TryGetNativeToManagedMapping(IntPtr native, out global::LibWsl.WslConfig managed) { - + return NativeToManagedMap.TryGetValue(native, out managed); } @@ -171,7 +171,7 @@ internal static void __RecordNativeToManagedMapping(IntPtr native, global::LibWs internal static bool __TryGetNativeToManagedMapping(IntPtr native, out global::LibWsl.WslConfigSetting managed) { - + return NativeToManagedMap.TryGetValue(native, out managed); } diff --git a/src/windows/wslsettings/Views/Settings/AboutPage.xaml b/src/windows/wslsettings/Views/Settings/AboutPage.xaml index b49027996..70865d760 100644 --- a/src/windows/wslsettings/Views/Settings/AboutPage.xaml +++ b/src/windows/wslsettings/Views/Settings/AboutPage.xaml @@ -7,6 +7,12 @@ xmlns:controls="using:WslSettings.Controls" behaviors:NavigationViewHeaderBehavior.HeaderMode="Never"> + + + + + + @@ -20,10 +26,10 @@ - - - - + + + + diff --git a/src/windows/wslsettings/Views/Settings/DeveloperPage.xaml b/src/windows/wslsettings/Views/Settings/DeveloperPage.xaml index db83f8a39..7c1280c8a 100644 --- a/src/windows/wslsettings/Views/Settings/DeveloperPage.xaml +++ b/src/windows/wslsettings/Views/Settings/DeveloperPage.xaml @@ -25,12 +25,12 @@ - + -