winget-cli

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

EnvironmentChangeListener.cs (5962B)


      1 // Copyright (c) Microsoft Corporation.
      2 // Licensed under the MIT License.
      3 
      4 using System.Runtime.InteropServices;
      5 using System.Threading;
      6 
      7 namespace ConfigurationRemotingServer
      8 {
      9     internal class EnvironmentChangeListener
     10     {
     11         // Window message constants
     12         private const int WM_SETTINGCHANGE = 0x001A;
     13         private const int WM_QUIT = 0x0012;
     14 
     15         // User32.dll imports
     16         [DllImport("user32.dll")]
     17         private static extern IntPtr CreateWindowEx(
     18             uint dwExStyle, string lpClassName, string lpWindowName, uint dwStyle,
     19             int x, int y, int nWidth, int nHeight, IntPtr hWndParent, IntPtr hMenu,
     20             IntPtr hInstance, IntPtr lpParam);
     21 
     22         [DllImport("user32.dll")]
     23         private static extern bool DestroyWindow(IntPtr hWnd);
     24 
     25         [DllImport("user32.dll")]
     26         private static extern IntPtr DefWindowProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
     27 
     28         [DllImport("user32.dll")]
     29         private static extern bool GetMessage(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax);
     30 
     31         [DllImport("user32.dll")]
     32         private static extern bool PostMessageW(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
     33 
     34         [DllImport("user32.dll")]
     35         private static extern bool TranslateMessage(ref MSG lpMsg);
     36 
     37         [DllImport("user32.dll")]
     38         private static extern IntPtr DispatchMessage(ref MSG lpMsg);
     39 
     40         [DllImport("user32.dll")]
     41         private static extern ushort RegisterClass(ref WNDCLASS lpWndClass);
     42 
     43         [DllImport("kernel32.dll")]
     44         private static extern IntPtr GetModuleHandle(string? lpModuleName);
     45 
     46         // Windows message structure
     47         [StructLayout(LayoutKind.Sequential)]
     48         public struct MSG
     49         {
     50             public IntPtr hwnd;
     51             public uint message;
     52             public IntPtr wParam;
     53             public IntPtr lParam;
     54             public uint time;
     55             public POINT pt;
     56             public uint lPrivate;
     57         }
     58 
     59         [StructLayout(LayoutKind.Sequential)]
     60         public struct POINT
     61         {
     62             public int x;
     63             public int y;
     64         }
     65 
     66         // Window class structure
     67         [StructLayout(LayoutKind.Sequential)]
     68         public struct WNDCLASS
     69         {
     70             public uint style;
     71             public IntPtr lpfnWndProc;
     72             public int cbClsExtra;
     73             public int cbWndExtra;
     74             public IntPtr hInstance;
     75             public IntPtr hIcon;
     76             public IntPtr hCursor;
     77             public IntPtr hBackground;
     78             [MarshalAs(UnmanagedType.LPStr)]
     79             public string? lpszMenuName;
     80             [MarshalAs(UnmanagedType.LPStr)]
     81             public string lpszClassName;
     82         }
     83 
     84         // Window procedure delegate
     85         private delegate IntPtr WndProcDelegate(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
     86         // Keep a reference to the delegate to prevent it from being garbage collected
     87         private static WndProcDelegate? _wndProcDelegate;
     88 
     89         private IntPtr _hwnd;
     90         private Thread _thread;
     91 
     92         public delegate void EnvironmentChangedEventHandler();
     93         public static event EnvironmentChangedEventHandler? EnvironmentChanged;
     94 
     95         public EnvironmentChangeListener()
     96         {
     97             _thread = new Thread(MessageLoop);
     98             _thread.Start();
     99         }
    100 
    101         public void Stop()
    102         {
    103             if (PostMessageW(_hwnd, WM_QUIT, IntPtr.Zero, IntPtr.Zero))
    104             {
    105                 _thread.Join();
    106             }
    107         }
    108 
    109         private void MessageLoop()
    110         {
    111             // Register window class
    112             string className = "EnvironmentMonitorClass";
    113             _wndProcDelegate = WndProc;
    114 
    115             WNDCLASS wndClass = new WNDCLASS
    116             {
    117                 style = 0,
    118                 lpfnWndProc = Marshal.GetFunctionPointerForDelegate(_wndProcDelegate),
    119                 cbClsExtra = 0,
    120                 cbWndExtra = 0,
    121                 hInstance = GetModuleHandle(null),
    122                 hIcon = IntPtr.Zero,
    123                 hCursor = IntPtr.Zero,
    124                 hBackground = IntPtr.Zero,
    125                 lpszMenuName = "",
    126                 lpszClassName = className
    127             };
    128 
    129             RegisterClass(ref wndClass);
    130 
    131             // Create hidden window
    132             const uint WS_OVERLAPPED = 0x00000000;
    133             const uint WS_EX_TOOL_WINDOW = 0x00000080;
    134 
    135             _hwnd = CreateWindowEx(
    136                 WS_EX_TOOL_WINDOW,
    137                 className,
    138                 "Environment Monitor",
    139                 WS_OVERLAPPED,
    140                 0, 0, 0, 0,
    141                 IntPtr.Zero, IntPtr.Zero,
    142                 GetModuleHandle(null), IntPtr.Zero);
    143 
    144             if (_hwnd == IntPtr.Zero)
    145             {
    146                 return;
    147             }
    148 
    149             MSG msg;
    150             while (GetMessage(out msg, IntPtr.Zero, 0, 0))
    151             {
    152                 TranslateMessage(ref msg);
    153                 DispatchMessage(ref msg);
    154             }
    155 
    156             // Clean up
    157             DestroyWindow(_hwnd);
    158         }
    159 
    160         private static IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
    161         {
    162             switch (msg)
    163             {
    164                 case WM_SETTINGCHANGE:
    165                     // Check if this is an environment variable change notification
    166                     string? msgInfo = Marshal.PtrToStringAnsi(lParam);
    167                     if (msgInfo == "Environment")
    168                     {
    169                         if (EnvironmentChanged != null)
    170                         {
    171                             EnvironmentChanged.Invoke();
    172                         }
    173                     }
    174                     break;
    175             }
    176 
    177             return DefWindowProc(hWnd, msg, wParam, lParam);
    178         }
    179     }
    180 }