From 8e053a3d4b74e2b831024a71d167b2cc141f4c26 Mon Sep 17 00:00:00 2001 From: Derek Christensen Date: Sun, 8 Aug 2021 15:08:30 -0600 Subject: [PATCH] Update environment variables changed --- .../PowerLauncher/Helper/EnvironmentHelper.cs | 98 +++++++++++++++++++ .../PowerLauncher/Helper/NativeMethods.cs | 2 + .../launcher/PowerLauncher/MainWindow.xaml | 1 + .../launcher/PowerLauncher/MainWindow.xaml.cs | 17 +++- 4 files changed, 114 insertions(+), 4 deletions(-) create mode 100644 src/modules/launcher/PowerLauncher/Helper/EnvironmentHelper.cs diff --git a/src/modules/launcher/PowerLauncher/Helper/EnvironmentHelper.cs b/src/modules/launcher/PowerLauncher/Helper/EnvironmentHelper.cs new file mode 100644 index 000000000000..dd21b5851263 --- /dev/null +++ b/src/modules/launcher/PowerLauncher/Helper/EnvironmentHelper.cs @@ -0,0 +1,98 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Linq; +using System.Runtime.InteropServices; +using System.Security.Principal; +using Microsoft.Win32.SafeHandles; +using Wox.Plugin.Logger; + +namespace PowerLauncher.Helper +{ + public static class EnvironmentHelper + { + private const string EnvironmentChangeType = "Environment"; + private const string Username = "USERNAME"; + private const string ProcessorArchitecture = "PROCESSOR_ARCHITECTURE"; + private const string Path = "PATH"; + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA1801:Review unused parameters", Justification = "Params are required for delegate signature requirements.")] + public static IntPtr ProcessWindowMessages(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam, ref bool handled) + { + switch ((WM)msg) + { + case WM.SETTINGCHANGE: + string changeType = Marshal.PtrToStringUni(lparam); + if (changeType == EnvironmentChangeType) + { + Log.Info("Reload environment", typeof(EnvironmentHelper)); + UpdateEnvironment(); + handled = true; + } + + break; + } + + return IntPtr.Zero; + } + + private static void UpdateEnvironment() + { + // Username and process architecture are set by the machine vars, this + // may lead to incorrect values so save off the current values to restore. + string originalUsername = Environment.GetEnvironmentVariable(Username, EnvironmentVariableTarget.Process); + string originalArch = Environment.GetEnvironmentVariable(ProcessorArchitecture, EnvironmentVariableTarget.Process); + + var environment = new Dictionary(); + MergeTargetEnvironmentVariables(environment, EnvironmentVariableTarget.Process); + MergeTargetEnvironmentVariables(environment, EnvironmentVariableTarget.Machine); + + if (!IsRunningAsSystem()) + { + MergeTargetEnvironmentVariables(environment, EnvironmentVariableTarget.User); + + // Special handling for PATH - merge Machine & User instead of override + var pathTargets = new[] { EnvironmentVariableTarget.Machine, EnvironmentVariableTarget.User }; + var paths = pathTargets + .Select(t => Environment.GetEnvironmentVariable(Path, t)) + .Where(e => e != null) + .SelectMany(e => e.Split(';', StringSplitOptions.RemoveEmptyEntries)) + .Distinct(); + + environment[Path] = string.Join(';', paths); + } + + environment[Username] = originalUsername; + environment[ProcessorArchitecture] = originalArch; + + foreach (KeyValuePair kv in environment) + { + Environment.SetEnvironmentVariable(kv.Key, kv.Value, EnvironmentVariableTarget.Process); + } + } + + private static void MergeTargetEnvironmentVariables( + Dictionary environment, EnvironmentVariableTarget target) + { + IDictionary variables = Environment.GetEnvironmentVariables(target); + foreach (DictionaryEntry entry in variables) + { + environment[(string)entry.Key] = (string)entry.Value; + } + } + + private static bool IsRunningAsSystem() + { + using (var identity = WindowsIdentity.GetCurrent()) + { + return identity.IsSystem; + } + } + } +} diff --git a/src/modules/launcher/PowerLauncher/Helper/NativeMethods.cs b/src/modules/launcher/PowerLauncher/Helper/NativeMethods.cs index 44fe5d35d905..80903bdaee98 100644 --- a/src/modules/launcher/PowerLauncher/Helper/NativeMethods.cs +++ b/src/modules/launcher/PowerLauncher/Helper/NativeMethods.cs @@ -7,6 +7,7 @@ using System.Runtime.InteropServices; using System.Security; using System.Text; +using Microsoft.Win32.SafeHandles; using static PowerLauncher.Helper.WindowsInteropHelper; // http://blogs.microsoft.co.il/arik/2010/05/28/wpf-single-instance-application/ @@ -108,6 +109,7 @@ internal enum WM ERASEBKGND = 0x0014, SYSCOLORCHANGE = 0x0015, SHOWWINDOW = 0x0018, + SETTINGCHANGE = 0x001A, ACTIVATEAPP = 0x001C, SETCURSOR = 0x0020, MOUSEACTIVATE = 0x0021, diff --git a/src/modules/launcher/PowerLauncher/MainWindow.xaml b/src/modules/launcher/PowerLauncher/MainWindow.xaml index 6a267a733c0a..1eac377595b1 100644 --- a/src/modules/launcher/PowerLauncher/MainWindow.xaml +++ b/src/modules/launcher/PowerLauncher/MainWindow.xaml @@ -16,6 +16,7 @@ ShowInTaskbar="False" Icon="Images/app.dark.png" AllowsTransparency="True" + SourceInitialized="OnSourceInitialized" Loaded="OnLoaded" Closing="OnClosing" Background="Transparent" diff --git a/src/modules/launcher/PowerLauncher/MainWindow.xaml.cs b/src/modules/launcher/PowerLauncher/MainWindow.xaml.cs index c2aea008123e..30b27ce51631 100644 --- a/src/modules/launcher/PowerLauncher/MainWindow.xaml.cs +++ b/src/modules/launcher/PowerLauncher/MainWindow.xaml.cs @@ -9,6 +9,7 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Input; +using System.Windows.Interop; using interop; using Microsoft.PowerLauncher.Telemetry; using Microsoft.PowerToys.Telemetry; @@ -29,8 +30,10 @@ public partial class MainWindow : IDisposable private readonly MainViewModel _viewModel; private bool _isTextSetProgrammatically; private bool _deletePressed; + private HwndSource _hwndSource; private Timer _firstDeleteTimer = new Timer(); private bool _coldStateHotkeyPressed; + private bool _disposedValue; public MainWindow(PowerToysRunSettings settings, MainViewModel mainVM) : this() @@ -93,6 +96,12 @@ private void BringProcessToForeground() Activate(); } + private void OnSourceInitialized(object sender, EventArgs e) + { + _hwndSource = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle); + _hwndSource.AddHook(EnvironmentHelper.ProcessWindowMessages); + } + private void OnLoaded(object sender, RoutedEventArgs e) { WindowsInteropHelper.DisableControlBox(this); @@ -358,8 +367,6 @@ private void SuggestionsList_SelectionChanged(object sender, SelectionChangedEve } } - private bool disposedValue; - private void QueryTextBox_TextChanged(object sender, TextChangedEventArgs e) { var textBox = (TextBox)sender; @@ -450,7 +457,7 @@ private void SearchBox_InputLanguageChanged(object sender, InputLanguageEventArg protected virtual void Dispose(bool disposing) { - if (!disposedValue) + if (!_disposedValue) { if (disposing) { @@ -458,12 +465,14 @@ protected virtual void Dispose(bool disposing) { _firstDeleteTimer.Dispose(); } + + _hwndSource?.Dispose(); } // TODO: free unmanaged resources (unmanaged objects) and override finalizer // TODO: set large fields to null _firstDeleteTimer = null; - disposedValue = true; + _disposedValue = true; } }