[C#] 送るメニューにショートカットを登録/解除する方法

エクスプローラーなどでファイルを右クリックしたときに出てくる「送る」メニュー。
この送るメニューにアプリケーションを登録しておくと、選択したファイルの情報を
アプリケーションに渡して起動できるため、使い方によっては便利な機能。
01
例えば、logファイルを”メモ帳”に送って起動するといったことができる。


で、この「送る」メニューに登録するためには、特定のフォルダにショートカットをコピーする必要がある。
例えば、Windows8の場合、以下のフォルダにショートカットを置く。
C:\Users\<ユーザー名>\AppData\Roaming\Microsoft\Windows\SendTo
でもこれ、OS毎にパスが異なったりするので、人に説明するのは厄介だったりする。
知っている人なら、何てことない「送る」メニューへの登録でも、初心者にとっては分かりにくいし煩わしい。
なので、これをプログラム側から簡単に登録できないか調べてみた。
いろいろやり方はあると思うけど、4つほどご紹介します。

方法1:インストーラから登録する方法

セットアップする際に、「送る」メニューへ登録させる方法。
これが一番簡単だけどVisual Studioの場合、Professional Editionなどでなければセットアップを作れないし、インストーラ付きアプリは敬遠されがちなので今回は割愛。

方法2:WSHを使用しショートカット作成する方法

Visual Studio からプロジェクト>参照を追加>COM から、「Windows Script Host Object Model」
を追加し、ショートカットを作成する方法。
まず、Visual Studioのソリューションエクスプローラからプロジェクトを選択し「参照の追加」。
参照の追加
参照マネージャダイアログでCOMの中にある「Windows Script Host Object Model」をチェックし追加する。
参照マネージャ
これでショートカット作成に必要な、IWshRuntimeLibrary.IWshShortcutなどが使用できるようになる。
「送る」フォルダのパス自体は、
System.Environment.GetFolderPath(Environment.SpecialFolder.SendTo)
で取得できるので、あとは、これのショートカットを登録する。
ショートカット作成方法詳細は、こちらが参考になるかも。
http://dobon.net/vb/dotnet/file/createshortcut.html
が、しかし、何故か.NET Framework 4以降を利用するとビルドできなかったため、この方法での実装は止めておくことにした。

方法3:WSHのスクリプトを実行して登録する方法

アプリ(exe)とは関係なく、WSHのスクリプト実行のみで「送る」メニューに登録する方法。
以下の例では、同一フォルダ内の”sendTo.exe”を「送る」メニューに登録(ショートカット作成)します。

  • Sample.js
ws = WScript.CreateObject('WScript.Shell');
ln = ws.SpecialFolders('SendTo') + '\\' + 'テスト.lnk';
sc = ws.CreateShortcut(ln);
sc.TargetPath = ws.CurrentDirectory + '\\sendTo.exe';
sc.Save();

これはシンプルだけど、exeと、jsファイル2つを配布しなければならない。
ちょっとカッコ悪いし、スクリプトを別に実行してくれとも言いにくいので、この方法はパスすることにした。

方法4:exeだけで実現する方法

方法3を応用した形で、C#のアプリ内でWSHのスクリプトファイルを吐き出し、それを実行するという方法。
実行後は後始末も行う。
サンプルは以下の通り。
ちょっと雑だけど、これなら利用者は簡単に「送る」メニューに登録することができそう。

  • Form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
// 追加
using System.IO;
using System.Reflection;
using System.Diagnostics;
namespace sendTo
{
	public partial class Form1 : Form
	{
		private string aplTitle = null; // アプリ名
		private string exeFile = null;  // exeファイル名
		private string jsFile = null;   // スクリプトファイル名
		private string lnkFile = null;  // リンク名
		public Form1()
		{
			InitializeComponent();
		}

		private void Form1_Load(object sender, EventArgs e)
		{
			// プロジェクト>プロパティ>アセンブリ情報 で指定した「タイトル」を取得
			var assembly = Assembly.GetExecutingAssembly();
			var attribute = Attribute.GetCustomAttribute(
				assembly,
				typeof(AssemblyTitleAttribute)
			) as AssemblyTitleAttribute;
			aplTitle = attribute.Title;
			this.Text = aplTitle;
			// 自身のexeファイル名を取得
			exeFile = Path.GetFileName(Application.ExecutablePath);
			// WSHスクリプト名
			jsFile = Directory.GetCurrentDirectory() + "\\sendtoAdd.js";
			// ショートカットのリンク名
			lnkFile = Environment.GetFolderPath(Environment.SpecialFolder.SendTo) + "\\" + aplTitle + ".lnk";
			// [送る]フォルダのパスをtextBox1に表示
			textBox1.Text = Environment.GetFolderPath(Environment.SpecialFolder.SendTo);
			textBox1.ReadOnly = true;
			// ボタンマスク処理
			maskButton();
			// 起動時にコマンドライン引数指定があればメッセージボックス表示
			string[] args = Environment.GetCommandLineArgs();
			string msg = null;
			for (int i = 1; i < args.Length; i++)
			{
				msg += args[i] + "\n";
			}
			if (msg != null)
			{
				MessageBox.Show(
				"起動時にコマンドライン引数を受け取りました。\n\n" + msg,
				aplTitle,
				MessageBoxButtons.OK,
				MessageBoxIcon.Information);
			}
		}

		// [登録]ボタン
		private void button1_Click(object sender, EventArgs e)
		{
			// [送る]フォルダにショートカット作成
			try
			{
				// WSHファイル作成
				using (StreamWriter w = new StreamWriter(
				jsFile, false, System.Text.Encoding.GetEncoding("shift_jis")))
				{
					w.WriteLine("ws = WScript.CreateObject('WScript.Shell');");
					w.WriteLine("ln = ws.SpecialFolders('SendTo') + '\\\\' + '" + aplTitle + ".lnk';");
					w.WriteLine("sc = ws.CreateShortcut(ln);");
					w.WriteLine("sc.TargetPath = ws.CurrentDirectory + '\\\\" + exeFile + "';");
					w.WriteLine("sc.Save();");
				}
				// sendtoAdd.jsを実行し[送る]メニューにショートカット作成
				if (File.Exists(jsFile))
				{
					Process p = Process.Start(jsFile);
					p.WaitForExit(10000); // 終了まで待つ(最大10秒)
					File.Delete(jsFile);
				}
				// [送る]フォルダに登録されたか確認
				if (File.Exists(lnkFile))
				{
					MessageBox.Show(
					"[送る]メニューに登録しました。\n\n" + lnkFile,
					aplTitle,
					MessageBoxButtons.OK,
					MessageBoxIcon.Information);
				}
				else
				{
					MessageBox.Show(
					"[送る]メニューの登録に失敗しました。\n\n" + lnkFile,
					aplTitle,
					MessageBoxButtons.OK,
					MessageBoxIcon.Warning);
				}
			}
			catch (Exception ex)
			{
				MessageBox.Show(
				"[送る]メニューの登録に失敗しました。\n\n" + ex.Message,
				aplTitle,
				MessageBoxButtons.OK,
				MessageBoxIcon.Warning);
			}
			maskButton();
		}

		// [解除]ボタン
		private void button2_Click(object sender, EventArgs e)
		{
			// [送る]フォルダに登録済みか確認
			if (File.Exists(lnkFile))
			{
				try
				{
					File.Delete(lnkFile);
					MessageBox.Show(
					"[送る]メニューからショートカットを削除しました。",
					aplTitle,
					MessageBoxButtons.OK,
					MessageBoxIcon.Information);
				}
				catch (IOException ex)
				{
					MessageBox.Show(
					"ショートカットの削除に失敗しました。\n\n" + ex.Message,
					aplTitle,
					MessageBoxButtons.OK,
					MessageBoxIcon.Warning);
				}
			}
			maskButton();
		}

		// ボタンマスク
		private void maskButton()
		{
			// [送る]フォルダに登録済みか確認
			if (File.Exists(lnkFile))
			{
				button1.Enabled = false;
				button2.Enabled = true;
			}
			else
			{
				button1.Enabled = true;
				button2.Enabled = false;
			}
		}
	}
}

画面デザイン。
GUIデザイン
textBox1、button1、button2を用意。
あと、プロジェクト>プロパティ>アセンブリ情報 内の「タイトル」に「送るサンプル」と指定。
実行時画面。
実行画面
一応、登録済みならボタンをマスク処理とかもしている。
「登録」をクリックすると、[送る]フォルダで示したパス配下に、「送るサンプル」のショートカットを登録する。
あと、「送る」から起動された場合、送られてきた情報をメッセージボックスで表示する。
メッセージボックス
ちなみに、この方法4を「画像振り分けツール」で採用してたりする。

 
 

追記

上記の方法4で完璧。
・・と思い込んでいたら、*.jsファイルの関連付けが異なる環境では以下のようなメッセージが出て実行できない場合があった。
この種類のファイル(.js)を開くには、どのアプリを使いますか?

この種類のファイル(.js)を開くには、どのアプリを使いますか?

 
 
ファイル拡張子".js"を持つスクリプトエンジンはありません。

ファイル拡張子”.js”を持つスクリプトエンジンはありません。

 
 

関連付けが出来ていない環境でも動くよう、”cscript”を直接呼び出し実行する。
具体的には、上記ソースの84行目の「Process p = Process.Start(jsFile);」を以下のように書き換えることで解決できた。

//Process p = Process.Start(jsFile);
ProcessStartInfo psi = (new ProcessStartInfo());
psi.FileName = "cscript";
psi.Arguments = @"//e:jscript " + jsFile;
psi.WindowStyle = ProcessWindowStyle.Hidden;
Process p = Process.Start(psi);

 
 


Leave a comment

メールアドレスが公開されることはありません。

CAPTCHA


*