3流プログラマのメモ書き

元開発職→社内SE→派遣で営業支援→開発戻り浦島太郎状態の三流プログラマのIT技術メモ書き。 このメモが忘れっぽい自分とググってきた技術者の役に立ってくれれば幸いです。

(.Net)特定のターミナルサーバ上のユーザにメッセージを送信する

.Netアプリケーションから任意のWindowsユーザにメッセージを送信する方法です。

他のコンピュータやターミナルサービス利用中のユーザにメッセージを送信するには msg コマンドが使えます。

(学生時代 net send というコマンドで他のWindowsPCにメッセージを送れることを知ってよく遊んでたんですが、今は net send コマンドの代わり msg コマンドを使うようです。Vista以降は net send コマンド自体が使えないようですし。。)

ちなみに、Windows7Vistaではターミナルサービスセッションに対するリモートからのRPC接続が無効に設定されているようなので、普通にはメッセージを送れないようです。

クライアントPCのレジストリで HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server キーの AllowRemoteRPC の値を 1 にするとRPC接続が有効にされれるようです。このあたりの詳しい話は【Management】Msgコマンドを使用すると「セッション名の取得エラー5」 - フィールドSEあがりの安納ですが参考になります。

msg コマンドは、ユーザ or セッションID or セッション名 でどのセッションに送信するかや、どのコンピュータに送信するかなどが指定できます。(どのコンピュータに送るかについて、ホスト名でなくてもIPアドレスでもOKでした。)

ただし、そのセッションに対するアクセス権を持っていないと「エラー [5]:アクセスが拒否されました。」となってしまいます。

まぁドメイン管理者権限なら大抵大丈夫なはずです。

msg コマンドでデフォルトの設定で送信すると、どうやら60秒間しかメッセージが表示されませんでした。ということで、1分では足りない場合は /time:秒数 で指定した方がよさそうです。

また、msgコマンドでアクセス権等不足してたり送った先のユーザが既にログアウトしてたりとエラーがあった場合ですが、どうやらエラーがあってもプロセスの戻り値は0のようです。

なので、エラーがあったかどうかは標準エラー出力からとらないとわかりません。

で、.Netでコンソールアプリケーションの標準出力や標準エラー出力を取得する方法ですが、Process.StandardError プロパティで標準エラー出力Process.StandardOutput プロパティで標準出力の値が取れるようです。

ただし、Process.StandardError プロパティにアクセスするには ProcessStartInfo.RedirectStandardError を true に、Process.StandardOutput プロパティにアクセスするには、ProcessStartInfo.RedirectStandardOutput を true にしないといけないようです。

(上記の方法は同期式の方法と呼ばれており、処理中プロセスの標準出力を取ろうとしたりするとデッドロックになってしまう可能性があるようです。その場合はイベントハンドラを設定する非同期式を使うことが勧められています。)

ということで下記が.Netからmsgコマンドをキックしてメッセージを送信するコードです。コマンドプロンプトウィンドウは表示しないようにしてます。(C#)

///

/// メッセージを送信する

///

/// ユーザ名

/// コンピュータ名orIPアドレス

/// メッセージ

/// メッセージ表示秒数

/// エラーメッセージ(nullの場合は正常)

public static string SendMessage(string strUserName, string strHostName, string strMsg, int iDispTime){

//プロセスオブジェクト生成

Process proc = new Process();

// 起動するアプリケーションを設定する

proc.StartInfo.FileName = "msg.exe";

//引数指定

proc.StartInfo.Arguments = string.Format("{0} /server:{1} /time:{2} {3} ", strUserName, strHostName, iDispTime.ToString() , strMsg);

// ウィンドウを作成しないかどうかを設定する (初期値 false)trueにするとウィンドウ表示なしとなる

proc.StartInfo.CreateNoWindow = true;

// シェルを使用するかどうか設定する (初期値 true)falseにするとコンソールウィンドウが表示なしとなる

proc.StartInfo.UseShellExecute = false;

// 起動できなかった時にエラーダイアログを表示するかどうかを設定する (初期値 false)

proc.StartInfo.ErrorDialog = false;

//標準エラー出力を取得できるようにする。

proc.StartInfo.RedirectStandardError = true;

 

// 起動する

proc.Start();

//起動したプロセスが終了するまで待つ。1秒のみ

proc.WaitForExit(1000);

 

//標準エラー出力取得

if (proc.HasExited){

string strErr = proc.StandardError.ReadToEnd();

strErr = strErr.Replace("\r\n", "");

if (String.IsNullOrEmpty(strErr)){

return null; //正常終了

}

return strErr; //異常終了 エラーメッセージ返す

}

return "タイムアウトしました。";

}

ただドメインのAdministrator以外だとアクセス権の問題で送信できないことがあるので、管理者ユーザ以外で使う場合は(.Net)別ユーザでプロセスを起動するで書いたようにAdministrator権限でmsgをキックするようにするといいと思います。

参考:

msg - Windowsコマンド集:ITpro

[Terminal Server コマンド] MSG

【Windows7/Vista】ポップアップメッセージを送りたい(net send あらため msg.exe) - フィールドSEあがりの安納です - Site Home - TechNet Blogs

コマンド・プロンプトでファイル共有を管理する - @IT コマンドで管理する場合の情報です。

DOS「メッセージ送信」コマンド(Hishidama's Windows DOS command "msg" Memo)

[.NET] コンソールアプリケーションの標準入出力をリダイレクトする - あおいるかの倉庫