microsoft / WindowsAppSDK

The Windows App SDK empowers all Windows desktop apps with modern Windows UI, APIs, and platform features, including back-compat support, shipped via NuGet.
https://docs.microsoft.com/windows/apps/windows-app-sdk/
MIT License
3.81k stars 320 forks source link

Cannot set the taskbar icon #2730

Open Boontje opened 2 years ago

Boontje commented 2 years ago

Describe the bug

Setting the icon for the app/window using the net 'SetIcon' function on the AppWindow, sets the icon for the title bar but not for the Windows Taskbar.

Steps to reproduce the bug

Create a new blank WinUI project.

Replace the code for 'mybutton_click' with:

private void myButton_Click(object sender, RoutedEventArgs e) { // set icon in taskbar string iconPath = @"C:\Program Files\Internet Explorer\images\bing.ico";

        IntPtr hWnd = WindowNative.GetWindowHandle(this);
        WindowId myWndId = Microsoft.UI.Win32Interop.GetWindowIdFromWindow(hWnd);
        var appWindow = AppWindow.GetFromWindowId(myWndId);
        appWindow.SetIcon(iconPath);

        myButton.Content = "Clicked";
    }

Run the project. Click the button, the icon in the title bar will be updated to Bing icon. But the icon in the Taskbar will stay the same.

Expected behavior

Icon in the taskbar to be updated as well

Screenshots

No response

NuGet package version

1.1.2

Packaging type

Packaged (MSIX)

Windows version

Windows 11 version 21H2 (22000)

IDE

Visual Studio 2022

Additional context

No response

riverar commented 2 years ago

Probably belongs in the https://github.com/microsoft/microsoft-ui-xaml repository.

Repro project for others: issue_2730.zip

@rkarman suspect this is in your area. MUX bug or WM_SETICON regression?

Link1J commented 2 years ago

This only happens if the app is packaged. If the app is unpackaged, it works just fine.

wjk commented 2 years ago

This only happens if the app is packaged.

In that case, this behavior is by design. If you are running packaged, you need to edit the assets referenced in your AppX manifest. Other ways of setting the taskbar icon will not be honored. Unfortunately, getting this right is very difficult, due to how many different PNGs are needed, and the fact that VS will not generate all of them. IIRC the manifest editor would generate “regular” unplated assets, but not light-mode unplated assets. Without separate light-mode unplated assets, the user will see a colored box instead of your precisely drawn icon if they are running the Start menu in light mode, which I believe has been the default for many Windows builds now. You will need to manually duplicate the altform_unplated assets and change the names of the copies to altform_lightunplated. Hope this helps!

Boontje commented 2 years ago

The reason why I want to dynamically set the taskbar icon, is that the application needs to show a status or count as a small 'icon in icon'. If the taskbar icon is out of control of the app, is there another way to display an overlay icon?

Link1J commented 2 years ago

@Boontje Maybe ITaskbarList3::SetOverlayIcon is want you wanted? And it does work on Packaged Win32 Apps. https://docs.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-itaskbarlist3-setoverlayicon

EDIT: I would note that what the author of the issue was trying to do (set the taskbar icon) isn't even documented as something that WM_SETICON or AppWindow.SetIcon say they do. How the taskbar icon is set is undocumented and could change. In fact, since Windows 7, windows icon and taskbar icon aren't always the same thing. I was surprised to find that explorer.exe doesn't have a special trick to avoid changing icon on the taskbar, but that pinning the app to the taskbar is what did it.

riverar commented 2 years ago

This only happens if the app is packaged.

In that case, this behavior is by design. If you are running packaged, you need to edit the assets referenced in your AppX manifest. Other ways of setting the taskbar icon will not be honored. [...]

@wjk Not sure I agree with that conclusion. If that's the case, this API should fail appropriately.

Link1J commented 2 years ago

@riverar But it is working as intended, as it is setting the window icon. They were relying on undocumented behavior, because the API doesn't say it sets the taskbar icon. In fact there is a scenario where the API fails to set the taskbar icon with unpackaged apps, pinning the app to the taskbar. And that scenario has been around since Windows 7.

What is needed is a SetOverlayIcon function on AppWindow, but that probably needs a its own issue.

riverar commented 2 years ago

@Link1J No it's not.

The system displays a window's large class icon in the task-switch window that appears when the user presses ALT+TAB, and in the large icon views of the task bar and explorer. The small class icon appears in a window's title bar and in the small icon views of the task bar and explorer. [...] You can override the large or small class icon for a particular window by using the WM_SETICON message. You can retrieve the current large or small class icon by using the WM_GETICON message.

https://docs.microsoft.com/windows/win32/winmsg/about-window-classes#class-icons

riverar commented 2 years ago

Also, looking under the covers (Microsoft.UI.Windowing.Core.dll), we can see AppWindow::SetIcon is implemented roughly as such. It seems the intent is to indeed set the taskbar icon.

iconHandleFromPath = Windowing::Icon::GetIconHandleFromPath(path);
Windowing::Icon::SetTaskBarIcon(hwnd, iconHandleFromPath);
Windowing::Icon::SetTitleBarIcon(hwnd, iconHandleFromPath);
Link1J commented 2 years ago

Well then, both AppWindow.SetIcon and WM_SETICON do a really bad job at setting the taskbar icon. Because here is an unpackaged app, where the taskbar icon and the window icon do not match. All I did was pin the app to the taskbar. image

So maybe documentation needs to be updated to match current behavior then? Because, the pinned app behavior has been in Windows since 7.