diff --git a/ChangeLog b/ChangeLog index 7b6bc81e0a..a7992b4f21 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +Thu Jul 3 19:23:20 2008 Masaki Suketa + + * ext/win32ole/win32ole.c: avoid creating Ruby object during + GC. thanks to arton . [ruby-dev:35313] + + * ext/win32ole/tests: add test_win32ole_event.rb, remove + testOLEEVENT.rb + + * ext/win32ole/tests/testWIN32OLE.rb(test_convert_bignum): + fix test. + Thu Jul 3 16:46:56 2008 Tanaka Akira * include/ruby/ruby.h (POSFIXABLE): use FIXNUM_MAX+1 instead of diff --git a/ext/win32ole/tests/testOLEEVENT.rb b/ext/win32ole/tests/testOLEEVENT.rb deleted file mode 100644 index 0901158642..0000000000 --- a/ext/win32ole/tests/testOLEEVENT.rb +++ /dev/null @@ -1,91 +0,0 @@ -require 'rubyunit' -require 'win32ole' - -class TestWIN32OLE_EVENT < RUNIT::TestCase - def setup - @excel = WIN32OLE.new("Excel.Application") - @excel.visible = true - @event = "" - @event2 = "" - end - def test_on_event - book = @excel.workbooks.Add - value = "" - begin - ev = WIN32OLE_EVENT.new(book, 'WorkbookEvents') - ev.on_event('SheetChange'){|arg1, arg2| - begin - value = arg1.value - rescue - value = $!.message - end - } - book.Worksheets(1).Range("A1").value = "OK" - ensure - book.saved = true - end - assert_equal("OK", value) - end - - def handler1 - @event += "handler1" - end - def handler2 - @event += "handler2" - end - - def handler3 - @event += "handler3" - end - - def test_on_event2 - book = @excel.workbooks.Add - begin - ev = WIN32OLE_EVENT.new(book, 'WorkbookEvents') - ev.on_event('SheetChange'){|arg1, arg2| - handler1 - } - ev.on_event('SheetChange'){|arg1, arg2| - handler2 - } - book.Worksheets(1).Range("A1").value = "OK" - ensure - book.saved = true - end - assert_equal("handler2", @event) - end - - def test_on_event3 - book = @excel.workbooks.Add - begin - ev = WIN32OLE_EVENT.new(book, 'WorkbookEvents') - ev.on_event{ handler1 } - ev.on_event{ handler2 } - book.Worksheets(1).Range("A1").value = "OK" - ensure - book.saved = true - end - assert_equal("handler2", @event) - end - - def test_on_event4 - book = @excel.workbooks.Add - begin - ev = WIN32OLE_EVENT.new(book, 'WorkbookEvents') - ev.on_event{ handler1 } - ev.on_event{ handler2 } - ev.on_event('SheetChange'){|arg1, arg2| handler3 } - book.Worksheets(1).Range("A1").value = "OK" - ensure - book.saved = true - end - assert_equal("handler3", @event) - end - - def teardown - @excel.quit - @excel = nil - GC.start - end -end - diff --git a/ext/win32ole/tests/testWIN32OLE.rb b/ext/win32ole/tests/testWIN32OLE.rb index 5c01507377..d7f9dd2543 100644 --- a/ext/win32ole/tests/testWIN32OLE.rb +++ b/ext/win32ole/tests/testWIN32OLE.rb @@ -156,7 +156,8 @@ class TestWin32OLE < RUNIT::TestCase sheet.range("A3").value = "=A1*10 + 9" assert_equal(9999999999, sheet.range("A2").value) assert_equal(9999999999, sheet.range("A3").value) - + sheet.range("A4").value = "2008/03/04" + assert_equal("2008/03/04 00:00:00", sheet.range("A4").value) ensure book.saved = true end diff --git a/ext/win32ole/tests/testall.rb b/ext/win32ole/tests/testall.rb index d45541f571..553ce88509 100644 --- a/ext/win32ole/tests/testall.rb +++ b/ext/win32ole/tests/testall.rb @@ -12,4 +12,5 @@ require "testNIL2VTEMPTY" require "test_ole_methods.rb" require "test_propertyputref.rb" require "test_word.rb" +require "test_win32ole_event.rb" # require "testOLEEVENT" diff --git a/ext/win32ole/win32ole.c b/ext/win32ole/win32ole.c index 94eff3aa51..ae0066ffc9 100644 --- a/ext/win32ole/win32ole.c +++ b/ext/win32ole/win32ole.c @@ -29,7 +29,7 @@ #include #define va_init_list(a,b) va_start(a) #endif - +#include #define DOUT fprintf(stderr,"[%d]\n",__LINE__) #define DOUTS(x) fprintf(stderr,"[%d]:" #x "=%s\n",__LINE__,x) @@ -79,7 +79,7 @@ #define WC2VSTR(x) ole_wc2vstr((x), TRUE) -#define WIN32OLE_VERSION "0.7.5" +#define WIN32OLE_VERSION "0.7.6" typedef HRESULT (STDAPICALLTYPE FNCOCREATEINSTANCEEX) (REFCLSID, IUnknown*, DWORD, COSERVERINFO*, DWORD, MULTI_QI*); @@ -157,6 +157,9 @@ static VALUE com_hash; static IDispatchVtbl com_vtbl; static UINT cWIN32OLE_cp = CP_ACP; static VARTYPE g_nil_to = VT_ERROR; +static IMessageFilterVtbl message_filter; +static IMessageFilter imessage_filter = { &message_filter }; +static IMessageFilter* previous_filter; struct oledata { IDispatch *pDispatch; @@ -203,6 +206,101 @@ static char *ole_wc2mb(LPWSTR); static VALUE ole_variant2val(VARIANT*); static void ole_val2variant(VALUE, VARIANT*); +static HRESULT (STDMETHODCALLTYPE mf_QueryInterface)( + IMessageFilter __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject) +{ + if (MEMCMP(riid, &IID_IUnknown, GUID, 1) == 0 + || MEMCMP(riid, &IID_IMessageFilter, GUID, 1) == 0) + { + *ppvObject = &message_filter; + return S_OK; + } + return E_NOINTERFACE; +} + +static ULONG (STDMETHODCALLTYPE mf_AddRef)( + IMessageFilter __RPC_FAR * This) +{ + return 1; +} + +static ULONG (STDMETHODCALLTYPE mf_Release)( + IMessageFilter __RPC_FAR * This) +{ + return 1; +} + +static DWORD (STDMETHODCALLTYPE mf_HandleInComingCall)( + IMessageFilter __RPC_FAR * pThis, + DWORD dwCallType, //Type of incoming call + HTASK threadIDCaller, //Task handle calling this task + DWORD dwTickCount, //Elapsed tick count + LPINTERFACEINFO lpInterfaceInfo //Pointer to INTERFACEINFO structure + ) +{ +#ifdef DEBUG_MESSAGEFILTER + printf("incoming %08X, %08X, %d\n", dwCallType, threadIDCaller, dwTickCount); + fflush(stdout); +#endif + switch (dwCallType) + { + case CALLTYPE_ASYNC: + case CALLTYPE_TOPLEVEL_CALLPENDING: + case CALLTYPE_ASYNC_CALLPENDING: + if (rb_during_gc()) { + return SERVERCALL_RETRYLATER; + } + break; + default: + break; + } + if (previous_filter) { + return previous_filter->lpVtbl->HandleInComingCall(previous_filter, + dwCallType, + threadIDCaller, + dwTickCount, + lpInterfaceInfo); + } + return SERVERCALL_ISHANDLED; +} + +static DWORD (STDMETHODCALLTYPE mf_RetryRejectedCall)( + IMessageFilter* pThis, + HTASK threadIDCallee, //Server task handle + DWORD dwTickCount, //Elapsed tick count + DWORD dwRejectType //Returned rejection message + ) +{ + if (previous_filter) { + return previous_filter->lpVtbl->RetryRejectedCall(previous_filter, + threadIDCallee, + dwTickCount, + dwRejectType); + } + return 1000; +} + +static DWORD (STDMETHODCALLTYPE mf_MessagePending)( + IMessageFilter* pThis, + HTASK threadIDCallee, //Called applications task handle + DWORD dwTickCount, //Elapsed tick count + DWORD dwPendingType //Call type + ) +{ + if (rb_during_gc()) { + return PENDINGMSG_WAITNOPROCESS; + } + if (previous_filter) { + return previous_filter->lpVtbl->MessagePending(previous_filter, + threadIDCallee, + dwTickCount, + dwPendingType); + } + return PENDINGMSG_WAITNOPROCESS; +} + typedef struct _Win32OLEIDispatch { IDispatch dispatch; @@ -625,6 +723,11 @@ ole_initialize() /* atexit((void (*)(void))ole_uninitialize); */ + hr = CoRegisterMessageFilter(&imessage_filter, &previous_filter); + if(FAILED(hr)) { + previous_filter = NULL; + ole_raise(hr, rb_eRuntimeError, "fail: install OLE MessageFilter"); + } } } @@ -6183,6 +6286,14 @@ Init_win32ole() com_vtbl.GetTypeInfo = GetTypeInfo; com_vtbl.GetIDsOfNames = GetIDsOfNames; com_vtbl.Invoke = Invoke; + + message_filter.QueryInterface = mf_QueryInterface; + message_filter.AddRef = mf_AddRef; + message_filter.Release = mf_Release; + message_filter.HandleInComingCall = mf_HandleInComingCall; + message_filter.RetryRejectedCall = mf_RetryRejectedCall; + message_filter.MessagePending = mf_MessagePending; + com_hash = Data_Wrap_Struct(rb_cData, rb_mark_hash, st_free_table, st_init_numtable()); cWIN32OLE = rb_define_class("WIN32OLE", rb_cObject);