OpenSSLでAES-256で暗号化したものをSystem.Security.Cryptography.RijndaelManaged
クラスで復号するためのメモ。
タグ別アーカイブ: C#
WPFのListBoxでItemsSourceにアイテムが追加・更新されたら最終行にスクロールさせる
はじめに
WPFでListBoxをログ表示などに使っているとき、BindingしているItemsSource
に新しいアイテムが追加されたら、その行を表示する方法です。
続きを読む WPFのListBoxでItemsSourceにアイテムが追加・更新されたら最終行にスクロールさせる
C#でWindowsサービスのexeにインストーラと自動起動を組み込む
はじめに
C#でのWindowsサービス開発のはじめ方でとりあえずサービスの開発はできるのだが、インストールがめんどくさいのとインストールしたあとに自動で起動してくれないので、これをexe自身でできるように機能を組み込んでみる。
細かいことを考えるよりテンプレートとして使うほうがラクなので、一番下にProgram.csの内容をすべて載せておく。簡単な説明を見たあとでもいいし、面倒なら丸ごとコピペすればそれなりに動くと思う。
主要クラスの説明
インストール・アンインストールはManagedInstallerClassクラス
サービスをインストール・アンインストールにはManagedInstallerClass
クラスのInstallHelper(string[])
メソッドを使う。このメソッドは内部的にInstallUtil.exeを呼び出しているらしい。
インストール時は引数にexeのパスだけ、アンインストール時は”/u”とexeのパスを渡すだけだ。
var myAssembly = System.Reflection.Assembly.GetEntryAssembly();
string path = myAssembly.Location;
var param = (isInstallMode) ? new[] { path } : new[] { "/u", path };
ManagedInstallerClass.InstallHelper(param);
起動(開始)・停止はServiceControllerクラス
サービスの開始や停止にはServiceController
クラスのStart()
メソッド、Stop()
メソッドを使う。ServiceController
のコンストラクタにサービス名を渡すだけでよい。
起動(開始)
ServiceController sc = new ServiceController(SERVICE_NAME);
if (sc.Status != ServiceControllerStatus.Running)
{
sc.Start();
}
停止
ServiceController sc = new ServiceController(SERVICE_NAME);
if (sc.Status != ServiceControllerStatus.Stopped)
{
sc.Stop();
}
全体的なロジック
Main
メソッドではコマンドライン引数が渡されていれば、コンソールモードとして起動する。
コンソールモード(RunAsConsoleMode
メソッド内)では引数に応じて、インストール(/i)・アンインストール(/u)・開始(/start)・停止(/stop)を行う。
インストールでは終了後に自動的に開始し、アンインストールでは停止してからアンインストールを行うようにしてある。
ワンポイント
呼び出し方
exeファイル名をAwesomeService.exeだとすると
AwesomeService.exe /i
などとなる。
ソースコード (Program.cs)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration.Install;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;
namespace WindowsService1
{
static class Program
{
public const string SERVICE_NAME = "AwesomeService";
public const string DISPLAY_NAME = "Awesome Service";
static void Main(string[] args)
{
// Run as console mode when an argument is provided.
if (1 <= args.Length)
{
RunAsConsoleMode(args[0]);
return;
}
// Run as Windows Service
ServiceBase.Run(new Service1()
{
CanShutdown = true,
CanPauseAndContinue = false,
});
}
/// <summary>
/// Run as console mode when an argument is provided.
/// </summary>
/// <param name="arg"></param>
/// <returns></returns>
static void RunAsConsoleMode(string arg)
{
string mode = arg.ToLower();
var myAssembly = System.Reflection.Assembly.GetEntryAssembly();
string path = myAssembly.Location;
if (mode == "/i" || mode == "/u")
{
bool isInstallMode = (mode == "/i");
var mes = (isInstallMode) ? "installed" : "uninstalled";
if (IsServiceExists())
{
Console.WriteLine("{0} has been already {1}.", DISPLAY_NAME, mes);
}
else
{
if (!isInstallMode) { StopService(); }
var param = (mode == "/i") ? new[] { path } : new[] { "/u", path };
ManagedInstallerClass.InstallHelper(param);
Console.WriteLine("{0} has been successfully {1}.", DISPLAY_NAME, mes);
if (isInstallMode) { StartService(); }
}
}
else if (mode == "/start")
{
StartService();
}
else if (mode == "/stop")
{
StopService();
}
else
{
Console.WriteLine("Provided arguments are unrecognized.");
}
}
/// <summary>
/// Whether the service already exists in the computer or not.
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
static bool IsServiceExists(string name = SERVICE_NAME)
{
ServiceController[] services = ServiceController.GetServices();
return services.Any(s => s.ServiceName == name);
}
/// <summary>
/// Start the service.
/// </summary>
/// <returns></returns>
static void StartService()
{
if (IsServiceExists())
{
Console.WriteLine("Starting {0}...", SERVICE_NAME);
ServiceController sc = new ServiceController(SERVICE_NAME);
if (sc.Status == ServiceControllerStatus.Running)
{
Console.WriteLine("{0} has been already started.", DISPLAY_NAME);
}
else
{
try
{
sc.Start();
Console.WriteLine("{0} has been started.", DISPLAY_NAME);
}
catch (Exception)
{
Console.WriteLine("{0} could not be started.", DISPLAY_NAME);
}
}
}
}
/// <summary>
/// Stop the service.
/// </summary>
/// <returns></returns>
static void StopService()
{
if (IsServiceExists())
{
Console.WriteLine("Stopping {0}...", SERVICE_NAME);
ServiceController sc = new ServiceController(SERVICE_NAME);
if (sc.Status == ServiceControllerStatus.Stopped)
{
Console.WriteLine("{0} has been already stopped.", DISPLAY_NAME);
}
else
{
try
{
sc.Stop();
Console.WriteLine("{0} has been stopped.", DISPLAY_NAME);
}
catch (Exception)
{
Console.WriteLine("{0} could not be stopped.", DISPLAY_NAME);
}
}
}
}
}
[RunInstaller(true)]
public class ProjectInstaller : Installer
{
public ProjectInstaller()
{
var spi = new ServiceProcessInstaller
{
Account = ServiceAccount.LocalSystem
};
var si = new ServiceInstaller
{
ServiceName = Program.SERVICE_NAME,
DisplayName = Program.DISPLAY_NAME,
Description = "Awesome Service.",
StartType = ServiceStartMode.Automatic,
};
this.Installers.Add(spi);
this.Installers.Add(si);
}
}
}
参考
C#でWindowsサービス開発のはじめ方
準備
- 表示名 (DisplayName):サービス一覧などで「名前」に表示される名前(スペースを含んでもよいし、日本語も可)
- サービス名 (ServiceName):サービスの識別用の名前(英数字のみ。スペースなども含まない)
プロジェクトの作成
プロジェクトの新規作成で右上の検索ボックスに “サービス” と入力するのが一番手っ取り早い。
通常通り、デフォルトではプロジェクト名がサービスのexe名になるので、先に考えたサービス名を入力しておく。
ここでデザイナからインストーラクラスを追加することもできるが、コードで書いたほうが短くて使いやすいので、コード側で用意することにする。
Program.csの編集
Program.csを開く。
Mainメソッドはそのままでもかまわないが、なぜか冗長な書き方なので、下記のように変更する。
CanShutdown
をtrue
にしておくとPCがシャットダウンするときのイベントを拾うことができる。凝った作りにしない限り一時停止と再開はできなくてもいいと思うので、適宜CanPauseAndContinue
はfalse
にしておく。
static void Main(string[] args)
{
ServiceBase.Run(new SKYMAgentService()
{
CanShutdown = true,
CanPauseAndContinue = false,
});
}
Program
クラスの下に下記のProjectInstaller
クラスを追加する(誤ってProgram
クラスの中に書かないこと)。当然別ファイルに分けてもよい。
[RunInstaller(true)]
public class ProjectInstaller : Installer
{
public ProjectInstaller()
{
var spi = new ServiceProcessInstaller
{
Account = ServiceAccount.LocalSystem
};
var si = new ServiceInstaller
{
ServiceName = "サービス名",
DisplayName = "表示名",
Description = "サービスの説明",
StartType = ServiceStartMode.Automatic,
};
this.Installers.Add(spi);
this.Installers.Add(si);
}
}
Serviceクラスの実装
Service1.csのデザイナのプロパティにServiceName
があるのでこれもサービス名に統一しておく。
デザイナからコードビューに切り替えるとOnStart
とOnStop
が存在しているので、この中に開始時の処理と停止時の処理を書く。OnShutdown
が必要な場合は下記のように追加する。
protected override void OnShutdown()
{
server.Stop();
}
あとはビルドすればbin\Debugかbin\Releaseにexeが生成される。
インストールとアンインストール
インストールとアンインストールにはInstalUtil.exeを使う。このコマンドはVSにパスの通ったコマンドプロンプトでないといけないので、スタート→プログラム→VS→Visual Studio コマンドプロンプトを管理者権限で起動する。
cd "exeのあるフォルダ"
installutil "exeパス"
でインストールできる。ちなみにアンインストールはinstallutil /u "exeパス"
でできる。
参考
バインディングオブジェクト内の変更をコントロールに反映するには
概要
WPFでコントロールにオブジェクトをBindingしている場合、オブジェクトのプロパティが変更されたときにコントロールの表示も伴って更新されてほしいわけだが、残念ながら更新されない。
これは、更新されたことを通知する機構がオブジェクトに備わっていないからである。 このページではこれの対応策を示す。
対策
まず INotifyPropertyChanged インターフェースを実装
コントロールにプロパティが変更されたことを通知するには、まずバインドするクラスにINotifyPropertyChanged
インターフェースを実装しておく。
INotifyPropertyChanged
を実装するとPropertyChanged
イベントが追加されるので、これをコールするための下記のようなメソッドを作っておく。
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(name));
}
}
この引数name
には変更されたプロパティの名前を渡す。
そして変更を通知すべきプロパティ(表示を更新する必要のあるプロパティ)のset
アクセサで次のようにコールする。
private string _userId;
public string UserId
{
get
{
return _userId;
}
set
{
_userId = value;
OnPropertyChanged("UserId");
}
}
これでUserId
に値がセットされると、変更が通知されるようになる。
コレクションには ObservableCollection<T> クラス
たとえばTreeView
に階層構造のオブジェクトをバインドしているとする。
オブジェクトの中のコレクションをList<T>
などで実装しているとこれらのコレクションに要素が追加もしくは削除されたとき、TreeView
の表示が更新されない。 また、これはコレクションのプロパティからPropertyChanged
を呼んでも同じである。
これを自動的に反映されるようにするにはコレクションにObservableCollection<T>
クラスを利用すればいい。
ソースコード
以上の実装例を示す。
public class TopLevel : System.ComponentModel.INotifyPropertyChanged
{
private string _userId;
public string UserId
{
get
{
return _userId;
}
set
{
_userId = value;
OnPropertyChanged("UserId");
}
}
public ObservableCollection<SecondLevel> SecondLevels { get; set; }
#region INotifyPropertyChanged メンバー
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(name));
}
}
#endregion
}