Skip to content

Setting NotifyIcon.TooltipText to non-ASCII text renders as ? characters in the tray tooltip #1705

Closed

Description

@soleon

Summary

Setting NotifyIcon.TooltipText to a string containing non-ASCII characters results in those characters being replaced with ? in the tray tooltip on systems whose system ANSI codepage cannot represent them. The same defect affects szInfo (balloon body) and szInfoTitle (balloon title).

Reproduction

Tested on en-US Windows 11 24H2 (build 26200), WPF-UI 4.2.1, .NET 10:

<tray:NotifyIcon TooltipText="80% 已连接(未充电)" Icon="..." />

Hovering the tray icon shows 80% ???(???). The percentage and ASCII parens render correctly; only the CJK characters are lost.

Root cause

src/Wpf.Ui.Tray/Interop/Shell32.cs::NOTIFYICONDATA is declared without an explicit CharSet:

[StructLayout(LayoutKind.Sequential)]
public class NOTIFYICONDATA
{
    ...
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x80)]
    public string? szTip;
    ...
}

C# defaults StructLayoutAttribute.CharSet to Ansi. UnmanagedType.ByValTStr follows the enclosing struct's charset, so szTip/szInfo/szInfoTitle are marshaled via Encoding.Default.GetBytes(...), which substitutes ? for code points unrepresentable in the active ANSI codepage. The matching import:

[DllImport(Libraries.Shell32)]
public static extern bool Shell_NotifyIcon([In] NIM dwMessage, [In] NOTIFYICONDATA lpdata);

also has no CharSet, so it resolves to Shell_NotifyIconA and Windows stores the already-corrupted text.

Suggested fix

Two-line change:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public class NOTIFYICONDATA { ... }

[DllImport(Libraries.Shell32, CharSet = CharSet.Unicode)]
public static extern bool Shell_NotifyIcon([In] NIM dwMessage, [In] NOTIFYICONDATA lpdata);

ByValTStr's SizeConst slots are interpreted per the new charset (i.e. 128 wide chars instead of 128 ANSI bytes for szTip), so the struct's binary size grows accordingly. cbSize = Marshal.SizeOf(typeof(NOTIFYICONDATA)) recomputes correctly and routes the call through Shell_NotifyIconW. No call-site changes required.

Workaround for consumers

Subclass NotifyIcon, override OnPropertyChanged, and after base re-send the tooltip via your own Unicode-charset NOTIFYICONDATA mirror + Shell_NotifyIconW(NIM.MODIFY, NIF.TIP). Reflection on the existing internal ShellIconData recovers hWnd and uID. Re-apply on both TooltipTextProperty and IconProperty changes — TrayManager.ModifyIcon re-sends the same ANSI szTip from the cached struct, which would otherwise overwrite the Unicode fix on every icon update.

Environment

  • WPF-UI: 4.2.1
  • Target framework: net10.0-windows10.0.26100.0 (also reproduces on net8.0-windows)
  • OS: Windows 11 24H2 (build 26200)
  • System ANSI codepage: 1252 (en-US)
  • Tooltip text language: zh-Hans

Activity

  1. chucker commented on May 2, 2026

    @chucker
    Collaborator

    I seem to have a fix.

    Image

    However, it looks like the tray library should be arch-specific, and I need to figure out the tooling for that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions