diff --git a/src/modules/powerdisplay/PowerDisplay/Assets/PowerDisplay.ico b/src/modules/powerdisplay/PowerDisplay/Assets/PowerDisplay.ico
deleted file mode 100644
index a9f170a8bd..0000000000
Binary files a/src/modules/powerdisplay/PowerDisplay/Assets/PowerDisplay.ico and /dev/null differ
diff --git a/src/modules/powerdisplay/PowerDisplay/Helpers/TrayIconHelper.cs b/src/modules/powerdisplay/PowerDisplay/Helpers/TrayIconHelper.cs
deleted file mode 100644
index 58d5326ff9..0000000000
--- a/src/modules/powerdisplay/PowerDisplay/Helpers/TrayIconHelper.cs
+++ /dev/null
@@ -1,484 +0,0 @@
-// 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.Drawing;
-using System.Runtime.InteropServices;
-using ManagedCommon;
-using Microsoft.UI.Xaml;
-using PowerDisplay.Native;
-using static PowerDisplay.Native.PInvoke;
-
-namespace PowerDisplay.Helpers
-{
- ///
- /// System tray icon helper class
- ///
- public partial class TrayIconHelper : IDisposable
- {
- private const uint NifMessage = 0x00000001;
- private const uint NifIcon = 0x00000002;
- private const uint NifTip = 0x00000004;
- private const uint NifInfo = 0x00000010;
-
- private const uint NimAdd = 0x00000000;
- private const uint NimModify = 0x00000001;
- private const uint NimDelete = 0x00000002;
-
- private const uint WmUser = 0x0400;
- private const uint WmTrayicon = WmUser + 1;
- private const uint WmLbuttonup = 0x0202;
- private const uint WmRbuttonup = 0x0205;
- private const uint WmCommand = 0x0111;
-
- private uint _wmTaskbarCreated; // TaskbarCreated message ID
-
- // Menu item IDs
- private const int IdShow = 1001;
- private const int IdExit = 1002;
- private const int IdRefresh = 1003;
- private const int IdSettings = 1004;
-
- private delegate IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
-
- private const uint MfString = 0x00000000;
- private const uint MfSeparator = 0x00000800;
- private const uint TpmLeftalign = 0x0000;
- private const uint TpmReturncmd = 0x0100;
-
- private const int SwHide = 0;
- private const int SwShow = 5;
-
- private IntPtr _messageWindowHandle;
- private NOTIFYICONDATA _notifyIconData;
- private bool _isDisposed;
- private WndProc _wndProc;
- private Window _mainWindow;
- private Action? _onShowWindow;
- private Action? _onExitApplication;
- private Action? _onRefresh;
- private Action? _onSettings;
- private bool _isWindowVisible = true;
- private System.Drawing.Icon? _trayIcon; // Keep icon reference to prevent garbage collection
-
- public TrayIconHelper(Window mainWindow)
- {
- _mainWindow = mainWindow;
- _wndProc = WindowProc;
-
- // Register TaskbarCreated message
- _wmTaskbarCreated = RegisterWindowMessage("TaskbarCreated");
- Logger.LogInfo($"Registered TaskbarCreated message: {_wmTaskbarCreated}");
-
- if (!CreateMessageWindow())
- {
- Logger.LogError("Failed to create message window");
- return;
- }
-
- CreateTrayIcon();
- }
-
- ///
- /// Set callback functions
- ///
- public void SetCallbacks(Action onShow, Action onExit, Action? onRefresh = null, Action? onSettings = null)
- {
- _onShowWindow = onShow;
- _onExitApplication = onExit;
- _onRefresh = onRefresh;
- _onSettings = onSettings;
- }
-
- ///
- /// Create message window - using system predefined Message window class
- ///
- private bool CreateMessageWindow()
- {
- try
- {
- Logger.LogDebug("Creating message window using system Message class...");
-
- // Use system predefined "Message" window class, no registration needed
- // HWND_MESSAGE (-3) creates pure message window, no hInstance needed
- _messageWindowHandle = CreateWindowEx(
- 0, // dwExStyle
- "Message", // lpClassName - system predefined message window class
- string.Empty, // lpWindowName
- 0, // dwStyle
- 0,
- 0,
- 0,
- 0, // x, y, width, height
- new IntPtr(-3), // hWndParent = HWND_MESSAGE (pure message window)
- IntPtr.Zero, // hMenu
- IntPtr.Zero, // hInstance - not needed
- IntPtr.Zero); // lpParam
-
- if (_messageWindowHandle == IntPtr.Zero)
- {
- var error = Marshal.GetLastWin32Error();
- Logger.LogError($"CreateWindowEx failed with error: {error}");
- return false;
- }
-
- Logger.LogInfo($"Message window created successfully: {_messageWindowHandle}");
-
- // Set window procedure to handle our messages
- SetWindowLongPtr(_messageWindowHandle, -4, Marshal.GetFunctionPointerForDelegate(_wndProc));
-
- return true;
- }
- catch (Exception ex)
- {
- Logger.LogError($"CreateMessageWindow exception: {ex.Message}");
- return false;
- }
- }
-
- ///
- /// Create tray icon
- ///
- private unsafe void CreateTrayIcon()
- {
- if (_messageWindowHandle == IntPtr.Zero)
- {
- Logger.LogError("Cannot create tray icon: invalid message window handle");
- return;
- }
-
- // First try to delete any existing old icon (if any)
- var tempData = new NOTIFYICONDATA
- {
- CbSize = (uint)sizeof(NOTIFYICONDATA),
- HWnd = _messageWindowHandle,
- UID = 1,
- };
- Shell_NotifyIcon(NimDelete, ref tempData);
-
- // Get icon handle
- var iconHandle = GetDefaultIcon();
- if (iconHandle == IntPtr.Zero)
- {
- Logger.LogError("Cannot create tray icon: invalid icon handle");
- return;
- }
-
- _notifyIconData = new NOTIFYICONDATA
- {
- CbSize = (uint)sizeof(NOTIFYICONDATA),
- HWnd = _messageWindowHandle,
- UID = 1,
- UFlags = NifMessage | NifIcon | NifTip,
- UCallbackMessage = WmTrayicon,
- HIcon = iconHandle,
- };
- _notifyIconData.SetTip("Power Display");
-
- // Retry mechanism: try up to 3 times to create tray icon
- const int maxRetries = 3;
- const int retryDelayMs = 500;
-
- for (int attempt = 1; attempt <= maxRetries; attempt++)
- {
- Logger.LogDebug($"Creating tray icon (attempt {attempt}/{maxRetries})...");
-
- bool result = Shell_NotifyIcon(NimAdd, ref _notifyIconData);
- if (result)
- {
- Logger.LogInfo($"Tray icon created successfully on attempt {attempt}");
- return;
- }
-
- var lastError = Marshal.GetLastWin32Error();
- Logger.LogWarning($"Failed to create tray icon on attempt {attempt}. Error: {lastError}");
-
- // Analyze specific error and provide suggestions
- switch (lastError)
- {
- case 0: // ERROR_SUCCESS - may be false success
- Logger.LogWarning("Shell_NotifyIcon returned false but GetLastWin32Error is 0");
- break;
- case 1400: // ERROR_INVALID_WINDOW_HANDLE
- Logger.LogWarning("Invalid window handle - message window may not be properly created");
- break;
- case 1418: // ERROR_THREAD_1_INACTIVE
- Logger.LogWarning("Thread inactive - may need to wait for Explorer to be ready");
- break;
- case 1414: // ERROR_INVALID_ICON_HANDLE
- Logger.LogWarning("Invalid icon handle - icon may have been garbage collected");
- break;
- default:
- Logger.LogWarning($"Unexpected error code: {lastError}");
- break;
- }
-
- // If not the last attempt, wait and retry
- if (attempt < maxRetries)
- {
- Logger.LogDebug($"Retrying in {retryDelayMs}ms...");
- System.Threading.Thread.Sleep(retryDelayMs);
-
- // Re-get icon handle to prevent handle invalidation
- iconHandle = GetDefaultIcon();
- _notifyIconData.HIcon = iconHandle;
- }
- }
-
- Logger.LogError($"Failed to create tray icon after {maxRetries} attempts");
- }
-
- ///
- /// Get default icon
- ///
- private IntPtr GetDefaultIcon()
- {
- try
- {
- // First release previous icon
- _trayIcon?.Dispose();
- _trayIcon = null;
-
- // Try to load icon from Assets folder in exe directory
- var exePath = System.Diagnostics.Process.GetCurrentProcess().MainModule?.FileName;
- if (!string.IsNullOrEmpty(exePath))
- {
- var exeDir = System.IO.Path.GetDirectoryName(exePath);
- if (!string.IsNullOrEmpty(exeDir))
- {
- var iconPath = System.IO.Path.Combine(exeDir, "Assets", "PowerDisplay.ico");
-
- Logger.LogDebug($"Attempting to load icon from: {iconPath}");
-
- if (System.IO.File.Exists(iconPath))
- {
- // Create icon and save as class member to prevent garbage collection
- _trayIcon = new System.Drawing.Icon(iconPath);
- Logger.LogInfo($"Successfully loaded custom icon from {iconPath}");
- return _trayIcon.Handle;
- }
- else
- {
- Logger.LogWarning($"Icon file not found at: {iconPath}");
- }
- }
- }
- }
- catch (Exception ex)
- {
- Logger.LogError($"Failed to load PowerDisplay icon: {ex.Message}");
- _trayIcon?.Dispose();
- _trayIcon = null;
- }
-
- // If loading fails, use system default icon
- var systemIconHandle = LoadIcon(IntPtr.Zero, new IntPtr(32512)); // IDI_APPLICATION
- Logger.LogInfo($"Using system default icon: {systemIconHandle}");
- return systemIconHandle;
- }
-
- ///
- /// Window message processing
- ///
- private IntPtr WindowProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
- {
- if (msg == _wmTaskbarCreated)
- {
- // Explorer restarted, need to recreate tray icon
- Logger.LogInfo("TaskbarCreated message received - recreating tray icon");
- CreateTrayIcon();
- return IntPtr.Zero;
- }
-
- switch (msg)
- {
- case WmTrayicon:
- HandleTrayIconMessage(lParam);
- break;
- case WmCommand:
- HandleMenuCommand(wParam);
- break;
- }
-
- return DefWindowProc(hWnd, msg, wParam, lParam);
- }
-
- ///
- /// Handle tray icon messages
- ///
- private void HandleTrayIconMessage(IntPtr lParam)
- {
- switch ((uint)lParam)
- {
- case WmLbuttonup:
- // Left click - show/hide window
- ToggleWindowVisibility();
- break;
- case WmRbuttonup:
- // Right click - show menu
- ShowContextMenu();
- break;
- }
- }
-
- ///
- /// Toggle window visibility state
- ///
- private void ToggleWindowVisibility()
- {
- _isWindowVisible = !_isWindowVisible;
- if (_isWindowVisible)
- {
- _onShowWindow?.Invoke();
- }
- else
- {
- // Hide window logic will be implemented in MainWindow
- if (_mainWindow != null)
- {
- var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(_mainWindow);
- ShowWindow(hWnd, SwHide);
- }
- }
- }
-
- ///
- /// Show right-click menu
- ///
- private void ShowContextMenu()
- {
- var hMenu = CreatePopupMenu();
-
- AppendMenu(hMenu, MfString, IdShow, _isWindowVisible ? "Hide Window" : "Show Window");
- if (_onRefresh != null)
- {
- AppendMenu(hMenu, MfString, IdRefresh, "Refresh Monitors");
- }
-
- if (_onSettings != null)
- {
- AppendMenu(hMenu, MfString, IdSettings, "Settings");
- }
-
- AppendMenu(hMenu, MfSeparator, 0, string.Empty);
- AppendMenu(hMenu, MfString, IdExit, "Exit");
-
- GetCursorPos(out POINT pt);
- SetForegroundWindow(_messageWindowHandle);
-
- var cmd = TrackPopupMenu(hMenu, TpmLeftalign | TpmReturncmd, pt.X, pt.Y, 0, _messageWindowHandle, IntPtr.Zero);
-
- if (cmd != 0)
- {
- HandleMenuCommand(new IntPtr(cmd));
- }
-
- DestroyMenu(hMenu);
- }
-
- ///
- /// Handle menu commands
- ///
- private void HandleMenuCommand(IntPtr commandId)
- {
- switch (commandId.ToInt32())
- {
- case IdShow:
- ToggleWindowVisibility();
- break;
- case IdRefresh:
- _onRefresh?.Invoke();
- break;
- case IdSettings:
- _onSettings?.Invoke();
- break;
- case IdExit:
- _onExitApplication?.Invoke();
- break;
- }
- }
-
- ///
- /// Show balloon tip
- ///
- public void ShowBalloonTip(string title, string text, uint timeout = 3000)
- {
- _notifyIconData.UFlags |= NifInfo;
- _notifyIconData.SetInfoTitle(title);
- _notifyIconData.SetInfo(text);
- _notifyIconData.UTimeout = timeout;
- _notifyIconData.DwInfoFlags = 1; // NIIF_INFO
-
- Shell_NotifyIcon(NimModify, ref _notifyIconData);
- }
-
- ///
- /// Update tray icon tooltip text
- ///
- public void UpdateTooltip(string tooltip)
- {
- _notifyIconData.SetTip(tooltip);
- Shell_NotifyIcon(NimModify, ref _notifyIconData);
- }
-
- ///
- /// Recreate tray icon (for failure recovery)
- ///
- public void RecreateTrayIcon()
- {
- Logger.LogInfo("Manually recreating tray icon...");
- CreateTrayIcon();
- }
-
- public void Dispose()
- {
- if (!_isDisposed)
- {
- Logger.LogDebug("Disposing TrayIconHelper...");
-
- // Remove tray icon
- try
- {
- Shell_NotifyIcon(NimDelete, ref _notifyIconData);
- Logger.LogInfo("Tray icon removed successfully");
- }
- catch (Exception ex)
- {
- Logger.LogError($"Error removing tray icon: {ex.Message}");
- }
-
- // Release icon resources
- try
- {
- _trayIcon?.Dispose();
- _trayIcon = null;
- Logger.LogInfo("Icon resources disposed successfully");
- }
- catch (Exception ex)
- {
- Logger.LogError($"Error disposing icon: {ex.Message}");
- }
-
- // Destroy message window
- try
- {
- if (_messageWindowHandle != IntPtr.Zero)
- {
- DestroyWindow(_messageWindowHandle);
- _messageWindowHandle = IntPtr.Zero;
- Logger.LogInfo("Message window destroyed successfully");
- }
- }
- catch (Exception ex)
- {
- Logger.LogError($"Error destroying message window: {ex.Message}");
- }
-
- _isDisposed = true;
- GC.SuppressFinalize(this);
- Logger.LogDebug("TrayIconHelper disposed completely");
- }
- }
- }
-}
diff --git a/src/modules/powerdisplay/PowerDisplay/PowerDisplay.csproj b/src/modules/powerdisplay/PowerDisplay/PowerDisplay.csproj
index 208852f3c3..2e9f8ff011 100644
--- a/src/modules/powerdisplay/PowerDisplay/PowerDisplay.csproj
+++ b/src/modules/powerdisplay/PowerDisplay/PowerDisplay.csproj
@@ -17,7 +17,6 @@
true
..\..\..\..\$(Platform)\$(Configuration)\WinUI3Apps
PowerToys.PowerDisplay
- Assets\PowerDisplay.ico
PowerToys.PowerDisplay.pri
true
diff --git a/src/modules/powerdisplay/PowerDisplay/PowerDisplayXAML/MainWindow.xaml.cs b/src/modules/powerdisplay/PowerDisplay/PowerDisplayXAML/MainWindow.xaml.cs
index 0eeac3f537..d8a2adf68d 100644
--- a/src/modules/powerdisplay/PowerDisplay/PowerDisplayXAML/MainWindow.xaml.cs
+++ b/src/modules/powerdisplay/PowerDisplay/PowerDisplayXAML/MainWindow.xaml.cs
@@ -36,7 +36,6 @@ namespace PowerDisplay
{
private readonly ISettingsUtils _settingsUtils = new SettingsUtils();
private MainViewModel _viewModel = null!;
- private TrayIconHelper _trayIcon = null!;
private AppWindow _appWindow = null!;
private bool _isExiting;
@@ -56,9 +55,6 @@ namespace PowerDisplay
// Setup window properties
SetupWindow();
- // Initialize tray icon
- InitializeTrayIcon();
-
// Initialize UI text
InitializeUIText();
@@ -230,29 +226,6 @@ namespace PowerDisplay
HideWindow();
}
- private void InitializeTrayIcon()
- {
- _trayIcon = new TrayIconHelper(this);
- _trayIcon.SetCallbacks(
- onShow: ShowWindow,
- onExit: ExitApplication,
- onRefresh: () => _viewModel?.RefreshCommand?.Execute(null),
- onSettings: OpenSettings);
- }
-
- private void OpenSettings()
- {
- try
- {
- // Open PowerToys Settings to PowerDisplay page
- PowerDisplay.Helpers.SettingsDeepLink.OpenPowerDisplaySettings();
- }
- catch (Exception ex)
- {
- Logger.LogError($"Failed to open settings: {ex.Message}");
- }
- }
-
public void ShowWindow()
{
Logger.LogInfo("[SHOWWINDOW] Method entry");
@@ -450,9 +423,6 @@ namespace PowerDisplay
{
_isExiting = true;
- // 立即释放托盘图标
- _trayIcon?.Dispose();
-
// 快速清理 ViewModel
if (_viewModel != null)
{
@@ -907,7 +877,6 @@ namespace PowerDisplay
public void Dispose()
{
_viewModel?.Dispose();
- _trayIcon?.Dispose();
GC.SuppressFinalize(this);
}
}