C#と諸々

C#がメインで他もまぁ諸々なブログです
おかしなこと書いてたら指摘してくれると嬉しいです(´・∀・`)
つーかコメント欲しい(´・ω・`)

2007/04/01 03:19
今日 ( いや、もう昨日になるか ) 我が家に Windows PowerShell宣言! が届きました。

で、なんか僕も PowerShell と何かを連携させたいなとうずうずしてきたので、最近マイブームの WCF を PowerShell で扱ってみようかと思います。

構成はこんな感じ
  • WCF サービスは C# で作成 ( どうせだから Visual Studio は使わず )
  • WCF のホストは PowerShell
  • WCF のクライアントも PowerShell


[ 0. 準備 ]
この記事では、基本的に PowerShell を使ってほとんどの操作を進めていきます。

0-1.  必要なもの
以下の3つがあれば大丈夫なはずです。


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 の起動時にコマンドライン引数としてスクリプト ファイルのファイル名を指定してやっても実行されます。
タグ: .NET C# WCF PowerShell











トラックバックURL↓
http://csharper.blog57.fc2.com/tb.php/117-cbdfbeab