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

例えば、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;
}
}
}
}
画面デザイン。

textBox1、button1、button2を用意。
あと、プロジェクト>プロパティ>アセンブリ情報 内の「タイトル」に「送るサンプル」と指定。
実行時画面。

一応、登録済みならボタンをマスク処理とかもしている。
「登録」をクリックすると、[送る]フォルダで示したパス配下に、「送るサンプル」のショートカットを登録する。
あと、「送る」から起動された場合、送られてきた情報をメッセージボックスで表示する。

ちなみに、この方法4を「画像振り分けツール」で採用してたりする。
追記
上記の方法4で完璧。
・・と思い込んでいたら、*.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);





