winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 8f17b49398635c5fbe178bbbe3895870ebbe7b81
parent 539667c85c726cd137e4fb661ed896e7654499e2
Author: KEINOS <github+fork-qiita-news@keinos.com>
Date:   Tue, 17 Jun 2025 14:30:34 +0000

Merge remote-tracking branch 'upstream/master'

Diffstat:
M.github/actions/spelling/expect.txt | 2+-
Msrc/ConfigurationRemotingServer/ConfigurationRemotingServer.csproj | 1-
Asrc/ConfigurationRemotingServer/EnvironmentChangeListener.cs | 180+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/ConfigurationRemotingServer/Program.cs | 29++++-------------------------
4 files changed, 185 insertions(+), 27 deletions(-)

diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt @@ -599,7 +599,7 @@ winreg winrtact winstring WMI -Wnd +wnd WNDCLASS WNDCLASSEX workaround diff --git a/src/ConfigurationRemotingServer/ConfigurationRemotingServer.csproj b/src/ConfigurationRemotingServer/ConfigurationRemotingServer.csproj @@ -3,7 +3,6 @@ <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net8.0-windows10.0.26100.0</TargetFramework> - <UseWindowsForms>true</UseWindowsForms> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> <SupportedOSPlatformVersion>10.0.17763.0</SupportedOSPlatformVersion> diff --git a/src/ConfigurationRemotingServer/EnvironmentChangeListener.cs b/src/ConfigurationRemotingServer/EnvironmentChangeListener.cs @@ -0,0 +1,180 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Runtime.InteropServices; +using System.Threading; + +namespace ConfigurationRemotingServer +{ + internal class EnvironmentChangeListener + { + // Window message constants + private const int WM_SETTINGCHANGE = 0x001A; + private const int WM_QUIT = 0x0012; + + // User32.dll imports + [DllImport("user32.dll")] + private static extern IntPtr CreateWindowEx( + uint dwExStyle, string lpClassName, string lpWindowName, uint dwStyle, + int x, int y, int nWidth, int nHeight, IntPtr hWndParent, IntPtr hMenu, + IntPtr hInstance, IntPtr lpParam); + + [DllImport("user32.dll")] + private static extern bool DestroyWindow(IntPtr hWnd); + + [DllImport("user32.dll")] + private static extern IntPtr DefWindowProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam); + + [DllImport("user32.dll")] + private static extern bool GetMessage(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax); + + [DllImport("user32.dll")] + private static extern bool PostMessageW(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam); + + [DllImport("user32.dll")] + private static extern bool TranslateMessage(ref MSG lpMsg); + + [DllImport("user32.dll")] + private static extern IntPtr DispatchMessage(ref MSG lpMsg); + + [DllImport("user32.dll")] + private static extern ushort RegisterClass(ref WNDCLASS lpWndClass); + + [DllImport("kernel32.dll")] + private static extern IntPtr GetModuleHandle(string? lpModuleName); + + // Windows message structure + [StructLayout(LayoutKind.Sequential)] + public struct MSG + { + public IntPtr hwnd; + public uint message; + public IntPtr wParam; + public IntPtr lParam; + public uint time; + public POINT pt; + public uint lPrivate; + } + + [StructLayout(LayoutKind.Sequential)] + public struct POINT + { + public int x; + public int y; + } + + // Window class structure + [StructLayout(LayoutKind.Sequential)] + public struct WNDCLASS + { + public uint style; + public IntPtr lpfnWndProc; + public int cbClsExtra; + public int cbWndExtra; + public IntPtr hInstance; + public IntPtr hIcon; + public IntPtr hCursor; + public IntPtr hBackground; + [MarshalAs(UnmanagedType.LPStr)] + public string? lpszMenuName; + [MarshalAs(UnmanagedType.LPStr)] + public string lpszClassName; + } + + // Window procedure delegate + private delegate IntPtr WndProcDelegate(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam); + // Keep a reference to the delegate to prevent it from being garbage collected + private static WndProcDelegate? _wndProcDelegate; + + private IntPtr _hwnd; + private Thread _thread; + + public delegate void EnvironmentChangedEventHandler(); + public static event EnvironmentChangedEventHandler? EnvironmentChanged; + + public EnvironmentChangeListener() + { + _thread = new Thread(MessageLoop); + _thread.Start(); + } + + public void Stop() + { + if (PostMessageW(_hwnd, WM_QUIT, IntPtr.Zero, IntPtr.Zero)) + { + _thread.Join(); + } + } + + private void MessageLoop() + { + // Register window class + string className = "EnvironmentMonitorClass"; + _wndProcDelegate = WndProc; + + WNDCLASS wndClass = new WNDCLASS + { + style = 0, + lpfnWndProc = Marshal.GetFunctionPointerForDelegate(_wndProcDelegate), + cbClsExtra = 0, + cbWndExtra = 0, + hInstance = GetModuleHandle(null), + hIcon = IntPtr.Zero, + hCursor = IntPtr.Zero, + hBackground = IntPtr.Zero, + lpszMenuName = "", + lpszClassName = className + }; + + RegisterClass(ref wndClass); + + // Create hidden window + const uint WS_OVERLAPPED = 0x00000000; + const uint WS_EX_TOOL_WINDOW = 0x00000080; + + _hwnd = CreateWindowEx( + WS_EX_TOOL_WINDOW, + className, + "Environment Monitor", + WS_OVERLAPPED, + 0, 0, 0, 0, + IntPtr.Zero, IntPtr.Zero, + GetModuleHandle(null), IntPtr.Zero); + + if (_hwnd == IntPtr.Zero) + { + return; + } + + MSG msg; + while (GetMessage(out msg, IntPtr.Zero, 0, 0)) + { + TranslateMessage(ref msg); + DispatchMessage(ref msg); + } + + // Clean up + DestroyWindow(_hwnd); + } + + private static IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam) + { + switch (msg) + { + case WM_SETTINGCHANGE: + // Check if this is an environment variable change notification + string? msgInfo = Marshal.PtrToStringAnsi(lParam); + if (msgInfo == "Environment") + { + if (EnvironmentChanged != null) + { + EnvironmentChanged.Invoke(); + } + } + break; + } + + return DefWindowProc(hWnd, msg, wParam, lParam); + } + } +} diff --git a/src/ConfigurationRemotingServer/Program.cs b/src/ConfigurationRemotingServer/Program.cs @@ -9,7 +9,6 @@ using System.Text.Json.Serialization; using Microsoft.Management.Configuration; using Microsoft.Management.Configuration.Processor; using Microsoft.Management.Configuration.Processor.Helpers; -using Microsoft.Win32; using WinRT; using IConfigurationSetProcessorFactory = global::Microsoft.Management.Configuration.IConfigurationSetProcessorFactory; @@ -82,16 +81,8 @@ namespace ConfigurationRemotingServer string staticsCallback = args[1]; // Listen for setting change message and update PATH if needed. - HiddenForm hiddenForm = new HiddenForm(); - var settingChangedListenerThread = new Thread( - () => - { - SystemEvents.UserPreferenceChanged += OnUserPreferenceChanged; - Application.Run(hiddenForm); - SystemEvents.UserPreferenceChanged -= OnUserPreferenceChanged; - }); - - settingChangedListenerThread.Start(); + EnvironmentChangeListener.EnvironmentChanged += OnEnvironmentChanged; + EnvironmentChangeListener environmentChangeListener = new EnvironmentChangeListener(); try { @@ -168,23 +159,11 @@ namespace ConfigurationRemotingServer } finally { - hiddenForm.BeginInvoke(new Action(() => { hiddenForm.Close(); })); - settingChangedListenerThread.Join(); - } - } - - private class HiddenForm : Form - { - public HiddenForm() - { - this.Width = 0; - this.Height = 0; - this.Visible = false; - this.Load += (sender, e) => this.Hide(); + environmentChangeListener.Stop(); } } - private static void OnUserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e) + private static void OnEnvironmentChanged() { PathEnvironmentVariableHandler.UpdatePath(); }