トップ «前の日記(2011-01-15) 最新 次の日記(2011-01-17)» 編集

日々の破片

著作一覧

2011-01-16

_ VBでWCFサービス

これまでDCOMのサーバとして動かしていたVB6のプログラムがあるのだが、ファイアウォールの向こうに設置する必要が出てきたので、Webサービスに変える必要が出てきた。

プログラムそのものは、1つのfrmと1つのclsから構成されていたのだがどうもfrmは使っていないらしく、単なる空っぽのフォームだ。外部COMサーバとして利用するから、とりあえず簡単に起動可能アプリケーションとするためにフォームを作ったのだろう。しかし、clsの中はなんかごちゃごちゃしたプログラムなので全面書き換えは避けたいところだ。

とりあえず、VS2010のVBに変換しようとしたら、なんとVS2010はVB6の変換ツールが搭載されていない。いや、まだVB6から移行していないユーザがここにいるのだが。とか思ったけどしょうがないのでしばらく困っていたが、VS2008をアンインストールしていないことを思い出して、VS2008を立ち上げたら、こっちには移行ツールが載っていた。で、変換しようとしたらエラーになる。参照しているCOMコンポーネント(これはDCOMではなくローカル)が開発PCに登録されていないからだ。しょうがないので登録してから変換する。このCOMコンポーネント(当然32ビット)は、運用ホストではそのまま利用することにする。

その昔、16ビットVBを32ビットVBに変換するツールがあって、これっぽっちも役に立たなかったのだが、さすがにVB6からVB2008への変換は問題なくできた。ディレクトリ構成が1段階なのは(今となっては)気持ち悪いが、まあ許容範囲だ。32ビットCOMコンポーネントを参照しているからか、.NETのコンパイル構成でターゲットはx86に固定されていた(Anyになっていたらおそらくはまるところだ。というのは運用サーバは64ビットWindowsだからだ)。で、そのプロジェクトをVS2010で読み込んでVS2010へ変換。

で、どうしようかとしばし悩むが、とりあえず、COM Interopに関する属性を削除して、単なるクラスに変えてみる。それから、WCFサービスを追加した。App.configに変更が入り、WCFサービスの実装ファイルとインターフェイスファイルが追加される。

次に、WCFサービスのインターフェイスファイルを元のclsだった名前にIを付けたものに変えてからメソッド定義を元のclsのほうからコピペした。そして元のclsのほうのメソッド定義をインターフェイス実装に変える。追加したWCFサービスの実装ファイルのほうは削除。

ここまでで、こんな感じになる。

'インターフェイスファイル
Imports System.ServiceModel
' メモ: コンテキスト メニューの [名前の変更] コマンドを使用すると、コードと config ファイルの両方で同時にインターフェイス名 "IService" を変更できます。(注:IServiceはとりあえず追加したWCFサービスクラスの実装ファイル名Serviceから生成されたもの)
<ServiceContract()>
Public Interface IOnceComServer
    <OperationContract()>
    Function Hello(ByVal name As String) As String
End Interface
---
'実装ファイル(元はclsだったファイル)
Public Class OnceComServer
    Implements IOnceComServer
    Public Function Hello(ByVal name As String) As String Implements OnceComServer.Hello
        Hello = "Hello " & name & " !"
    End Function
End Class

App.configはざっくり大ナタを振るってserviceBehaviorだけ残す。この時点でWCFホストにするつもりだから本当はApp.Configはいらないのだが、httpGetEnalbedだけは既定で欲しいからだ。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
        <behaviors>
            <serviceBehaviors>
                <behavior name="">
                    <serviceMetadata httpGetEnabled="true" />
                    <serviceDebug includeExceptionDetailInFaults="false" />
                </behavior>
            </serviceBehaviors>
        </behaviors>
      </system.serviceModel>
</configuration>

それから、DCOMサーバだったときは全く意味がなかったfrmのほうに手を入れて、WCFサービスをホストできるようにする。

Imports System.ServiceModel
Imports System.Net
Public Class Form1
    Dim host As ServiceHost
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim uri As New Uri("http://" & Dns.GetHostName() & "/VBService") 'ホスト名は、クライアントPCからのWSDL取得用HTMLに埋め込まれるので正しいものを設定する。
        Dim binding As New BasicHttpBinding  '認証とか不要なのでWSHttpBindingは使わない。
        Try
            host = New ServiceHost(GetType(OnceComServer), uri)
            host.AddServiceEndpoint(GetType(IOnceComServer), binding, String.Empty)
            host.Open()
        Catch ex As Exception
            MessageBox.Show(ex.ToString())
        End Try
    End Sub
 
    Private Sub Form1_FormClosed(ByVal sender As System.Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles MyBase.FormClosed
        If Not host Is Nothing Then
            host.Close()
        End If
    End Sub
End Class

これで、HTTPポート共有が可能なら起動しておけば(疑問点: HTTPアクティブ化を使えば、起動しておかなくてもオンデマンドで起動できるのかな?)サービスリクエストをポート80で受けられる。(実行するサーバがWindows 2008 Serverの場合、役割の設定でアプリケーションサーバーのWebサーバー(IIS)サポート機能をインストールしておく必要がある)。

せっかくフォームがあるのだから、普段は最小化か通知エリア押し込みにしておいて、表示すると呼び出し回数とかメソッド平均実行時間とかの統計情報を表示するようにしておくと良いかも。

で、クライアントはサービス参照で、"http://ホスト/VBService"を指定して(ここではVBServiceReferenceという名称で取り込み)以下のようになる(面倒なのでC#で例)。

VBServiceReference.ServiceClient svc = new VBServiceReference.ServiceClient(
    "BasicHttpBinding_IOnceComServer", 
    "http://" + 稼働ホスト名 + "/VBService");
MessageBox.Show(svc.Hello("boofoowoo"));

実行時にサービス参照したホストではなく、稼働しているホストに変えるのは、イントラネットでは当たり前(開発用サーバと運用用サーバは異なる)なのだが、例があまり出ていない。普通にあるユースケースだと思うのだがなぁ。しかもURIのホスト部だけを入れ替えられるようなコードを生成してくれれば良いのだが、自動生成されるServiceClientクラスのテンプレートには、ホストのみを交換できるコンストラクタが無い(ようにおれには見える)。そこで、エンドポイント構成名(「バインディングタイプ_インターフェイス名」の形式)とリモートアドレス(参照先のURI)を指定する。

実際は、クライアントもVB6のプログラムなのだが、同じくVB2010にVB2008経由で変換。で、CreateObjectしているところを上のサービス参照して生成したClientクラスの作成に変更。

それにしてもWCFについて調べるのは意外とやっかいだ。結構導入事例は多いと思うのだが、MSクラスターはどうも情報公開がいまひとつな気がする。

エッセンシャルWCF:Windows Communication Foundation (Programmer’s Selection―Microsoft .net Development Series)(Steve Resnick)

やはり、このあたりは持っておくべきかも。(野村さんにもらったような気がするのだが、手元にはWPFとWFしか無いなぁ)


2003|06|07|08|09|10|11|12|
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|11|12|
2020|01|02|03|04|05|06|07|08|09|10|11|12|
2021|01|02|03|04|05|06|07|08|09|10|11|12|
2022|01|02|03|04|05|06|07|08|09|10|11|12|
2023|01|02|03|04|05|06|07|08|09|10|11|12|
2024|01|02|03|

ジェズイットを見習え