C#(csc.exe) - トレイアイコンをクリックするとToast通知を出すサンプル

# サンプルアプリの仕様概要

Windows10で動作します。ただしVisual Studio等でWindows SDK入れてないとwindows.winmdファイルがなくてコンパイルできないっぽい

- アイコンを左クリックで通知が出ます。（連打すると通知が延々と何度も出るので注意）
- アイコンを右クリック→Exitで終了できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/438183/40dfe2cd-9b7a-6a15-b5bb-ae0d43de9651.png)



# ソースコード

`ToastNotificationManager.CreateToastNotifier("Microsoft.Windows.Computer");`のところはかなり強引です。[^1]

```cs

using System;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;

using Windows.UI.Notifications;

public static class IconUtil
{
    static class NativeMethods
    {
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public extern static bool DestroyIcon(IntPtr handle);
    } 

    static readonly string[] iconDot = new string[]{
        "................",
        ".###.###..##.###",
        "..#..#...#....#.",
        "..#..#...#....#.",
        "..#..#...#....#.",
        "..#..#...#....#.",
        "..#..#...#....#.",
        "..#..###..#...#.",
        "..#..#.....#..#.",
        "..#..#.....#..#.",
        "..#..#.....#..#.",
        "..#..#.....#..#.",
        "..#..#.....#..#.",
        "..#..#.....#..#.",
        "..#..###.##...#.",
        "................",
    };

    public static Icon MakeDefaultIcon()
    {
        using ( Bitmap bmp = new Bitmap(16,16) ) {
            using ( Graphics g = Graphics.FromImage(bmp) ) {
                g.Clear(Color.Blue);
            }
            for(int y=0;y<16;y++){
                for(int x=0;x<16;x++){
                    if (iconDot[y][x]=='#') {
                        bmp.SetPixel(x,y,Color.Yellow);
                    }
                }
            }

            IntPtr Hicon = bmp.GetHicon();
            return Icon.FromHandle(Hicon);
        }
    }

    public static void DestroyIcon(Icon ico)
    {
        NativeMethods.DestroyIcon(ico.Handle);
    }
}

class TaskTrayLauncher
{
    NotifyIcon trayIcon;

    static void ShowSampleToast()
    {
        string xmlStr = File.ReadAllText("sample.xml", Encoding.GetEncoding("Shift_JIS"));
        var content = new Windows.Data.Xml.Dom.XmlDocument();
        content.LoadXml(xmlStr);
        var notifier = ToastNotificationManager.CreateToastNotifier("Microsoft.Windows.Computer");
        notifier.Show(new ToastNotification(content));
    }

    TaskTrayLauncher()
    {
        trayIcon = new NotifyIcon();
        Icon tmpIcon = IconUtil.MakeDefaultIcon();
        trayIcon.Icon = tmpIcon;
        trayIcon.Visible = true;

        trayIcon.Text = "Launcher";
        var menu = new ContextMenuStrip();
        var menuItem = new ToolStripMenuItem();

        menu.Items.AddRange(new ToolStripMenuItem[]{
            new ToolStripMenuItem("E&xit", null, (s,e)=>{Application.Exit();}, "Exit")
        });

        trayIcon.MouseClick += TrayIcon_MouseClick;
        trayIcon.ContextMenuStrip = menu;
    }

    void TrayIcon_MouseClick(object sender, MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Left) {
            ShowSampleToast();
        }
        // 右クリックはcontextmenuを表示させるので、ここでは何もしない
    }

    [STAThread]
    static void Main(string[] args)
    {
        new TaskTrayLauncher();
        Application.Run();
    }
}
```

# toastのxml

同じフォルダにおいてください。
Shift_JIS[^2]で保存してください。

```xml:sample.xml

<toast activationType='foreground' launch='args'>
    <visual>
        <binding template='ToastGeneric'>
            <text>test</text>
            <text>testtest</text>
        </binding>
    </visual>
    <audio src='ms-winsoundevent:Notification.SMS' />
</toast>
```

# コンパイルバッチ

使い方：
`compile.bat ファイル名.cs`

```text:compile.bat

csc /r:C:\Windows\Microsoft.NET\assembly\GAC_MSIL\System.Runtime.WindowsRuntime\v4.0_4.0.0.0__b77a5c561934e089\system.runtime.windowsruntime.dll ^
/r:C:\Windows\Microsoft.NET\assembly\GAC_MSIL\System.Runtime.InteropServices.WindowsRuntime\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Runtime.InteropServices.WindowsRuntime.dll ^
/r:C:\Windows\Microsoft.NET\assembly\GAC_MSIL\System.Runtime\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Runtime.dll ^
"/r:C:\Program Files (x86)\Windows Kits\8.1\References\CommonConfiguration\Neutral\Annotated\Windows.winmd" %*
```

# 参考記事(参考サイト)

1. [Win向け通知アプリにトーストを使う - Qiita](https://qiita.com/120byte/items/5f3993350478a395779e)
2. [Windows 10 でトースト通知を飛ばす - みかづきメモ](https://mikazuki.hatenablog.jp/entry/2015/11/16/015833)

# ボタンの追加とイベント処理

参考記事2にあるように、xmlを修正すればToast通知上にボタンを追加することは簡単にできるのですが、
参考記事1で言及されている通り、イベントを受け取るのは困難で、特に、
https://docs.microsoft.com/ja-jp/windows/uwp/design/shell/tiles-and-notifications/send-local-toast-desktop#step-4-implement-the-activator
にあるGUIDの登録が必要になるっぽいです。

[^1]: 詳しくは[参考記事1のコードの説明](https://qiita.com/120byte/items/5f3993350478a395779e#%E3%82%B3%E3%83%BC%E3%83%89)を参照ください。

[^2]: UTF-8を使うべきだと思いますが、PC環境の私的な事情でShift_JISにしています・・