荒井省三のBlog

  • Deep Dive to .NET Framework CLR を作成中です

    TechEd 2009 横浜で「Deep Dive to .NET Framework CLR」というセッションを担当します。この資料を作成しています。このセッションは、私の著書である「The Root of .NET Framework」という書籍をモチーフにして、資料を構成しています。どのような内容が良いか、悩みながら作成を行っています。その途中経過を以下に引用します。

    T2-07-01

    PEヘッダーから CLIヘッダーのアドレスを見つけて

    T2-07-02

     

    CLI ヘッダーを読み解くのが、上記のスライドです。ここまで来るとメタデータが、どのように格納されているかを知りたくなることでしょう。それらも作成しているのですが、どこまで作るかが難しいところです。

    これ以外にも、GCやCLRのホストインターフェース、RCWなどとアイディアがあるのですが、如何とも時間の制約がありますので作成したスライドを全部、説明しきれないかもしれません。現時点で30スライド程度が出来上がっていて、ここから SOSデバッガ拡張や.NET Framework 4.0の話を入れようと思っていますので。まだまだ、悩みは尽きません。公開されているセッションレベルは400番台なので、最低でも16進数ダンプの見方を知っている方が対象になると思います。

  • DLR の COM バインダーに対する考察

    DLRを使ったExcelプログラミングというエントリーで、興味深いご指摘をいただきました。それは、COMオブジェクトのリリースを誰が面倒を見てくれるのかというものです。この問題を考える上で意識しないといけないのが、オブジェクトのライフサイクルの管理という側面です。具体的には、以下のようなものです。

    • マネージ オブジェクトは、GCによって回収される。
    • COM オブジェクトは、COMサーバーが参照カウンタが0になった時点で消滅させる。

    オブジェクトの生存の可否そのものが、異なる観点で管理されているのです。つまり、

    • マネージ オブジェクトは、マネージヒープ上で参照されないものがGCによって回収される。つまり、ルート オブジェクトから辿っていけないオブジェクトが、回収の対象になる。
    • COMは参照カウンタで管理していおり、マネージコードからはRCWが内部で参照カウンタに対する操作を行う。従って、RCWが回収されない限り参照カウンタがデクリメントされない。

    ということです。COM参照をカウンタを適切なタイミングでデクリメントするには、Marshal.ReleaseComObjectメソッドを呼ぶ必要があります。
    (注)GCはCLRホスト内に存在しますので、アプリケーションがアンロードされれば解放されます。アンロード前にCOMサーバーを解放するために、Marchal.ReleaseComObjectを呼ぶかGC.Collectを呼び出す必要があるだけです。

    この考えをベースに.NET Framework 4.0で導入されるDLRについて考えていきます。C#言語では、dynamicというキーワードによってレイトバインディングが実現されます。dynamicというキーワードを付与した変数は、コンパイルされたILを見るとSystem.Object型がSystem.Runtime.CompilerServices.DynamicAttributeによって振る舞いを変えるようになっています。この振る舞いを変えるという言葉の意味は、以下のようなことを意味しています。

    • 通常のメソッド呼び出し:IL上は、直接メソッドを呼び出す(MethodInfoを示すマネージポインタである)。
    • dynamic属性を持つオブジェクトのメソッド呼び出し:名前によってMethodInfoを取得してから、Invokeで呼び出す(この意味で、リフレクションを使ってMethodInfoを取得してからInvokeするのと同じです。厳密には、DLRのバインダーによってこれらの処理が行われます)。

    つまりdynamicというキーワードは、リフレクションを使ってメソッドを呼び出す代わりにDLRが実行時にメソッド名の文字列からメソッドを呼び出してくれることになります。この時のパラメータの型に応じた呼び出しを最適化するために、CallSiteキャッシングという仕組みをDLRは用意しているのです。CallSiteキャッシングによって、同じパラメータを使ったメソッド呼び出しが高速化されるというメリットがあります。つまり実行時に文字列でメンバーを解決するが、繰り返し呼び出す場合の高速化メカニズムが用意されているのがDLRというレイトバインディングになります。今までのメソッド呼び出し比較した場合に、コンパイラが解決するか、実行時に解決するかという違いから速度的にdynamicの方が不利になるケースもあることでしょう。ですが、それは初回のメソッド呼び出し時のオーバーヘッドの違いで、2回目以降は同じとは言いませんが遜色ない程度に早くなると言えるでしょう。

    これらの動的なメンバー呼び出しをCOMオブジェクトに適用するのが、DLRのCOMバインダーの役割になります。COMバインダーの設計思想は、VB6.0と同じようにCOMのAutomationインタフェースを使えるようにすることにあります。このため名前を使ってIDispatch::GetIDsOfNameメソッドでDispIDを取得してから、IDispatch::Invokeを呼び出すメカニズムを提供します。このことは、CLRのCOMインタロップの仕組みとは異なっています。COMインタロップでは、インタロップ・アセンブリ(TLBから生成-tlbimp.exe-)を生成してアーリーバインディングを実現します。もちろん、自分でコードを記述するかVBコンパイラを使うことで、レイトバインディングを実現することもできました。これらのレイトバインディングが、コードを記述することなくCOMのAutomationインターフェース経由でCOMを扱えるようになるのが、DLRのCOMバインダーです。

    ここまででdynamicキーワードでCOMを扱う時の特徴が理解できたのではないでしょうか。具体的には、以下のようなことです。

    • COMオブジェクトのインスタンスは、RCWでラップされている。
      参照カウンタは、RCWが管理している。
    • メンバー呼び出しは、COMバインダーがGetIDsOfName、Invokeを使って呼び出している。

    そうするとCOMオブジェクトのインスタンスを早期に回収するには、プログラマがコードを記述する必要があるということです。この意味において、今までと何も変わらないということができます。異なるのは、メンバーを呼び出す内部の仕組みだけです。

    但し、.NET Framework 4.0ベータ1では実装されていませんが、DLR-0.91のソースコードに含まれるMicrosoft.Scripting.ComRuntimeHelpers.IUnknownReleaseDelegate(Microsoft.Dynamicの中にあります)のコメントなど参照してみてください。この実装などが、DLRの最終形に入ってくればDLRのCOMバインダーでCOMオブジェクトのリリースまで管理してくれる可能性があります。まだ開発途中ですので、DLRの開発方向を調べたい場合はcodeplexのDLRを参照するようにしてください。

    PS.DLRのCOMバインダーは、System.Dynamic.dllアセンブリに実装されています。

  • 書籍紹介: C# .NET アプリケーション開発 徹底攻略

    毎日コミュニケーション 山口様より、献本していただきました。

    タイトル:C# .NET アプリケーション 徹底攻略
    作者:伊藤 真二
    発売日:2009/4/23
    メディア:単行本(ソフトカバー)

    経験値をあげて高品質な業務アプリを作ろう

    内容は、以下のような構成になっています。

    1. 導入
    2. .NET Frameworkアプリケーション設計
    3. チューニング
    4. リリース管理/セキュリティ
    5. COMアプリケーション連携
    6. 新しい.NET Framework/Silverlight

    いわゆるTips系やリファレンス系とは異なる書籍で、.NET Frameworkを使ったプログラミングの定石というか王道を語っています。たとえば、Windows Formsではメインスレッドでしかコントロールを操作することができないため、デリゲートを使ってマルチスレッド プログラミングをするとか、FormのLoadイベントは、初期化処理には向かない。なぜなら、何度も呼び出される場合があるからなどです。
     この意味で実際に開発して経験したノウハウを形式知化することで、ハマらないアプリケーション開発=高品質な業務アプリを作れるようにすることを目指した書籍です。中でもChapter4「チューニング」は約40ページあり、著者の論理的思考を裏付けるためのノウハウで満載です。チューニングは、先入観よりプロファイリングした結果に基づいてボトルネックに対処すべしというものです。試行錯誤するよりも、プロファイラで事実を把握することが、チューニングの王道であり、早道だということを丁寧に書かれています。

     この意味で業務アプリケーションの実装方法を検討される方には、メリットがあるかと思います。もっとも、経験を積まれた方には、周知のことが多すぎるかも知れません。が、この経験した知識を形式知化したところにこそ本書の意義があると言えるでしょう。

    PS.かなり前に読み終わったのですが、書評をまとめる時間がとれませんでした。お陰で、もう一度、査読することができました。

  • Rack ベースの Web アプリを IIS で動かしてみました

    前回にご紹介した Railsカンファレンスの IronRuby on Railsセッションで、Rails以外のWebフレームワークとしてrackが紹介されていました。rackは、Ruby向けの Webインターフェースを提供するフレームワークで、Ruby on Rails 2.3.x系も内部でrackと統合されています。このrackをIronRubyを使って、IISで動かしてみました。その動かし方を以下に記載します。

    1.必要なもの

    2.rackのインストール

    • 「igem install rack」コマンドでインストールします。
      私の場合は、PATHとGEM_PATH環境変数を設定後に実行しました。実行すると以下のようなメッセージが出力されます。
      C:>igem install rack
      Successfully installed rack-1.0.0
      1 gem installed
      Installing ri documentation for rack-1.0.0...
      Installing RDoc documentation for rack-1.0.0...
      mscorlib:0:in `GetBytes': 値が有効な範囲にありません。
                    (System::Text::EncoderFallbackException)
              from :0:in `write'
              from :0:in `puts'
              from c:/ruby/lib/ruby/site_ruby/1.8/rubygems/
                   user_interaction.rb:227:in`alert_error'
              from c:/ruby/lib/ruby/site_ruby/1.8/rubygems/
                   user_interaction.rb:103:in`alert_error'
              from c:/ruby/lib/ruby/site_ruby/1.8/rubygems/
                   command_manager.rb:77:in `run'
              from c:/ruby/lib/ruby/site_ruby/1.8/rubygems/
                   gem_runner.rb:39:in `run'
              from C:/IronRuby0.5.0/bin/igem:24

      このエラーはIronRubyのバグのようで、riとrdocのインストールに問題があることを示しています。このエラーを発生させないためには、--no-ri --no-rdocオプションでriとrdocをインストールしない方法があります。
      インストールが終了すると、IronRuby 0.5.0のライブラリパスへとrackがインストールされます。

    3.HTTPハンドラをビルドします

    • githubからダウンロードしたIronRuby.Rackをビルドします。
    • 私の場合は、以下のような手順で作業を行いました。
      git clone git://github.com/jschementi/ironruby.git
      コピーしたソースコードの中から、C:\Users\shozoa\wk\ironruby\Merlin\Main\Hosts\IronRuby.Rackフォルダを作業用フォルダへコピー。
      IronRuby.Rack.slnファイルをVisual Studioで開いて、IronRuby.RackプロジェクトとIronRuby.Rack.Exampleプロジェクト以外のプロジェクトを削除(環境によって、IronRuby.Rack.Exampleプロジェクトは無効になっているかも知れません)
      IronRuby 0.5.0の7つのアセンブリに対して参照をIronRuby.Rackプロジェクトへ追加しました。
      これでIronRuby.Rackプロジェクトをビルドできるようになりました。

    4.IISの環境を作成します

    • 管理ツールのIISマネージャを開きます。
    • Default Web Sitesの下へ新規のアプリケーションを作成します。
      エイリアスを「IronRuby.Rack.Example」にします。
      物理パスにコピーしたソースコードの「IronRuby.Rack\IronRuby.Rack.Example」を指定します。
    • IronRuby.Rack.Example\web.config の configセクションを編集します。
      <section name='microsoft.scripting'
      type='Microsoft.Scripting.Hosting.Configuration.Section,
      Microsoft.Scripting, Version=0.9.6.10, Culture=neutral,
      PublicKeyToken=31bf3856ad364e35'
      requirePermission='false' />

      この変更は、アセンブリのバージョン番号とパブリックキートークンが異なるために行っています。
    • Visual Studio で IISのWebプロジェクトを編集するには、IISにいくつかのオプションが必要になります。このオプションとは、Windowsの機能のことです。追加されるには、アプリケーションの追加で以下の機能を追加します。
      セキュリティ-要求フィルタリング、Windows認証
      管理ツール-IIS6と互換性のある管理-IISメタベースとIIS6構成との互換性
      管理ツール-IIS管理コンソール、IIS管理サービス、IIS管理スクリプトとツール
      (注)Visual Studio 2008で編集しないのであれば、この作業は必要はありません。私は、IIS7で動作確認を行っています。
    • IronRuby.Rack.Example\Binフォルダへ必要なアセンブリを配置します。
      IronRuby.Rackプロジェクトでビルドして出来たアセンブリ(私の場合Debug\Binフォルダにある8つ)をコピーしました。
    • RubyとIronRubyのライブラリに対して、IISを起動するアカウント(Network Service)に対してアクセス権限を付与します。
      「C:Ruby」と「C:\IronRuby-0.5.0」へ読み取り権限を私は付与しました。

    5.ブラウザでテストします

    • 「http://localhost/IronRuby.Rack.Example」へアクセスします。
      IronRuby running Rack says "Hello, World"
       at 木 6 11 16:22:32 +09:00 20
    • 上記のように表示されれば成功です。この表示は、IronRuby.Rack.Example\app.rbに記述されたスクリプトを実行して出力しています。config.ru(rackの構成ファイル)などをカスタマイズすれば、別のアプリケーションへURLをマップしたりすることもできます。

    6.IronRuby.Rackの制限事項

    • GEM_PATH環境変数が「C:\ruby\lib\ruby\gems\1.8」固定になっている(Application.cs)ため、ruby 1.8系のインストールフォルダが「c:\ruby」で無ければならないということです。
    • rackの構成ファイルが、Webアプリケーションのルート直下に無ければならない
    • 制限ではないですが、HTTPハンドラは内部でRackupを行っています。
      (注)この個所を応用すれば、別のフレームワークもサポートできるかも知れません。
    • Visual Studioの開発用Webサーバーには対応していません。
  • IronRuby で Ruby on Rails を動かしてみました

    昨年のRailsカンファレンスに続いて今年のRailsカンファレンスでは、「IronRuby on Rails」というセッションが行われました。このセッションでは、公開されたIronRuby 0.5.0とRuby on Rails 2.3.2を使って実際にRailsをIronRubyで動かしています。少し(大分かも)前から、IronRubyでRailsを動かすためのドキュメントが公開されています。このカンファレンスで何を紹介したかというサマリーが、Jimmyさんのブログで紹介されています。Railsを動かすドキュメントを使って、実際にRailsを動かしてみましたので、その手順を以下に記載していきます。

    1.環境構築

    IronRuby 0.5.0をダウンロードしてC:\IronRuby-0.5.0フォルダへ展開し、OneClick Installerで使ってC:\RubyへRubyをインストールしました。

    2.Rails 2.3.2 の導入

    • RubyGems のアップデート
    • Ruby on Rails 2.3.2をRubyGemsを使ってインストール
    #コマンドプロンプトで作業
    gem update --system
    gem install rails --v2.3.2 --include-dependencies
    


     (注)RubyGemsを利用するために、Ruby 1.8系が必要になります。

    3.環境変数の設定

    • PATH環境変数へ IronRubyのBinフォルダへのパスを追加します。
    • GEM_PATH環境変数へRubyGemsのパスを設定します。
    SET PATH=%PATH%;C:\IronRuby-0.5.0\bin
    SET GEM_PATH=C:\ruby\lib\ruby\gems\1.8

     

    4.IronRubyの構成ファイルである「ir.exe.config」のLibralyPathsの値にRuby1.8のライブラリパスを設定します。

    <set language="Ruby" value="..\lib\IronRuby;
           c:\ruby\lib\ruby\site_ruby\1.8\;
           c:\ruby\lib\ruby\1.8\" option="LibraryPaths" />
    


    (注)valueの値は読みやすいように改行していますが、実際に設定する場合は改行を含めないで下さい。またir.exe.configは、IronRuby 0.5.0を展開した中のBinサブフォルダに存在します。

    4.SQL Server のActiveRecord Adapter の導入

    • MS SQL Server Adapter
    • ダウンロードした「mssql_adapter.rb」を「C:\Ruby\lib\ruby\gems\1.8\gems\activerecord-2.3.2\lib\active_record\connection_adapters」フォルダーにコピーします。

    (注)もちろん、SQL Serverをインストールしておく必要があります。私の場合は、SQL Server 2008 Express Editionで確認を行いました。このアダプターでは、以下のようなデータ型の対応付けが行われています。

    :primary_key => "int not null identity(1,1) primary key ",
    :string      => { :name => "varchar", :limit => 255 },
    :text        => { :name => "text" },
    :integer     => { :name => "int" },
    :float       => { :name => "float" },
    :decimal     => { :name => "numeric" },
    :datetime    => { :name => "datetime" },
    :timestamp   => { :name => "datetime" },
    :time        => { :name => "datetime" },
    :date        => { :name => "datetime" },
    :binary      => { :name => "varbinary", :limit => 'MAX' },
    :binary      => { :name => "image" },
    :boolean     => { :name => "bit" }
    

     

    5.WEBrickに対するパッチを投入します

    • 「C\Ruby\lib\ruby\1.8\webrick\httprequest.rb」へパッチを投入します。 
    • パッチは、githubから入手します。

     パッチの内容は、2か所あります。最初に、163行目のHttpRequestクラスのto_sメソッドです。

    #      ret = @request_line.dup
    #      @raw_header.each{|line| ret << line }
           ret = @request_line ? @request_line.dup : ""
           @raw_header.each{|line| ret << line } if @raw_header
    


    次は、8行目(require)の直後に追加します。
    # IronRuby bug: IO#read seems to chop off the first char
    class TCPSocket
      def read size
        recv size
      end
    end
    

    6.Railsからエンコード指定を取り除きます。2.3.2のRailsはデフォルトがUTF-8のエンコーディングです。が、IronRubyではUTF-8エンコーディングの文字列が組み合わさるとバグが発生するためです

    • 「C:\Ruby\lib\ruby\gems\1.8\gems\rails-2.3.2\lib\initializer.rb」の「initializ_encoding」メソッドの実装をコメントアウトします。
      407行目の「$KCODE='u' if RUBY_VERSION < '1.9'」をコメントにします。

    ここまでで、準備が完了です。ここからは、Railsアプリケーションを作成していく作業となります。

    7.新しいRailsアプリケーションを作成します

    • 「irails IronRubyOnRails」コマンドを使用します。
      「3.環境変数」を設定したコマンドプロンプトで作業を行います。

    (注)成功すれば、コマンドプロンプトに「create ....」というログが表示されます。IronRubyでは、各種のコマンドの先頭に「i」というプレフィックスが付きます。インタラクティブRuby(irb)なら、「iirb」となります。これらのコマンドは、Windows向けにはBATファイルがあり、Linux向けには拡張子無しのシェルスクリプトファイルが提供されています。

    8.データベースの設定を行います

    • IronRubyOnRails\config\database.ymlファイルを編集します。
    development:
      adapter: mssql
      host: (local)\SQLEXPRESS
      database: ironruby_dev
      integrated_security: true
    


    上記のように設定するか、host、database、integrated_securityパラメータを「connection_string」パラメータで置き換えます。

    続いて「ironruby_dev」という名前のデータベースをSQL Serverで作成します。SQL Server 2008 Management Studio Expressを使用するのが簡単です。
    (注)このパラメータ形式は、SQL Serverをご存じな方には馴染み易いことでしょう。ADO.NETなどで、指定する接続文字列をパラメータとして指定しているのです。

    9.Scaffolding を使ってサンプルのアプリケーションを作成します

    • 「ir script/generate scaffold post title:string body:text published:boolean」コマンドをIronRubyOnRailsフォルダで実行します。

    (注)成功すればコマンドプロンプトに「create....」というログが表示されます。

    10.アプリケーションコントローラーのバグ対応を行います。

    • 「app\controllers\posts_controller.rb」で「default_url_options」メソッドをpublicに定義します。


    class PostsController < ApplicationController
    ...
    ...
      # 私の場合は、最後に追加しました
      public :default_url_options
    end
    
    

    (注)この変更は、IronRubyのバグ1354に対する対応です。rails 2.3.2では、default_url_optionsメソッドがprotectedとして定義されています。IronRubyでは、publicである必要があります。publicに変更しないでWEBrickを起動すると、http リクエストに対するレスポンスが帰ってきません。default_url_optionsメソッドのエラーがコンソールに出力される場合もあります。

    11.データベースのテーブルを作成します

    • 「irake db:migrate」コマンドを実行してテーブルを作成します。


    C:\IronRubyOnRails>irake db:migrate
    (in C:/IronRubyOnRails)
    ==  CreatePosts: migrating =================
    -- create_table(:posts)
       -> 0.0550s
       -> -1 rows
    ==  CreatePosts: migrated (0.0990s) ========
    成功すれば上記のように「create table...」などのログがコンソールに出力されます。失敗すると以下のように「System::Text::EncoderFallbackException」が出力されます。
    C:\IronRubyOnRails>irake db:migrate
    (in C:/IronRubyOnRails)
    rake aborted!
    rake aborted!
    mscorlib:0:in `GetBytes': 値が有効な範囲にありません。
              (System::Text::EncoderFallbackException)
            from :0:in `write'
            from :0:in `puts'
    .....

    失敗した場合は、「config\database.yml」の設定内容を見直してください。host: (local)\SQLEXPRESSなどを書き間違えると、このエラーが出力されます(私も書き間違いをしていて、このエラー原因を探すのに苦労しました)。

    11.WEBrickを起動してテストを行います

    • 「ir script/server」コマンドを実行します。


    C:\IronRubyOnRails>ir script/server
    => Booting WEBrick
    => Rails 2.3.2 application starting on http://0.0.0.0:3000
    => Call with -d to detach
    => Ctrl-C to shutdown server
    [2009-06-09 17:14:17] INFO  WEBrick 1.3.1
    [2009-06-09 17:14:17] INFO  ruby 1.8.6 (2008-05-28) [i386-mswin32]
    [2009-06-09 17:14:17] INFO  WEBrick::HTTPServer#start:
                                                pid=4568 port=3000
    

     

    正常に起動すれば、上記のようなログが出力されます。しかしながら、私の環境(Windows Vista with SP2)では少し時間がかかります。

    • ブラウザで「http://localhost:3000/」へアクセスすれば、railsのページが表示されることでしょう。
      rails aborted
    • 「about your application's environment」をクリックします。
      about application environment 
      正常に実行されれば実行環境が上記のように表示されます。が、この情報はRailsのInfoControllerが処理する関係で、IronRubyとは表示されることはありません。あくまでも、Ruby 1.8系として表示されるだけです。
    • 次に「http://localhost:3000/posts」へブラウザでアクセスします。
      Listing posts 
      上記のようにpostsテーブルの一覧が表示されます。new postsのリンクをクリックしてデータを追加していけば、問題なく動作することを確認していくことができます。
      この時点で、コンソールを確認すると以下のようなログが出力されていました。
      Processing Rails::InfoController#properties
       (for 127.0.0.1 at 2009-06-09 17:21:5) [GET]
      Completed in 584ms (View: 189, DB: 0) | 200 OK
                   [http://localhost/rails/info/proerties]
      
      
      Processing PostsController#index
       (for 127.0.0.1 at 2009-06-09 17:25:21) [GET]
      Rendering template within layouts/posts
      Rendering posts/index
      Completed in 735ms (View: 540, DB: 0) | 200 OK
                  [http://localhost/posts]

      これらのログが表示されるのが、私の環境では少し遅くなっています。ブラウザでは問題なく動作しているのですが、WEBrickのログ出力が遅くなるという問題があるようです。もしかすると、環境依存かもしれません。

    ここまでの動作で、IronRubyを使ってRuby on Rails 2.3.2が動作していることを確認することができました。データベースとしては、SQL Serverです。まだまだパフォーマンス的な問題や、バグが残っているにしてもIronRubyによる Ruby on Railsの実現が近くなってきたのを感じ取ることができます。是非、皆さんも試してみてください。そしてバグを見つけたら、フィードバックをしてください。

    PS.昨年のRailsカンファレンスでもIronRubyでrailsを動かしていますが、日本では情報が少ないように感じています。是非、皆さんも動かしてみてください。

    追記:WEBrickへのパッチを投入するファイル名を追加しました。

  • DLR を使った Excel プログラミング

    Visual Studio 2010ベータ1のウォークスルーの中に Office プログラマビリティ があります。このウォークスルーの中で、1か所だけ 動的呼び出しになると記述されたところがあります。この個所を、このウォークスルーではNo PIA(埋め込みPIA)というシナリオを確認するために、最終的には、キャストします。なぜキャストするかといえば、埋め込みPIAでは必要な型情報のみを取り込むからだと説明されています。この内容は、さておきNo PIAにしないコードの抜粋を以下に引用します。

    public class Account
    {
        public int ID { get; set; }
        public double Balance { get; set; }
    }
    
    using Excel = Microsoft.Office.Interop.Excel;
    class Program
    {
        static void Main(string[] args)
        {
            var checkAccounts = new List<Account> {
                new Account { ID = 345, Balance = 541.27 },
                new Account { ID = 123, Balance = -127.44 } };
        
            DisplayInExcel(checkAccounts, (account, cell) =>
            {   cell.Value2 = account.ID;
                cell.get_Offset(0, 1).Value2 = account.Balance;
                if (account.Balance < 0)
                {   cell.Interior.Color = 255;
                    cell.get_Offset(0, 1).Interior.Color = 255;
                }
            });
        }
    
        public static void DisplayInExcel(
               IEnumerable<Account> accounts,
               Action<Account, Excel.Rage> DisplayFunc)
        {
            var xl = new Excel.Application();
            xl.Workbooks.Add();
            xl.Visible = true;
            xl.get_Range("A1").Value2 = "ID";
            xl.get_Range("B1").Value2 = "Balance";
            xl.get_Range("A2").Select();
            foreach (var ac in accounts)
            {   DisplayFunc(ac, xl.ActiveCell);
                xl.ActiveCell.get_Offset(1, 0).Select();
            }
            xl.get_Range("A1:B3").Copy();
    
            xl.Columns[1].AutoFit();
            xl.Columns[2].AutoFit();
        }
    }
    

    xl.Columns[1].AutoFit();」が実行時に解決されて、AutoFitメソッドが呼ばれます。この実行時に解決するというのは、xl.Columns[1]が実行時にRangeオブジェクトを返すために、AutoFitメソッドを呼び出せるようになるという意味です。このウォークスルーでは埋め込みPIAを確認するためですが、最終的に「((Excel.Range) xl.Columns[1]).AutoFit();」とキャストを追加するようになります。

     しかし、.NET Framework 4.0ベータ1には動的呼び出しをサポートするために DLRが含まれています。DLRを使うようにすることで、埋め込みPIAどころか、型情報自体を実行時に解決することができます。そのように改造した Programクラスを以下に示します。

    //using Excel = Microsoft.Office.Interop.Excel;
    class Program
    {
        static void Main(string[] args)
        {
            var checkAccounts = new List<Account> {
                new Account { ID = 345, Balance = 541.27 },
                new Account { ID = 123, Balance = -127.44 } };
        
            DisplayInExcel(checkAccounts, (account, cell) =>
            {   cell.Value2 = account.ID;
                cell.Offset(0, 1).Value2 = account.Balance;
                if (account.Balance < 0)
                {   cell.Interior.Color = 255;
                    cell.Offset(0, 1).Interior.Color = 255;
                }
            });
        }
    
        public static void DisplayInExcel(
               IEnumerable<Account> accounts,
               Action<Account, dynamic> DisplayFunc)
        {
            dynamic xl = GetComInstance("Excel.Application");
            xl.Workbooks.Add();
            xl.Visible = true;
            xl.Range("A1").Value2 = "ID";
            xl.Range("B1").Value2 = "Balance";
            xl.Range("A2").Select();
            foreach (var ac in accounts)
            {   DisplayFunc(ac, xl.ActiveCell);
                xl.ActiveCell.Offset(1, 0).Select();
            }
            xl.Range("A1:B3").Copy();
    
            xl.Columns[1].AutoFit();
            xl.Columns[2].AutoFit();
        }
        // COMインスタンス作成用のヘルパーメソッド
        public static dynamic GetComInstance(string progID)
        {   Type comType = Type.GetTypeFromProgID(progID);
            return System.Activator.CreateInstance(comType);
        }
    }
    


    プロジェクトからPIAアセンブリへの参照を削除して、ビルドすれば DLR を活用した動的呼び出しが完成します。C#では、PIAを使っている時にGet_RangeやGet_Offsetというメソッドだったことにも注意してください。VBと同じように「Get_」プレフィックスのつかないメソッドで呼び出せるようになります。これは、dynamicキーワードによってC# Binderが DLR のCOM呼び出し(VB6のレイトバインディングと同等)を呼び出すからです。

    VB のウォークスルーのコードも、型名を Object に変更すれば、同じように動的呼び出しを実現することができます。VBの場合は、VBコンパイラがVB Binderである NewLateBindingクラスを利用するコードを出力することで実現しています。

    PIAがあれば実行時の型変換の規則がありますので実行速度的には良いでしょうが、DLRを使った動的呼び出しも私には良さそうに思えます。皆さんは、どちらがお好きでしょうか?

    追記:VBではNewLateBindingクラスが活躍しますが、DLRに対する呼び出しなどは IDOBinderクラスとIDOUtilsクラスを使って処理しているようです。C#では、CTPの時と違いMicrosoft.CSharp.dllアセンブリにRuntimeBinderネームスペースが存在しています。

  • Visual Stduio 2010 ベータ1に対応した DSL Tools が公開されています

    Visual Studio 2010 ベータ1が公開されてから、このベータに対応した DSL Tools も公開されました。必要になるものは、VS SDK と DSL SDKの2つになります。

    1. Visual Studio 2010 Beta1 SDK
    2. Visual Studio 2010 DSL SDK Beta1

    今回の DSL Toolsとしては、 Visual Studio SDKとは分離しての提供になっています。ドキュメント類は、上記のコードギャラリより入手することができます。新しい言語用のテンプレートとして、Windows FormsとWPFのMinimal Languageが含まれています。これは、作成したモデルデザイナの描画方法が、Windows FormsはGridを使った表形式、WPFは多分リストボックスを使った一覧形式になっています。つまり、ダイアグラム要素を定義しないモデルデザイナになっています。

    Visual Studio 2008からの移行については、移行ツールが提供されています。この移行ツールはインストールしたフォルダにマイグレーション・ガイドと一緒に含まれています。もしくは、冒頭のコードギャラリのサイトからもダウンロードすることができます。

    PS. .NET Framework 4.0 ベータ1に対応したIronRubyのバイナリが codeplexのサイトで公開されています。

  • IronPython 2.6 CTP for .NET Framework Beta1 が公開されました

    .NET Framework 4.0 ベータ1に同期したCTPが公開されました。これで C#のdynamicキーワードを使って、Pythonスクリプトを実行することができます。

  • Visual Studio 2010 Beta1 がリリースされました

    Visual Studio 2010ベータ1が MSDN サブスクリプション会員向けにリリースされました。Readmeによると ドキュメントはオンラインだけの提供だそうです。
    アナウンスされていましたように F# が含まれています。ドキュメントでは、 Visual F# となっています。含まれているのは、F# のコアだけでパワーパックなどは含まれていません。
    .NET Framework 4.0 What's New には、BigInteger や Tuple といった新しいデータ型が追加されたと記載されています。そして、DLR をサポートするネームスペースである System.Dynamic も追加されています。

    Visual Studio 2010 Beta1 に同期した IronPython のバイナリなどが公開されれば、C# の dynamic キーワードで Python スクリプトなどを試せることでしょう。

    PS. コードギャラリの C# Future や VB Future のサンプルコードがベータ1向けに更新されています。

  • Silverline を試してみました

    昨年のRails Conference 2008 でデモが行われた Ruby on Rails のプラグインである Silverline を試してみました。このプラグインは、RoR 上で Silverlight アプリを生成するためのものです。Web上でのデモも確認することができます。この環境を構築するの使用したのは、以下のものになります。

    1. One Click Ruby Installer  1.8.6-27 RC2
    2. Ruby on Rails 2.0.2
      gem install rails --version 2.0.2 --include-dependencyでインストール
    3. Silverline
    4. Silverline-Demos
    5. sqlite3-ruby 1.2.4
    6. SQLite 3.6.11


    Ruby on Rails をgemでインストールした時に問題になったのが、proxy設定です。これには、「--http-proxy http://<サーバのIPアドレス>:<ポート名>」オプションを指定することで対応しました。必要なモジュールのインストールが完了したという前提で、Silverline-demosに含まれるサンプルを動作させた方法を以下に記述します。

    1. 新規のRailsアプリケーションを作成する
      コマンド「rails new-silverline」を使って新規のrailsアプリケーションを作成します。
    2. 以下はrailsの環境設定です
      config\database.ymlファイル
      development:
        adapter: sqlite3
        encoding: utf-8     # 追加
        database: db/development.sqlite3
        timeout: 5000
      

      config\environment.rbファイル
      
      Rails::Initializer.run do |config|
        #
        :略
        :
        # 以下を追加
        config.action_controller.default_charset='UTF-8'
      end

    3. Silverlineプラグインのインストール
      gitを使ってcloneしたsilverlineフォルダを vendor\pluginsフォルダへコピーします。
    4. app\controlers\client_controller.rbを準備
      silverline-demos\app\controlers\client_controller.rbをコピーします。この理由は、silverlineプラグインがclient_controller.rbをチェックするためです。
    5. app\views\layouts\client.html.erbを準備
      silverline-demos\app\views\layouts\client.html.erb をコピーします。これが、先程のコントローラーに対するビューになります。
      この時点で「ruby script/server」でサーバーを起動して、http://localhost:3000/clientへ アクセスすることで動作を確認することができます。「Show Time」をクリックすることで、時刻を取得した新しいページがレンダリングされると思います。
    6. 私の場合は、public\silverline.html(demosで提供されるindex.html)とpublic\clientaccesspolicy.xmlを準備しました。
      両方ともsilverline-demos\publicにあります。

    ここまでで、本当に基本的なSilverlightアプリケーションが動作しました。UIとしては、HTMLでコードとしてはIronRubyというものです。この動きの延長として、Read Evalute Print Loop(略してrepl、Web上のコンソール)のサンプルとして「TryRuby」があります。このTryRuby環境の作り方を以下を記述します。

    1. 必要なファイルをsilverline-demosからコピーします。
      app\controllers\tryruby_controller.rb
      app\helpers\tryruby_helper.rb
      app\views\layouts\tryruby.html.erb
      app\views\tryruby(_evalute.rb、index.html.erb)
      app\public\images(loading2.gif、loading.net.gif)
      app\public\javascripts(scrolling.js)
      app\public\stylesheets(error.css)
    2. index.html.erbの11行目を以下のように修正します
      シングルクオーテーションが抜けているためです
      <a href="javascript:void(0)" mce_href="javascript:void(0)" id="next" onclick="alert('next')">
    3. config\route.rb に JSON のマップを追加します
      map.connect "tryruby/instructions.:format", :controller => 'tryruby', :action => 'instructions'

      これで http://localhost:3000/tryrubyへアクセスすることで動作を確認
    4. することができます。
    このTryRubyサンプルは、

    起動時にSilverlightからRoRのtryruby\instructionsへリクエストします。そして、返ってきたJSON形式のデータを使って、操作のガイドを表示するようになっています。

    silverline-demos自体を動作させるためには、以下の2つの操作が必要になります。

    1. 「raike db:migrate」コマンドでsqlliteの環境を作成する。
      これは、postsというサンプルがDBを使用するためです。
    2. vender\plugins\silverlineフォルダへ、silverlineプラグインの内容をコピーする。

    他にも動作のおかしな所もありますが、基本的な動作を以上の操作で確認することができます。これが、Ruby on Railsに対するプラグインであるsilverlineの動作になります。

    silverlineが行っている動作は、2つあります。

    1. サーバー側で必要なコンテンツを生成すると同時にclient.xapを生成して、コンテンツをブラウザに返す
    2. クライアント側のlclient.xapに含まれたIronRubyスクリプトを使って、XAMLやJSONへのリクエストをサーバーへ投げること。

    これらの動きを行うために、コントローラーやビュー(サーバー側とSilverlight側)を提供しているのがSilverlineとなります。

  • Tech Days 2009 で使用した F#入門で使用したサンプル

    Tech Days 2009のT3-310 F#入門で使用したサンプルコードを公開させていただきます。このサンプルには、以下のものが含まれています。

    • IntroFSharp:セッション資料に記載したコードを試すためのサンプルです。
    • FSharpFractal3D:セッションで使用していませんが、F#でWPFを使用するサンプルです。
    • FSharpRayTracer:セッションで使用していませんが、Parallel Extensionsを使用したマルチコアにおけるCPU負荷を確認するためのサンプルです。

    ご利用はZIPアーカイブに含まれるReadme.txtに添ってお願いいたします。

    To:セッションにご参加された方達へ
     関数型言語を知っていらっしゃった方には、申し訳ございません。参加された方のほとんどが関数型言語を使っていないということで、説明させていただいた内容が基本的なものになってしまいました。この中で私が強調したのが、関数型言語における肝とは「式(Expression)」であるというものです。このためにF#のコンパイラは、「1.ToString()」と「(1).ToString()」が異なる意味に解釈すると説明しました。前者は、リテラルなので何のメソッドも持っていないとF#コンパイラが解釈した結果としてエラーになりました。後者は「(1)」という式をF#コンパイラが解釈した結果、Int32オブジェクトを式が返した結果としてToStringメソッドを呼び出すことができました。
     それからカリー化に関して、式は入れ子になった関数として表現でき、結果として引数は左に収束し、式は右に収束するという説明をさせていただきました。このことを説明するために「let add1 a b = a + b」は、「let add2 a = fun b -> a + b」という例をお見せしました。この2つの関数をF#コンパイラは、「int -> int -> int」と表現します。この表現を正しく説明するとすると、int型の引数を取りint型を返す関数(int -> int)を戻し、戻した関数にint型の引数を渡すと、int型の戻り値を返すとなります。「->」が、F#におけるラムダ演算子になります。このため「add1 10」という関数呼び出しが、「val it : (int -> int) = <fun:clo@2>」という関数を戻したのです。また、引数が左に収束し、式は右に収束するという仕組みを理解すると、add2関数を「add2 10 2」と記述することが可能になります。これを正確に記述するとすれば、「(add2 10) 2」ということです。括弧内の式が関数を戻し、戻した関数に引数として2を与えているのです。このような特徴が、カリー化によってもたらされていることになります。

    いげた太さんのコメントにもあるように、「1.ToString()」というのは、少し適切じゃないかも知れません。ここで説明したかったのは、()で囲むと式になっているという点です。そしてセッションでお話しましたが、式は必ず値を返す必要があるということです。数学で学んだ式とは、方程式や計算式になると思いますが、これらの式は必ず値(変数を含んでいても)を返しますよね。これが式の1つとして関数があると説明した理由です。従って、何も値を返さないということは、有り得ません。このために空の値として、unit(単一の値)というデータ型が用意されています。「()」という表現が、F#ではunit型になります。

    Posted Friday, January 30, 2009 12:31 PM by shozoa | 4 Comments
    Filed under: ,

    Attachment(s): T3-310.zip
  • Tech Days 2009 Dynamic Language Runtimeで使用したサンプル

    Tech Days 2009のT3-403 Dynamic Language Runtimeで使用したサンプルコードを公開します。このサンプルには、以下のものが含まれています。

    • DynamicCS:C#4.0のDynamicキーワードを使ったサンプルです。このサンプルの実行には、Visual Studio 2010CTPとIronPython v2.0 for VS10 CTP(http://www.codeplex.com/IronPython/)が必要になります。
    • AstTest:DLR 0.9のASTのみを使った Hello Worldサンプルです。
    • ToosSupport:セッションでは使用しませんでしたが、DLR 0.9のホスティングAPIを使ってIronPythonを使ったAPIの使い方のサンプルです。
    • MyCalc:セッションでは使用しませんでしたが、DLR 0.9を使って作成した簡単な四則演算の言語サンプルです。

    ご利用は、自己責任でお願いいたします。

    セッションに参加した方々へのお願いです。
    是非ともDLRやIronPython2.0、IronRubyを試してください。そして試した結果のフィードバックをしてください。開発チームは、皆さんのフィードバックを心待ちにしています。宜しくお願いいたします。

    Posted Thursday, January 29, 2009 4:21 PM by shozoa | 1 Comments
    Filed under: , ,

    Attachment(s): T3-403.zip
  • Tail call 最適化を使った再帰関数呼び出し

    F# で気がついたことをメモ代わりに記述していきます。最初に取り上げるのは、再帰呼び出しの最適化(Tail call 最適化)についてです。Manningのアーリーアクセスプログラムで読める Real World Functinal Programmingの10章に詳しく載っています。F#のインタラクティブシェルで以下のようなコードを入力します。

    let test1 = [1..10000]   // 1万までのリスト
    let test2 = [1..100000]  // 10万までのリスト
    let rec sumList lst =    // 合計を算出する関数
      match lst with
      |[]      -> 0
      |hd::tl  -> hd + (sumList tl)
    ;;
    

    このコードを実行してみます。

    > sumList test1;;
    val it : int = 50005000
    > sumList test2;;
    
    Process is terminated due to StackOverflowException.

    そうすると「sumList test2」を実行したところでスタックオーバーフローが発生して、インタラクティブシェルが異常終了します。このような問題を解決する場合に利用するのが、Tail Call最適化となります。上記のコードがスタックをどのように使っているかを以下に示します。
    TailCall1

    図に示したようにスタックを順番に呼び出して行って、逆に戻ることで結果を返しています。この時に作成できるスタックの個数の制限を超えたために、スタックオーバーフローが発生しています。この制限を回避するには、Tail Callの最適化を利用します。
    TailCall2
    Tail Callの最適化とは、図に示したように順番にスタックを辿って戻っていくのではなく、一気に戻ることです。これを使うメリットは、複数のスタックを使用する必要がなくなることです。F#でTall Call最適化を利用するようにsumList関数を以下のように書きなおします。

    let rec sumList lst total =
      match lst with
      |[]       -> total
      |hd::tl   ->
        let ntotal = hd + total
        sumList tl ntotal
    これで呼び出してみた結果が以下です。
    > sumList test1 0;;
    val it : int = 50005000
    > sumList test2 0;;
    val it : int = 705082704
    今度は問題なく計算することができました。つまりTail Callの最適化が行われたということです。F#では、計算する引数を渡した場合にTail Call最適化が行われるようです。上記で試した関数をListクラスが提供するsumやfold_left関数で試したのが以下です。
    > List.sum test2;;
    System.OverflowException: 算術演算の結果オーバーフローが発生しました。
       場所 .$FSI_0006._main()
    stopped due to error
    > List.fold_left (fun a b -> a  + b) 0 test2;;
    val it : int = 705082704

    この結果を見れば、List.sum関数はTail callの最適化が行われていないということが理解できます。
    いげ太さんのご指摘通り、List.sumは内部でSeq.sumを呼び出しているだけで、Seq.sumはLanguagePrimitives.GenericZero< (^a) >を使って合計を格納するmutableな変数を定義していています。この変数の型がオーバーフローしただけでした。この意味では、List.sumやfold_leftの例は、適切ではありませんでした。Seq.sumは、再帰関数ですらなくイテレータを使って愚直にMoveNextで和を計算しているだけです。これに対してList.fold_leftは、fold_left f s l と定義しており、ローカル関数としてloop s l を再帰関数として定義しています。そして空のリストのときにsを返すようになっているところが、この結果になっています。

    次にF#における名前空間(namespace)とモジュール(module)で気がついた点を記載します。最初にモジュールも名前空間も宣言しないスクリプトをライブラリとしてコンパイルします。こうすると、モジュール名はファイル名(最初の一文字は大文字)となります。作成されたアセンブリを調べるとわかりますが、C#のstatic classの名前がモジュール名になります。
     今度は、スクリプトの先頭に「module 名前」か「module 名前空間.名前」と記載してライブラリとしてコンパイルした場合です。最初の記述は先ほどと同じで、C#のstatic classの名前に相当するものが、moduleで指定した名前になります。後者のパターンは、名前空間は正しく名前空間になり、モジュール名はC#のstatic classの名前に相当します。後者の記述をnamaespaceキーワードを使って記述するには、以下のようになります。

    namespace 名前空間
      module public モジュール名
      実装コード
    


    namespaceキーワードを使用する場合は、moduleにpublicを指定する必要があります。指定しないとprivateになるからです。またnamespaceキーワードは、スクリプトファイルのみに記述できるものとなります。このためインタラクティブシェルでは、記述できませんのでご注意ください。

    PS.いげ太さんのご指摘により、list、seq、prim-typeのソースコードを参照して正しい内容に変更しました。

  • Tech Days 2009 の準備に追われています

    只今、Tech Days 2009の準備に追われています。今回は、T3-403 Dynamic Language Runtime と T3-310 F#入門を担当しています。

    F#に関しては、.NET対応の関数型言語ということでご存じの方もいらしゃると思います。私自身は、関数型に精通しているわけでもないのですが、手続き(命令)型に慣れていると関数型の考え方が理解しにくいかも知れません。何と言っても、Immutableという特徴がありますし、この意味に置いては変数という表現方法が無いと言っても過言ではないと思います。つまり状態(値)を保持する値(領域)に対する操作(たとえば、加算、減算など)が無いということです。四則演算した結果を新しい値(領域)として作成するという特徴を持っています。もちろん値を書き換えるために、mutableな変数も作成できるのですが。もちろん、これだけではなく、何と言ってもExpression(式)を定義できるところに特徴があります。式の中には、演算式、ラムダ式などが含まれています。
    F#を知るための情報源を以下に記載します。
    F# Developer Center
    F# サンプル(MSDN コードギャラリ)
    F# サンプル(コミュニティベース)

    現在試せる2008 Sep CTPである1.9.6.2に合わせて文法などを見るにはコードギャラリのサンプルのサンプル101が便利です。それから、Parallele Extentions 2008 Jun CTPにもF#のサンプルでRaytracerが含まれています。このサンプルは、1.9.6.2よりも前のバージョンで作成されたものです。このサンプルを1.9.6.2に移植するには、以下の手順で操作をします。

    1. Visual Stduio 2008でF#の新しいアプリケーションを作成します。
    2. ソリューションエクスプローラーで、program.fsを削除します。
    3. サンプルのraytracer.fsを追加します(追加する順番が重要です)。
    4. サンプルのharness.fsを追加します(追加する順番が重要です)。
    5. ソリューションエクスプローラで、Parallele Extensionsで提供されるSystem.Threading.dllへの参照を追加します。
    6. System.Drawing.dllとSystem.Windows.Forms.dllへの参照を追加します。
    7. プロジェクトのプロパティでWindows アプリに設定します。
    8. harness.fs の do startStopButton.Click.Addメソッド呼び出し内の「renderTask <- Task.Create(renderLoop, taskManager )」を「renderTask <- Task.Create( (fun o -> renderLoop o ) , taskManager, TaskCreationOptions.None )」に修正します。
    9. ビルドします。

    もし、raytaracer.fsとharness.fsの追加順番を間違えたら、プロジェクトファイルをテキストエディタで開いて、定義している順番を訂正してください。ビルド順序が、raytracer.fs、harness.fsになっている必要があるからです。

    DLR に関しては、昨年に0.9というIronPython 2.0に同期したバージョンが公開されています。そして、ドキュメント類も一緒に公開されています。このバージョンのホスティングモデルもIronPython 2.0 ベータ5と同じになっています。このため以前から作成していた、簡単な言語を作るシリーズのサンプルもベータ5に対応したものが、そのままで動作しています。PDCで公開されたVisual Studio 2010CTPにおけるDLR対応を確認するには、IronPython v2 VS10 CTPが必要になります。 この組み合わせで、C#に追加されたdynamicキーワード使った場合に、C#ようのBinderが使われていることを確認しています。

  • The Root of .NET Framework の書評

    The Root of .NET Frameworkが昨年に出版できてから、徐々に読まれた方の書評が検索して見れるようになってきました。総じて、異端だけどこういうのも有りとか、ワクワクしたというものが多かったです。私の書籍に込めた思いが、読まれた方に届いたようでホッとしています。この書籍は、別の見方をすればCLRハッキング日記というようなものです。帯についている宣伝文句は、決して鵜呑みにしないでください。本書では、問題を探るための手法は解説していますが、解決方法を決めるのは問題解析した結果から判断すべきことですから。
    以下に気がついた書評へのリンクを掲載しておきます、

    .NETのRoot
    The Root of .NET Framework
    複数プログラム言語間相互呼び出しはCOMや.NET Frameworkの新機能ではなくマイクロソフトの根深い伝統であるか!?
    The Root of .NET Framework メモ その2
    宣伝コーナー
    ■The Root of .NET Framework を読んだ

    追記
    カバーを掛けて貰ったよ
    The Root of .NET Frameworkでデジャヴを感じる
    Amazon.co.jp: The Root of .NET Framework: 荒井 省三: 本

More Posts Next page »

© 2009 Microsoft Corporation. All rights reserved. Terms of Use  |  Trademarks  |  Privacy Statement
Microsoft
Page view tracker