メモログ

塵が積もって山とならないメモのログ

watir: click_no_wait が動作しない その2

watir 1.6.5ではclick_no_waitが動作しないという話をして、その後watir 1.6.6では修正されているという話をしました。1.6.6では性能も改善されていて、click_no_waitのメソッドを実行すると、10〜20秒くらい止まってしまうということもなくなりました。

それで基本的に動作に問題はなくなったのですが、自分の環境で動作させたときにclick_no_waitが動作しない場合がありました。それのメモ。

click_no_waitのメソッドのソースをrdoc.infoから抜粋

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# File 'lib/watir/element.rb', line 234

def click_no_wait
assert_exists
assert_enabled
highlight(:set)
element = "#{self.class}.new(#{@page_container.attach_command}, :unique_number, #{self.unique_number})"
ruby_code = "require 'rubygems';" <<
"require '#{File.expand_path(File.dirname(__FILE__))}/core';" <<
"#{element}.click!"
system(spawned_click_no_wait_command(ruby_code))
highlight(:clear)
end

element = ほにゃららというところで、"Button.new(Watir::IE.attach(:hwnd, 11111),:unique_number, 1)"みたいな文字列を生成する。最初がself.classなので、buttonメソッドを実行しているときはButtonとなる。attach_commandというメソッドは"Watir::IE.attach(:hwnd, #{hwnd})"という書式の文字列を返す。hwndはwindowのhandleを返す。このhandleのIDでwindowを特定してattachしている。

次のruby_codeの行で、systemに渡すコマンドの文字列を生成しています。rubygemsをrequireしたあとに、__FILE__(実行中のファイル)からパスをとって、watir/lib/coreをrequireして、上の行で作成したエレメントをclick!する。その下のspawned_click_no_wait_commandというのは、ruby_codeで作成した文字列に"start rubyw -e"を追加する。また、$DEBUGがtrueの場合はコマンドの実行ログ的なものを出力します。

ruby_codeの最後に指定しているelement.click!のclick!メソッドのソースは下記のような感じ。ole_objectというCOMで操作できるオブジェクトがあってclickメソッドを実行します。

1
2
3
4
5
6
7
8
9
def click!
assert_exists
assert_enabled

highlight(:set)
ole_object.click
highlight(:clear)
end

COMのclickメソッドについては詳細はよくわからず… とにかくクリックした後に戻りを待つようでボタン押した後にエラーが発生すると、待ちっぱなしの状態になってしまいます。そのために、click_no_waitのメソッドではそれを回避するために、systemメソッドを使用して動作中のrubyとは別のプロセスでclickメソッドを実行するようにしています。

別のプロセスで実行されるため、click_no_waitが失敗した場合は($DEBUGをtrueにしていない限り)エラーとして何も出力されずに次の処理に進んでしまう。なので外部的にはいつまでもクリックが実行されないで止まっているように見える(実際に実行に時間がかかっている場合もあるが、外部的には失敗しているか実行待ちの状態かはわからない)。

実行が失敗する要因としては2カ所あって、ひとつはwindowを操作するhwndが異なる場合。hwndは最初にhwndが呼ばれたときに@ie(WIN32OLE.new(‘InternetExplorer.Application’)のオブジェクト)のhwndを参照する。それ以降はその値が保存されている限りは更新されないので、一度closeしたあとに_new_window_initとかで新しいウィンドウを呼び出し直すと、hwndが更新されないということが起きます。自分でhwnd=ie.hwnd的なことをしないといけない(あとcloseしたあとは@closingがtrueになるので、ie.closing=nil とかしないとexist?したときにfalseになってしまう)。これをしないとattachしたときに失敗してしまいます。

もう一つはclick_no_waitメソッドを自分用のスクリプトファイルで上書きした場合。__FILE__が実行中のファイルのパスを表示するので、自分用のスクリプトファイルで上書きしていると__FILE__の場所が変わってしまって、うまくrequire ができなくなってしまう。click_no_waitメソッドは上書きしないか、__FILE__の場所を"#{Gem.loaded_specs(“watir”).full_gem_path}/lib/watir/"とかに変更する(または直接watirのディレクトリを指定)。

という感じのメモ、です(ながい)。click_no_waitはどうしても時間がかかってしまうのでできるだけ使用しない方が良いですね。

これはまだ実験中なのですが、Thread.new{ buttons.first.click! } みたいにThreadで囲んだらどうかなーと思ったのですが、やはりボタンが押された後で止まってしまう。下記のような感じでWatir::IE.attachで同じブラウザを別のオブジェクトでボタンだけ操作するというのはどうかなあ。まだ試していないので結果は不明。

1
2
3
4
5
6
7
8
9
10
11
12
b = Watir::Browser.new
b.goto "http://memolog.org"

Thread.new do
c = Watir::Attach(:hwnd,b.hwnd)
c.button(:id,"id").click
end

b.wait_until(30){ Watir.autoit.WinExists(title) == 1 }
...
...