こんばんは。今日はジャンプリストの小技について。
Windows Formのアプリケーションを開発していると、
任意のファイルを読み込む、というシーンがよくあります。
読み込んだファイルの履歴から、1アクションで読み込みまで行えれば便利ですよね。
この記事では、そんな要件がある場合に、
「ジャンプリストから選択したファイルを読み込む」
という処理を実装したいと思います。
ジャンプリスト?と思った方に説明すると、
Windows7から導入された、タスクバーから
「最近つかったもの」などを表示/実行できるものです。
こんなイメージ↓

ジャンプリストは標準の機能ではないため、WindowsAPICodePackを使用します。
NuGet パッケージマネージャーから下記の2つをインストールします。
ジャンプリストの実行は、シェルをたたくイメージで設計されていて、
パスやファイル名を渡すと選択した際に実行してくれます。
過去に開いたファイルをメモ帳で開いたり、フォルダをエクスプローラーで開いたりがすんなりできます。
上記がオーソドックスな使用法でしょうか。
今回は少し応用して選択した項目にちなんだ処理を実行したい場合。
処理のイメージ↓
実装はこんな感じ。
Program.cs
 
using System;
using System.Windows.Forms;
namespace WindowsAPICodePackSample
{
    static class Program
    {
        /// <summary>
        /// アプリケーションのメイン エントリ ポイントです。
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            if (!WindowMessageHelper.IsExistWindow(nameof(Form1)))
            {
                // Form1が起動していない場合は起動する
                Application.Run(new Form1());
            }
            // Form1にSendMessage実施
            SendMessageToForm1();
        }
        /// <summary>
        /// 指定の引数がある場合にForm1にSendMessage実施
        /// </summary>
        public static void SendMessageToForm1()
        {
            if (Environment.GetCommandLineArgs().Length > 1)
            {
                WindowMessageHelper.SendMessage(nameof(Form1), Environment.GetCommandLineArgs()[1]);
            }
        }
    }
}
Form1.cs(読み取りボタンのみ配置)
 
using Microsoft.WindowsAPICodePack.Taskbar;
using System;
using System.IO;
using System.Reflection;
using System.Windows.Forms;
namespace WindowsAPICodePackSample
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        /// <summary>
        /// 読み込みボタンクリック
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnRead_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            DialogResult result = ofd.ShowDialog();
            if (result == DialogResult.OK)
            {
                // 読取処理実施
                ReadFile(ofd.FileName);
                // ジャンプリスト更新
                RefreshJumpList(ofd.FileName);
            }
        }
        /// <summary>
        /// 読取処理
        /// </summary>
        /// <param name="filePath"></param>
        private void ReadFile(string filePath)
        {
            // TODO:ここで読取処理を行う
            MessageBox.Show($"読み取りファイル={filePath}");
        }
        /// <summary>
        /// ジャンプリストを更新
        /// </summary>
        /// <param name="filePath"></param>
        private void RefreshJumpList(string filePath)
        {
            JumpList jumpList = JumpList.CreateJumpList();
            JumpListCustomCategory category = new JumpListCustomCategory("最近使ったもの");
            // 自身の実行パスを指定
            string self = Assembly.GetEntryAssembly().Location;
            JumpListLink link = new JumpListLink(self, Path.GetFileName(filePath));
            link.Arguments = filePath;
            category.AddJumpListItems(link);
            jumpList.AddCustomCategories(category);
            // 更新
            jumpList.Refresh();
        }
        protected override void WndProc(ref Message m)
        {
            if (m.Msg == WindowMessageHelper.WM_COPYDATA)
            {   // 文字列の場合
                WindowMessageHelper.COPYDATASTRUCT cds = new WindowMessageHelper.COPYDATASTRUCT();
                Type t = cds.GetType();
                cds = (WindowMessageHelper.COPYDATASTRUCT)m.GetLParam(t);
                string fileName = cds.lpData;
                // 読取処理実施
                ReadFile(fileName);
            }
            else
            {
                base.WndProc(ref m);
            }
        }
    }
}
WindowMessageHelper.cs
using System;
using System.Runtime.InteropServices;
namespace WindowsAPICodePackSample
{
    public class WindowMessageHelper
    {
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        private static extern int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, ref COPYDATASTRUCT cds);
        [DllImport("user32.dll")]
        private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
        public static bool SendMessage(string windowTitle, string msg)
        {
            IntPtr windowToFind = FindWindow(null, windowTitle);
            if (windowToFind == IntPtr.Zero) return false;
            COPYDATASTRUCT cds;
            cds.dwData = 0;
            cds.lpData = msg;
            cds.cbData = cds.lpData.Length + 1;
            long result = SendMessage(windowToFind, WM_COPYDATA, IntPtr.Zero, ref cds);
            return (result == 0);
        }
        public static bool IsExistWindow(string windowTitle)
        {
            IntPtr windowToFind = FindWindow(null, windowTitle);
            return (windowToFind != IntPtr.Zero);
        }
        public const int WM_COPYDATA = 0x4a;
        public struct COPYDATASTRUCT
        {
            public int dwData;
            public int cbData;
            public string lpData;
        }
    }
}
上記を実装したら、読み込みボタンから任意のファイルを読み込んでみましょう。

読み込んだ後、タスクバーのアイコンを右クリックすると、
読み込んだファイル名が表示が表示されると思います。

次にジャンプリストからクリック

上記が表示されればジャンプリストからの読み取り処理成功です。
なお、ジャンプリストはexeを終了しても保持され、明示的にリフレッシュを行うまで変更されません。
いかがでしたか?
エンドユーザからジャンプリストまで指定されることはないかもしれませんが、
知っておくと便利かと思います。
ではでは。