picoe / Eto

Cross platform GUI framework for desktop and mobile applications in .NET
Other
3.57k stars 325 forks source link

Changing the tray icon lots of times crashes an app #1342

Open thomas-goerlich opened 5 years ago

thomas-goerlich commented 5 years ago

Expected Behavior

I can change the tray icon as often as i want.

Actual Behavior

After a certain amount of changes the app crashes. Granted in a normal use case i wouldn't expect anyone to reach this point 😄

Steps to Reproduce the Problem

Create an app that changes the tray icon every second or so and wait.

Code that Demonstrates the Problem

tray.Image = Icon.FromResource("breaktimer.Assets.tray.tray-" + icon + ".png");

Specifications

Stacktrace

A Generic Error occurred in GDI+ at System.Drawing.Bitmap.GetHicon() at Eto.Wpf.WpfConversions.ToSDIcon(Image image) at Eto.Wpf.Forms.TrayIndicatorHandler.set_Image(Image value) at Eto.Forms.TrayIndicator.set_Image(Image value) at breaktimer.MainForm.UpdateTrayIcon(Int32 currentTime, Int32 totalTime) at breaktimer.MainForm.TimerOnTimerUpdate(Object sender, TimerEventArgs e) at breaktimer.Timer.IntervalTimerElapsed(Object sender, EventArgs e) at Eto.Forms.UITimer.OnElapsed(EventArgs e) at Eto.Forms.UITimer.Callback.OnElapsed(UITimer widget, EventArgs e) at Eto.Wpf.Forms.UITimerHandler.<.ctor>b__0_0(Object sender, EventArgs e) at System.Windows.Threading.DispatcherTimer.FireTick(Object unused) at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs) at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler) at System.Windows.Threading.DispatcherOperation.InvokeImpl() at System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(Object state) at MS.Internal.CulturePreservingExecutionContext.CallbackWrapper(Object obj) at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at MS.Internal.CulturePreservingExecutionContext.Run(CulturePreservingExecutionContext executionContext, ContextCallback callback, Object state) at System.Windows.Threading.DispatcherOperation.Invoke() at System.Windows.Threading.Dispatcher.ProcessQueue() at System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled) at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled) at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o) at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs) at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler) at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs) at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam) at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg) at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame) at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame) at System.Windows.Application.RunDispatcher(Object ignore) at System.Windows.Application.RunInternal(Window window) at System.Windows.Application.Run(Window window) at Eto.Wpf.Forms.ApplicationHandler.Run() at Eto.Forms.Application.Run(Form mainForm) at breaktimer.Wpf.MainClass.Main(String[] args) in [...]breaktimer\breaktimer\breaktimer.Wpf\Program.cs:Line 16.

I found a Stackoverflow entry which seems to match this problem: https://stackoverflow.com/questions/12026664/a-generic-error-occurred-in-gdi-when-calling-bitmap-gethicon I can confirm that the GDI objects increase by every icon change until reaching 10.000.

How i came to produce this error

I am creating a pomodoro timer app. The app lives in the tray icon area. The icon is basically a pie chart which gets filled out as time goes on. I was changing the icon every second. Resulting in the app crashing in less than an hour. Every icon change seems to increase the GDI objects by 3 to 5. I changed my code to only change the icon if the icon actually needs to change resulting in way less changes.

cwensley commented 5 years ago

Hey @thomas-goerlich,

Thanks for submitting the issue! I had no clue that Icon.FromHandle leaks in that way, so thanks for pointing that out. Looks Icon.FromHandle is used in a few places, but only this single part of WPF.

josegomez commented 4 years ago

@thomas-goerlich I am trying to use the tray feature but cannot for the life of me find a working example anywhere. Any chance you got something handy?

thomas-goerlich commented 4 years ago

Hey @josegomez, did you check out the official example at https://github.com/picoe/Eto/blob/develop/test/Eto.Test/Sections/Behaviors/TrayIndicatorSection.cs?

My code is still on eto.forms v2.4.1. No idea if maybe something changed in a later version.

partial class MainForm : Form
{
    TrayIndicator tray = new TrayIndicator();
    private void InitializeComponent()
    {
        [...]
        ShowInTaskbar = false;
        [...]
        var trayMenu = new ContextMenu();
        trayMenu.Items.Add(showMainWindowCommand);
        trayMenu.Items.Add(showSettingsCommand);
        trayMenu.Items.Add(quitCommand);
        [...]
        tray.Menu = trayMenu;
        tray.Title = "breaktimer";
        tray.Image = Icon.FromResource("breaktimer.Assets.tray.tray-paused.png");
        tray.Activated += ShowMainWindow;
        tray.Show();
        [...]
    }
}

Hope it helps :)

josegomez commented 4 years ago

Thank you!