From 2e400ad1b8a7e424b72bb2841e6a9bef7d6fba1c Mon Sep 17 00:00:00 2001 From: suke Date: Sat, 26 Jul 2008 12:27:57 +0000 Subject: [PATCH] * ext/win32ole/win32ole.c (Init_win32ole): add WIN32OLE_EVENT#handler=, WIN32OLE_EVENT#handler * test/win32ole/test_win32ole_event.rb: ditto. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@18225 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 7 ++ ext/win32ole/win32ole.c | 116 +++++++++++++++++++++--- test/win32ole/test_win32ole_event.rb | 130 ++++++++++++++++----------- 3 files changed, 192 insertions(+), 61 deletions(-) diff --git a/ChangeLog b/ChangeLog index 3b31c25ff0..c8c6b804fd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +Sat Jul 26 21:17:18 2008 Masaki Suketa + + * ext/win32ole/win32ole.c (Init_win32ole): add + WIN32OLE_EVENT#handler=, WIN32OLE_EVENT#handler + + * test/win32ole/test_win32ole_event.rb: ditto. + Sat Jul 26 07:44:14 2008 Masaki Suketa * ext/win32ole/win32ole.c (add_event_call_back): remove unused diff --git a/ext/win32ole/win32ole.c b/ext/win32ole/win32ole.c index afe9843adf..22cec8e49d 100644 --- a/ext/win32ole/win32ole.c +++ b/ext/win32ole/win32ole.c @@ -118,7 +118,7 @@ #define WC2VSTR(x) ole_wc2vstr((x), TRUE) -#define WIN32OLE_VERSION "1.2.9" +#define WIN32OLE_VERSION "1.3.0" typedef HRESULT (STDAPICALLTYPE FNCOCREATEINSTANCEEX) (REFCLSID, IUnknown*, DWORD, COSERVERINFO*, DWORD, MULTI_QI*); @@ -499,6 +499,7 @@ static VALUE foleparam_default(VALUE self); static VALUE foleparam_inspect(VALUE self); static long ole_search_event_at(VALUE ary, VALUE ev); static VALUE ole_search_event(VALUE ary, VALUE ev, BOOL *is_default); +static VALUE ole_search_handler_method(VALUE handler, VALUE ev, BOOL *is_default_handler); static void ole_delete_event(VALUE ary, VALUE ev); static void hash2ptr_dispparams(VALUE hash, ITypeInfo *pTypeInfo, DISPID dispid, DISPPARAMS *pdispparams); static VALUE hash2result(VALUE hash); @@ -520,6 +521,8 @@ static VALUE fev_on_event(int argc, VALUE *argv, VALUE self); static VALUE fev_on_event_with_outargs(int argc, VALUE *argv, VALUE self); static VALUE fev_off_event(int argc, VALUE *argv, VALUE self); static VALUE fev_unadvise(VALUE self); +static VALUE fev_set_handler(VALUE self, VALUE val); +static VALUE fev_get_handler(VALUE self); static VALUE evs_push(VALUE ev); static VALUE evs_delete(long i); static VALUE evs_entry(long i); @@ -7434,6 +7437,23 @@ ole_search_event(VALUE ary, VALUE ev, BOOL *is_default) } return def_event; } +static VALUE +ole_search_handler_method(VALUE handler, VALUE ev, BOOL *is_default_handler) +{ + VALUE mid; + + *is_default_handler = FALSE; + mid = rb_to_id(rb_sprintf("on%s", StringValuePtr(ev))); + if (rb_respond_to(handler, mid)) { + return mid; + } + mid = rb_intern("method_missing"); + if (rb_respond_to(handler, mid)) { + *is_default_handler = TRUE; + return mid; + } + return Qnil; +} static void ole_delete_event(VALUE ary, VALUE ev) @@ -7502,8 +7522,9 @@ exec_callback(VALUE arg) { VALUE *parg = (VALUE *)arg; VALUE handler = parg[0]; - VALUE args = parg[1]; - return rb_apply(handler, rb_intern("call"), args); + VALUE mid = parg[1]; + VALUE args = parg[2]; + return rb_apply(handler, mid, args); } static VALUE @@ -7540,9 +7561,11 @@ STDMETHODIMP EVENTSINK_Invoke( unsigned int i; ITypeInfo *pTypeInfo; VARIANT *pvar; - VALUE ary, obj, event, handler, args, outargv, ev, result; - VALUE arg[2]; - VALUE is_outarg; + VALUE ary, obj, event, args, outargv, ev, result; + VALUE handler = Qnil; + VALUE arg[3]; + VALUE mid; + VALUE is_outarg = Qfalse; BOOL is_default_handler = FALSE; int state; @@ -7564,9 +7587,21 @@ STDMETHODIMP EVENTSINK_Invoke( } ev = WC2VSTR(bstr); event = ole_search_event(ary, ev, &is_default_handler); - if (NIL_P(event)) { - return NOERROR; + if (TYPE(event) == T_ARRAY) { + handler = rb_ary_entry(event, 0); + mid = rb_intern("call"); + is_outarg = rb_ary_entry(event, 3); + } else { + handler = rb_ivar_get(obj, rb_intern("handler")); + if (handler == Qnil) { + return NOERROR; + } + mid = ole_search_handler_method(handler, ev, &is_default_handler); } + if (handler == Qnil || mid == Qnil) { + return NOERROR; + } + args = rb_ary_new(); if (is_default_handler) { rb_ary_push(args, ev); @@ -7577,8 +7612,6 @@ STDMETHODIMP EVENTSINK_Invoke( pvar = &pdispparams->rgvarg[pdispparams->cArgs-i-1]; rb_ary_push(args, ole_variant2val(pvar)); } - handler = rb_ary_entry(event, 0); - is_outarg = rb_ary_entry(event, 3); outargv = Qnil; if (is_outarg == Qtrue) { outargv = rb_ary_new(); @@ -7593,7 +7626,8 @@ STDMETHODIMP EVENTSINK_Invoke( * and exit ruby process by Win32OLE itself. */ arg[0] = handler; - arg[1] = args; + arg[1] = mid; + arg[2] = args; result = rb_protect(exec_callback, (VALUE)arg, &state); if (state != 0) { rescue_callback(Qnil); @@ -8260,6 +8294,64 @@ evs_length() return rb_funcall(ary_ole_event, rb_intern("length"), 0); } +/* + * call-seq: + * WIN32OLE_EVENT#handler= + * + * sets event handler object. If handler object has onXXX + * method according to XXX event, then onXXX method is called + * when XXX event occurs. + * + * If handler object has method_missing and there is no + * method according to the event, then method_missing + * called and 1-st argument is event name. + * + * If handler object has onXXX method and there is block + * defined by WIN32OLE_EVENT#on_event('XXX'){}, + * then block is executed but handler object method is not called + * when XXX event occurs. + * + * class Handler + * def onStatusTextChange(text) + * puts "StatusTextChanged" + * end + * def onPropertyChange(prop) + * puts "PropertyChanged" + * end + * def method_missing(ev, *arg) + * puts "other event #{ev}" + * end + * end + * + * handler = Handler.new + * ie = WIN32OLE.new('InternetExplorer.Application') + * ev = WIN32OLE_EVENT.new(ie) + * ev.on_event("StatusTextChange") {|*args| + * puts "this block executed." + * puts "handler.onStatusTextChange method is not called." + * } + * ev.handler = handler + * + */ +static VALUE +fev_set_handler(VALUE self, VALUE val) +{ + return rb_ivar_set(self, rb_intern("handler"), val); +} + +/* + * call-seq: + * WIN32OLE_EVENT#handler + * + * returns handler object. + * + */ +static VALUE +fev_get_handler(VALUE self) +{ + return rb_ivar_get(self, rb_intern("handler")); +} + static void olevariant_free(struct olevariantdata *pvar) { @@ -8896,6 +8988,8 @@ Init_win32ole() rb_define_method(cWIN32OLE_EVENT, "on_event_with_outargs", fev_on_event_with_outargs, -1); rb_define_method(cWIN32OLE_EVENT, "off_event", fev_off_event, -1); rb_define_method(cWIN32OLE_EVENT, "unadvise", fev_unadvise, 0); + rb_define_method(cWIN32OLE_EVENT, "handler=", fev_set_handler, 1); + rb_define_method(cWIN32OLE_EVENT, "handler", fev_get_handler, 0); cWIN32OLE_VARIANT = rb_define_class("WIN32OLE_VARIANT", rb_cObject); rb_define_alloc_func(cWIN32OLE_VARIANT, folevariant_s_allocate); diff --git a/test/win32ole/test_win32ole_event.rb b/test/win32ole/test_win32ole_event.rb index 6ab165bd67..276a018167 100644 --- a/test/win32ole/test_win32ole_event.rb +++ b/test/win32ole/test_win32ole_event.rb @@ -6,6 +6,8 @@ require 'test/unit' if defined?(WIN32OLE_EVENT) class TestWIN32OLE_EVENT < Test::Unit::TestCase + module IE + end def create_temp_html fso = WIN32OLE.new('Scripting.FileSystemObject') dummy_file = fso.GetTempName + ".html" @@ -22,10 +24,18 @@ if defined?(WIN32OLE_EVENT) sleep 0.1 end + def wait_ie + while @ie.readyState != IE::READYSTATE_COMPLETE + message_loop + end + end + def setup WIN32OLE_EVENT.message_loop @ie = WIN32OLE.new("InternetExplorer.Application") - message_loop + if !defined?(IE::READYSTATE_COMPLETE) + WIN32OLE.const_load(@ie, IE) + end @ie.visible = true message_loop @event = "" @@ -61,11 +71,7 @@ if defined?(WIN32OLE_EVENT) ev = WIN32OLE_EVENT.new(@ie, 'DWebBrowserEvents') ev.on_event {|*args| default_handler(*args)} @ie.navigate("file:///#{@f}") - while @ie.busy - WIN32OLE_EVENT.new(@ie, 'DWebBrowserEvents') - GC.start - message_loop - end + wait_ie assert_match(/BeforeNavigate/, @event) assert_match(/NavigateComplete/, @event) end @@ -75,9 +81,7 @@ if defined?(WIN32OLE_EVENT) ev.on_event('BeforeNavigate') {|*args| handler1} ev.on_event('BeforeNavigate') {|*args| handler2} @ie.navigate("file:///#{@f}") - while @ie.busy - message_loop - end + wait_ie assert_equal("handler2", @event2) end @@ -86,9 +90,7 @@ if defined?(WIN32OLE_EVENT) ev.on_event {|*args| handler1} ev.on_event {|*args| handler2} @ie.navigate("file:///#{@f}") - while @ie.busy - message_loop - end + wait_ie assert_equal("handler2", @event2) end @@ -98,9 +100,7 @@ if defined?(WIN32OLE_EVENT) ev.on_event{|*args| handler2} ev.on_event('NavigateComplete'){|*args| handler3(*args)} @ie.navigate("file:///#{@f}") - while @ie.busy - message_loop - end + wait_ie assert(@event3!="") assert("handler2", @event2) end @@ -110,9 +110,7 @@ if defined?(WIN32OLE_EVENT) ev.on_event {|*args| default_handler(*args)} ev.on_event('NavigateComplete'){|*args| handler3(*args)} @ie.navigate("file:///#{@f}") - while @ie.busy - message_loop - end + wait_ie assert_match(/BeforeNavigate/, @event) assert(/NavigateComplete/ !~ @event) assert(@event!="") @@ -122,16 +120,12 @@ if defined?(WIN32OLE_EVENT) ev = WIN32OLE_EVENT.new(@ie, 'DWebBrowserEvents') ev.on_event {|*args| default_handler(*args)} @ie.navigate("file:///#{@f}") - while @ie.busy - message_loop - end + wait_ie assert_match(/BeforeNavigate/, @event) ev.unadvise @event = "" @ie.navigate("file:///#{@f}") - while @ie.busy - message_loop - end + wait_ie assert_equal("", @event); assert_raise(WIN32OLERuntimeError) { ev.on_event {|*args| default_handler(*args)} @@ -158,9 +152,7 @@ if defined?(WIN32OLE_EVENT) } bl = @ie.locationURL @ie.navigate("file:///#{@f}") - while @ie.busy - message_loop - end + wait_ie assert_equal(bl, @ie.locationURL) end @@ -171,9 +163,7 @@ if defined?(WIN32OLE_EVENT) } bl = @ie.locationURL @ie.navigate("file:///#{@f}") - while @ie.busy - message_loop - end + wait_ie assert_equal(bl, @ie.locationURL) end @@ -184,9 +174,7 @@ if defined?(WIN32OLE_EVENT) } bl = @ie.locationURL @ie.navigate("file:///#{@f}") - while @ie.busy - message_loop - end + wait_ie assert_equal(bl, @ie.locationURL) end @@ -197,9 +185,7 @@ if defined?(WIN32OLE_EVENT) } bl = @ie.locationURL @ie.navigate("file:///#{@f}") - while @ie.busy - message_loop - end + wait_ie assert_equal(bl, @ie.locationURL) end @@ -210,9 +196,7 @@ if defined?(WIN32OLE_EVENT) } bl = @ie.locationURL @ie.navigate("file:///#{@f}") - while @ie.busy - message_loop - end + wait_ie assert_equal(bl, @ie.locationURL) end @@ -223,9 +207,7 @@ if defined?(WIN32OLE_EVENT) } bl = @ie.locationURL @ie.navigate("file:///#{@f}") - while @ie.busy - message_loop - end + wait_ie assert_equal(bl, @ie.locationURL) end @@ -234,10 +216,7 @@ if defined?(WIN32OLE_EVENT) ev.on_event{handler1} ev.off_event @ie.navigate("file:///#{@f}") - while @ie.busy - message_loop - end - WIN32OLE_EVENT.message_loop + wait_ie assert_equal("", @event2) end @@ -246,10 +225,7 @@ if defined?(WIN32OLE_EVENT) ev.on_event('BeforeNavigate2'){handler1} ev.off_event('BeforeNavigate2') @ie.navigate("file:///#{@f}") - while @ie.busy - message_loop - end - WIN32OLE_EVENT.message_loop + wait_ie assert_equal("", @event2) end @@ -267,6 +243,7 @@ if defined?(WIN32OLE_EVENT) def teardown @ie.quit + message_loop @ie = nil i = 0 begin @@ -280,5 +257,58 @@ if defined?(WIN32OLE_EVENT) GC.start message_loop end + + class Handler1 + attr_reader :val1, :val2, :val3, :val4 + def initialize + @val1 = nil + @val2 = nil + @val3 = nil + @val4 = nil + end + def onStatusTextChange(t) + @val1 = t + end + def onProgressChange(p, pmax) + @val2 = p + @val3 = pmax + end + def onPropertyChange(p) + @val4 = p + end + end + + class Handler2 + attr_reader :ev + def initialize + @ev = "" + end + def method_missing(ev, *arg) + @ev += ev + end + end + + def test_handler1 + ev = WIN32OLE_EVENT.new(@ie) + h1 = Handler1.new + ev.handler = h1 + @ie.navigate("file:///#{@f}") + wait_ie + assert(h1.val1) + assert_equal(h1.val1, ev.handler.val1) + assert(h1.val2) + assert(h1.val3) + assert(h1.val4) + end + + def test_handler2 + ev = WIN32OLE_EVENT.new(@ie) + h2 = Handler2.new + ev.handler = h2 + @ie.navigate("file:///#{@f}") + wait_ie + assert(h2.ev != "") + end + end end