で、なんか僕も PowerShell と何かを連携させたいなとうずうずしてきたので、最近マイブームの WCF を PowerShell で扱ってみようかと思います。
構成はこんな感じ
- WCF サービスは C# で作成 ( どうせだから Visual Studio は使わず )
- WCF のホストは PowerShell
- WCF のクライアントも PowerShell
[ 0. 準備 ]
この記事では、基本的に PowerShell を使ってほとんどの操作を進めていきます。
0-1. 必要なもの
以下の3つがあれば大丈夫なはずです。
- Windows PowerShell 1.0 ( 適切なのを選んでください。 )
- .NET Framework 3.0 ( .NET 3.0 は .NET 2.0 + α です。 )
- .NET Framework 2.0 SDK ( .NET 3.0 入れてからね。 )
0-2. OS が Windows Vistaの場合の注意点
もし、OS が Windows Vistaである場合、管理者権限を要求される場面があります。なので、 OS が Windows Vista の場合は、PowerShell を管理者モードで実行してください。
管理者モードで実行するには、PowerShell のショートカットを右クリックして [ 管理者として実行 ] を選択します。
0-3. 作業フォルダ
C ドライブ直下に work という名前のフォルダを作成し、そこで作業を行うことにします。
以下のコマンドを実行してください。
(New-Item "C:\work1" -itemType "directory") | Set-Location -path {$_.FullName}
これで、 work フォルダが作成され、PowerShell のカレントパスが "C:\work" に移動します。
[ 1. WCF サービス作成 ]
1-0. 概要
まずは WCF サービス をクラス ライブラリとして作成します。
Visual Studio は使わず、メモ帳でコードファイルを書いて、コンパイルにはC# コンパイラをPowerShell 上から扱います。
1-1. WCF サービスの概要
アクセス数をカウントする簡単なサービスです。
Access メソッドを呼び出すたびにカウントされます。
GetCount メソッドでアクセス数を取得できます。
Release メソッドでサービスのインスタンスが解放されます。つまり、カウントがリセットされます。 ( この程度のサービスなら、インスタンスはそのままで、カウントを 0 に設定すれば済みますが^^; )
サービスのインスタンスはクライアント毎に用意されます。つまり、アクセス数は各クライアント毎にカウントされます。
アセンブリ名
Sample.WCFServices.dll
定義する型
Sample.WCFServices.ICounterService インターフェイス
Sample.WCFServices.CounterService クラス
1-2. ICounterService インターフェイスの作成
まず、 ICounterService.cs ファイルを作成し、そのファイルをメモ帳で開きます。
以下のコマンドを実行してください。
$icounterServiceCodeFile = New-Item "ICounterService.cs" -itemType "file";
notepad $icounterServiceCodeFile.FullName;
これで、作業フォルダに ICounterService.cs ファイルが作成され、そのファイルがメモ帳で開かれます。
次に、メモ帳で以下のコードを貼り付けます。
using System;
using System.ServiceModel;
namespace Sample.WCFServices
{
[ServiceContract(Namespace = "http://schemas.sample.net/WCFServices", SessionMode = SessionMode.Allowed)]
public interface ICounterService
{
[OperationContract]
void Access();
[OperationContract]
void Release();
[OperationContract]
int GetCount();
}
}
貼り付けましたら、保存を行い、メモ帳を閉じます。
1-3. CounterService クラスの作成
CounterService.cs ファイルを作成し、そのファイルをメモ帳で開きます。
以下のコマンドを実行してください。
$counterServiceCodeFile = New-Item "CounterService.cs" -itemType "file";
notepad $counterServiceCodeFile.FullName;
これで、作業フォルダに CounterService.cs ファイルが作成され、そのファイルがメモ帳で開かれます。
次に、メモ帳で以下のコードを貼り付けます。
using System;
using System.ServiceModel;
namespace Sample.WCFServices
{
[ServiceBehavior(Namespace = "http://schemas.sample.net/WCFServices", InstanceContextMode = InstanceContextMode.PerSession)]
public class CounterService : ICounterService
{
private int count;
public void Access()
{
try
{
checked
{
this.count++;
}
}
catch (OverflowException)
{
FaultReasonText overflowReasonText = new FaultReasonText("もう数えられない。。。");
FaultReason overflowReason = new FaultReason(overflowReasonText);
FaultCode overflowCode = FaultCode.CreateReceiverFaultCode("overflow", "http://schemas.sample.net/WCFServices/Fault");
throw new FaultException(overflowReason, overflowCode);
}
}
[OperationBehavior(ReleaseInstanceMode = ReleaseInstanceMode.AfterCall)]
public void Release()
{
}
public int GetCount()
{
return this.count;
}
}
}
貼り付けましたら、保存を行い、メモ帳を閉じます。
1-4. アセンブリに署名を付けるためにキー ペアを生成
えっと、アセンブリに署名を付けます。なんで付けるのかというと、今回、クライアントも PowerShell で作成します。その際、ジェネリック クラスを利用し、ジェネリック 引数に ICounterService インターフェイスを指定します。・・・で、どうも PowerShell でジェネリック引数に指定する型のアセンブリは、GAC に登録されている必要があるみたいです。なので、Sample.WCFServices.dll に署名を付けて GAC に登録します。
まぁ、複数のアプリで参照されるアセンブリは GAC に登録するべきですしね。( WSDL を公開して、Svcutil.exe でプロキシ クラスを生成する場合は不要です。でも、今回はそうしません^^; )
以下のコマンドを実行してください。
&"$env:programfiles\Microsoft Visual Studio 8\SDK\v2.0\Bin\sn.exe" -k Sample.WCFServices.snk
このコマンドにより、作業フォルダに Sample.WCFServices.snk という名前のキー ペア ファイルが生成されます。
1-5. コンパイル
1-2 と 1-3 で作成した2つのコードファイルを、クラス ライブラリ形式にコンパイルします。
以下のコマンドを実行してください。
&"$env:windir/Microsoft.NET/Framework/v2.0.50727/csc.exe" /out:"Sample.WCFServices.dll" /target:library /lib:"$env:windir\Microsoft.NET\Framework\v3.0\Windows Communication Foundation" /reference:"System.ServiceModel.dll" /optimize+ /keyfile:"Sample.WCFServices.snk" *.cs;
このコマンドにより、2つのコードファイルがコンパイルされ、作業フォルダに Sample.WCFServices.dll というファイルが出力されます。また、先ほど生成したキー ペアにより、アセンブリに署名が施されます。
1-6. GAC に登録
1-4 で解説したように、今回は WCF サービスのアセンブリを GAC へと登録します。
以下のコマンドを実行してください。
&"$env:programfiles\Microsoft Visual Studio 8\SDK\v2.0\Bin\gacutil.exe" /i "Sample.WCFServices.dll"
このコマンドにより、 Sample.WCFService.dll が GAC へと登録されます。
なお、後で Sample.WCFService.dll を GAC から削除する際には、以下のコマンドで削除可能です。 ( 今はまだ削除しないでくださいね。 )
&"$env:programfiles\Microsoft Visual Studio 8\SDK\v2.0\Bin\gacutil.exe" /u "Sample.WCFServices"
[ 2. PowerShell で WCF をホスティング ]
ExecuteCounterServiceHosting という関数を定義し、その中でホスティングの一連の処理を行います。ここでは、NetTcpBinding を使うことにします。
以下のコードを全てコピーし、PowerShell に貼り付けてください。
function ExecuteCounterServiceHosting
{
# WCF必須のクラス ライブラリを GAC からロード
[void][System.Reflection.Assembly]::LoadWithPartialName("System.ServiceModel");
# 自作の WCF サービス クラス ライブラリを GAC からロード
[void][System.Reflection.Assembly]::LoadWithPartialName("Sample.WCFServices");
# サービス クラスの型情報
$counterServiceType = [Sample.WCFServices.CounterService];
# ベース アドレス
$counterServiceAddress = New-Object "System.Uri" @("net.tcp://localhost:10000/Sample/WCFServices/CounterService.svc");
# バインディングの設定
$counterServiceBinding = New-Object "System.ServiceModel.NetTcpBinding" @([System.ServiceModel.SecurityMode]::None);
$counterServiceBinding.Namespace = "http://schemas.sample.net/WCFServices";
# コントラクトの取得
$counterServiceContractType = [Sample.WCFServices.ICounterService];
# サービス ホスト作成
$counterServiceHost = New-Object "System.ServiceModel.ServiceHost" @($counterServiceType, $counterServiceAddress);
[void]$counterServiceHost.AddServiceEndpoint($counterServiceContractType, $counterServiceBinding, [System.String]::Empty);
# ホスティング 開始
$counterServiceHost.Open();
# 待機
[System.Console]::WriteLine("WCF サービス の ホスティングを開始しました。");
foreach($dispatcher in $counterServiceHost.ChannelDispatchers)
{
[System.Console]::WriteLine("Listening uri: {0}", $dispatcher.Listener.Uri.ToString());
}
[System.Console]::WriteLine("Enter キーを押すと終了します。");
[System.Console]::ReadLine();
# ホスティング 終了
$counterServiceHost.Close();
# エラートラップ
trap
{
if (($counterServiceHost -ne $()) -and ($counterServiceHost.State -eq [System.ServiceModel.CommunicationState]::Opened))
{
$counterServiceHost.Close();
}
break;
}
}
この関数を呼び出せば、PowerShell 上で WCF ホスティングが開始されます。
この関数を呼び出すには、以下のコードを実行してください。
ExecuteCounterServiceHosting;
成功すると、以下のようなメッセージが出力されます。
WCF サービス の ホスティングを開始しました。
Listening uri: net.tcp://localhost:10000/Sample/WCFServices/CounterService.svc
Enter キーを押すと終了します。
ホスティングを終了させるには、Enter キーを押します。 ( でも、まだ押しちゃだめ>< )
[ 3. PowerShell で WCF サービスにアクセス ]
現在開いている PowerShell とは別に、もうひとつ PowerShellを起動してください。
この別 PowerShell が、クライアントを担当します。
ConnectToCounterService という関数を定義し、その中で WCF サービスへのアクセスの一連の処理を行います。
以下のコードを全てコピーし、PowerShell に貼り付けてください。
function ConnectToCounterService
{
# WCF必須のクラス ライブラリ
[void][System.Reflection.Assembly]::LoadWithPartialName("System.ServiceModel");
# 自作の WCF サービス クラス ライブラリ
[void][System.Reflection.Assembly]::LoadWithPartialName("Sample.WCFServices");
# CounterService のチャネルを生成
$contractAssemblyQualifiedName = [Sample.WCFServices.ICounterService].AssemblyQualifiedName;
$counterServiceBinding = New-Object "System.ServiceModel.NetTcpBinding" @([System.ServiceModel.SecurityMode]::None);
$counterServiceChannelFactory = New-Object "System.ServiceModel.ChannelFactory``1[[$contractAssemblyQualifiedName]]" @($counterServiceBinding, "net.tcp://localhost:10000/Sample/WCFServices/CounterService.svc");
$counterServiceProxy = $counterServiceChannelFactory.CreateChannel();;
# 選択肢作成
$accessMethodChoice = New-Object "System.Management.Automation.Host.ChoiceDescription" @("&Access", "CounterService サービスの Access メソッドを実行");
$releaseMethodChoice = New-Object "System.Management.Automation.Host.ChoiceDescription" @("&Release", "CounterService サービスの Release メソッドを実行");
$getCountMethodChoice = New-Object "System.Management.Automation.Host.ChoiceDescription" @("&GetCount", "CounterService サービスの GetCount メソッドを実行して戻り値を表示");
$exitChoice = New-Object "System.Management.Automation.Host.ChoiceDescription" @("&Exit", "終了");
$methodChoiceDescription = [System.Management.Automation.Host.ChoiceDescription[]]@($accessMethodChoice, $releaseMethodChoice, $getCountMethodChoice, $exitChoice);
# メソッド選択肢ループ
$run = $True;
while ($run)
{
$result = $Host.UI.PromptFOrChoice("[ 処理選択 ]", [System.String]::Empty, $methodChoiceDescription, 0);
switch ($result)
{
0 {$counterServiceProxy.Access();},
1 {$counterServiceProxy.Release();},
2 {$counterServiceProxy.GetCount();},
3 {$run = $False;}
}
}
# 後処理
# プロキシ オブジェクトを IClientChannel にキャストして CloseしたいんだけどPowerShell のキャスト チェックに弾かれる。
# ChannelFactory の Close メソッドが内部で Close してくれているからまぁいいんだけど^^;
#$counterServiceChannel = [System.ServiceModel.IClientChannel]$counterServiceProxy
#$counterServiceChannel.Close();
$counterServiceChannelFactory.Close();
# エラートラップ
trap
{
if (($counterServiceChannelFactory -ne $()) -and ($counterServiceChannelFactory.State -eq [System.ServiceModel.CommunicationState]::Opened))
{
#if (($counterServiceChannel -ne $()) -and ($counterServiceChannel.State -eq [System.ServiceModel.CommunicationState]::Opened))
#{
# $counterServiceChannel.Close();
#}
$counterServiceChannelFactory.Close();
}
}
}
この関数を呼び出せば、PowerShell 上で CounterService サービスへのアクセスが行えます。
この関数を呼び出すには、以下のコードを実行してください。 ( さっき作成した ExecuteCounterServiceHosting 関数はちゃんと実行中のままにしてありますよね? )
ConnectToCounterService;
成功すると、以下のようなメッセージが出力されます。
[ 処理選択 ]
[A] Access [R] Release [G] GetCount [E] Exit [?] ヘルプ (既定値は "A"):
実行したい処理の頭文字を入力して Enter を押せば、処理が実行されます。各処理の説明を表示するには、 "?" と入力して Enter を押してください。
[ 4. 完成 ]
これで完成です。クライアントを複数用意したりして試してみてください。
[ 5. スクリプト ファイル化 ]
毎回コマンドをコピペじゃあちょっと不便なので、スクリプト ファイルとして保存しておく方法を最後に紹介しておきます。
PowerShell のスクリプト ファイルの拡張子は "ps1" です。
ただ、PowerShell の既定の設定ではスクリプト ファイルの実行が許可されていません。
以下のコマンドを実行すると、ローカルファイルとして保存されているスクリプト ファイルの実行が許可されるように設定が変更されます。
Set-ExecutionPolicy RemoteSigned
このあたりの詳細な解説は、以下のコマンドを実行することで見ることができます。この解説を読んだ上で、上記コマンドを実行するかどうかを判断してください。
get-help about_signing | out-host -paging
で、スクリプト ファイルの実行方法ですが、ファイル名を ( 必ずパス付きで ) 入力して Enter を押すだけです。あと、PowerShell の起動時にコマンドライン引数としてスクリプト ファイルのファイル名を指定してやっても実行されます。
トラックバックURL↓
http://csharper.blog57.fc2.com/tb.php/117-cbdfbeab