HavenDV/H.NotifyIcon

Bug: Accessing some of TaskbarIcon's properties results in an COMException

myfix16 opened this issue · 1 comments

Describe the bug

Accessing some of TaskbarIcon's properties, such as Icon, GeneratedIcon, Margin, etc., results in a System.Runtime.InteropServices.COMException.

Exception message:

An application calls an interface that has been marshaled for another thread

Steps to reproduce the bug

  1. Create a sample WindowsAppSDK 1.2 packaged C# app
  2. Add a windowless TaskbarIcon using the same method as this sample
  3. Access TaskbarIcon's properties, for example,
TaskbarIcon icon = <Initialization>;
icon.GeneratedIcon.Text = "Test";
  1. Observe the exception in a debug session.

Expected behavior

No exception should be thrown.

Screenshots

Exception details:
image

NuGet package version

H.NotifyIcon.WinUI 2.0.75
Microsoft.WindowsAppSDK 1.2.221209.1

Platform

WinUI

IDE

Visual Studio 2022

Windows Version

Windows 11

WindowsAppSDK Version

Other

WindowsAppSDK Type

Packaged

Manifest

No response

Additional context

No response

The problem is resolved with the solution posted by @JasonWei512 in #54 (comment), quoted below:

Use a DispatcherQueue to execute UI related code on the main UI thread. Otherwise you may get an exception.

I recommend using the CommunityToolkit.WinUI nuget package's DispatcherQueueExtensions.EnqueueAsync.

using CommunityToolkit.WinUI;

class MyClass
{
    // Get a DispatcherQueue instance for later use. This has to be called on the UI thread,
    // but it can then be cached for later use and accessed from a background thread as well.
    private readonly DispatcherQueue dispatcherQueue = DispatcherQueue.GetForCurrentThread();

    // ...

    // Your tray icon event handler. This may not be called from the main UI thread
    private async void ExitApplicationCommand_ExecuteRequested(object? sender, EventArgs args)
    {
        // Execute some code on the target dispatcher queue
        await dispatcherQueue.EnqueueAsync(() =>
        {
            if (App.MainWindow.Visible)
            {
                // ...
            }
        });
    }
}