diff options
-rw-r--r-- | ChangeLog | 9 | ||||
-rw-r--r-- | ext/win32ole/depend | 1 | ||||
-rw-r--r-- | ext/win32ole/win32ole.c | 1269 | ||||
-rw-r--r-- | ext/win32ole/win32ole.h | 13 | ||||
-rw-r--r-- | ext/win32ole/win32ole_event.c | 1254 | ||||
-rw-r--r-- | ext/win32ole/win32ole_event.h | 6 |
6 files changed, 1287 insertions, 1265 deletions
@@ -1,3 +1,12 @@ +Fri Aug 15 20:34:17 2014 Masaki Suketa <[email protected]> + + * ext/win32ole/win32ole.c: seperate WIN32OLE_EVENT src from + win32ole.c. + * ext/win32ole/win32ole.h: ditto. + * ext/win32ole/win32ole_event.c: ditto. + * ext/win32ole/win32ole_event.h: ditto. + * ext/win32ole/depend: ditto. + Fri Aug 15 19:38:00 2014 Koichi Sasada <[email protected]> * iseq.c (rb_iseq_clone): Should not insert write barrier from diff --git a/ext/win32ole/depend b/ext/win32ole/depend index d46250c886..c8445ad5fa 100644 --- a/ext/win32ole/depend +++ b/ext/win32ole/depend @@ -7,5 +7,6 @@ win32ole_variable.o : win32ole_variable.c $(WIN32OLE_HEADERS) win32ole_method.o : win32ole_method.c $(WIN32OLE_HEADERS) win32ole_param.o : win32ole_param.c $(WIN32OLE_HEADERS) win32ole_variant.o : win32ole_variant.c $(WIN32OLE_HEADERS) +win32ole_event.o : win32ole_event.c $(WIN32OLE_HEADERS) win32ole_record.o : win32ole_record.c $(WIN32OLE_HEADERS) win32ole_error.o : win32ole_error.c $(WIN32OLE_HEADERS) diff --git a/ext/win32ole/win32ole.c b/ext/win32ole/win32ole.c index 24cc184fcb..3c52c9c088 100644 --- a/ext/win32ole/win32ole.c +++ b/ext/win32ole/win32ole.c @@ -26,13 +26,6 @@ const IID IID_IMultiLanguage2 = {0xDCCFC164, 0x2B38, 0x11d2, {0xB7, 0xEC, 0x00, 0xC0, 0x4F, 0x8F, 0x5D, 0x9A}}; #endif -#define OLEData_Get_Struct(obj, pole) {\ - Data_Get_Struct(obj, struct oledata, pole);\ - if(!pole->pDispatch) {\ - rb_raise(rb_eRuntimeError, "failed to get Dispatch Interface");\ - }\ -} - #define WIN32OLE_VERSION "1.7.7" typedef HRESULT (STDAPICALLTYPE FNCOCREATEINSTANCEEX) @@ -41,60 +34,7 @@ typedef HRESULT (STDAPICALLTYPE FNCOCREATEINSTANCEEX) typedef HWND (WINAPI FNHTMLHELP)(HWND hwndCaller, LPCSTR pszFile, UINT uCommand, DWORD dwData); typedef BOOL (FNENUMSYSEMCODEPAGES) (CODEPAGE_ENUMPROC, DWORD); -typedef struct { - struct IEventSinkVtbl * lpVtbl; -} IEventSink, *PEVENTSINK; - -typedef struct IEventSinkVtbl IEventSinkVtbl; - -struct IEventSinkVtbl { - STDMETHOD(QueryInterface)( - PEVENTSINK, - REFIID, - LPVOID *); - STDMETHOD_(ULONG, AddRef)(PEVENTSINK); - STDMETHOD_(ULONG, Release)(PEVENTSINK); - - STDMETHOD(GetTypeInfoCount)( - PEVENTSINK, - UINT *); - STDMETHOD(GetTypeInfo)( - PEVENTSINK, - UINT, - LCID, - ITypeInfo **); - STDMETHOD(GetIDsOfNames)( - PEVENTSINK, - REFIID, - OLECHAR **, - UINT, - LCID, - DISPID *); - STDMETHOD(Invoke)( - PEVENTSINK, - DISPID, - REFIID, - LCID, - WORD, - DISPPARAMS *, - VARIANT *, - EXCEPINFO *, - UINT *); -}; - -typedef struct tagIEVENTSINKOBJ { - IEventSinkVtbl *lpVtbl; - DWORD m_cRef; - IID m_iid; - int m_event_id; - ITypeInfo *pTypeInfo; -}IEVENTSINKOBJ, *PIEVENTSINKOBJ; - VALUE cWIN32OLE; -VALUE cWIN32OLE_EVENT; - -static VALUE ary_ole_event; -static ID id_events; #if defined(RB_THREAD_SPECIFIC) && (defined(__CYGWIN__) || defined(__MINGW32__)) static RB_THREAD_SPECIFIC BOOL g_ole_initialized; @@ -133,22 +73,11 @@ static IMultiLanguage *pIMultiLanguage = NULL; #define pIMultiLanguage NULL /* dummy */ #endif -struct oledata { - IDispatch *pDispatch; -}; - -struct oleeventdata { - DWORD dwCookie; - IConnectionPoint *pConnectionPoint; - long event_id; -}; - struct oleparam { DISPPARAMS dp; OLECHAR** pNamedArgs; }; - static HRESULT ( STDMETHODCALLTYPE QueryInterface )(IDispatch __RPC_FAR *, REFIID riid, void __RPC_FAR *__RPC_FAR *ppvObject); static ULONG ( STDMETHODCALLTYPE AddRef )(IDispatch __RPC_FAR * This); static ULONG ( STDMETHODCALLTYPE Release )(IDispatch __RPC_FAR * This); @@ -166,10 +95,8 @@ NORETURN(static void failed_load_conv51932(void)); static void load_conv_function51932(void); #endif static UINT ole_init_cp(void); -static char *ole_wc2mb(LPWSTR pw); static void ole_freeexceptinfo(EXCEPINFO *pExInfo); static VALUE ole_excepinfo2msg(EXCEPINFO *pExInfo); -static void ole_msg_loop(void); static void ole_free(struct oledata *pole); static LPWSTR ole_mb2wc(char *pm, int len); static VALUE ole_ary_m_entry(VALUE val, LONG *pid); @@ -178,7 +105,6 @@ static void * get_ptr_of_variant(VARIANT *pvar); static void ole_set_safe_array(long n, SAFEARRAY *psa, LONG *pid, long *pub, VALUE val, long dim, VARTYPE vt); static long dimension(VALUE val); static long ary_len_of_dim(VALUE ary, long dim); -static void ole_val2ptr_variant(VALUE val, VARIANT *var); static VALUE ole_set_member(VALUE self, IDispatch *dispatch); static VALUE fole_s_allocate(VALUE klass); static VALUE create_win32ole_object(VALUE klass, IDispatch *pDispatch, int argc, VALUE *argv); @@ -239,38 +165,6 @@ static VALUE ole_ptrtype2val(ITypeInfo *pTypeInfo, TYPEDESC *pTypeDesc, VALUE ty static VALUE fole_method_help(VALUE self, VALUE cmdname); static VALUE fole_activex_initialize(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); -static void ary2ptr_dispparams(VALUE ary, DISPPARAMS *pdispparams); -static VALUE exec_callback(VALUE arg); -static VALUE rescue_callback(VALUE arg); -static HRESULT find_iid(VALUE ole, char *pitf, IID *piid, ITypeInfo **ppTypeInfo); -static HRESULT find_coclass(ITypeInfo *pTypeInfo, TYPEATTR *pTypeAttr, ITypeInfo **pTypeInfo2, TYPEATTR **pTypeAttr2); -static HRESULT find_default_source_from_typeinfo(ITypeInfo *pTypeInfo, TYPEATTR *pTypeAttr, ITypeInfo **ppTypeInfo); -static HRESULT find_default_source(VALUE ole, IID *piid, ITypeInfo **ppTypeInfo); -static void ole_event_free(struct oleeventdata *poleev); -static VALUE fev_s_allocate(VALUE klass); -static VALUE ev_advise(int argc, VALUE *argv, VALUE self); -static VALUE fev_initialize(int argc, VALUE *argv, VALUE self); -static VALUE fev_s_msg_loop(VALUE klass); -static void add_event_call_back(VALUE obj, VALUE event, VALUE data); -static VALUE ev_on_event(int argc, VALUE *argv, VALUE self, VALUE is_ary_arg); -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); -static VALUE evs_length(void); - static void init_enc2cp(void); static void free_enc2cp(void); @@ -791,7 +685,7 @@ ole_alloc_str(UINT size, void *arg) return ALLOC_N(char, size + 1); } -static char * +char * ole_wc2mb(LPWSTR pw) { return ole_wc2mb_alloc(pw, ole_alloc_str, NULL); @@ -887,15 +781,6 @@ ole_initialize(void) } static void -ole_msg_loop() { - MSG msg; - while(PeekMessage(&msg,NULL,0,0,PM_REMOVE)) { - TranslateMessage(&msg); - DispatchMessage(&msg); - } -} - -static void ole_free(struct oledata *pole) { OLE_FREE(pole->pDispatch); @@ -1365,74 +1250,6 @@ ole_val2variant(VALUE val, VARIANT *var) } } -static void -ole_val2ptr_variant(VALUE val, VARIANT *var) -{ - switch (TYPE(val)) { - case T_STRING: - if (V_VT(var) == (VT_BSTR | VT_BYREF)) { - *V_BSTRREF(var) = ole_vstr2wc(val); - } - break; - case T_FIXNUM: - switch(V_VT(var)) { - case (VT_UI1 | VT_BYREF) : - *V_UI1REF(var) = NUM2CHR(val); - break; - case (VT_I2 | VT_BYREF) : - *V_I2REF(var) = (short)NUM2INT(val); - break; - case (VT_I4 | VT_BYREF) : - *V_I4REF(var) = NUM2INT(val); - break; - case (VT_R4 | VT_BYREF) : - *V_R4REF(var) = (float)NUM2INT(val); - break; - case (VT_R8 | VT_BYREF) : - *V_R8REF(var) = NUM2INT(val); - break; - default: - break; - } - break; - case T_FLOAT: - switch(V_VT(var)) { - case (VT_I2 | VT_BYREF) : - *V_I2REF(var) = (short)NUM2INT(val); - break; - case (VT_I4 | VT_BYREF) : - *V_I4REF(var) = NUM2INT(val); - break; - case (VT_R4 | VT_BYREF) : - *V_R4REF(var) = (float)NUM2DBL(val); - break; - case (VT_R8 | VT_BYREF) : - *V_R8REF(var) = NUM2DBL(val); - break; - default: - break; - } - break; - case T_BIGNUM: - if (V_VT(var) == (VT_R8 | VT_BYREF)) { - *V_R8REF(var) = rb_big2dbl(val); - } - break; - case T_TRUE: - if (V_VT(var) == (VT_BOOL | VT_BYREF)) { - *V_BOOLREF(var) = VARIANT_TRUE; - } - break; - case T_FALSE: - if (V_VT(var) == (VT_BOOL | VT_BYREF)) { - *V_BOOLREF(var) = VARIANT_FALSE; - } - break; - default: - break; - } -} - void ole_val2variant2(VALUE val, VARIANT *var) { @@ -3695,10 +3512,10 @@ fole_respond_to(VALUE self, VALUE method) DISPID DispID; HRESULT hr; if(!RB_TYPE_P(method, T_STRING) && !RB_TYPE_P(method, T_SYMBOL)) { - rb_raise(rb_eTypeError, "wrong argument type (expected String or Symbol)"); + rb_raise(rb_eTypeError, "wrong argument type (expected String or Symbol)"); } if (RB_TYPE_P(method, T_SYMBOL)) { - method = rb_sym_to_s(method); + method = rb_sym_to_s(method); } OLEData_Get_Struct(self, pole); wcmdname = ole_vstr2wc(method); @@ -3969,1070 +3786,6 @@ fole_activex_initialize(VALUE self) return Qnil; } -/* - * Document-class: WIN32OLE_EVENT - * - * <code>WIN32OLE_EVENT</code> objects controls OLE event. - */ - -static IEventSinkVtbl vtEventSink; -static BOOL g_IsEventSinkVtblInitialized = FALSE; - -void EVENTSINK_Destructor(PIEVENTSINKOBJ); - -STDMETHODIMP -EVENTSINK_QueryInterface( - PEVENTSINK pEV, - REFIID iid, - LPVOID* ppv - ) { - if (IsEqualIID(iid, &IID_IUnknown) || - IsEqualIID(iid, &IID_IDispatch) || - IsEqualIID(iid, &((PIEVENTSINKOBJ)pEV)->m_iid)) { - *ppv = pEV; - } - else { - *ppv = NULL; - return E_NOINTERFACE; - } - ((LPUNKNOWN)*ppv)->lpVtbl->AddRef((LPUNKNOWN)*ppv); - return NOERROR; -} - -STDMETHODIMP_(ULONG) -EVENTSINK_AddRef( - PEVENTSINK pEV - ){ - PIEVENTSINKOBJ pEVObj = (PIEVENTSINKOBJ)pEV; - return ++pEVObj->m_cRef; -} - -STDMETHODIMP_(ULONG) EVENTSINK_Release( - PEVENTSINK pEV - ) { - PIEVENTSINKOBJ pEVObj = (PIEVENTSINKOBJ)pEV; - --pEVObj->m_cRef; - if(pEVObj->m_cRef != 0) - return pEVObj->m_cRef; - EVENTSINK_Destructor(pEVObj); - return 0; -} - -STDMETHODIMP EVENTSINK_GetTypeInfoCount( - PEVENTSINK pEV, - UINT *pct - ) { - *pct = 0; - return NOERROR; -} - -STDMETHODIMP EVENTSINK_GetTypeInfo( - PEVENTSINK pEV, - UINT info, - LCID lcid, - ITypeInfo **pInfo - ) { - *pInfo = NULL; - return DISP_E_BADINDEX; -} - -STDMETHODIMP EVENTSINK_GetIDsOfNames( - PEVENTSINK pEventSink, - REFIID riid, - OLECHAR **szNames, - UINT cNames, - LCID lcid, - DISPID *pDispID - ) { - ITypeInfo *pTypeInfo; - PIEVENTSINKOBJ pEV = (PIEVENTSINKOBJ)pEventSink; - pTypeInfo = pEV->pTypeInfo; - if (pTypeInfo) { - return pTypeInfo->lpVtbl->GetIDsOfNames(pTypeInfo, szNames, cNames, pDispID); - } - return DISP_E_UNKNOWNNAME; -} - -static long -ole_search_event_at(VALUE ary, VALUE ev) -{ - VALUE event; - VALUE event_name; - long i, len; - long ret = -1; - len = RARRAY_LEN(ary); - for(i = 0; i < len; i++) { - event = rb_ary_entry(ary, i); - event_name = rb_ary_entry(event, 1); - if(NIL_P(event_name) && NIL_P(ev)) { - ret = i; - break; - } - else if (RB_TYPE_P(ev, T_STRING) && - RB_TYPE_P(event_name, T_STRING) && - rb_str_cmp(ev, event_name) == 0) { - ret = i; - break; - } - } - return ret; -} - -static VALUE -ole_search_event(VALUE ary, VALUE ev, BOOL *is_default) -{ - VALUE event; - VALUE def_event; - VALUE event_name; - int i, len; - *is_default = FALSE; - def_event = Qnil; - len = RARRAY_LEN(ary); - for(i = 0; i < len; i++) { - event = rb_ary_entry(ary, i); - event_name = rb_ary_entry(event, 1); - if(NIL_P(event_name)) { - *is_default = TRUE; - def_event = event; - } - else if (rb_str_cmp(ev, event_name) == 0) { - *is_default = FALSE; - return event; - } - } - 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%"PRIsVALUE, 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) -{ - long at = -1; - at = ole_search_event_at(ary, ev); - if (at >= 0) { - rb_ary_delete_at(ary, at); - } -} - -static void -hash2ptr_dispparams(VALUE hash, ITypeInfo *pTypeInfo, DISPID dispid, DISPPARAMS *pdispparams) -{ - BSTR *bstrs; - HRESULT hr; - UINT len, i; - VARIANT *pvar; - VALUE val; - VALUE key; - len = 0; - bstrs = ALLOCA_N(BSTR, pdispparams->cArgs + 1); - hr = pTypeInfo->lpVtbl->GetNames(pTypeInfo, dispid, - bstrs, pdispparams->cArgs + 1, - &len); - if (FAILED(hr)) - return; - - for (i = 0; i < len - 1; i++) { - key = WC2VSTR(bstrs[i + 1]); - val = rb_hash_aref(hash, INT2FIX(i)); - if (val == Qnil) - val = rb_hash_aref(hash, key); - if (val == Qnil) - val = rb_hash_aref(hash, rb_str_intern(key)); - pvar = &pdispparams->rgvarg[pdispparams->cArgs-i-1]; - ole_val2ptr_variant(val, pvar); - } -} - -static VALUE -hash2result(VALUE hash) -{ - VALUE ret = Qnil; - ret = rb_hash_aref(hash, rb_str_new2("return")); - if (ret == Qnil) - ret = rb_hash_aref(hash, rb_str_intern(rb_str_new2("return"))); - return ret; -} - -static void -ary2ptr_dispparams(VALUE ary, DISPPARAMS *pdispparams) -{ - int i; - VALUE v; - VARIANT *pvar; - for(i = 0; i < RARRAY_LEN(ary) && (unsigned int) i < pdispparams->cArgs; i++) { - v = rb_ary_entry(ary, i); - pvar = &pdispparams->rgvarg[pdispparams->cArgs-i-1]; - ole_val2ptr_variant(v, pvar); - } -} - -static VALUE -exec_callback(VALUE arg) -{ - VALUE *parg = (VALUE *)arg; - VALUE handler = parg[0]; - VALUE mid = parg[1]; - VALUE args = parg[2]; - return rb_apply(handler, mid, args); -} - -static VALUE -rescue_callback(VALUE arg) -{ - - VALUE error; - VALUE e = rb_errinfo(); - VALUE bt = rb_funcall(e, rb_intern("backtrace"), 0); - VALUE msg = rb_funcall(e, rb_intern("message"), 0); - bt = rb_ary_entry(bt, 0); - error = rb_sprintf("%"PRIsVALUE": %"PRIsVALUE" (%s)\n", bt, msg, rb_obj_classname(e)); - rb_write_error(StringValuePtr(error)); - rb_backtrace(); - ruby_finalize(); - exit(-1); - - return Qnil; -} - -STDMETHODIMP EVENTSINK_Invoke( - PEVENTSINK pEventSink, - DISPID dispid, - REFIID riid, - LCID lcid, - WORD wFlags, - DISPPARAMS *pdispparams, - VARIANT *pvarResult, - EXCEPINFO *pexcepinfo, - UINT *puArgErr - ) { - - HRESULT hr; - BSTR bstr; - unsigned int count; - unsigned int i; - ITypeInfo *pTypeInfo; - VARIANT *pvar; - 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; - - PIEVENTSINKOBJ pEV = (PIEVENTSINKOBJ)pEventSink; - pTypeInfo = pEV->pTypeInfo; - obj = evs_entry(pEV->m_event_id); - if (!rb_obj_is_kind_of(obj, cWIN32OLE_EVENT)) { - return NOERROR; - } - - ary = rb_ivar_get(obj, id_events); - if (NIL_P(ary) || !RB_TYPE_P(ary, T_ARRAY)) { - return NOERROR; - } - hr = pTypeInfo->lpVtbl->GetNames(pTypeInfo, dispid, - &bstr, 1, &count); - if (FAILED(hr)) { - return NOERROR; - } - ev = WC2VSTR(bstr); - event = ole_search_event(ary, ev, &is_default_handler); - if (RB_TYPE_P(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); - } - - /* make argument of event handler */ - for (i = 0; i < pdispparams->cArgs; ++i) { - pvar = &pdispparams->rgvarg[pdispparams->cArgs-i-1]; - rb_ary_push(args, ole_variant2val(pvar)); - } - outargv = Qnil; - if (is_outarg == Qtrue) { - outargv = rb_ary_new(); - rb_ary_push(args, outargv); - } - - /* - * if exception raised in event callback, - * then you receive cfp consistency error. - * to avoid this error we use begin rescue end. - * and the exception raised then error message print - * and exit ruby process by Win32OLE itself. - */ - arg[0] = handler; - arg[1] = mid; - arg[2] = args; - result = rb_protect(exec_callback, (VALUE)arg, &state); - if (state != 0) { - rescue_callback(Qnil); - } - if(RB_TYPE_P(result, T_HASH)) { - hash2ptr_dispparams(result, pTypeInfo, dispid, pdispparams); - result = hash2result(result); - }else if (is_outarg == Qtrue && RB_TYPE_P(outargv, T_ARRAY)) { - ary2ptr_dispparams(outargv, pdispparams); - } - - if (pvarResult) { - VariantInit(pvarResult); - ole_val2variant(result, pvarResult); - } - - return NOERROR; -} - -PIEVENTSINKOBJ -EVENTSINK_Constructor() { - PIEVENTSINKOBJ pEv; - if (!g_IsEventSinkVtblInitialized) { - vtEventSink.QueryInterface=EVENTSINK_QueryInterface; - vtEventSink.AddRef = EVENTSINK_AddRef; - vtEventSink.Release = EVENTSINK_Release; - vtEventSink.Invoke = EVENTSINK_Invoke; - vtEventSink.GetIDsOfNames = EVENTSINK_GetIDsOfNames; - vtEventSink.GetTypeInfoCount = EVENTSINK_GetTypeInfoCount; - vtEventSink.GetTypeInfo = EVENTSINK_GetTypeInfo; - - g_IsEventSinkVtblInitialized = TRUE; - } - pEv = ALLOC_N(IEVENTSINKOBJ, 1); - if(pEv == NULL) return NULL; - pEv->lpVtbl = &vtEventSink; - pEv->m_cRef = 0; - pEv->m_event_id = 0; - pEv->pTypeInfo = NULL; - return pEv; -} - -void EVENTSINK_Destructor( - PIEVENTSINKOBJ pEVObj - ) { - if(pEVObj != NULL) { - OLE_RELEASE(pEVObj->pTypeInfo); - free(pEVObj); - pEVObj = NULL; - } -} - -static HRESULT -find_iid(VALUE ole, char *pitf, IID *piid, ITypeInfo **ppTypeInfo) -{ - HRESULT hr; - IDispatch *pDispatch; - ITypeInfo *pTypeInfo; - ITypeLib *pTypeLib; - TYPEATTR *pTypeAttr; - HREFTYPE RefType; - ITypeInfo *pImplTypeInfo; - TYPEATTR *pImplTypeAttr; - - struct oledata *pole; - unsigned int index; - unsigned int count; - int type; - BSTR bstr; - char *pstr; - - BOOL is_found = FALSE; - LCID lcid = cWIN32OLE_lcid; - - OLEData_Get_Struct(ole, pole); - - pDispatch = pole->pDispatch; - - hr = pDispatch->lpVtbl->GetTypeInfo(pDispatch, 0, lcid, &pTypeInfo); - if (FAILED(hr)) - return hr; - - hr = pTypeInfo->lpVtbl->GetContainingTypeLib(pTypeInfo, - &pTypeLib, - &index); - OLE_RELEASE(pTypeInfo); - if (FAILED(hr)) - return hr; - - if (!pitf) { - hr = pTypeLib->lpVtbl->GetTypeInfoOfGuid(pTypeLib, - piid, - ppTypeInfo); - OLE_RELEASE(pTypeLib); - return hr; - } - count = pTypeLib->lpVtbl->GetTypeInfoCount(pTypeLib); - for (index = 0; index < count; index++) { - hr = pTypeLib->lpVtbl->GetTypeInfo(pTypeLib, - index, - &pTypeInfo); - if (FAILED(hr)) - break; - hr = OLE_GET_TYPEATTR(pTypeInfo, &pTypeAttr); - - if(FAILED(hr)) { - OLE_RELEASE(pTypeInfo); - break; - } - if(pTypeAttr->typekind == TKIND_COCLASS) { - for (type = 0; type < pTypeAttr->cImplTypes; type++) { - hr = pTypeInfo->lpVtbl->GetRefTypeOfImplType(pTypeInfo, - type, - &RefType); - if (FAILED(hr)) - break; - hr = pTypeInfo->lpVtbl->GetRefTypeInfo(pTypeInfo, - RefType, - &pImplTypeInfo); - if (FAILED(hr)) - break; - - hr = pImplTypeInfo->lpVtbl->GetDocumentation(pImplTypeInfo, - -1, - &bstr, - NULL, NULL, NULL); - if (FAILED(hr)) { - OLE_RELEASE(pImplTypeInfo); - break; - } - pstr = ole_wc2mb(bstr); - if (strcmp(pitf, pstr) == 0) { - hr = pImplTypeInfo->lpVtbl->GetTypeAttr(pImplTypeInfo, - &pImplTypeAttr); - if (SUCCEEDED(hr)) { - is_found = TRUE; - *piid = pImplTypeAttr->guid; - if (ppTypeInfo) { - *ppTypeInfo = pImplTypeInfo; - (*ppTypeInfo)->lpVtbl->AddRef((*ppTypeInfo)); - } - pImplTypeInfo->lpVtbl->ReleaseTypeAttr(pImplTypeInfo, - pImplTypeAttr); - } - } - free(pstr); - OLE_RELEASE(pImplTypeInfo); - if (is_found || FAILED(hr)) - break; - } - } - - OLE_RELEASE_TYPEATTR(pTypeInfo, pTypeAttr); - OLE_RELEASE(pTypeInfo); - if (is_found || FAILED(hr)) - break; - } - OLE_RELEASE(pTypeLib); - if(!is_found) - return E_NOINTERFACE; - return hr; -} - -static HRESULT -find_coclass( - ITypeInfo *pTypeInfo, - TYPEATTR *pTypeAttr, - ITypeInfo **pCOTypeInfo, - TYPEATTR **pCOTypeAttr) -{ - HRESULT hr = E_NOINTERFACE; - ITypeLib *pTypeLib; - int count; - BOOL found = FALSE; - ITypeInfo *pTypeInfo2; - TYPEATTR *pTypeAttr2; - int flags; - int i,j; - HREFTYPE href; - ITypeInfo *pRefTypeInfo; - TYPEATTR *pRefTypeAttr; - - hr = pTypeInfo->lpVtbl->GetContainingTypeLib(pTypeInfo, &pTypeLib, NULL); - if (FAILED(hr)) { - return hr; - } - count = pTypeLib->lpVtbl->GetTypeInfoCount(pTypeLib); - for (i = 0; i < count && !found; i++) { - hr = pTypeLib->lpVtbl->GetTypeInfo(pTypeLib, i, &pTypeInfo2); - if (FAILED(hr)) - continue; - hr = OLE_GET_TYPEATTR(pTypeInfo2, &pTypeAttr2); - if (FAILED(hr)) { - OLE_RELEASE(pTypeInfo2); - continue; - } - if (pTypeAttr2->typekind != TKIND_COCLASS) { - OLE_RELEASE_TYPEATTR(pTypeInfo2, pTypeAttr2); - OLE_RELEASE(pTypeInfo2); - continue; - } - for (j = 0; j < pTypeAttr2->cImplTypes && !found; j++) { - hr = pTypeInfo2->lpVtbl->GetImplTypeFlags(pTypeInfo2, j, &flags); - if (FAILED(hr)) - continue; - if (!(flags & IMPLTYPEFLAG_FDEFAULT)) - continue; - hr = pTypeInfo2->lpVtbl->GetRefTypeOfImplType(pTypeInfo2, j, &href); - if (FAILED(hr)) - continue; - hr = pTypeInfo2->lpVtbl->GetRefTypeInfo(pTypeInfo2, href, &pRefTypeInfo); - if (FAILED(hr)) - continue; - hr = OLE_GET_TYPEATTR(pRefTypeInfo, &pRefTypeAttr); - if (FAILED(hr)) { - OLE_RELEASE(pRefTypeInfo); - continue; - } - if (IsEqualGUID(&(pTypeAttr->guid), &(pRefTypeAttr->guid))) { - found = TRUE; - } - } - if (!found) { - OLE_RELEASE_TYPEATTR(pTypeInfo2, pTypeAttr2); - OLE_RELEASE(pTypeInfo2); - } - } - OLE_RELEASE(pTypeLib); - if (found) { - *pCOTypeInfo = pTypeInfo2; - *pCOTypeAttr = pTypeAttr2; - hr = S_OK; - } else { - hr = E_NOINTERFACE; - } - return hr; -} - -static HRESULT -find_default_source_from_typeinfo( - ITypeInfo *pTypeInfo, - TYPEATTR *pTypeAttr, - ITypeInfo **ppTypeInfo) -{ - int i = 0; - HRESULT hr = E_NOINTERFACE; - int flags; - HREFTYPE hRefType; - /* Enumerate all implemented types of the COCLASS */ - for (i = 0; i < pTypeAttr->cImplTypes; i++) { - hr = pTypeInfo->lpVtbl->GetImplTypeFlags(pTypeInfo, i, &flags); - if (FAILED(hr)) - continue; - - /* - looking for the [default] [source] - we just hope that it is a dispinterface :-) - */ - if ((flags & IMPLTYPEFLAG_FDEFAULT) && - (flags & IMPLTYPEFLAG_FSOURCE)) { - - hr = pTypeInfo->lpVtbl->GetRefTypeOfImplType(pTypeInfo, - i, &hRefType); - if (FAILED(hr)) - continue; - hr = pTypeInfo->lpVtbl->GetRefTypeInfo(pTypeInfo, - hRefType, ppTypeInfo); - if (SUCCEEDED(hr)) - break; - } - } - return hr; -} - -static HRESULT -find_default_source(VALUE ole, IID *piid, ITypeInfo **ppTypeInfo) -{ - HRESULT hr; - IProvideClassInfo2 *pProvideClassInfo2; - IProvideClassInfo *pProvideClassInfo; - void *p; - - IDispatch *pDispatch; - ITypeInfo *pTypeInfo; - ITypeInfo *pTypeInfo2 = NULL; - TYPEATTR *pTypeAttr; - TYPEATTR *pTypeAttr2 = NULL; - - struct oledata *pole; - - OLEData_Get_Struct(ole, pole); - pDispatch = pole->pDispatch; - hr = pDispatch->lpVtbl->QueryInterface(pDispatch, - &IID_IProvideClassInfo2, - &p); - if (SUCCEEDED(hr)) { - pProvideClassInfo2 = p; - hr = pProvideClassInfo2->lpVtbl->GetGUID(pProvideClassInfo2, - GUIDKIND_DEFAULT_SOURCE_DISP_IID, - piid); - OLE_RELEASE(pProvideClassInfo2); - if (SUCCEEDED(hr)) { - hr = find_iid(ole, NULL, piid, ppTypeInfo); - } - } - if (SUCCEEDED(hr)) { - return hr; - } - hr = pDispatch->lpVtbl->QueryInterface(pDispatch, - &IID_IProvideClassInfo, - &p); - if (SUCCEEDED(hr)) { - pProvideClassInfo = p; - hr = pProvideClassInfo->lpVtbl->GetClassInfo(pProvideClassInfo, - &pTypeInfo); - OLE_RELEASE(pProvideClassInfo); - } - if (FAILED(hr)) { - hr = pDispatch->lpVtbl->GetTypeInfo(pDispatch, 0, cWIN32OLE_lcid, &pTypeInfo ); - } - if (FAILED(hr)) - return hr; - hr = OLE_GET_TYPEATTR(pTypeInfo, &pTypeAttr); - if (FAILED(hr)) { - OLE_RELEASE(pTypeInfo); - return hr; - } - - *ppTypeInfo = 0; - hr = find_default_source_from_typeinfo(pTypeInfo, pTypeAttr, ppTypeInfo); - if (!*ppTypeInfo) { - hr = find_coclass(pTypeInfo, pTypeAttr, &pTypeInfo2, &pTypeAttr2); - if (SUCCEEDED(hr)) { - hr = find_default_source_from_typeinfo(pTypeInfo2, pTypeAttr2, ppTypeInfo); - OLE_RELEASE_TYPEATTR(pTypeInfo2, pTypeAttr2); - OLE_RELEASE(pTypeInfo2); - } - } - OLE_RELEASE_TYPEATTR(pTypeInfo, pTypeAttr); - OLE_RELEASE(pTypeInfo); - /* Now that would be a bad surprise, if we didn't find it, wouldn't it? */ - if (!*ppTypeInfo) { - if (SUCCEEDED(hr)) - hr = E_UNEXPECTED; - return hr; - } - - /* Determine IID of default source interface */ - hr = (*ppTypeInfo)->lpVtbl->GetTypeAttr(*ppTypeInfo, &pTypeAttr); - if (SUCCEEDED(hr)) { - *piid = pTypeAttr->guid; - (*ppTypeInfo)->lpVtbl->ReleaseTypeAttr(*ppTypeInfo, pTypeAttr); - } - else - OLE_RELEASE(*ppTypeInfo); - - return hr; - -} - -static void -ole_event_free(struct oleeventdata *poleev) -{ - if (poleev->pConnectionPoint) { - poleev->pConnectionPoint->lpVtbl->Unadvise(poleev->pConnectionPoint, poleev->dwCookie); - OLE_RELEASE(poleev->pConnectionPoint); - poleev->pConnectionPoint = NULL; - } - free(poleev); -} - -static VALUE -fev_s_allocate(VALUE klass) -{ - VALUE obj; - struct oleeventdata *poleev; - obj = Data_Make_Struct(klass,struct oleeventdata,0,ole_event_free,poleev); - poleev->dwCookie = 0; - poleev->pConnectionPoint = NULL; - poleev->event_id = 0; - return obj; -} - -static VALUE -ev_advise(int argc, VALUE *argv, VALUE self) -{ - - VALUE ole, itf; - struct oledata *pole; - char *pitf; - HRESULT hr; - IID iid; - ITypeInfo *pTypeInfo = 0; - IDispatch *pDispatch; - IConnectionPointContainer *pContainer; - IConnectionPoint *pConnectionPoint; - IEVENTSINKOBJ *pIEV; - DWORD dwCookie; - struct oleeventdata *poleev; - void *p; - - rb_scan_args(argc, argv, "11", &ole, &itf); - - if (!rb_obj_is_kind_of(ole, cWIN32OLE)) { - rb_raise(rb_eTypeError, "1st parameter must be WIN32OLE object"); - } - - if(!RB_TYPE_P(itf, T_NIL)) { - if (rb_safe_level() > 0 && OBJ_TAINTED(itf)) { - rb_raise(rb_eSecurityError, "Insecure Event Creation - %s", - StringValuePtr(itf)); - } - SafeStringValue(itf); - pitf = StringValuePtr(itf); - hr = find_iid(ole, pitf, &iid, &pTypeInfo); - } - else { - hr = find_default_source(ole, &iid, &pTypeInfo); - } - if (FAILED(hr)) { - ole_raise(hr, rb_eRuntimeError, "interface not found"); - } - - OLEData_Get_Struct(ole, pole); - pDispatch = pole->pDispatch; - hr = pDispatch->lpVtbl->QueryInterface(pDispatch, - &IID_IConnectionPointContainer, - &p); - if (FAILED(hr)) { - OLE_RELEASE(pTypeInfo); - ole_raise(hr, rb_eRuntimeError, - "failed to query IConnectionPointContainer"); - } - pContainer = p; - - hr = pContainer->lpVtbl->FindConnectionPoint(pContainer, - &iid, - &pConnectionPoint); - OLE_RELEASE(pContainer); - if (FAILED(hr)) { - OLE_RELEASE(pTypeInfo); - ole_raise(hr, rb_eRuntimeError, "failed to query IConnectionPoint"); - } - pIEV = EVENTSINK_Constructor(); - pIEV->m_iid = iid; - hr = pConnectionPoint->lpVtbl->Advise(pConnectionPoint, - (IUnknown*)pIEV, - &dwCookie); - if (FAILED(hr)) { - ole_raise(hr, rb_eRuntimeError, "Advise Error"); - } - - Data_Get_Struct(self, struct oleeventdata, poleev); - pIEV->m_event_id - = NUM2INT(evs_length()); - pIEV->pTypeInfo = pTypeInfo; - poleev->dwCookie = dwCookie; - poleev->pConnectionPoint = pConnectionPoint; - poleev->event_id = pIEV->m_event_id; - - return self; -} - -/* - * call-seq: - * WIN32OLE_EVENT.new(ole, event) #=> WIN32OLE_EVENT object. - * - * Returns OLE event object. - * The first argument specifies WIN32OLE object. - * The second argument specifies OLE event name. - * ie = WIN32OLE.new('InternetExplorer.Application') - * ev = WIN32OLE_EVENT.new(ie, 'DWebBrowserEvents') - */ -static VALUE -fev_initialize(int argc, VALUE *argv, VALUE self) -{ - ev_advise(argc, argv, self); - evs_push(self); - rb_ivar_set(self, id_events, rb_ary_new()); - fev_set_handler(self, Qnil); - return self; -} - -/* - * call-seq: - * WIN32OLE_EVENT.message_loop - * - * Translates and dispatches Windows message. - */ -static VALUE -fev_s_msg_loop(VALUE klass) -{ - ole_msg_loop(); - return Qnil; -} - - -static void -add_event_call_back(VALUE obj, VALUE event, VALUE data) -{ - VALUE events = rb_ivar_get(obj, id_events); - if (NIL_P(events) || !RB_TYPE_P(events, T_ARRAY)) { - events = rb_ary_new(); - rb_ivar_set(obj, id_events, events); - } - ole_delete_event(events, event); - rb_ary_push(events, data); -} - -static VALUE -ev_on_event(int argc, VALUE *argv, VALUE self, VALUE is_ary_arg) -{ - struct oleeventdata *poleev; - VALUE event, args, data; - Data_Get_Struct(self, struct oleeventdata, poleev); - if (poleev->pConnectionPoint == NULL) { - rb_raise(eWIN32OLERuntimeError, "IConnectionPoint not found. You must call advise at first."); - } - rb_scan_args(argc, argv, "01*", &event, &args); - if(!NIL_P(event)) { - if(!RB_TYPE_P(event, T_STRING) && !RB_TYPE_P(event, T_SYMBOL)) { - rb_raise(rb_eTypeError, "wrong argument type (expected String or Symbol)"); - } - if (RB_TYPE_P(event, T_SYMBOL)) { - event = rb_sym_to_s(event); - } - } - data = rb_ary_new3(4, rb_block_proc(), event, args, is_ary_arg); - add_event_call_back(self, event, data); - return Qnil; -} - -/* - * call-seq: - * WIN32OLE_EVENT#on_event([event]){...} - * - * Defines the callback event. - * If argument is omitted, this method defines the callback of all events. - * If you want to modify reference argument in callback, return hash in - * callback. If you want to return value to OLE server as result of callback - * use `return' or :return. - * - * ie = WIN32OLE.new('InternetExplorer.Application') - * ev = WIN32OLE_EVENT.new(ie) - * ev.on_event("NavigateComplete") {|url| puts url} - * ev.on_event() {|ev, *args| puts "#{ev} fired"} - * - * ev.on_event("BeforeNavigate2") {|*args| - * ... - * # set true to BeforeNavigate reference argument `Cancel'. - * # Cancel is 7-th argument of BeforeNavigate, - * # so you can use 6 as key of hash instead of 'Cancel'. - * # The argument is counted from 0. - * # The hash key of 0 means first argument.) - * {:Cancel => true} # or {'Cancel' => true} or {6 => true} - * } - * - * ev.on_event(...) {|*args| - * {:return => 1, :xxx => yyy} - * } - */ -static VALUE -fev_on_event(int argc, VALUE *argv, VALUE self) -{ - return ev_on_event(argc, argv, self, Qfalse); -} - -/* - * call-seq: - * WIN32OLE_EVENT#on_event_with_outargs([event]){...} - * - * Defines the callback of event. - * If you want modify argument in callback, - * you could use this method instead of WIN32OLE_EVENT#on_event. - * - * ie = WIN32OLE.new('InternetExplorer.Application') - * ev = WIN32OLE_EVENT.new(ie) - * ev.on_event_with_outargs('BeforeNavigate2') {|*args| - * args.last[6] = true - * } - */ -static VALUE -fev_on_event_with_outargs(int argc, VALUE *argv, VALUE self) -{ - return ev_on_event(argc, argv, self, Qtrue); -} - -/* - * call-seq: - * WIN32OLE_EVENT#off_event([event]) - * - * removes the callback of event. - * - * ie = WIN32OLE.new('InternetExplorer.Application') - * ev = WIN32OLE_EVENT.new(ie) - * ev.on_event('BeforeNavigate2') {|*args| - * args.last[6] = true - * } - * ... - * ev.off_event('BeforeNavigate2') - * ... - */ -static VALUE -fev_off_event(int argc, VALUE *argv, VALUE self) -{ - VALUE event = Qnil; - VALUE events; - - rb_scan_args(argc, argv, "01", &event); - if(!NIL_P(event)) { - if(!RB_TYPE_P(event, T_STRING) && !RB_TYPE_P(event, T_SYMBOL)) { - rb_raise(rb_eTypeError, "wrong argument type (expected String or Symbol)"); - } - if (RB_TYPE_P(event, T_SYMBOL)) { - event = rb_sym_to_s(event); - } - } - events = rb_ivar_get(self, id_events); - if (NIL_P(events)) { - return Qnil; - } - ole_delete_event(events, event); - return Qnil; -} - -/* - * call-seq: - * WIN32OLE_EVENT#unadvise -> nil - * - * disconnects OLE server. If this method called, then the WIN32OLE_EVENT object - * does not receive the OLE server event any more. - * This method is trial implementation. - * - * ie = WIN32OLE.new('InternetExplorer.Application') - * ev = WIN32OLE_EVENT.new(ie) - * ev.on_event() {...} - * ... - * ev.unadvise - * - */ -static VALUE -fev_unadvise(VALUE self) -{ - struct oleeventdata *poleev; - Data_Get_Struct(self, struct oleeventdata, poleev); - if (poleev->pConnectionPoint) { - ole_msg_loop(); - evs_delete(poleev->event_id); - poleev->pConnectionPoint->lpVtbl->Unadvise(poleev->pConnectionPoint, poleev->dwCookie); - OLE_RELEASE(poleev->pConnectionPoint); - poleev->pConnectionPoint = NULL; - } - return Qnil; -} - -static VALUE -evs_push(VALUE ev) -{ - return rb_ary_push(ary_ole_event, ev); -} - -static VALUE -evs_delete(long i) -{ - rb_ary_store(ary_ole_event, i, Qnil); - return Qnil; -} - -static VALUE -evs_entry(long i) -{ - return rb_ary_entry(ary_ole_event, i); -} - -static VALUE -evs_length(void) -{ - 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")); -} - HRESULT typelib_from_val(VALUE obj, ITypeLib **pTypeLib) { @@ -5069,9 +3822,6 @@ Init_win32ole(void) { cWIN32OLE_lcid = LOCALE_SYSTEM_DEFAULT; g_ole_initialized_init(); - ary_ole_event = rb_ary_new(); - rb_gc_register_mark_object(ary_ole_event); - id_events = rb_intern("events"); com_vtbl.QueryInterface = QueryInterface; com_vtbl.AddRef = AddRef; @@ -5229,18 +3979,7 @@ Init_win32ole(void) Init_win32ole_variable(); Init_win32ole_method(); Init_win32ole_param(); - - cWIN32OLE_EVENT = rb_define_class("WIN32OLE_EVENT", rb_cObject); - rb_define_singleton_method(cWIN32OLE_EVENT, "message_loop", fev_s_msg_loop, 0); - rb_define_alloc_func(cWIN32OLE_EVENT, fev_s_allocate); - rb_define_method(cWIN32OLE_EVENT, "initialize", fev_initialize, -1); - rb_define_method(cWIN32OLE_EVENT, "on_event", fev_on_event, -1); - 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); - + Init_win32ole_event(); Init_win32ole_variant(); Init_win32ole_record(); Init_win32ole_error(); diff --git a/ext/win32ole/win32ole.h b/ext/win32ole/win32ole.h index 672e3472f3..880fe86466 100644 --- a/ext/win32ole/win32ole.h +++ b/ext/win32ole/win32ole.h @@ -108,6 +108,17 @@ #define OLE_GET_TYPEATTR(X, Y) ((X)->lpVtbl->GetTypeAttr((X), (Y))) #define OLE_RELEASE_TYPEATTR(X, Y) ((X)->lpVtbl->ReleaseTypeAttr((X), (Y))) +struct oledata { + IDispatch *pDispatch; +}; + +#define OLEData_Get_Struct(obj, pole) {\ + Data_Get_Struct(obj, struct oledata, pole);\ + if(!pole->pDispatch) {\ + rb_raise(rb_eRuntimeError, "failed to get Dispatch Interface");\ + }\ +} + VALUE cWIN32OLE; LCID cWIN32OLE_lcid; @@ -119,6 +130,7 @@ VALUE reg_get_val(HKEY hkey, const char *subkey); VALUE reg_get_val2(HKEY hkey, const char *subkey); void ole_initialize(void); VALUE default_inspect(VALUE self, const char *class_name); +char *ole_wc2mb(LPWSTR pw); VALUE ole_wc2vstr(LPWSTR pw, BOOL isfree); #define WC2VSTR(x) ole_wc2vstr((x), TRUE) @@ -141,6 +153,7 @@ HRESULT typelib_from_val(VALUE obj, ITypeLib **pTypeLib); #include "win32ole_variable.h" #include "win32ole_method.h" #include "win32ole_param.h" +#include "win32ole_event.h" #include "win32ole_variant.h" #include "win32ole_record.h" #include "win32ole_error.h" diff --git a/ext/win32ole/win32ole_event.c b/ext/win32ole/win32ole_event.c new file mode 100644 index 0000000000..79602eef72 --- /dev/null +++ b/ext/win32ole/win32ole_event.c @@ -0,0 +1,1254 @@ +#include "win32ole.h" + +/* + * Document-class: WIN32OLE_EVENT + * + * <code>WIN32OLE_EVENT</code> objects controls OLE event. + */ + +typedef struct { + struct IEventSinkVtbl * lpVtbl; +} IEventSink, *PEVENTSINK; + +typedef struct IEventSinkVtbl IEventSinkVtbl; + +struct IEventSinkVtbl { + STDMETHOD(QueryInterface)( + PEVENTSINK, + REFIID, + LPVOID *); + STDMETHOD_(ULONG, AddRef)(PEVENTSINK); + STDMETHOD_(ULONG, Release)(PEVENTSINK); + + STDMETHOD(GetTypeInfoCount)( + PEVENTSINK, + UINT *); + STDMETHOD(GetTypeInfo)( + PEVENTSINK, + UINT, + LCID, + ITypeInfo **); + STDMETHOD(GetIDsOfNames)( + PEVENTSINK, + REFIID, + OLECHAR **, + UINT, + LCID, + DISPID *); + STDMETHOD(Invoke)( + PEVENTSINK, + DISPID, + REFIID, + LCID, + WORD, + DISPPARAMS *, + VARIANT *, + EXCEPINFO *, + UINT *); +}; + +typedef struct tagIEVENTSINKOBJ { + IEventSinkVtbl *lpVtbl; + DWORD m_cRef; + IID m_iid; + int m_event_id; + ITypeInfo *pTypeInfo; +}IEVENTSINKOBJ, *PIEVENTSINKOBJ; + +struct oleeventdata { + DWORD dwCookie; + IConnectionPoint *pConnectionPoint; + long event_id; +}; + +static VALUE ary_ole_event; +static ID id_events; + +VALUE cWIN32OLE_EVENT; + +static BOOL g_IsEventSinkVtblInitialized = FALSE; + +static IEventSinkVtbl vtEventSink; + +void EVENTSINK_Destructor(PIEVENTSINKOBJ); +static void ole_val2ptr_variant(VALUE val, VARIANT *var); +static void hash2ptr_dispparams(VALUE hash, ITypeInfo *pTypeInfo, DISPID dispid, DISPPARAMS *pdispparams); +static VALUE hash2result(VALUE hash); +static void ary2ptr_dispparams(VALUE ary, DISPPARAMS *pdispparams); +static VALUE exec_callback(VALUE arg); +static VALUE rescue_callback(VALUE arg); +static HRESULT find_iid(VALUE ole, char *pitf, IID *piid, ITypeInfo **ppTypeInfo); +static HRESULT find_coclass(ITypeInfo *pTypeInfo, TYPEATTR *pTypeAttr, ITypeInfo **pTypeInfo2, TYPEATTR **pTypeAttr2); +static HRESULT find_default_source_from_typeinfo(ITypeInfo *pTypeInfo, TYPEATTR *pTypeAttr, ITypeInfo **ppTypeInfo); +static HRESULT find_default_source(VALUE ole, IID *piid, ITypeInfo **ppTypeInfo); +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 ole_event_free(struct oleeventdata *poleev); +static VALUE fev_s_allocate(VALUE klass); +static VALUE ev_advise(int argc, VALUE *argv, VALUE self); +static VALUE fev_initialize(int argc, VALUE *argv, VALUE self); +static void ole_msg_loop(void); +static VALUE fev_s_msg_loop(VALUE klass); +static void add_event_call_back(VALUE obj, VALUE event, VALUE data); +static VALUE ev_on_event(int argc, VALUE *argv, VALUE self, VALUE is_ary_arg); +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); +static VALUE evs_length(void); + +STDMETHODIMP EVENTSINK_Invoke( + PEVENTSINK pEventSink, + DISPID dispid, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS *pdispparams, + VARIANT *pvarResult, + EXCEPINFO *pexcepinfo, + UINT *puArgErr + ) { + + HRESULT hr; + BSTR bstr; + unsigned int count; + unsigned int i; + ITypeInfo *pTypeInfo; + VARIANT *pvar; + 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; + + PIEVENTSINKOBJ pEV = (PIEVENTSINKOBJ)pEventSink; + pTypeInfo = pEV->pTypeInfo; + obj = evs_entry(pEV->m_event_id); + if (!rb_obj_is_kind_of(obj, cWIN32OLE_EVENT)) { + return NOERROR; + } + + ary = rb_ivar_get(obj, id_events); + if (NIL_P(ary) || !RB_TYPE_P(ary, T_ARRAY)) { + return NOERROR; + } + hr = pTypeInfo->lpVtbl->GetNames(pTypeInfo, dispid, + &bstr, 1, &count); + if (FAILED(hr)) { + return NOERROR; + } + ev = WC2VSTR(bstr); + event = ole_search_event(ary, ev, &is_default_handler); + if (RB_TYPE_P(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); + } + + /* make argument of event handler */ + for (i = 0; i < pdispparams->cArgs; ++i) { + pvar = &pdispparams->rgvarg[pdispparams->cArgs-i-1]; + rb_ary_push(args, ole_variant2val(pvar)); + } + outargv = Qnil; + if (is_outarg == Qtrue) { + outargv = rb_ary_new(); + rb_ary_push(args, outargv); + } + + /* + * if exception raised in event callback, + * then you receive cfp consistency error. + * to avoid this error we use begin rescue end. + * and the exception raised then error message print + * and exit ruby process by Win32OLE itself. + */ + arg[0] = handler; + arg[1] = mid; + arg[2] = args; + result = rb_protect(exec_callback, (VALUE)arg, &state); + if (state != 0) { + rescue_callback(Qnil); + } + if(RB_TYPE_P(result, T_HASH)) { + hash2ptr_dispparams(result, pTypeInfo, dispid, pdispparams); + result = hash2result(result); + }else if (is_outarg == Qtrue && RB_TYPE_P(outargv, T_ARRAY)) { + ary2ptr_dispparams(outargv, pdispparams); + } + + if (pvarResult) { + VariantInit(pvarResult); + ole_val2variant(result, pvarResult); + } + + return NOERROR; +} + +STDMETHODIMP +EVENTSINK_QueryInterface( + PEVENTSINK pEV, + REFIID iid, + LPVOID* ppv + ) { + if (IsEqualIID(iid, &IID_IUnknown) || + IsEqualIID(iid, &IID_IDispatch) || + IsEqualIID(iid, &((PIEVENTSINKOBJ)pEV)->m_iid)) { + *ppv = pEV; + } + else { + *ppv = NULL; + return E_NOINTERFACE; + } + ((LPUNKNOWN)*ppv)->lpVtbl->AddRef((LPUNKNOWN)*ppv); + return NOERROR; +} + +STDMETHODIMP_(ULONG) +EVENTSINK_AddRef( + PEVENTSINK pEV + ){ + PIEVENTSINKOBJ pEVObj = (PIEVENTSINKOBJ)pEV; + return ++pEVObj->m_cRef; +} + +STDMETHODIMP_(ULONG) EVENTSINK_Release( + PEVENTSINK pEV + ) { + PIEVENTSINKOBJ pEVObj = (PIEVENTSINKOBJ)pEV; + --pEVObj->m_cRef; + if(pEVObj->m_cRef != 0) + return pEVObj->m_cRef; + EVENTSINK_Destructor(pEVObj); + return 0; +} + +STDMETHODIMP EVENTSINK_GetTypeInfoCount( + PEVENTSINK pEV, + UINT *pct + ) { + *pct = 0; + return NOERROR; +} + +STDMETHODIMP EVENTSINK_GetTypeInfo( + PEVENTSINK pEV, + UINT info, + LCID lcid, + ITypeInfo **pInfo + ) { + *pInfo = NULL; + return DISP_E_BADINDEX; +} + +STDMETHODIMP EVENTSINK_GetIDsOfNames( + PEVENTSINK pEventSink, + REFIID riid, + OLECHAR **szNames, + UINT cNames, + LCID lcid, + DISPID *pDispID + ) { + ITypeInfo *pTypeInfo; + PIEVENTSINKOBJ pEV = (PIEVENTSINKOBJ)pEventSink; + pTypeInfo = pEV->pTypeInfo; + if (pTypeInfo) { + return pTypeInfo->lpVtbl->GetIDsOfNames(pTypeInfo, szNames, cNames, pDispID); + } + return DISP_E_UNKNOWNNAME; +} + +PIEVENTSINKOBJ +EVENTSINK_Constructor() { + PIEVENTSINKOBJ pEv; + if (!g_IsEventSinkVtblInitialized) { + vtEventSink.QueryInterface=EVENTSINK_QueryInterface; + vtEventSink.AddRef = EVENTSINK_AddRef; + vtEventSink.Release = EVENTSINK_Release; + vtEventSink.Invoke = EVENTSINK_Invoke; + vtEventSink.GetIDsOfNames = EVENTSINK_GetIDsOfNames; + vtEventSink.GetTypeInfoCount = EVENTSINK_GetTypeInfoCount; + vtEventSink.GetTypeInfo = EVENTSINK_GetTypeInfo; + + g_IsEventSinkVtblInitialized = TRUE; + } + pEv = ALLOC_N(IEVENTSINKOBJ, 1); + if(pEv == NULL) return NULL; + pEv->lpVtbl = &vtEventSink; + pEv->m_cRef = 0; + pEv->m_event_id = 0; + pEv->pTypeInfo = NULL; + return pEv; +} + +void +EVENTSINK_Destructor( + PIEVENTSINKOBJ pEVObj + ) { + if(pEVObj != NULL) { + OLE_RELEASE(pEVObj->pTypeInfo); + free(pEVObj); + pEVObj = NULL; + } +} + +static void +ole_val2ptr_variant(VALUE val, VARIANT *var) +{ + switch (TYPE(val)) { + case T_STRING: + if (V_VT(var) == (VT_BSTR | VT_BYREF)) { + *V_BSTRREF(var) = ole_vstr2wc(val); + } + break; + case T_FIXNUM: + switch(V_VT(var)) { + case (VT_UI1 | VT_BYREF) : + *V_UI1REF(var) = NUM2CHR(val); + break; + case (VT_I2 | VT_BYREF) : + *V_I2REF(var) = (short)NUM2INT(val); + break; + case (VT_I4 | VT_BYREF) : + *V_I4REF(var) = NUM2INT(val); + break; + case (VT_R4 | VT_BYREF) : + *V_R4REF(var) = (float)NUM2INT(val); + break; + case (VT_R8 | VT_BYREF) : + *V_R8REF(var) = NUM2INT(val); + break; + default: + break; + } + break; + case T_FLOAT: + switch(V_VT(var)) { + case (VT_I2 | VT_BYREF) : + *V_I2REF(var) = (short)NUM2INT(val); + break; + case (VT_I4 | VT_BYREF) : + *V_I4REF(var) = NUM2INT(val); + break; + case (VT_R4 | VT_BYREF) : + *V_R4REF(var) = (float)NUM2DBL(val); + break; + case (VT_R8 | VT_BYREF) : + *V_R8REF(var) = NUM2DBL(val); + break; + default: + break; + } + break; + case T_BIGNUM: + if (V_VT(var) == (VT_R8 | VT_BYREF)) { + *V_R8REF(var) = rb_big2dbl(val); + } + break; + case T_TRUE: + if (V_VT(var) == (VT_BOOL | VT_BYREF)) { + *V_BOOLREF(var) = VARIANT_TRUE; + } + break; + case T_FALSE: + if (V_VT(var) == (VT_BOOL | VT_BYREF)) { + *V_BOOLREF(var) = VARIANT_FALSE; + } + break; + default: + break; + } +} + +static void +hash2ptr_dispparams(VALUE hash, ITypeInfo *pTypeInfo, DISPID dispid, DISPPARAMS *pdispparams) +{ + BSTR *bstrs; + HRESULT hr; + UINT len, i; + VARIANT *pvar; + VALUE val; + VALUE key; + len = 0; + bstrs = ALLOCA_N(BSTR, pdispparams->cArgs + 1); + hr = pTypeInfo->lpVtbl->GetNames(pTypeInfo, dispid, + bstrs, pdispparams->cArgs + 1, + &len); + if (FAILED(hr)) + return; + + for (i = 0; i < len - 1; i++) { + key = WC2VSTR(bstrs[i + 1]); + val = rb_hash_aref(hash, INT2FIX(i)); + if (val == Qnil) + val = rb_hash_aref(hash, key); + if (val == Qnil) + val = rb_hash_aref(hash, rb_str_intern(key)); + pvar = &pdispparams->rgvarg[pdispparams->cArgs-i-1]; + ole_val2ptr_variant(val, pvar); + } +} + +static VALUE +hash2result(VALUE hash) +{ + VALUE ret = Qnil; + ret = rb_hash_aref(hash, rb_str_new2("return")); + if (ret == Qnil) + ret = rb_hash_aref(hash, rb_str_intern(rb_str_new2("return"))); + return ret; +} + +static void +ary2ptr_dispparams(VALUE ary, DISPPARAMS *pdispparams) +{ + int i; + VALUE v; + VARIANT *pvar; + for(i = 0; i < RARRAY_LEN(ary) && (unsigned int) i < pdispparams->cArgs; i++) { + v = rb_ary_entry(ary, i); + pvar = &pdispparams->rgvarg[pdispparams->cArgs-i-1]; + ole_val2ptr_variant(v, pvar); + } +} + +static VALUE +exec_callback(VALUE arg) +{ + VALUE *parg = (VALUE *)arg; + VALUE handler = parg[0]; + VALUE mid = parg[1]; + VALUE args = parg[2]; + return rb_apply(handler, mid, args); +} + +static VALUE +rescue_callback(VALUE arg) +{ + + VALUE error; + VALUE e = rb_errinfo(); + VALUE bt = rb_funcall(e, rb_intern("backtrace"), 0); + VALUE msg = rb_funcall(e, rb_intern("message"), 0); + bt = rb_ary_entry(bt, 0); + error = rb_sprintf("%"PRIsVALUE": %"PRIsVALUE" (%s)\n", bt, msg, rb_obj_classname(e)); + rb_write_error(StringValuePtr(error)); + rb_backtrace(); + ruby_finalize(); + exit(-1); + + return Qnil; +} + +static HRESULT +find_iid(VALUE ole, char *pitf, IID *piid, ITypeInfo **ppTypeInfo) +{ + HRESULT hr; + IDispatch *pDispatch; + ITypeInfo *pTypeInfo; + ITypeLib *pTypeLib; + TYPEATTR *pTypeAttr; + HREFTYPE RefType; + ITypeInfo *pImplTypeInfo; + TYPEATTR *pImplTypeAttr; + + struct oledata *pole; + unsigned int index; + unsigned int count; + int type; + BSTR bstr; + char *pstr; + + BOOL is_found = FALSE; + LCID lcid = cWIN32OLE_lcid; + + OLEData_Get_Struct(ole, pole); + + pDispatch = pole->pDispatch; + + hr = pDispatch->lpVtbl->GetTypeInfo(pDispatch, 0, lcid, &pTypeInfo); + if (FAILED(hr)) + return hr; + + hr = pTypeInfo->lpVtbl->GetContainingTypeLib(pTypeInfo, + &pTypeLib, + &index); + OLE_RELEASE(pTypeInfo); + if (FAILED(hr)) + return hr; + + if (!pitf) { + hr = pTypeLib->lpVtbl->GetTypeInfoOfGuid(pTypeLib, + piid, + ppTypeInfo); + OLE_RELEASE(pTypeLib); + return hr; + } + count = pTypeLib->lpVtbl->GetTypeInfoCount(pTypeLib); + for (index = 0; index < count; index++) { + hr = pTypeLib->lpVtbl->GetTypeInfo(pTypeLib, + index, + &pTypeInfo); + if (FAILED(hr)) + break; + hr = OLE_GET_TYPEATTR(pTypeInfo, &pTypeAttr); + + if(FAILED(hr)) { + OLE_RELEASE(pTypeInfo); + break; + } + if(pTypeAttr->typekind == TKIND_COCLASS) { + for (type = 0; type < pTypeAttr->cImplTypes; type++) { + hr = pTypeInfo->lpVtbl->GetRefTypeOfImplType(pTypeInfo, + type, + &RefType); + if (FAILED(hr)) + break; + hr = pTypeInfo->lpVtbl->GetRefTypeInfo(pTypeInfo, + RefType, + &pImplTypeInfo); + if (FAILED(hr)) + break; + + hr = pImplTypeInfo->lpVtbl->GetDocumentation(pImplTypeInfo, + -1, + &bstr, + NULL, NULL, NULL); + if (FAILED(hr)) { + OLE_RELEASE(pImplTypeInfo); + break; + } + pstr = ole_wc2mb(bstr); + if (strcmp(pitf, pstr) == 0) { + hr = pImplTypeInfo->lpVtbl->GetTypeAttr(pImplTypeInfo, + &pImplTypeAttr); + if (SUCCEEDED(hr)) { + is_found = TRUE; + *piid = pImplTypeAttr->guid; + if (ppTypeInfo) { + *ppTypeInfo = pImplTypeInfo; + (*ppTypeInfo)->lpVtbl->AddRef((*ppTypeInfo)); + } + pImplTypeInfo->lpVtbl->ReleaseTypeAttr(pImplTypeInfo, + pImplTypeAttr); + } + } + free(pstr); + OLE_RELEASE(pImplTypeInfo); + if (is_found || FAILED(hr)) + break; + } + } + + OLE_RELEASE_TYPEATTR(pTypeInfo, pTypeAttr); + OLE_RELEASE(pTypeInfo); + if (is_found || FAILED(hr)) + break; + } + OLE_RELEASE(pTypeLib); + if(!is_found) + return E_NOINTERFACE; + return hr; +} + +static HRESULT +find_coclass( + ITypeInfo *pTypeInfo, + TYPEATTR *pTypeAttr, + ITypeInfo **pCOTypeInfo, + TYPEATTR **pCOTypeAttr) +{ + HRESULT hr = E_NOINTERFACE; + ITypeLib *pTypeLib; + int count; + BOOL found = FALSE; + ITypeInfo *pTypeInfo2; + TYPEATTR *pTypeAttr2; + int flags; + int i,j; + HREFTYPE href; + ITypeInfo *pRefTypeInfo; + TYPEATTR *pRefTypeAttr; + + hr = pTypeInfo->lpVtbl->GetContainingTypeLib(pTypeInfo, &pTypeLib, NULL); + if (FAILED(hr)) { + return hr; + } + count = pTypeLib->lpVtbl->GetTypeInfoCount(pTypeLib); + for (i = 0; i < count && !found; i++) { + hr = pTypeLib->lpVtbl->GetTypeInfo(pTypeLib, i, &pTypeInfo2); + if (FAILED(hr)) + continue; + hr = OLE_GET_TYPEATTR(pTypeInfo2, &pTypeAttr2); + if (FAILED(hr)) { + OLE_RELEASE(pTypeInfo2); + continue; + } + if (pTypeAttr2->typekind != TKIND_COCLASS) { + OLE_RELEASE_TYPEATTR(pTypeInfo2, pTypeAttr2); + OLE_RELEASE(pTypeInfo2); + continue; + } + for (j = 0; j < pTypeAttr2->cImplTypes && !found; j++) { + hr = pTypeInfo2->lpVtbl->GetImplTypeFlags(pTypeInfo2, j, &flags); + if (FAILED(hr)) + continue; + if (!(flags & IMPLTYPEFLAG_FDEFAULT)) + continue; + hr = pTypeInfo2->lpVtbl->GetRefTypeOfImplType(pTypeInfo2, j, &href); + if (FAILED(hr)) + continue; + hr = pTypeInfo2->lpVtbl->GetRefTypeInfo(pTypeInfo2, href, &pRefTypeInfo); + if (FAILED(hr)) + continue; + hr = OLE_GET_TYPEATTR(pRefTypeInfo, &pRefTypeAttr); + if (FAILED(hr)) { + OLE_RELEASE(pRefTypeInfo); + continue; + } + if (IsEqualGUID(&(pTypeAttr->guid), &(pRefTypeAttr->guid))) { + found = TRUE; + } + } + if (!found) { + OLE_RELEASE_TYPEATTR(pTypeInfo2, pTypeAttr2); + OLE_RELEASE(pTypeInfo2); + } + } + OLE_RELEASE(pTypeLib); + if (found) { + *pCOTypeInfo = pTypeInfo2; + *pCOTypeAttr = pTypeAttr2; + hr = S_OK; + } else { + hr = E_NOINTERFACE; + } + return hr; +} + +static HRESULT +find_default_source_from_typeinfo( + ITypeInfo *pTypeInfo, + TYPEATTR *pTypeAttr, + ITypeInfo **ppTypeInfo) +{ + int i = 0; + HRESULT hr = E_NOINTERFACE; + int flags; + HREFTYPE hRefType; + /* Enumerate all implemented types of the COCLASS */ + for (i = 0; i < pTypeAttr->cImplTypes; i++) { + hr = pTypeInfo->lpVtbl->GetImplTypeFlags(pTypeInfo, i, &flags); + if (FAILED(hr)) + continue; + + /* + looking for the [default] [source] + we just hope that it is a dispinterface :-) + */ + if ((flags & IMPLTYPEFLAG_FDEFAULT) && + (flags & IMPLTYPEFLAG_FSOURCE)) { + + hr = pTypeInfo->lpVtbl->GetRefTypeOfImplType(pTypeInfo, + i, &hRefType); + if (FAILED(hr)) + continue; + hr = pTypeInfo->lpVtbl->GetRefTypeInfo(pTypeInfo, + hRefType, ppTypeInfo); + if (SUCCEEDED(hr)) + break; + } + } + return hr; +} + +static HRESULT +find_default_source(VALUE ole, IID *piid, ITypeInfo **ppTypeInfo) +{ + HRESULT hr; + IProvideClassInfo2 *pProvideClassInfo2; + IProvideClassInfo *pProvideClassInfo; + void *p; + + IDispatch *pDispatch; + ITypeInfo *pTypeInfo; + ITypeInfo *pTypeInfo2 = NULL; + TYPEATTR *pTypeAttr; + TYPEATTR *pTypeAttr2 = NULL; + + struct oledata *pole; + + OLEData_Get_Struct(ole, pole); + pDispatch = pole->pDispatch; + hr = pDispatch->lpVtbl->QueryInterface(pDispatch, + &IID_IProvideClassInfo2, + &p); + if (SUCCEEDED(hr)) { + pProvideClassInfo2 = p; + hr = pProvideClassInfo2->lpVtbl->GetGUID(pProvideClassInfo2, + GUIDKIND_DEFAULT_SOURCE_DISP_IID, + piid); + OLE_RELEASE(pProvideClassInfo2); + if (SUCCEEDED(hr)) { + hr = find_iid(ole, NULL, piid, ppTypeInfo); + } + } + if (SUCCEEDED(hr)) { + return hr; + } + hr = pDispatch->lpVtbl->QueryInterface(pDispatch, + &IID_IProvideClassInfo, + &p); + if (SUCCEEDED(hr)) { + pProvideClassInfo = p; + hr = pProvideClassInfo->lpVtbl->GetClassInfo(pProvideClassInfo, + &pTypeInfo); + OLE_RELEASE(pProvideClassInfo); + } + if (FAILED(hr)) { + hr = pDispatch->lpVtbl->GetTypeInfo(pDispatch, 0, cWIN32OLE_lcid, &pTypeInfo ); + } + if (FAILED(hr)) + return hr; + hr = OLE_GET_TYPEATTR(pTypeInfo, &pTypeAttr); + if (FAILED(hr)) { + OLE_RELEASE(pTypeInfo); + return hr; + } + + *ppTypeInfo = 0; + hr = find_default_source_from_typeinfo(pTypeInfo, pTypeAttr, ppTypeInfo); + if (!*ppTypeInfo) { + hr = find_coclass(pTypeInfo, pTypeAttr, &pTypeInfo2, &pTypeAttr2); + if (SUCCEEDED(hr)) { + hr = find_default_source_from_typeinfo(pTypeInfo2, pTypeAttr2, ppTypeInfo); + OLE_RELEASE_TYPEATTR(pTypeInfo2, pTypeAttr2); + OLE_RELEASE(pTypeInfo2); + } + } + OLE_RELEASE_TYPEATTR(pTypeInfo, pTypeAttr); + OLE_RELEASE(pTypeInfo); + /* Now that would be a bad surprise, if we didn't find it, wouldn't it? */ + if (!*ppTypeInfo) { + if (SUCCEEDED(hr)) + hr = E_UNEXPECTED; + return hr; + } + + /* Determine IID of default source interface */ + hr = (*ppTypeInfo)->lpVtbl->GetTypeAttr(*ppTypeInfo, &pTypeAttr); + if (SUCCEEDED(hr)) { + *piid = pTypeAttr->guid; + (*ppTypeInfo)->lpVtbl->ReleaseTypeAttr(*ppTypeInfo, pTypeAttr); + } + else + OLE_RELEASE(*ppTypeInfo); + + return hr; +} + +static long +ole_search_event_at(VALUE ary, VALUE ev) +{ + VALUE event; + VALUE event_name; + long i, len; + long ret = -1; + len = RARRAY_LEN(ary); + for(i = 0; i < len; i++) { + event = rb_ary_entry(ary, i); + event_name = rb_ary_entry(event, 1); + if(NIL_P(event_name) && NIL_P(ev)) { + ret = i; + break; + } + else if (RB_TYPE_P(ev, T_STRING) && + RB_TYPE_P(event_name, T_STRING) && + rb_str_cmp(ev, event_name) == 0) { + ret = i; + break; + } + } + return ret; +} + +static VALUE +ole_search_event(VALUE ary, VALUE ev, BOOL *is_default) +{ + VALUE event; + VALUE def_event; + VALUE event_name; + int i, len; + *is_default = FALSE; + def_event = Qnil; + len = RARRAY_LEN(ary); + for(i = 0; i < len; i++) { + event = rb_ary_entry(ary, i); + event_name = rb_ary_entry(event, 1); + if(NIL_P(event_name)) { + *is_default = TRUE; + def_event = event; + } + else if (rb_str_cmp(ev, event_name) == 0) { + *is_default = FALSE; + return event; + } + } + 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%"PRIsVALUE, 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) +{ + long at = -1; + at = ole_search_event_at(ary, ev); + if (at >= 0) { + rb_ary_delete_at(ary, at); + } +} + + +static void +ole_event_free(struct oleeventdata *poleev) +{ + if (poleev->pConnectionPoint) { + poleev->pConnectionPoint->lpVtbl->Unadvise(poleev->pConnectionPoint, poleev->dwCookie); + OLE_RELEASE(poleev->pConnectionPoint); + poleev->pConnectionPoint = NULL; + } + free(poleev); +} + +static VALUE +fev_s_allocate(VALUE klass) +{ + VALUE obj; + struct oleeventdata *poleev; + obj = Data_Make_Struct(klass,struct oleeventdata,0,ole_event_free,poleev); + poleev->dwCookie = 0; + poleev->pConnectionPoint = NULL; + poleev->event_id = 0; + return obj; +} + +static VALUE +ev_advise(int argc, VALUE *argv, VALUE self) +{ + + VALUE ole, itf; + struct oledata *pole; + char *pitf; + HRESULT hr; + IID iid; + ITypeInfo *pTypeInfo = 0; + IDispatch *pDispatch; + IConnectionPointContainer *pContainer; + IConnectionPoint *pConnectionPoint; + IEVENTSINKOBJ *pIEV; + DWORD dwCookie; + struct oleeventdata *poleev; + void *p; + + rb_scan_args(argc, argv, "11", &ole, &itf); + + if (!rb_obj_is_kind_of(ole, cWIN32OLE)) { + rb_raise(rb_eTypeError, "1st parameter must be WIN32OLE object"); + } + + if(!RB_TYPE_P(itf, T_NIL)) { + if (rb_safe_level() > 0 && OBJ_TAINTED(itf)) { + rb_raise(rb_eSecurityError, "Insecure Event Creation - %s", + StringValuePtr(itf)); + } + SafeStringValue(itf); + pitf = StringValuePtr(itf); + hr = find_iid(ole, pitf, &iid, &pTypeInfo); + } + else { + hr = find_default_source(ole, &iid, &pTypeInfo); + } + if (FAILED(hr)) { + ole_raise(hr, rb_eRuntimeError, "interface not found"); + } + + OLEData_Get_Struct(ole, pole); + pDispatch = pole->pDispatch; + hr = pDispatch->lpVtbl->QueryInterface(pDispatch, + &IID_IConnectionPointContainer, + &p); + if (FAILED(hr)) { + OLE_RELEASE(pTypeInfo); + ole_raise(hr, rb_eRuntimeError, + "failed to query IConnectionPointContainer"); + } + pContainer = p; + + hr = pContainer->lpVtbl->FindConnectionPoint(pContainer, + &iid, + &pConnectionPoint); + OLE_RELEASE(pContainer); + if (FAILED(hr)) { + OLE_RELEASE(pTypeInfo); + ole_raise(hr, rb_eRuntimeError, "failed to query IConnectionPoint"); + } + pIEV = EVENTSINK_Constructor(); + pIEV->m_iid = iid; + hr = pConnectionPoint->lpVtbl->Advise(pConnectionPoint, + (IUnknown*)pIEV, + &dwCookie); + if (FAILED(hr)) { + ole_raise(hr, rb_eRuntimeError, "Advise Error"); + } + + Data_Get_Struct(self, struct oleeventdata, poleev); + pIEV->m_event_id + = NUM2INT(evs_length()); + pIEV->pTypeInfo = pTypeInfo; + poleev->dwCookie = dwCookie; + poleev->pConnectionPoint = pConnectionPoint; + poleev->event_id = pIEV->m_event_id; + + return self; +} + +/* + * call-seq: + * WIN32OLE_EVENT.new(ole, event) #=> WIN32OLE_EVENT object. + * + * Returns OLE event object. + * The first argument specifies WIN32OLE object. + * The second argument specifies OLE event name. + * ie = WIN32OLE.new('InternetExplorer.Application') + * ev = WIN32OLE_EVENT.new(ie, 'DWebBrowserEvents') + */ +static VALUE +fev_initialize(int argc, VALUE *argv, VALUE self) +{ + ev_advise(argc, argv, self); + evs_push(self); + rb_ivar_set(self, id_events, rb_ary_new()); + fev_set_handler(self, Qnil); + return self; +} + +static void +ole_msg_loop() { + MSG msg; + while(PeekMessage(&msg,NULL,0,0,PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } +} + +/* + * call-seq: + * WIN32OLE_EVENT.message_loop + * + * Translates and dispatches Windows message. + */ +static VALUE +fev_s_msg_loop(VALUE klass) +{ + ole_msg_loop(); + return Qnil; +} + +static void +add_event_call_back(VALUE obj, VALUE event, VALUE data) +{ + VALUE events = rb_ivar_get(obj, id_events); + if (NIL_P(events) || !RB_TYPE_P(events, T_ARRAY)) { + events = rb_ary_new(); + rb_ivar_set(obj, id_events, events); + } + ole_delete_event(events, event); + rb_ary_push(events, data); +} + +static VALUE +ev_on_event(int argc, VALUE *argv, VALUE self, VALUE is_ary_arg) +{ + struct oleeventdata *poleev; + VALUE event, args, data; + Data_Get_Struct(self, struct oleeventdata, poleev); + if (poleev->pConnectionPoint == NULL) { + rb_raise(eWIN32OLERuntimeError, "IConnectionPoint not found. You must call advise at first."); + } + rb_scan_args(argc, argv, "01*", &event, &args); + if(!NIL_P(event)) { + if(!RB_TYPE_P(event, T_STRING) && !RB_TYPE_P(event, T_SYMBOL)) { + rb_raise(rb_eTypeError, "wrong argument type (expected String or Symbol)"); + } + if (RB_TYPE_P(event, T_SYMBOL)) { + event = rb_sym_to_s(event); + } + } + data = rb_ary_new3(4, rb_block_proc(), event, args, is_ary_arg); + add_event_call_back(self, event, data); + return Qnil; +} + +/* + * call-seq: + * WIN32OLE_EVENT#on_event([event]){...} + * + * Defines the callback event. + * If argument is omitted, this method defines the callback of all events. + * If you want to modify reference argument in callback, return hash in + * callback. If you want to return value to OLE server as result of callback + * use `return' or :return. + * + * ie = WIN32OLE.new('InternetExplorer.Application') + * ev = WIN32OLE_EVENT.new(ie) + * ev.on_event("NavigateComplete") {|url| puts url} + * ev.on_event() {|ev, *args| puts "#{ev} fired"} + * + * ev.on_event("BeforeNavigate2") {|*args| + * ... + * # set true to BeforeNavigate reference argument `Cancel'. + * # Cancel is 7-th argument of BeforeNavigate, + * # so you can use 6 as key of hash instead of 'Cancel'. + * # The argument is counted from 0. + * # The hash key of 0 means first argument.) + * {:Cancel => true} # or {'Cancel' => true} or {6 => true} + * } + * + * ev.on_event(...) {|*args| + * {:return => 1, :xxx => yyy} + * } + */ +static VALUE +fev_on_event(int argc, VALUE *argv, VALUE self) +{ + return ev_on_event(argc, argv, self, Qfalse); +} + +/* + * call-seq: + * WIN32OLE_EVENT#on_event_with_outargs([event]){...} + * + * Defines the callback of event. + * If you want modify argument in callback, + * you could use this method instead of WIN32OLE_EVENT#on_event. + * + * ie = WIN32OLE.new('InternetExplorer.Application') + * ev = WIN32OLE_EVENT.new(ie) + * ev.on_event_with_outargs('BeforeNavigate2') {|*args| + * args.last[6] = true + * } + */ +static VALUE +fev_on_event_with_outargs(int argc, VALUE *argv, VALUE self) +{ + return ev_on_event(argc, argv, self, Qtrue); +} + +/* + * call-seq: + * WIN32OLE_EVENT#off_event([event]) + * + * removes the callback of event. + * + * ie = WIN32OLE.new('InternetExplorer.Application') + * ev = WIN32OLE_EVENT.new(ie) + * ev.on_event('BeforeNavigate2') {|*args| + * args.last[6] = true + * } + * ... + * ev.off_event('BeforeNavigate2') + * ... + */ +static VALUE +fev_off_event(int argc, VALUE *argv, VALUE self) +{ + VALUE event = Qnil; + VALUE events; + + rb_scan_args(argc, argv, "01", &event); + if(!NIL_P(event)) { + if(!RB_TYPE_P(event, T_STRING) && !RB_TYPE_P(event, T_SYMBOL)) { + rb_raise(rb_eTypeError, "wrong argument type (expected String or Symbol)"); + } + if (RB_TYPE_P(event, T_SYMBOL)) { + event = rb_sym_to_s(event); + } + } + events = rb_ivar_get(self, id_events); + if (NIL_P(events)) { + return Qnil; + } + ole_delete_event(events, event); + return Qnil; +} + +/* + * call-seq: + * WIN32OLE_EVENT#unadvise -> nil + * + * disconnects OLE server. If this method called, then the WIN32OLE_EVENT object + * does not receive the OLE server event any more. + * This method is trial implementation. + * + * ie = WIN32OLE.new('InternetExplorer.Application') + * ev = WIN32OLE_EVENT.new(ie) + * ev.on_event() {...} + * ... + * ev.unadvise + * + */ +static VALUE +fev_unadvise(VALUE self) +{ + struct oleeventdata *poleev; + Data_Get_Struct(self, struct oleeventdata, poleev); + if (poleev->pConnectionPoint) { + ole_msg_loop(); + evs_delete(poleev->event_id); + poleev->pConnectionPoint->lpVtbl->Unadvise(poleev->pConnectionPoint, poleev->dwCookie); + OLE_RELEASE(poleev->pConnectionPoint); + poleev->pConnectionPoint = NULL; + } + return Qnil; +} + +static VALUE +evs_push(VALUE ev) +{ + return rb_ary_push(ary_ole_event, ev); +} + +static VALUE +evs_delete(long i) +{ + rb_ary_store(ary_ole_event, i, Qnil); + return Qnil; +} + +static VALUE +evs_entry(long i) +{ + return rb_ary_entry(ary_ole_event, i); +} + +static VALUE +evs_length(void) +{ + 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")); +} + +void +Init_win32ole_event() +{ + ary_ole_event = rb_ary_new(); + rb_gc_register_mark_object(ary_ole_event); + id_events = rb_intern("events"); + cWIN32OLE_EVENT = rb_define_class("WIN32OLE_EVENT", rb_cObject); + rb_define_singleton_method(cWIN32OLE_EVENT, "message_loop", fev_s_msg_loop, 0); + rb_define_alloc_func(cWIN32OLE_EVENT, fev_s_allocate); + rb_define_method(cWIN32OLE_EVENT, "initialize", fev_initialize, -1); + rb_define_method(cWIN32OLE_EVENT, "on_event", fev_on_event, -1); + 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); +} diff --git a/ext/win32ole/win32ole_event.h b/ext/win32ole/win32ole_event.h new file mode 100644 index 0000000000..509be05ea9 --- /dev/null +++ b/ext/win32ole/win32ole_event.h @@ -0,0 +1,6 @@ +#ifndef WIN32OLE_EVENT_H +#define WIN32OLE_EVENT_H 1 + +void Init_win32ole_event(); + +#endif |