ワナベと申します。

2008/07/12 15:26 Masaki Suketa <masaki.suketa / nifty.ne.jp>:
> 助田です。
> 次のスクリプトをtrunkで実行すると cfp consistency error になります。
>
> require 'win32ole'
> ie = WIN32OLE.new('InternetExplorer.Application')
> ev = WIN32OLE_EVENT.new(ie)
>
> ev.on_event('BeforeNavigate2') {|*args|
>  raise "exception"
> }
> ie.gohome
>
> error になるのは、ブロックの中の raise "exception" のところです。
> (ie.gohomeで BeforeNavigate2 イベントが発生し、
>
> win32ole.c でいうと EVENTSINK_Invoke の
>
>     result = rb_apply(handler, rb_intern("call"), args);
>
> の行で、ブロックがコールされて、その中で例外が raise されると
> cfp consistency error になります。
> この前のGC中にRubyオブジェクトを作ってはいけないのと同様、Rubyで
> 想定している以外のところから、 rb_apply を呼ぶのがいけないことを
> しているんだろうなと思うのですが、回避策はどのようにするのがいい
> んでしょうか?

Rubyの想定しているところに無理やり入ってしまえばいい、と考えますと
Threadオブジェクトを作ってその中で処理してしまうのはどうでしょうか。
たとえば、思いつきの実験的コードではありますがこんな感じで。

Index: win32ole.c
===================================================================
--- win32ole.c	(revision 18083)
+++ win32ole.c	(working copy)
@@ -7425,6 +7425,30 @@
     }
 }

+VALUE EVENTSINK_Invoke_i(void *iargs)
+{
+    VALUE result;
+    VALUE *vargs = (VALUE*) iargs;
+    VALUE handler = vargs[0];
+    VALUE args = vargs[1];
+    ITypeInfo *pTypeInfo = (ITypeInfo *)vargs[2];
+    DISPID dispid = (DISPID)vargs[3];
+    DISPPARAMS *pdispparams = (DISPPARAMS *)vargs[4];
+    VARIANT *pvarResult = (VARIANT *)vargs[5];
+
+    result = rb_apply(handler, rb_intern("call"), args);
+    if(TYPE(result) == T_HASH) {
+	    hash2ptr_dispparams(result, pTypeInfo, dispid, pdispparams);
+	    result = hash2result(result);
+    }
+    if (pvarResult) {
+        ole_val2variant(result, pvarResult);
+    }
+
+    free(iargs);
+    return Qnil;
+}
+
 STDMETHODIMP EVENTSINK_Invoke(
     PEVENTSINK pEventSink,
     DISPID dispid,
@@ -7491,11 +7515,15 @@
 	}
     }
     else {
-        result = rb_apply(handler, rb_intern("call"), args);
-	if(TYPE(result) == T_HASH) {
-	    hash2ptr_dispparams(result, pTypeInfo, dispid, pdispparams);
-	    result = hash2result(result);
-	}
+	VALUE *vargs = ALLOC_N(VALUE, 6);
+	vargs[0] = handler;
+	vargs[1] = args;
+	vargs[2] = pTypeInfo;
+	vargs[3] = dispid;
+	vargs[4] = pdispparams;
+	vargs[5] = pvarResult;
+	rb_thread_create(EVENTSINK_Invoke_i, (void*)vargs);
+	return NOERROR;
     }
     if (pvarResult) {
         ole_val2variant(result, pvarResult);

-- 
ワナベ