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 }