SyntaxHighlighter

2012年11月27日火曜日

AutoIt を Ruby から使い、それを ocra で EXE 化する

AutoIt を Ruby から使い、それを ocra で EXE 化する

ピンポイントな内容で、あまり他の人の役に立つか分かりませんが、ちょっと調べたので書いておきます。

今回の目的は、「AutoIt を呼び出す Ruby スクリプトを ocra で EXE 化し、制限ユーザーでも実行できるようにする」です。

実現方法

予備知識として、ocra による EXE 化はできる前提です。

準備

  1. RubyInstaller から Ruby 1.9.3 をダウンロードし、インストールしておきます。また、bin ディレクトリにパスを通しておきます。
  2. 以下のコマンドで ocra をインストールしておきます。
  3. 続いて AutoIt から AutoIt をダウンロードし、インストールしておきます。
  4. AutoItX/AutoItX3.dll を Ruby の bin ディレクトリ以下にコピーしておきます。
  5. 以下の内容を記述した ruby.exe.manifest を Ruby の bin ディレクトリ以下に作成します。

これで事前準備は完了です。

EXE の生成

以下のような sample.rb を考えます。

これは電卓を起動し、3 + 4 を実行するスクリプトです。電卓のウィンドウを表す文字列は、OS によっては異なるかもしれません。

これをそのまま EXE 化して EXE ファイルを別の実機に持っていっても、AutoItX3.dll が無いので実行できません。AutoItX3.dll も別の実機にコピーし、前もって 「regsvr32 AutoItX3.dll」を実行しておけば可能ですが、これには管理者権限が必要です。

これを回避するために、以下のようなコマンドで EXE 化します。

このようにして生成された EXE ファイルは、制限ユーザーであってもそのまま実行でき、AutoItX3.dll を呼び出すことができます。

説明と補足

以下は、原理の説明などです。

AutoIt

AutoIt とは、Windows の GUI を自動操作することができるツールのことです。

Windows 上で GUI を含んだツールのテストをしたり、GUI プログラムの定型処理を自動化したりするには、それなりに面倒なことが多いのですが、この AutoIt を使うと、割と楽に自動化できます。
同様なことは、.NET からだと Windows 標準の UI Automation などでも可能です。

この AutoIt には、COM インターフェースも用意されており、 COM オブジェクトにアクセスできる言語からは、自由に機能を利用することができます。
実体は AutoItX3.dll で、AutoItX3.Control という ProgID でアクセスできます。

もちろん Ruby からも WIN32OLE を利用することで、呼び出すことができます。
上に書いたように、例えば Windows の電卓 (calc.exe) を起動し、3+4 を計算させる場合は、以下のようになります。

ただし、このスクリプトを実行できるようにするためには、AutoItX3.dll をあらかじめ Windows に登録する必要があります。
インストーラでインストールした場合は登録されているはずですが、自己解凍形式でインストールした場合は、以下のコマンドを管理者権限で実行し、AutoItX3.dll を Windows に登録しておかなければなりません。

EXE 化して実行するときの問題点

上記の Ruby スクリプトを ocra で EXE 化した場合、その EXE を実行する Windows マシンにも AutoItX3.dll がインストール済みでなければなりません。これは多少面倒ですし、管理者権限も必要になってしまいます。
今回の本題は、そのあたりをどうするかについてです。

基本的には、ocra で EXE 化する際に AutoItX3.dll も EXE に含めてしまい、かつスクリプトの内部から制限ユーザーでも呼び出せるようにするということになります。

マニフェストファイル

大きな流れとしては、AutoItX3.dll を EXE の中に組込み、かつマニフェストファイルを作成することで、その DLL ファイルを COM としてアクセスできるようにしてやる、ということになります。

マニフェストファイルとは、EXE ファイルのメタ情報のようなもので、ロードすべき DLL を指定したり、UAC 昇格が必要であることを指定したりする XML ファイルです。

このファイルを用いて、AutoItX3.dll の中に AutoItX3.Control という ProgID の COM オブジェクトがあることを明示し、ruby.exe からアクセスできるようにしてやります。
このマニフェストファイルは、実行する EXE に .manifest をつけた名前にします。

ocra では、ruby.exe やその他の DLL を Temp ディレクトリに展開し、その後 ruby.exe を実行します。
そのため、Temp ディレクトリに展開される際に、マニフェストファイルも展開されるようにすれば、ruby.exe が実行される際にマニフェストファイルで COM 用 DLL を指定することができます。

そのため、以下のようなコマンドで EXE ファイルを作成すれば、AutoItX3.dllruby.exe.manifestruby.exe と同じディレクトリに展開されるようになります。

これで、制限ユーザーであっても、AutoItX3.dll がインストールされていない状況であっても、Ruby スクリプトから AutoIt の COM インターフェースにアクセスできる EXE ができあがります。

ポイント

--dll を使っているところがポイントです。

前提として、ocra で EXE 化されたスクリプトは、まず一時フォルダに ruby.exe 本体やスクリプトや DLL などを展開し、さらに展開した ruby プログラムを実行する、という二段構成になっています。
細かく書くと、EXE を実行すると、ruby.exe 本体が置かれる bin ディレクトリ、ライブラリが置かれる lib ディレクトリ、ユーザーのソースコードが置かれる src ディレクトリの三つをそれぞれ展開し、その後 src 以下のソースコードを bin ディレクトリ以下の ruby.exe によって実行する、という流れになります。

EXE 作成時に --dll オプションを使って指定されたファイルは、EXE 実行時に bin ディレクトリ以下に展開されることになります。
そのため、上の例では、EXE 実行時には以下のような配置になります。

/
bin
ruby.exe
AutoItX3.dll
ruby.exe.manifest
lib
require されたライブラリが配置される。
src
sample.rb

上のように ruby.exe.manifest ファイルを配置することで、展開された ruby.exe が実行される際に、ruby.exe.manifest を読み込ませることができ、結果として AutoItX の COM インターフェースにアクセスできるようになります。

ちなみに、ocraruby として一つのバイナリにまとめてある Ruby の中にも、同様の仕組みで AutoItX3.dll を組み込んであります。

というわけで、何回か仕事で使うツールの内容が続きましたが、忙しかった時期も過ぎつつあり、そろそろこの週末ぐらいからは mruby を触れそうです。

3 件のコメント:

  1. AutoItを楽に別のところで使いたいと思っていまして、ちょうどピンポイントな内容で解決できました。ありがとうございます。

    そこで質問なのですが、TEMPフォルダに展開されたファイルって自動で消すことってできないでしょうか?実行するたびにどんどん増えていくので困っています。現在は別ツールで消してもらうようにしていますが、できればひとつのプログラム内で解決できたらと思っています。何か解決策はありますでしょうか?

    返信削除
  2. > TEMPフォルダに展開されたファイルって自動で消すことってできないでしょうか?
    普通のスクリプトでは、ocraが消してくれると思います。
    実際に消えない時のサンプルソースなどはありますか?

    返信削除
    返信
    1. お早い対応に対して遅い返信申し訳ありません。

      確かめたところ確かにちゃんと消えているようです。これまでは×ボタンで終了させてることが多かったので、消えないものだと思い込んでいたようです。正常終了することで一応解決しそうです。
      ありがとうございました。

      削除