Visual Studio 2010 ベータ2に含まれているWPF 4でマルチタッチのアプリを作ろうとして、ベータ1との違いを調べていました。ローカルドキュメントには含まれていませんが、MSDNライブラリにウォークスルーが公開されています。
一番大きく変わったのが、FrameworkElementに記述していた「ManipulationMode="All"」が無くなって、「IsManipulationEnabled="True"」に変更になったことです。この変更に伴って、「ManipulationStartingイベント」でManipulationContainerを設定する必要があります。ManipulationModeは、ManipulationStartingEventArgs.Modeプロパティで設定するようになっています。この値のデフォルトが「All」になっています。
そしてウォークスルーでは、InertiaStartingのイベントハンドラでTranslationBehavior、ExpansionBehavior、RotationBehaiviorを設定しています。このBehaiviorに、移動量を設定しています。また、ManipulationDeltaイベントハンドラの最後にあるif文である「if (e.IsInertial && !containingRect.Contains(shapeBounds))」で境界を越えた時点で動作を終了するようになっています。この境界における制御なのですが、InertiaProcessorの場合はボーダーとエラスティック・マージンという考え方があるのですが、この考え方の適用方法はまだ調べきれていません。
これ以外には、WM_Touchイベントも処理できるようになっています。
MSDN会員向けに Visual Stduio 2010 ベータ2 日本語版が公開されました。英語版のベータ2から入れ替える場合の注意点を以下に記載します。
- Visual Studio 2010 Beta2 English のアンインストール
- Visual Studio 2010 Tools for Office Runtime Beta2 のアンインストール
- Microsoft .NET Framework 4 Extended Beta2のアンインストール
- Microsoft .NET Framework 4 Client Profile Beta2のアンインストール
- Microsoft Visual C++ Redistributable - x86 9.0.30729.4148のアンインストール
をしてからインストールします。気がついた注意点としては、Visual Studio 2010 Beta2のアンインストールが終了してからも、しばらくはsetup.exeが動き続けている点です。しばらく待てば、setuo.exeが終了しますので、それ以降のアンインストールを行うことができます。
また、日本語版ベータ2には、機械翻訳のドキュメントが付属しています。
Visual Studio 2010ベータ1のアンインストールは、ダウンロードページにある Instruction欄にあるHereからインストールに関するReadmeをダウンロードできますので、この手順に従った方が良いようです。簡単に手順を記載すると以下のようになります。
- Microsoft Team Foundation Server 2010 Object Modelのアンインストール
- Microsoft Visual Studio 2010 Team Suite Beta1のアンインストール
私の場合は、残っているVS2010関係をアンインストールしました
- Microsoft .NET Framework 4.0 Beta1のアンインストール
Language Pack、Extended、Client Profile
- Microsoft Visual C++ 2010関係のアンインストール
- Beta2のインストール
追記:ベータ2英語版のアンインストールの最後にsetup.exeがなかなか終了しないのは、環境に左右される模様です。もしかすると、インストールのフィードバック送信か何かで時間がかかる場合(未確認です)があるのかもしれません。
追記:ベータ2をアンインストールされる場合は、最初にVisual Studio ToolsからHelp Setting を使ってコンテンツを削除しないとヘルプコンテンツはHDD内に残りますのでご注意ください。
TechEd 2009のMVPラウンジでお見せしたSilverlight 3のマルチタッチ対応のピアノのデモですが、その後の調査で作成したデモのバグのためにSilverlight3ランタイムがハングアップしたようになっていたことが判明しました。この件は、多くの方が陥りそうな気がするので自戒の意味も込めて、状況を以下に記載します。
- Silverlight3でマルチタッチを使用するには、System.Windows.Input.Touchクラスを使用します。
具体的には、「Touch.FrameReported += Touch_FrameReported」のようにイベントハンドラを登録します。イベントハンドラは、「void Touch_FrameReported(object sebder, TouchFrameEventArgs e)」というシグネチャを持ちます。
- イベントハンドラ内では、タッチポイントコレクションを取得します。
「TouchPointCollection touchPoints e.GetTouchPoints(FrameworkElement)」メソッドでタッチポイントコレクションを取得します。タッチポイントコレクションには、複数のタッチされたポイントで構成されています。
- タッチポイントコレクションからタッチポイントを取り出して処理します。
「foreach (TouchPoint tp in touchPoints)」のようにタッチポイントを列挙します。タッチポイントには、ActionプロパティとTouchDeviceプロパティなどがあります。
- Actionプロパティによってタッチ動作を識別します。
TouchAction.Down、Move、Upという列挙値とActionプロパティを比較することでタッチ動作を記述します。タッチの動作の基本は、ダウン->移動->アップ(移動が無い場合もあります)になります。
- TouchDeviceプロパティによって、タッチされた場所を識別します。
TouchDevice.Idにタッチした場所の識別子が格納されています。つまり、ダウン->移動->アップが一回のタッチ動作であれば同じ識別子になります。複数(マルチ)のタッチであれば、TouchDevice.Idが異なるので一致するId毎にActionプロパティと組み合わせてタッチ動作を記述します。言い換えると、タッチ動作がダウン->移動->アップですからダウン時にTouchDevice.Idと座標を記録して、移動やアップ時に記録されたIdを取り出してタッチ動作にするのです。
- 私のサンプルで問題があった個所は、MouseLeftButtonDownイベントとタッチイベントの両方をハンドリングしていたことでした。これで何が問題になったかというと、軽いタッチの場合はOSというかブラウザがタッチダウンをMouseLeftButtonDownイベントに変換してしまうので、タッチダウンイベントを処理できないというものでした。
if tp.Action == TouchAction.Down)
{
Path hitKey = null;
// タッチされた場所の最上位のFrameworkElementを取得
FrameworkElement hitElement =
tp.TouchDevice.DirectOver as FrameworkElement;
// これが対処したコード
while (hitElement != null)
{
if (hitElement is Path)
{ hitKey = hitElement as Path;
Breake;
}
// Visual Tree を上へたどる
hitElement = hitElement.Parent as FrameworkElement;
}
if (hitKey != null) タッチダウンの動作を記述
}
上記のwhile文が対処コードです。このサンプルでは、Pathオブジェクトにタッチした場合に動作を作り込んでいます。while文の中で「hitElement = hitElement.Parent as FrameworkElement」とすることでVisual Treeを最上位までたどりnullでwhile文を抜けます。このwhile文を入れたことで、タッチダウンがMouseLeftButtonDownイベントに変換されなくなりました。
この動作はSilverlight3のマルチタッチ対応において仕様ともとれるものです。タッチデバイスでのタッチダウンイベントは、マウスのボタンダウンイベントと同様に扱うこともできるからです。
Windows 7もRTMしまして、マルチタッチに対応しているPCが幾つかあります。具体的には、以下のようなPCです。
- HP Touch Smart IQ800 や IQ500
- HP Touch Smart tx2 ノートブック
- DELL Latitude XT2 や XT
Microsoftで行うデモで良く見かけるのが、IQ800です。個人で所有するとなると、ノートブックになると思います。このように記述している私もHP Touch Smart tx2を購入しました。現在は、Windows 7 RTMをインストールして、マルチタッチ対応のプログラムを開発するのに使っています。マルチタッチ対応のプログラムの実行環境としては、以下のような方法があります。
- Silverlight 3
- .NET Framework 3.5
- WPF4(.NET Framework 4.0ベータ1)
- Win32 APIを使ったネイティブアプリケーション
Silverlight 3で開発するには、System.Windows.Input.Touchクラスを使用します。.NET Framework 3.5(WPFかWindows Forms)の場合は、Windows 7 Multitouch .NET Interop Sample Libraryを使用するのが簡単です。このライブラリの場合は、Win32 APIをラップしたヘルパークラスが提供されています。WPF4では、FrameworkElementにTouchイベントハンドラを記述できるようになっています。が、IDEで自動的には記述できませんのでXAMLに記述する必要があります。
Windows Touchを理解するには、ManipulationとInertiaを理解するのが良いでしょう。WM_GESTUREやWM_TOUCHメッセージは低レベルなものなので、自分で動作を作りこむ必要があります。これに対して、ManipulationやInertiaでは、複数のタッチによる振る舞いを処理するための仕組みが用意されているからです。デモなどで見かける、写真の移動や回転、ズームなどがManipulationで実現されているのです。
Silverlight3のWindows Touch対応は、WM_TOUCHメッセージのみの対応となります。従って、タッチによる振る舞いは、コードで作りこむ必要があります。
HP Touch Smart tx2を使っていて気がついた点は、マルチタッチのドライバーの動作が不安定だということです。色々と調べていくと、ACアダプタを抜いてバッテリモードで試すと安定していることに気がつきました。私が使用しているマルチタッチのドライバーでは、静電式パネルのタッチポイントの認識時に電源周りの磁界が影響するようです。この点を気をつけるようになってから、安定してマルチタッチを試すことができるようになりました。
TechEd 2009 横浜で「Deep Dive to .NET Framework CLR」というセッションを担当します。この資料を作成しています。このセッションは、私の著書である「The Root of .NET Framework」という書籍をモチーフにして、資料を構成しています。どのような内容が良いか、悩みながら作成を行っています。その途中経過を以下に引用します。
PEヘッダーから CLIヘッダーのアドレスを見つけて
CLI ヘッダーを読み解くのが、上記のスライドです。ここまで来るとメタデータが、どのように格納されているかを知りたくなることでしょう。それらも作成しているのですが、どこまで作るかが難しいところです。
これ以外にも、GCやCLRのホストインターフェース、RCWなどとアイディアがあるのですが、如何とも時間の制約がありますので作成したスライドを全部、説明しきれないかもしれません。現時点で30スライド程度が出来上がっていて、ここから SOSデバッガ拡張や.NET Framework 4.0の話を入れようと思っていますので。まだまだ、悩みは尽きません。公開されているセッションレベルは400番台なので、最低でも16進数ダンプの見方を知っている方が対象になると思います。
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 アプリケーション 徹底攻略
作者:伊藤 真二
発売日:2009/4/23
メディア:単行本(ソフトカバー)
経験値をあげて高品質な業務アプリを作ろう
内容は、以下のような構成になっています。
- 導入
- .NET Frameworkアプリケーション設計
- チューニング
- リリース管理/セキュリティ
- COMアプリケーション連携
- 新しい.NET Framework/Silverlight
いわゆるTips系やリファレンス系とは異なる書籍で、.NET Frameworkを使ったプログラミングの定石というか王道を語っています。たとえば、Windows Formsではメインスレッドでしかコントロールを操作することができないため、デリゲートを使ってマルチスレッド プログラミングをするとか、FormのLoadイベントは、初期化処理には向かない。なぜなら、何度も呼び出される場合があるからなどです。
この意味で実際に開発して経験したノウハウを形式知化することで、ハマらないアプリケーション開発=高品質な業務アプリを作れるようにすることを目指した書籍です。中でもChapter4「チューニング」は約40ページあり、著者の論理的思考を裏付けるためのノウハウで満載です。チューニングは、先入観よりプロファイリングした結果に基づいてボトルネックに対処すべしというものです。試行錯誤するよりも、プロファイラで事実を把握することが、チューニングの王道であり、早道だということを丁寧に書かれています。
この意味で業務アプリケーションの実装方法を検討される方には、メリットがあるかと思います。もっとも、経験を積まれた方には、周知のことが多すぎるかも知れません。が、この経験した知識を形式知化したところにこそ本書の意義があると言えるでしょう。
PS.かなり前に読み終わったのですが、書評をまとめる時間がとれませんでした。お陰で、もう一度、査読することができました。
前回にご紹介した Railsカンファレンスの IronRuby on Railsセッションで、Rails以外のWebフレームワークとしてrackが紹介されていました。rackは、Ruby向けの Webインターフェースを提供するフレームワークで、Ruby on Rails 2.3.x系も内部でrackと統合されています。このrackをIronRubyを使って、IISで動かしてみました。その動かし方を以下に記載します。
1.必要なもの
2.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.ブラウザでテストします
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サーバーには対応していません。
昨年の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のページが表示されることでしょう。
- 「about your application's environment」をクリックします。
正常に実行されれば実行環境が上記のように表示されます。が、この情報はRailsのInfoControllerが処理する関係で、IronRubyとは表示されることはありません。あくまでも、Ruby 1.8系として表示されるだけです。
- 次に「http://localhost:3000/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へのパッチを投入するファイル名を追加しました。
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 Studio 2010 ベータ1が公開されてから、このベータに対応した DSL Tools も公開されました。必要になるものは、VS SDK と DSL SDKの2つになります。
- Visual Studio 2010 Beta1 SDK
- 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のサイトで公開されています。
.NET Framework 4.0 ベータ1に同期したCTPが公開されました。これで C#のdynamicキーワードを使って、Pythonスクリプトを実行することができます。
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向けに更新されています。
昨年のRails Conference 2008 でデモが行われた Ruby on Rails のプラグインである Silverline を試してみました。このプラグインは、RoR 上で Silverlight アプリを生成するためのものです。Web上でのデモも確認することができます。この環境を構築するの使用したのは、以下のものになります。
- One Click Ruby Installer 1.8.6-27 RC2
- Ruby on Rails 2.0.2
gem install rails --version 2.0.2 --include-dependencyでインストール
- Silverline
- Silverline-Demos
- sqlite3-ruby 1.2.4
- SQLite 3.6.11
Ruby on Rails をgemでインストールした時に問題になったのが、proxy設定です。これには、「--http-proxy http://<サーバのIPアドレス>:<ポート名>」オプションを指定することで対応しました。必要なモジュールのインストールが完了したという前提で、Silverline-demosに含まれるサンプルを動作させた方法を以下に記述します。
- 新規のRailsアプリケーションを作成する
コマンド「rails new-silverline」を使って新規のrailsアプリケーションを作成します。
- 以下は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
- Silverlineプラグインのインストール
gitを使ってcloneしたsilverlineフォルダを vendor\pluginsフォルダへコピーします。
- app\controlers\client_controller.rbを準備
silverline-demos\app\controlers\client_controller.rbをコピーします。この理由は、silverlineプラグインがclient_controller.rbをチェックするためです。
- app\views\layouts\client.html.erbを準備
silverline-demos\app\views\layouts\client.html.erb をコピーします。これが、先程のコントローラーに対するビューになります。
この時点で「ruby script/server」でサーバーを起動して、http://localhost:3000/clientへ アクセスすることで動作を確認することができます。「Show Time」をクリックすることで、時刻を取得した新しいページがレンダリングされると思います。
- 私の場合は、public\silverline.html(demosで提供されるindex.html)とpublic\clientaccesspolicy.xmlを準備しました。
両方ともsilverline-demos\publicにあります。
ここまでで、本当に基本的なSilverlightアプリケーションが動作しました。UIとしては、HTMLでコードとしてはIronRubyというものです。この動きの延長として、Read Evalute Print Loop(略してrepl、Web上のコンソール)のサンプルとして「TryRuby」があります。このTryRuby環境の作り方を以下を記述します。
- 必要なファイルを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)
- index.html.erbの11行目を以下のように修正します
シングルクオーテーションが抜けているためです
<a href="javascript:void(0)" mce_href="javascript:void(0)" id="next" onclick="alert('next')">
config\route.rb に JSON のマップを追加します
map.connect "tryruby/instructions.:format",
:controller => 'tryruby', :action => 'instructions'
これで http://localhost:3000/tryrubyへアクセスすることで動作を確認
することができます。
このTryRubyサンプルは、
起動時にSilverlightからRoRのtryruby\instructionsへリクエストします。そして、返ってきたJSON形式のデータを使って、操作のガイドを表示するようになっています。
silverline-demos自体を動作させるためには、以下の2つの操作が必要になります。
- 「raike db:migrate」コマンドでsqlliteの環境を作成する。
これは、postsというサンプルがDBを使用するためです。
- vender\plugins\silverlineフォルダへ、silverlineプラグインの内容をコピーする。
他にも動作のおかしな所もありますが、基本的な動作を以上の操作で確認することができます。これが、Ruby on Railsに対するプラグインであるsilverlineの動作になります。
silverlineが行っている動作は、2つあります。
- サーバー側で必要なコンテンツを生成すると同時にclient.xapを生成して、コンテンツをブラウザに返す
- クライアント側のlclient.xapに含まれたIronRubyスクリプトを使って、XAMLやJSONへのリクエストをサーバーへ投げること。
これらの動きを行うために、コントローラーやビュー(サーバー側とSilverlight側)を提供しているのがSilverlineとなります。
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型になります。