UNIX USER 2003/10

窓辺でRuby − ExerbとVisualuRuby計画(仮称)を使ったWindows利用法

この項は、ソフトバンクパブリッシングのUNIX USER誌 2003/10月号に掲載された記事の原稿(校正前)を、2004/06〜7月に改変・公開したものです。
(そのため、古い情報が混じっていたらごめんなさい。)→混じってました。2004/10/28修正

もくじ

はじめに

皆さんは、人から「ちょっとしたプログラム」の作成を頼まれたりしないでしょうか。例えば、「毎日、○○なテキストフォーマットでメールが来るんだけど、Excelで楽に吸い込めないかなあ」とか、「どこそこのWebの様子を楽に確認できる方法作れない?」とか、そう言ったような話です。まあ要は楽をしたいという要望がもっぱらなのでしょうが、相手が、上司、客先、友人、彼氏、彼女、配偶者などで、無視するわけにも行かない場合も多く(逆に無視できない相手が頼んで来ますね)、結局何とかしなくてはなりません。そして頼んできた相手が使っているのは、大体Windowsで、ややこしいのをインストールするのは嫌(会社関係の場合、許可されないことも)、と言われてcygwinや他のツールを導入できなかったりします。

Windowsに詳しければいいのですが、もしWindowsへのなじみが薄かったらどうするか。今回はスクリプト言語Rubyを使い、Windows向けアプリケーションを作る方法を書いていくことにします。あらかじめ、言語としてのRubyについては知識があると想定させていただきます。

Ruby そしてExerb、VisualuRuby計画(仮称)

まず、何はともあれRubyですが、これはもう、紹介するまでも無いでしょう。詳しくは総本山(?)であるホームページ「オブジェクト指向スクリプト言語Ruby」(http://www.ruby-lang.org/ja/)をご覧下さい。簡単にご紹介しますと、Rubyとはまつもとゆきひろ氏によって開発された、手軽に楽しくオブジェクト指向出来る言語です。また、強力なライブラリが標準添付、または公開されており、膨大な処理が簡潔に書けるという特徴もあります。本誌2002年2月号にはRubyの特集がありました。

このRubyで書かれたスクリプトをWindowsの実行形式にするのが、Yuya氏によるExerbです。Exerbは、Rubyのスクリプトを実行形式のEXEファイルに変換します。ツールの基幹部分がrubyスクリプトを使って書かれているため、Windows以外のプラットフォームでも実行可能です。不特定多数への配布はライセンスなどに気を付けなければなりませんが、このExerbを使って、一般Windowsユーザ向けのフリーウエア作成をしている方もいらっしゃいます。

そしてWindowsならやはりGUIを使いたくなるということで、拙作のVisualuRuby計画(仮称)を使ったGUI付きのスクリプトのご紹介もしていきます。また、WindowsのAPIを知らないと書けないけれども、あると便利なライブラリも適宜紹介します。

では実際に、適当な例を取って、スクリプトを作成していきましょう。おっとその前に、自分自身の環境へのRubyその他のインストールが必要です。開発用のWindowsマシンにも入れておきましょう。

おことわり

本稿は筆者の持つ環境の都合により、Windows98SE上のRuby-1.8.0をベースに書かれています。Windows2000でもスクリプトの動作検証などはしていますが、記述の足らない点があるかもしれません。Windowsのバージョンに関してはかなり古くて申し訳ないのですが、どうかご容赦をお願いします。

ExerbによるWindows用実行形式(EXEファイル)作成はUNIX上でもできますが、本稿ではWindows上の操作を書いていきます。まあ、MS-DOSプロンプトからの操作なので、UNIXでの操作はすぐに想像が付くと思います。

Rubyのインストール

まずはrubyのインストールですが、これは当然お使いのOSによって方法が異なります。わざわざ私がここで書かなくてもRubyのホームページから辿れるところに「インストールガイド」(http://www.ruby-lang.org/ja/install.cgi?cmd=view;name=top)というのがありますので、そちらをご覧頂いた方が良いとは思います。Windows上のRubyには幾つか種類 <コラムへの参照> がありますが、ここでは、インストールの一番簡単なActiveScriptRubyをWindowsマシンにインストールすることにします。

ActiveScriptRubyのインストール

ActiveScriptRubyはarton氏のサイト(http://www.geocities.co.jp/SiliconValley-PaloAlto/9251/ruby/)からダウンロードできます。2004/07/06時点ではバージョンが1.8.1.2です。左から3つの数字はRuby本体のバージョンに相当している(少なくとも今までは)ので、このActiveScriptRubyは、ruby-1.8.1に相当します。ダウンロードページに移って、新しいActiveScriptRubyを入手してください。拡張子がmsiですが、これはWindows Installer用の形式です。Windows98/NT4以前ではコンポーネントのインストールが必要ですが、これはダウンロードページの指示に従ってください。Microsoft Wordなどをインストールした際に一緒に導入済みになっていることも多いです。Windows Installerがインストールされていれば、ダウンロードしたmsiファイルをダブルクリックすることでインストールが始まります。 普通のインストーラですので、画面の指示に従ってください。それでインストールは終わりです。インストールが終われば、スクリプトの開発に移って行きましょう。(ruby.exeのあるディレクトリにパスは通しておいてください)

Windows上でのRubyスクリプト開発のための環境がいくつか開発されています。代表的なものはRDE(http://homepage2.nifty.com/sakazuki/rde.html)やRubyWin(http://homepage1.nifty.com/markey/ruby/rubywin/)、Apollo(http://www.moriq.com/apollo/: Delphiのランタイムが別に必要)などでしょうか。海外ではSciTE(http://www.scintilla.org/SciTE.html)など他のものも出ていますが、Unicode以外の漢字コードを扱えないことが多いです。

Windows上のrubyの種類

この記事(web公開版)で使うrubyはActiveScriptRubyのバージョン1.8.1.2ですが、Windows上でのrubyには大きく分けて2種類があり、お互いに得失があります。 一つは、mswin32版rubyのグループです。この記事で使ったActiveScriptRubyや、その他でもMingw版、lcc版、bccwin32版なども、このカテゴリに入ります。それぞれの名前はActiveScriptRuby以外は利用するコンパイラに基づいて名付けられています。これらではUNIXの機能に依存した機能と標準ライブラリが動作しない欠点があります。またActiveScriptRubyは少し毛色が違っていて、RubyインタプリタをWindowsのActiveScriptingホストとして使えるようにします。そしてVisualBasicのプログラムやInternetExplorerのwebページからrubyインタプリタを呼び出せるようになります。 もう一つはcygwin版です。利用しているcygwin(http://www.cygwin.com)がLinuxの環境の模擬を目指しているため、cygwinの流儀に従って利用する場合は他のUNIX系のrubyとほぼ同様に使えます。これが利点です。逆にcygwinのバグその他が関与してくる可能性があります。さらに実行時にcygwin1.dllを必要とし、cygwinの環境をうまく設定する必要があるかもしれません。環境設定の例としては、ファイルシステムのテキストマウントとバイナリマウントの違いがあります。これは時々思いもかけない不具合をもたらします。特にスクリプトを多数者に配布する場合は、相手先の環境がどうなっているかわかりません。

Windows上でのRubyとExerbを使ったexeの作成

スクリプトの作成

ここで例となるスクリプトを作成してみます。お題は、デジカメ写真のサムネイル印刷用HTMLの作成です。実はちょうどこの記事を書いているときに要り用だったもので...

これは、画像ファイル群を与えると、その画像をHTMLのTABLEタグを使って配置し、HTMLを出力します。そのHTMLファイルをInternetExplorerやNetscapeなどの印刷機能を使えば紙に出力されたサムネイルを得ることができるというものです。Windowsだとフリーウエアを探しても似たようなものがたくさん手に入りますが、スクリプト書きに慣れてさえいれば、いちいち探して試しているよりは作っちゃったほうが早いことも多いです。

仕様

まずは何を入力にして何を出力にするかを決めねばなりませんが、目的に従い、

[入力]
・画像の入ったディレクトリのPathをコマンドラインから引数として渡す
画像ファイルはそのディレクトリに入っているJPEGファイルすべて。
ディレクトリを再帰的には読まない。
サムネイルのタイトルは、ディレクトリの名前を利用
・HTMLのフォーマットはスクリプト内に作り込む
[出力]
・印刷するべきHTMLファイル
ファイルはデスクトップに作成し、ファイル名はタイトル+".html"
印刷は自動では行わない。InternetExplorer上のユーザ操作に任せる
とします。印刷まで自動で行わせることも出来ますが、今回はその部分はパスしておきます。

実装

そしてこれを実装したのがthumb.rbです。Rubyのパワーのおかげで単純なスクリプトになっていますが、それでもWindowsならではの問題が出てきます。

Windows上での問題点

パス区切り文字

Windowsではパスの区切り文字として使われるのは"\"ですが、UNIXでは"/"です。パス区切り文字の問題が出る所としては、DirクラスやFileクラスのメソッドがあります。標準添付ライブラリではパス区切り文字を"/"として問題が無いように作られているので、Rubyを使う限りはパス区切り文字を"/"に統一したほうが良さそうに思えます。ただしそれは、Rubyインタプリタの組み込みクラスの世界に留まっていられればの話であって、system()などで外部コマンドを呼び出したり、ruby標準添付のWin32API拡張ライブラリを使ってWindowsのAPIを直接利用したり、その他のライブラリの利用を始めたりすると、このパス区切りの問題には時々悩まされることになるでしょう。InternetExplorerはパス区切り文字として"/"を使えるので今回は"\"が使われてそうなところでは毎回"/"に変換しています。to_slashpath()という関数(メソッド)がその機能を担っています。この変換をさぼると、Dir.glob()がちゃんと動かなくなりました。

8.3形式のファイル名

Windows9xシリーズのOSでは、いにしえより伝わる8.3形式(ショートファイルネーム)の問題があります。WindowsのExplorerの機能であるショートカット(左下に矢印のマークの付いたアイコンで表示されてるアレです)にファイルをドロップすると、Win98以前ではそのファイル名は8.3形式で渡されてしまいます。そうでなくても手で入力するのが面倒な時などにワザと8.3形式で入力することもあるでしょう。そのため場合によっては、8.3形式のファイル名を通常のロングファイルネームに変換する必要があります。

この変換にはWindowsのAPIを利用する必要がありますが、この機能を実装したものとして、lfn.rbを挙げておきます。この中では関数が2箇所で定義されていますが、これはGetLongPathName()という便利なAPIがWin98以降でしか定義されていないためで、念のためにWin95用の方法も実装しています。

ちなみに、GetLongPathName()はパス区切り文字として"/"を利用可能ですが、変換のついでに"/"を"\"に置換して返してくるようです。まるで2つの世界のせめぎ合いのようですが、時々こういうことがありますので、どうかご注意を。

ワイルドカード周り

ファイルシステム周りとしては、Dirクラスで利用するワイルドカードがDOSのものと違っているのでWindowsの知識のある方は逆に引っかかることがあるかもしれません。Dir.glob,Dir[]などで使えるワイルドカードは、WindowsやDOSで使うワイルドカードではありません。Rubyのドキュメントを参照してください。UNIXでシェルを使いこなしてきた方にはなじみのある仕様だと思います。

Rubyのワイルドカードでは大文字と小文字は区別されます。しかしWindowsではファイルシステムが大文字小文字を特に区別しないので、今回のようにパターンでケースを無視するか、Dir.glob()の第2引数に File::FNM_CASEFOLD を指定してケースを無視させる必要があります。そうしないとマッチして欲しいファイルがマッチしない原因になります。なぜかWindows(explorerかも?)が時々ファイル名の大文字小文字を勝手に替えてしまうことがありますので注意が必要です。以下に拡張子が".jpg"であるファイルの一覧を得る記述例を書きます。

Dir.glob()の仕様は時々変化しているようです。

行末とファイル終端

また、行末を示すコードのCRLFとLFの違いも気にしておいてください。これは組み込み変数の$\や$/の設定でうまくごまかせることもありますし、ファイルを開く時に常にバイナリモードで開けばOKという場合もあります。どういう使い方をするかで対処法も変わるので一般論はありません。

他にファイル終端の問題があります。今回のthumb.rbの実装では明示的にバイナリモードでの読み取りを指定しなかった場合、画像ファイル内の0x1A(CTRL-Z,EOF)で読み込みが終了してしまいました。これはMS-DOSのさらに前身のCP/Mあたりからの名残(注1)だと思いますが、こういうこともありますのでご注意を。

その他の問題

今回は利用しませんでしたがファイルシステム以外の有名な問題として、ActiveScriptRubyを含むmswin32版rubyではfork()が動きません。そのため、fork()を必要とするライブラリが動きません。また、getsやgetcを呼んでしまうとインタプリタ自体が止まってしまうため、他のスレッドも一緒に停止してしまいます。

system()回りも挙動の変更が多い部分です。(追記)

漢字コード

このほか、Windowsに限りませんが、漢字などの2バイトコードによる問題が発生することがあります。Windowsでは通常、ShiftJISが利用されていますので、そのような文字を扱うスクリプトを起動するときには、rubyに漢字コード設定の -Ks オプションを付けて起動しましょう。特に日本語版Windowsを常用する日本人にとっては、この漢字コード指定のオプションは常に付けておいてもいいくらいです。漢字コードの設定を忘れて文字列処理でハマったり、スクリプトのロードで失敗している人を良く見かけます。


注1
CP/Mではファイルの長さがセクタ長(128)の倍数という形で管理されていたために必要でした。

スクリプト実行

上のような問題点を解決してスクリプトが出来上がるわけですが、ここでスクリプトを実行してみます。まずMS-DOSプロンプト(もしくはコマンドプロンプト)を起動して、thumb.rbをおいたディレクトリに移動し、下のようにしてスクリプトを実行してください。ただし、picture-dirの部分は、画像ファイル(jpeg)の入ったディレクトリのパスを指定してください。

  c:\temp> ruby thumb.rb picture-dir

実行にはthumb.rbの他、「Windows上での問題点」で記述したlfn.rb、後述のimage_size.rbとsfolder.rbが必要です。

picture-dirの部分は、手で打たなくてもMS-DOSプロンプトへのディレクトリのドロップで入力できます。スクリプトを見ればすぐわかると思いますが、picture-dirは複数並べて指定できます。

thumb.rbのポイント

スクリプトを読まれる方のため、スクリプトのポイントを列挙しておきます。スクリプト中のコメントもご覧下さい。

画像ファイルの縦横サイズの取得には、標準添付でないライブラリの力を借りています。ここでは RAA(Ruby Application Archive)から取得した三並氏によるimage_sizeというライブラリ(http://raa.ruby-lang.org/list.rhtml?name=image_size)を利用しました。Rubyの標準添付でないライブラリを検索する手段として、RAAが便利です。

RAAについて

RAA(Ruby Application Library; http://raa.ruby-lang.org/)は、Ruby上で利用できるアプリケーション、ライブラリを集めたアーカイブです。「アプリケーション」と銘打ってはいますが、アプリケーションに限らずライブラリが多数登録されています。そして検索機能やカテゴリによる階層構造により、探しているライブラリを見つけることができます。登録も自由に行うことができます。

RAAには、純粋にRubyのみで書かれたライブラリやアプリケーションの他、Cコンパイラなどを使ってコンパイルしなければならない拡張ライブラリと呼ばれるライブラリも入っています。これらの作成には相応の開発環境が必要です。Web上でコンパイル済のバイナリを見つけることができる場合がありますが、特にWindows上ではどの種類のruby用か(cygwin版用かmswin32版用か)、そしてrubyのバージョンによって互いに互換性がありません。ご注意ください。もし運良く利用可能なバイナリを発見できた場合はruby.exeからみて ../lib/ruby/site_ruby/$RUBY_VERSION/i386-* (*の部分はいろいろ)に入れておきましょう。$RUBY_VERSIONは1.6や1.8など、お使いのrubyのバージョンです。

参考まで、今回の画像ファイルのサイズ取得のライブラリimage_sizeを検索するときには、RAAトップのページからの検索で、キーワード「jpeg」で引いて見つけました。デジカメ画像の情報のExifを扱うライブラリも幾つか登録されていますので、thumb.rbはそれを使ってさらに拡張できます。

Exerbで纏める(CUI版)

当たり前なのですが、Rubyのスクリプトを起動するためには条件があります。それは、rubyのインタプリタがインストールされていることと、必要なrubyのライブラリが揃っている事です。自分の開発環境で動作させる場合は全部揃っていて当然かもしれませんが、スクリプトを人に渡すときや、自分のマシンでも常用でないマシン上で使う場合にはこれが面倒な問題になることがあります。そこでExerbです。Exerbを使うと、Rubyがインストールされていなくても実行できるexeファイルを作成できます。

Exerbのインストール

Exerbはスクリプト、Rubyインタプリタ、Ruby拡張ライブラリを一つのファイルにまとめて実行形式(EXEファイル)を作成するものです。

まずはExerbを取得し、インストールしましょう。Exerbのホームページ(http://exerb.sourceforge.jp/)から最新バージョンをダウンロードします。2004/07/06にははexerb-3.2.0.tar.gzが最新でした。これを展開し、展開したディレクトリでドキュメント(Readme.ja.rd)に従って

  c:\temp>ruby setup.rb config
  c:\temp>ruby setup.rb setup
  c:\temp>ruby setup.rb install
としてインストールします。setup.rbの実行には特に問題がなさそうですが、tar.gzを展開する手段が問題になるかもしれません。Windows用のフリーウエアが多数あると思いますので見つけてきて何とかしてください。検索してみると、Lhacaデラックス版(http://www.vector.co.jp/vpack/browse/pickup/pw4/pw004638.html)がtar.gzを展開できるようです。

原稿執筆時点ではExerbはruby-1.6.8相当がデフォルトになっています。そのため、ruby-1.8用の拡張ライブラリの扱いに問題があります。そこでruby-1.8に対応させるためにExerb Core Collectionをインストールすることにします。これは先ほどのExerbのホームページから取得出来ます。執筆時では exerb-cc-2.6.6.tar.gz が最新で、これも展開してExerb本体と同様にinstall.rbを呼び出して(<手順1>)インストールします。ただし、このバージョンの問題は原稿掲載時点では改善されているかもしれません。Exerb本体がruby-1.8に対応していた場合はExerbCoreCollectionのインストールは不要です。(別にインストールしても困りません)

レシピファイルの作成

さて、インストールが終わったら、先ほどのthumb.rbを実行形式に変換してみましょう。実行形式の作成にはレシピファイルが必要となります。このレシピファイルは実行ファイルに格納するスクリプトやライブラリの一覧とその他の情報(漢字コードや内部名など)を記述するファイルです。実行時には、このレシピファイルに記述された一番最初のスクリプトが実行されます。このレシピファイルは手動で作成するよりは、ツールを使って自動生成するのが楽です。自動生成には以下のコマンドを実行します。

  c:\temp>ruby -r exerb/mkexr thumb.rb

このようにすると、thumb.rbが利用しているライブラリの一覧を自動的に保存しながらthumb.rbが実行されます。そして実行終了後、次のようなthumb.rbc というレシピファイルが生成されます。

thumb.rbc
  # generated by mkrbc.rb
  kcode	sjis
  script	thumb.rb
  library	Win32API.so	D:/ruby/lib/ruby/1.8/i386-mswin32/Win32API.so
  script	image_size.rb	D:/ruby/lib/ruby/site_ruby/1.8/image_size.rb
  script	lfn.rb
  script	sfolder.rb

実行時に動的に要求されるライブラリが登録されていないことがあります。その場合は手動で登録するか、そのライブラリが要求されるような状況を作り出して実行してください。

実行形式の作成

Exerb付属のチュートリアルではexerb.rbを実行形式として直接実行していますが、NT系列でないWindowsでは何か細工をしないと普通はこれは無理でしょう。exerb.rb自身はrubyインタプリタと同じディレクトリに入っていると思いますので、バッチファイルを作っておくなどした方が良いでしょう。 exerb.batとして、

exerb.bat
  @echo off
  ruby D:/ruby/bin/exerb.rb  %1 %2 %3 %4 %5 %6 %7 %8 %9

などです。このバッチファイルはパスの通ったところに置いておきましょう。

そうして、

  exerb.bat thumb.exr
とすることで、レシピファイルに指定されたファイルが纏められ、thumb.exeが生成されます。-c cui18 のオプションは、ExerbCoreCollectionの中からruby-1.8版CUIのインタプリタを使うためのものです。(当時コアコレクション利用が必要だった事による記述は見え消しにしておきます)

このthumb.exeにはスクリプトもライブラリもインタプリタ自身もすべて含まれているため、Windowsマシンであればどこへ持っていっても動くでしょう。ちなみに筆者の環境ではファイルサイズが500kByte弱(exerb-2.6.6時点)になりました。

このthumb.exeは全く普通のWindowsの実行形式として扱えます。ですから、単独でクリックして起動することも出来ますし、thumb.exe本体もしくはthumb.exeのショートカットへのファイルやディレクトリのドロップも出来ます(注2)。今回の用途では意味がありませんが、特定の拡張子を生成されたthumb.exeに関連付けることも出来ます。

さて、今回作ったスクリプトは画像ファイルを縮小して一覧するHTMLの生成を行う物でした。筆者はこれ(thumb.exe)のショートカットをデスクトップに置き、デジカメ画像のフォルダの名前を適切な名前に替えてからこのショートカットにドロップして使っています。画像数が多すぎるとリソース不足で落ちたりするのはご愛敬。

また、Exerb自身はrubyが動けばWindows以外でもインストールして動作させることが出来ます。実際にLinux上のExerbで処理して、それをWindowsで実行しましたが、問題なく動作しました。

今回はMS-DOSプロンプトやcmd.exeから使うCUIの実行形式として作成したため、実行するとそのMS-DOSプロンプトの画面が開いていました。この画面から旧来のCUIの流儀で人間と入出力するのでない限り、これはイマイチです。Windowsのソフトとしてはかっこわるいのです。そこで次章では、GUIを利用したスクリプトを作成し、Exerbを使ってMS-DOSプロンプトの開かない実行形式を作成することにします(注3)。


注2
執筆(2003/08)時点のExerbはruby-1.6系を対象にしていました。そのため、空白を含むディレクトリをドロップすると、Dirクラスが画像ファイルを取得できないという不具合がありました。Windows98以前では8.3形式でファイル名が渡されるので問題ありません。8.3形式などのこういう挙動の不統一が嫌なところです。
注3
標準入出力を使わないスクリプトの場合、GUIすら出さない実行形式を作成することができます。今回のthumb.rbもその一種になりえますが、こういった場合はGUIライブラリを使わずにGUIの実行形式を作成します。

GUIを使ったスクリプト作成

Windows上で使えるGUIライブラリ

Windows上でRubyから使えるGUI系のライブラリには以下のような物があります(全部ではありません)。[かっこ内]は依存する外部ライブラリです。

これからご紹介していくのは、この中のVisualuRuby計画(仮称)(http://www.osk.3web.ne.jp/~nyasu/software/vrproject.html)のライブラリです。拙作のこのライブラリは、上の一覧を見て頂いても分かるとおり、他のGUIライブラリと比べると、必要な外部ライブラリが存在しないという点が特徴です。Windows自身が持っている、ボタンやリストボックス、ツリービュー、メディアプレーヤなどのGUI部品を扱うため、外部ライブラリを必要としません。このことがExerbで処理したRubyスクリプトを配布する際には強力な利点となると思います。(Exerbはrubyと関係のない外部ライブラリまでは纏めません)

逆に欠点としては、Windowsにべったりであることから、他のOS上ではまったく動きません。GtkやTk、そしてDelphiまでもがマルチプラットフォームなライブラリとしてOS依存性を減らしています。しかしこのVisualuRuby計画(仮称)では、そういったOSに関するマルチプラットフォームは切り捨てています。敢えて言うならWindowsの世界内部での環境依存性(どんなライブラリがインストールされているかということ)を無くしています(注4)。


注4
実は少しだけシステムのDLLのバージョンに依存した部分がありますが、動作させるための必須条件ではありません。DLLのバージョンが若いと、コモンコントロールなどで使えるGUI部品が減るなどの機能制限がかかります。

VisualuRuby計画(仮称)インストール

まずはインストールしなくてはなりません。ただしActiveScriptRubyをインストールされたのでしたら、元から入っていると思います。他の配布を選択されている場合は、ご自分でインストールする必要があるかもしれません。

VisualuRuby計画(仮称)は2つの部分から成り立っています。Windowsのメッセージ処理やリソース管理などを行うswinと、もう一つはWindowsのGUI部品を扱うvrubyです。swinは単独でも利用可能ですが、煩雑なお約束事が多いのでvrubyも一緒に導入します。

ソースからのインストール

この節は、ActiveScriptRubyを導入された方は飛ばしてください。

まずswinですが、これは拡張ライブラリでありインタプリタの種類に依存します。適切なCコンパイラ(注5)が必要です。まずソースをhttp://www.osk.3web.ne.jp/~nyasu/vruby/core.htmlから取得して展開します。Rubyの拡張ライブラリの作成方法に従い、ソースのあるディレクトリで

  c:\temp> ruby extconf.rb
  c:\temp> make
  c:\temp> make install
と実行すればOKです。makeはnmakeなど、別のものかもしれません。

次にvrubyの方ですが、vrubyの一式はlzhまたはzipで固められていますので、それをswinソースと同じところから取得して展開し、

  c:\temp> ruby vrinstall.rb
としてインストーラを起動して下さい。[Install]というボタンを押せば終了です。ちなみにこのインストーラもvrubyを使って実装されています。


注5
mswin32版(ActiveScriptRuby含む)ならVisualC++、MinGWのgccなど。

vrubyの基礎

さて、もういい加減「VisualuRuby計画(仮称)」と長々と書くのが面倒くさくなってきましたし、とりあえず利用するのは本計画のvrubyなので、以後はvrubyと書きます。

このvrubyがどのようにGUI部品を配置し、スクリプトがどのようにイベントを受けるかを説明しておきます。vrubyに限らず、GUIのスクリプトが他のスクリプトと違って見えるのは、おそらくこれらのポイントのみでしょう。

ウインドウの表示

vrubyを利用して、GUI、というよりウインドウを表示するためには、まずトップレベルのウインドウが必要です。このようなウインドウは、VRFormというクラスで表現されます。何もないトップレベルウインドウを表示してみましょう。こんなものはファイルを作るまでもなく、コマンドラインから以下のように起動してみます。-eオプションは次の引数をスクリプトとして実行するオプションです。swinとvrubyのインストールが正しく済んでいれば、のっぺらぼうのウインドウが出るはずです。

  c:\temp> ruby -r vr/vruby -e 'VRLocalScreen.start(VRForm)'

あとはこののっぺらぼうのウインドウに対してボタンを乗せたりサイズを変えたりウインドウのタイトルを付けたりしていくことになります。ボタンなどのGUI部品を乗せる方法は、VRFormのインスタンスを作成してからそれにどんどん部品を乗せていくことも出来ますが、ここでは継承を使って部品込みのトップレベルウインドウを定義することにします。

GUI部品の利用

vrubyでは、トップレベルウインドウであるVRFormや、ただののっぺらぼうなGUI部品であるVRPanelなど、上にGUI部品を乗せる事が出来るウインドウのクラス(注6)では、インスタンスがウインドウの実体を生成した直後に、construct()というメソッドが呼ばれます。このconstructというメソッドの意義は、適切なタイミングで子となるGUI部品を生成することにあります。ですので、VRFormを継承した子クラス(注7)でconstruct()メソッドを定義して、そこでGUI部品を生成します。ボタンを一つ乗せてみましょう。simple.rbのようにaddControlメソッドを利用します。

addControl()メソッドの引数は数が多いのですが、順番に

・GUI部品のクラス
・インスタンスに付ける名前
・ウインドウのキャプション。
ボタンやラベル、チェックボックスならその表面に書かれる文字列
エディットコントロール(文字入力用)ならあらかじめセットする入力文字
・あと4つはGUI部品の左上のx,y座標、幅、高さ
座標系はGUI部品が乗っている親ウインドウが基準
・ウインドウのスタイル(省略可能)
今回は省略してます
となっています。このメソッドの返り値は、生成されたGUI部品のインスタンス(この例ではVRButtonのインスタンス)となります。また2番目に「インスタンスに付ける名前」というのがありますが、これは後でイベントを受ける際に必要になります。また、自動的に @名前 という形でインスタンス変数が定義されます。この例で言うと、@btnという名前のインスタンス変数が、MyFormのインスタンスに対して定義されます。 一度このスクリプトを動かしてみてください。
  c:\temp> ruby simple.rb
先ほどののっぺらほうのウインドウに「ボタン」と書かれたボタンが現れていませんか?

ちなみに、constructが実行されている状態では、ウインドウの実体は生成されていますが表示されていません。constructが呼ばれた後、表示されます。スクリプトでrequireしているvr/vrcontrolは、Windowsの標準コントロールと呼ばれる部品を利用するために必要です(コモンコントロールだとvr/vrcomctl)。詳しくはVisualuRuby計画(仮称)のwebを参照してください。


注6
VRParentというモジュールをMix-inしているとこれが出来ます。
注7
simple.rbとは形態が異なりますが、特異メソッドを使う方法など、継承を使わない他の方法もあります。必要な特性を持つオブジェクトに対して必要なメソッドが追加定義できればそれでいいのです。
simple.rb
  #! ruby -Ks
  require 'vr/vruby'         # vrubyのコア部分の利用(実はこの行は省略可)
  require 'vr/vrcontrol'     # Windows標準コントロール(ボタンなど)を利用
  
  class MyForm < VRForm
    def construct            # このメソッドはウインドウ生成直後に呼ばれる
      addControl(VRButton,"btn","ボタン",10,10,100,50) # ボタンを追加
    end
  end
  
  VRLocalScreen.start(MyForm)

イベントの捕捉

ボタンは表示しているだけでは意味がありません。ボタンが押されたことを検知しなくてはならないのです。次にイベントハンドリングを行ってみます。イベントハンドリングには、先ほどGUI部品のインスタンスに付けた名前と、イベントの名前を使います。 先ほどのsimple.rbのMyFormに、次のようなbtn_clickedというメソッドを追加してみましょう。(simple2.rb)

simple2.rb
  class MyForm < VRForm
    ... (略)
    def btn_clicked
      messageBox "CLICKED!"    # CLICKED!と表示したメッセージボックスで知らせる
    end
  end

このbtn_clickedが「ボタンが押された」というイベントを受けるためのメソッドです。あるGUI部品がイベントを発生すると、GUI部品の「インスタンスに付ける名前」と、イベント名を"_"で連結した名前の親ウインドウのメソッドが呼ばれます。simple2.rbでは、ボタン部品がbtnと名付けられていて、ボタンが押されたときのイベント名がclickedなのでイベントハンドラはbtn_clickedというメソッドになります。 一度スクリプトを走らせてボタンを押してみてください。メッセージボックスが出て"CLICKED!"と表示されると思います。


図1 simple2.rb実行結果

部品自身でイベントを捕捉せず、親で受けるのが奇異に感じられるかもしれませんが、これは元々のWindowsのモデルがそうなっているためです。イベントハンドラをこのような文字列連結したメソッドで受けることにしたのは、VisualBasicを参考にしたからです。各GUI部品のイベントの一覧は、webのリファレンスなどを参考にしてください。vrubyのスクリプト内部にもRDの形式で埋め込んであります。2004/7月には雪見酒氏によるHTMLヘルプもできました。

このほか、イベントとしてはGUI部品以外が発生するイベントもあります。例えば、「マウスがウインドウの上を通過した」や、「ウインドウにフォーカスが当たった」などです。これらを捉えるためには、適切なモジュールをincludeして、「インスタンスに付ける名前」を"self"にします。例えば、「フォーカスが当たった」のを捉えるイベントハンドラは、vr/vrhandlerをrequireし、VRFocusSensitiveをinclude(またはextend)したオブジェクトで、self_gotfocus()となります。ちょっと古めですが、リファレンスのページに一覧もあります。

スクリプトの実行がイベントドリブンとなるので、少しわかりにくいかもしれませんが、

という実行形態になるため、スクリプトがイベントハンドラだけになるのはGUIプログラミングでは良くあることです。もちろんRubyのスレッドを使って裏で何か作業を続けることも可能です。

GUI部品のレイアウトなど、見た目のデザインに関しては、雪見酒氏作成のFormDesignerという便利なrubyスクリプトがあり、筆者も重宝しています。これは原稿執筆時点ではVisualuRuby計画(仮称)のページから入手できます。

URLメモの作成

ここで一つアプリケーションを作ってみます。元々はこの原稿の依頼を頂いた編集さんの希望を縮小(笑)したものなのです。簡単な仕様ですが、

というものです。これを実装したのが、dropmemo.rbです。

結構GUIのアプリケーションのエッセンスが入っていると思います。

また、Windows独特の内容として、

なんてこともやってます。今時DDEなんて流行りませんけどね。

dropmemo.rbの前半部分はデータを扱う部分です。実はかなり手抜きです。 URLメモの保持するデータはファイルに格納され、以下の形式のテキストが0つ以上連なったものになっていますので、テキストエディタなどで編集できます。

  [
    TITLE=メモのテキスト
    URL=http://hoge.jp/ 
    DATE=2003:08:06:02:49:01
  ]
データファイルはスクリプトと同じ所に作成することにします。

表示に関しては、このファイルをStringクラスのscanメソッドで各メモ毎に分割し、リストビューに表示していきます。処理の流れだけを意識するとGUI生成のメソッドの中でデータファイルの解釈をやってしまいたくなりますが、バックエンドのデータ操作部分と、フロントエンドのGUIの部分はきっちり分けておきましょう。多少煩雑になりますが、デバッグやテストが容易になります。ということで、dropmemo.rbも、前半がデータ操作部分で、後半がGUI部分です。

前半のデータ操作部分は、ファイルの読み込みと、データの追加機能を実装しています。ファイルの読み込みは、ファイル全部を丸ごと読み込んでから正規表現でデータを抽出しています。

GUI部分では2つのクラスが定義されています。一つがダイアログ、もう一つがメモの一覧を表示するウインドウです。ダイアログの方はGUI部品を並べてイベントハンドラを書いているだけなので、特に問題がないかと思います。MyFormの方は、ボタンとは違った複雑な部品が幾つか入っていますので説明します。

このMyFormは3つのモジュールをincludeしています。最初のVROleDropTargetは、OLEドラッグ&ドロップを受けるために必要な機能を実装しています。constructメソッドの中でstart_oledroptarget()というメソッドを呼んでいますが、これがドロップターゲットとしての開始手続きです。ここではCF_URL(URLのドロップ)形式のドロップターゲットとなっています。MyFormクラスの最後の2つのメソッドは、OLEドラッグ&ドロップの状況に応じて呼び出されます。ドロップされたときにはself_oledrop()が呼び出されます。これはGUI部品以外が発生するイベントであるため、GUI部品名になるべき所が"self"になっています。

また、リストビューがダブルクリックされたときのイベントハンドラがlview_dblclickedです。ここでは、リストビューの選択されているメモのURLそれぞれを引数に、exec_urlを呼び出しています。exec_urlでは、まずDDE(DynamicDataExchange)という古い技術を使ってInternetExplorerにurlを開かせようとします。

カラムがクリックされたらイベントハンドラのlview_columnclickedが呼ばれます。ここでは、ファイルを読み直してメモをソートし、リストビューにセットし直しています。

実は、データを消去する部分は作っていません。練習がてら一度作ってみてください。筆者の手抜き?いえいえ、これは課題ということで何とぞ。

Exerbで纏める(GUI版)

dropmemo.rbをExerbで実行形式に変換するため、まずはレシピファイルを作成しましょう。

  c:\temp> ruby -r exerb/mkrbc dropmemo.rb

実行するのでウインドウが開いてきますが、いきなり終わって大丈夫です。requireされる物はスクリプトの最初にrequireしてしまってますので。終了すると無事にdropmemo.rbcが出来たと思います。今回は依存するファイルが多いので、先ほどよりは大きなファイルが出来たと思います。

さて、次にExerbで実行形式を作成します。Exerbは「コア」という形で複数のインタプリタを持っています。その中のGUI用のコアを使って実行形式を作成すると、お望みの品のできあがりです。コアの指定には -c オプションを利用します。

 c:\temp> exerb.bat -c gui dropmemo.rbc
とします。オプションの-c guiが、MS-DOSプロンプトを出さないGUI版の実行形式を作成するためのオプションです。これでdropmemo.exeが出来たと思います。実行してみましょう。もうMS-DOSプロンプトは出ません。純粋なWindowsのGUIアプリケーションです。何にも知らない人に配ってもちゃんと動くプログラムです。

InternetExplorerからURLをドロップすると、ダイアログが出てきてタイトルの入力を促します。適当に入れてENTERキーを押してください(図2)。これでリストビューに登録されたと思います。InternetExplorerの表示を別のページに変えてから、リストビューの項目をダブルクリックすると、InternetExplorerが登録したサイトにジャンプします。


図2 dropmemoの画面

Exerbでの__FILE__の問題

dropmemo.rbにはExerb特有の挙動に対する処置が入っています。それは、スクリプトの中から自分自身のファイル名を知るための変数、$0や__FILE__が、常にファイル名だけしか返さなくなることです。スクリプトを実行形式に変換してしまったので、これらの変数がおかしくなることはある意味当然ではありますが、Exerb側でファイル名だけはセットしてくれるようです。$0や__FILE__はスクリプトのあるパスを得るために使うことがありますが、Exerbによる変換の後はディレクトリの情報が無いので役に立たなくなります。

そこでdropmemo.rbでは、WindowsのAPIを利用して自分自身の実行形式(EXEファイル)のパスを得るメソッドWin32API.get_exepath()を作成しています。

おわりに

Windowsでrubyを使う時の雰囲気はつかんで頂けたでしょうか。Exerbを使うと、配布対象マシン(Windows)へのrubyのインストールなどの作業が不要なまま、rubyで書いたスクリプトを走らせることが出来ます。VisualuRuby計画(仮称)を使うと、その特徴を保ったままGUIを利用することが出来ます。

これでもう、めんどくさい「雑用」の数々も、rubyのパワーでさっくり終了、になりそうだと思いませんか? ただしこの記事の前半で書いたようにWindowsならではの落とし穴がありますので、うまく避けて行きましょう。