2007年 01月 04日

VisualStudio.NET と Ruby を密に連携してエロエロするメモ

一昨日と昨日やったことのまとめ。rubyclr (なんか rubyclr のサイトって title 要素の内容がないんだけど) について。

rubyclr 自体は .NET Framework があれば動くので VS.NET はいらないのですが、UI 開発などの行程で VS.NET が激しく便利なので少なくとも C# Express ぐらいはインストールしといたほうがいいと思います。(rubyclr の開発言語は C++ なので C++ のほうがいいかも)

わかったこと

  • 一旦アプリケーションループに入ると Ruby のスレッドスイッチが入らない (Timer で割り込みかけることで解決)
  • VS.NET で作ったクラスはそのまま rubyclr で読み込める (常に C# などと同時に開発可能)
  • clr なインスタンスに instance_eval かけると死ぬ
  • .NET はセキュリティに厳しい (ruby 関係ない)
  • .NET はスレッドに厳しい (ruby 関係ない)

とりあえず VS.NET で作ったファイルを読み込むには

require 'rubyclr'
reference_file "CompiledAssembly.exe"

とかやると、CompiledAssembly.exe に定義されたクラスが全てインポートされる。(もちろん public なものしかアクセスできない。フォームに配置したコントールはデフォルトで private なので Ruby から参照したいときは書き換える必要あり)

namespace Hogehoge 以下の Unco クラスは Ruby では Hogehoge::Unco で参照できる。

.exe にしてありますが、プロジェクトのプロパティで、出力の形式(デフォルトは Windows アプリケーション) をクラスライブラリにすれば dll になって単体で実行できなくなるみたいです。けど IDE で実行できなくなるのでデバッグし辛い。


Ruby のスレッドスイッチが起きないのは致命的というか、DRb 使えない Ruby とか Ruby で書く意味があんまりない、とまでは言わないけれど結構そんな残念な感じになってしまったので解決法考えた。というかトイレいったら思いついた。

# threadTimer という名前で配置。Interval は 10 で、最初から Enabled を true にしてある。
form.threadTimer.Tick do |sender, arg|
    Thread.pass
end

フォームに Timer を配置して、.NET 側から Ruby に定期的に割り込み入れるようにする。

ただし、この Thread.pass されたスレッドでフォーム内のコントロールを参照すると、参照した時点で謎のエラー (簡素なダイアログ) で落ちてしまう。(Thread.pass する前、つまりタイマー割り込みのイベント内では大丈夫です。)

subtech - MisuzillaSubtechImpl - Windows Forms とスレッド によると安全性のために落ちているらしい。Ruby のスレッドと .NET のスレッドとの関係性がいまいちわからないけれど、おまじない書くと一応大丈夫になる。

reference 'System.Windows.Forms'
System::Windows::Forms::Form.CheckForIllegalCrossThreadCalls = false

true にしといて別の方法で同期とったほうがいいかもしれない。(今回書いていたコードではこのおまじないを使わないで Timer 割り込みで UI を更新するようなコードにした)


あとは rubyclr のサンプルを見ながらやれば特に困ったことにはならなかった。でも exerb でまとめるとどうしてもセグフォるので誰かうまくいったら教えて欲しいです。

どうでもいいけど Ruby でスクリプト書くときも VS.NET を立ち上げっぱなしにしておけばどんなプロパティがあるかわかって便利だった。というか MSDN はどうも読みにくい……MSDN 見てから developer.apple.com 見ると癒される。


そういえば、もともと VB6 を触った事があったのでメソッド名とかはだいたい想像がついて .NET の浅い部分ではそんなに悩んだりはしなかった。

セキュリティシステムあたりがまだ理解できてない。


そんな感じの .NET プログラミング初体験☆でした。別にエロくない。

doc

ドキュメント書くのめんどす。。

publish memo

  1. mac で cd mac; rake publish
  2. win で cd win; rake package
  3. 鯖上で cd win; rake publish
  4. 鯖上で rake publish

IconReset

IconReset はコマンドラインからも書き換えができるので自動化しやすくていい感じ
ただこれを使うということはビルドプロセスが Windows でしか動かないということになるのでちょっとめんどいですね、みたいな

gerry++

itai

2007年 01月 03日

ruby clr, UI 作りに VS.Net のフォームデザイナつかう


できた!(表示させるところまで)
ハマったのはプロジェクトを適当に WindowsApplication1 とかにしていたため、namespace がそれに設定されて、ActiveSupport から uninitialized constant がでたらへん。全部 UserInterface とかって書き換えたらいけた。

さてあとはイベントハンドラの設定方法か。。


あと本筋とは関係ないけど

  • gems で rubyclr を入れてたけど、winforms を見つけてくれないので本家からダウンロードして RUBYLIB 設定するようにした。
  • でもどっちにしろ gems は必要 (AS に依存してる) なんだよね
  • っていうか起動おそいよ><


メモ

  • プロジェクトのプロパティで、出力の形式(デフォルトは Windows アプリケーション) をクラスライブラリにすれば dll になって単体で実行できなくなる
  • でも UI のチェックがしにくくなるから exe のほうがいい気がする

rubyclr フォームの要素にアクセス

デフォルトだと、フォームにおいたやつはそのフォームの private メンバになってしまうようなので、MainForm.Designer.cs を開いて一番したにあるやつを全部 public にしないといけない。

rubyclr スレッド

もしかして ruby のスレッド使えない疑惑
あーいっかい run に入るとスレッドスイッチおこらない。だめだ。ここでゲームオーバー。drb 使えないんじゃ意味がない。

ふと思いついて、Timer を配置して

# threadTimer という名前で配置。Interval は 10 で、最初から Enable を true にしてある。
form.threadTimer.Tick do |sender, arg|
    Thread.pass
end

したらスイッチした。いけるかも。単純なコードしか試してないけど……

あー。謎のエラーで終了することがある。無理かも……

TextBox が溢れてエラーになってただけっぽい。よくわからないけど、標準出力に出すようにしたら大丈夫になった。(時間を永遠表示させてる)

TextBox#Text= の処理の間にスレッドの割り込みが入ってまた TextBox#Text= が呼び出されると、もとのコンテキストに戻ったときにエラーになるのかな。そんな感じがするけどよくわからない

ちがうなぁ。p @form.chkHogehoge って参照しただけでも落ちる。何が悪いんだろう。回避方法あるのかなぁ。スレッド使うのは難しいようだ

rubyclr Timer でスレッドスイッチの問題

エラーでまくり (フォーム内の要素を参照した時点で落ちる)でだめなので原因を考える

  • Ruby インタプリタがびっくりすること
    • Ruby インタプリタだけの場合、スレッドをスイッチするのは Ruby インタプリタだけだけど、この場合 Timer による強制スイッチが入る
  • CLR 側がびっくりすること
    • Ruby インタプリタがスレッドスイッチでごっそりメモリいぢくること?

再現性100%の問題なので、タイミングがどうとかいう話ではない気がする。タイマーの Interval を変えてみても発生するので Ruby インタプリタがビックリしてるわけじゃなさそう。そもそも Ruby の例外は上がらない。

スレッドスイッチさせなければ普通に参照できるため、どっかでそのへんの何かが壊れてる予感。うーん

とりあえず、Ruby からスイッチ (pass) されたスレッド内でフォーム要素を参照しなければ落ちないみたいだ。つまり、本当のフォームの要素とは別に、状態設定用のインスタンス変数を作っておいて、実際のフォーム要素への反映はタイマーから起動されるクロージャ内でやれば大丈夫。
遠回りだけど(そしてこの回避方法で全てがうまくいくかわからない)

自動リロード GUI / Windows 版

http://lab.lowreal.net/trac/changeset/557
とりあえず動いたのでチェックイン

  • メニューのイベント割り当ててない
  • exe 化したい
  • この際多少マジカル☆なのは気にしない

AS と exerb

なんかうまく exe 化できない。AS の core_ext の require あたりがどうもダメみたいだ。core_ext は動的に require の文字列つくってやってるけど、これがだめなのかなぁ。
no such file って言われてしまう。

path に AS の lib ディレクトリを追加して、内部ファイル名のプリフィックス削除した
あとたぶんそれを読み込んでるファイルの書き換えもしないといけない。

gems とすんごい相性悪い。。。
rubygems.rb を偽装して自力で依存解決したファイルつくってエラーでないところまでやった、と思いきやセグフォって (ここは rubyclr) 泣きたい

なんか

無理に単体 exe 化するより rubyw.exe とか全部詰め込んだ方が楽な気がした。あるいは ASR をランタイムとしてインストールしてもらうとか。

Windows が RingServer になれない

てきとうに試してて気付いたけど、うちの環境だとなぜか Windows だけ RingServer になれない。直接 new_with_uri すればちゃんと接続できるけど、UDP 送って発見できないようだ。なんでだろう。

Windows 上の RingServer が見つからないのは rubyclr から起動した時だけっぽい。シンプルなやつは普通に発見できる。そもそもローカルからさえ見つけられない。

exe 化できない

rubyclr/core.rb でセグフォる。うーん。
依存ファイル全部もってきて、隔離した環境(になってるかわからないけど)にして実行すると

ハンドルされていない例外: System.IO.FileLoadException: ファイルまたはアセンブリ
'Runtime, Version=0.1.0.0, Culture=neutral, PublicKeyToken=null'、またはその依存
関係の 1 つが読み込めませんでした。要求された最小限のアクセス許可を与えることが
できませんでした。 (HRESULT からの例外: 0x80131417)
ファイル名 'Runtime, Version=0.1.0.0, Culture=neutral, PublicKeyToken=null' です
。 ---> System.Security.Policy.PolicyException: 必要なアクセス許可を取得できませ
ん。
場所 System.Security.SecurityManager.ResolvePolicy(Evidence evidence, Permiss
ionSet reqdPset, PermissionSet optPset, PermissionSet denyPset, PermissionSet& d
enied, Boolean checkExecutionPermission)
場所 System.Security.SecurityManager.ResolvePolicy(Evidence evidence, Permiss
ionSet reqdPset, PermissionSet optPset, PermissionSet denyPset, PermissionSet& d
enied, Int32& securitySpecialFlags, Boolean checkExecutionPermission)

とかいわれる。信頼できないコードになってる? 同じファイルなんだけどなぁ。Ruby みたいにディレクトリさかのぼって書き込み権限見てるとかそういうのなんかなぁ。

このエラーはセグフォとは無関係。ディレクトリ変えたら動いた。ネットワークドライブ上では実行できない?ようだ

諦めよう

いい方法思いつくまで保留

rubyclr

ruby 側でハンドルするまでもない処理 (this.Close(); だけとか)を C# で書いちゃえるのは素晴らしいなぁ。

.net のセキュリティ機構

やばい意味わからない。
例えば

System.Diagnostics.Process.Start('http://lowreal.net/'); 

はセキュリティエラーで例外になってしまう。

あ−

GUI いらないな

ライブラリとインタプリタをまとめただけ

http://lab.lowreal.net/trac/changeset/564
CUI バージョンのほうが罠がなくてよさそうだ……ファイルサイズ小さい (800KB) し(例えば gems とかを含まないからね!)
exerb は今のところアイコンに最大 8bit のものしか指定できない。ので作った後 IconReset とかいうので設定しなおしてみた。

2007年 01月 02日

タイトルなんていらない

なんかすんごい何度も書いてる気がするけど、モチベーションの維持が一番難しいのだ。だから自分はモチベーションがあるときは失わないように必死だし、モチベーションがないときはどうすればモチベーションがでるか考えている。ただこれは実際「自分の中で完結する解決方法」でしかない。外部的要因によって強制的にスイッチが切られたら終わりなのだ。そう都合良くいかない。解ってる。解ってる。

表現だけを考えている。伝えるのに一番いい方法があるはずだ。まんぞくして自信がついたら終わりだ。それを持ってなおかつ相反する何かを持ち続けられるほどメモリをつんでいない。

例えばコンピュータは、こちらの表現を正確に受け取る。コンピュータは誤解はしない。でもコンピュータはソースコードの美しさを評価しない。


相手の望んでいることを考えるのが心底嫌いだ。


友人 (方向が違うのだけど、その方向をおれは取り入れていきたい) と文章がどうとかいう話をした。よくわからない。必要以上に導きたくない。想像のためのきっかけを作りたいと思う。後に残らないものを作りたいとは思わない。でも入り込みやすい要素は必要だと思う。バランスが掴めない。あるいは距離感なのかな。必要最低限でいい。特に論理的に導きだせるなら答えを言う必要はない。みんな俺より頭いいんだから、それぐらいできるはずだ。自分はそれをする気になるまでの導入を作れるようにならないといけない。


何周して戻ってくる。二週目? 眠い。

今まで「眠い」で終わる日記を何回かいたんだろう。

CSS Bullets


オートリロードツールの GUI 作った。Cocoa なやつは一応完成? 既に RingServer があればそれを使う。ないなら自分が鯖になる。
実装がキモいのだけどいまいちいい方法が浮かばない。リロード対象を決め打ちしてるあたりがアレ。

あと rubyclr 使って win 版を作りたいけどめんどい (環境つくってない)

Ring reload

ネットワークまたぐときは Hamachi 使えばいいだけか。。あれ前はどうしたんだっけ。
でもサーバーになるホストを微妙に選ぶみたいだなぁ。OS X からだと Hamachi 先の RingServer を見つけてくれない。
あとちょっとまえにごろうさんがハマってた DNS の問題があるからちょっとめんどくなってしまう。うーん。常に IP を渡すようにしないとだめだな

rubyclr

実行可能な形にするとなると exerb とかしないといけないのがめんどいよなぁ。Ruby/Cocoa なら Ruby/Cocoa インストールすればいいってだけなんだけど(インストーラ親切だし)

っていうか rubyclr って GUI のビルダーない(ソース直書き)じゃんgtlt. なんか簡単なほうほうないかなぁ。Interface Builder ってすげなぁ。

そんな難しいの作る訳じゃないんだから、ってのはもっともだ><

rubyclr instance_eval

instance_eval すると死ぬね!!! 何回も同じ変数名書くのやだからホゲってみたけどだめだった。

rubyclr exe ファイルを UI に?

なんかサンプル読んでたら、RssReader のやつで exe ファイルを UI として読み込んでるみたいなことしてる。
VS.Net Express ダウンコして(してあるけど)フォームつくって(つくってあるけど)、ビルドしたら使えるのかな

exe を UI に

ようは中間言語にコンパイル済みのを読み込んでクラス生成してるだけ、でいいのかな。C# のコードと Ruby のコードは普通に共存できるんだなぁ。っていうか当たり前か、そういうものなのだから……
RssReader の例だとアセンブリ名 UserInterface でその下のいろいろクラスが作ってあるから、これを Ruby 側で読み込んであげるとそれが使えるようになるみたいだ。まだちゃんと試してないけど

require 'winforms'

reference_file "UserInterface.exe"

WinFormApp.run UserInterface::MainForm.new # MainForm という C# クラスが .exe にある