2014-08-15 07:36:12 -04:00
|
|
|
#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;
|
2014-08-16 06:34:24 -04:00
|
|
|
long m_event_id;
|
2014-08-15 07:36:12 -04:00
|
|
|
ITypeInfo *pTypeInfo;
|
|
|
|
}IEVENTSINKOBJ, *PIEVENTSINKOBJ;
|
|
|
|
|
|
|
|
struct oleeventdata {
|
|
|
|
DWORD dwCookie;
|
|
|
|
IConnectionPoint *pConnectionPoint;
|
2014-09-12 22:28:47 -04:00
|
|
|
IDispatch *pDispatch;
|
2014-08-15 07:36:12 -04:00
|
|
|
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);
|
2014-11-17 06:18:54 -05:00
|
|
|
static void oleevent_free(void *ptr);
|
|
|
|
static size_t oleevent_size(const void *ptr);
|
2014-08-15 07:36:12 -04:00
|
|
|
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);
|
2014-08-16 06:34:24 -04:00
|
|
|
static long evs_length(void);
|
2014-08-15 07:36:12 -04:00
|
|
|
|
2014-11-17 06:18:54 -05:00
|
|
|
|
|
|
|
static const rb_data_type_t oleevent_datatype = {
|
|
|
|
"win32ole_event",
|
|
|
|
{NULL, oleevent_free, oleevent_size,},
|
2014-12-01 01:38:04 -05:00
|
|
|
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
|
2014-11-17 06:18:54 -05:00
|
|
|
};
|
|
|
|
|
2014-08-15 07:36:12 -04:00
|
|
|
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
|
2014-10-04 19:29:24 -04:00
|
|
|
EVENTSINK_Constructor(void)
|
|
|
|
{
|
2014-08-15 07:36:12 -04:00
|
|
|
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;
|
|
|
|
|
2014-12-01 06:03:13 -05:00
|
|
|
struct oledata *pole = NULL;
|
2014-08-15 07:36:12 -04:00
|
|
|
unsigned int index;
|
|
|
|
unsigned int count;
|
|
|
|
int type;
|
|
|
|
BSTR bstr;
|
|
|
|
char *pstr;
|
|
|
|
|
|
|
|
BOOL is_found = FALSE;
|
|
|
|
LCID lcid = cWIN32OLE_lcid;
|
|
|
|
|
2014-12-01 06:03:13 -05:00
|
|
|
pole = oledata_get_struct(ole);
|
2014-08-15 07:36:12 -04:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2014-12-01 06:03:13 -05:00
|
|
|
struct oledata *pole = NULL;
|
2014-08-15 07:36:12 -04:00
|
|
|
|
2014-12-01 06:03:13 -05:00
|
|
|
pole = oledata_get_struct(ole);
|
2014-08-15 07:36:12 -04:00
|
|
|
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
|
2014-11-17 06:18:54 -05:00
|
|
|
oleevent_free(void *ptr)
|
2014-08-15 07:36:12 -04:00
|
|
|
{
|
2014-11-17 06:18:54 -05:00
|
|
|
struct oleeventdata *poleev = ptr;
|
2014-08-15 07:36:12 -04:00
|
|
|
if (poleev->pConnectionPoint) {
|
|
|
|
poleev->pConnectionPoint->lpVtbl->Unadvise(poleev->pConnectionPoint, poleev->dwCookie);
|
|
|
|
OLE_RELEASE(poleev->pConnectionPoint);
|
|
|
|
poleev->pConnectionPoint = NULL;
|
|
|
|
}
|
2014-09-12 22:28:47 -04:00
|
|
|
OLE_RELEASE(poleev->pDispatch);
|
2014-08-15 07:36:12 -04:00
|
|
|
free(poleev);
|
|
|
|
}
|
|
|
|
|
2014-11-17 06:18:54 -05:00
|
|
|
static size_t
|
|
|
|
oleevent_size(const void *ptr)
|
|
|
|
{
|
|
|
|
return ptr ? sizeof(struct oleeventdata) : 0;
|
|
|
|
}
|
|
|
|
|
2014-08-15 07:36:12 -04:00
|
|
|
static VALUE
|
|
|
|
fev_s_allocate(VALUE klass)
|
|
|
|
{
|
|
|
|
VALUE obj;
|
|
|
|
struct oleeventdata *poleev;
|
2014-11-17 06:18:54 -05:00
|
|
|
obj = TypedData_Make_Struct(klass, struct oleeventdata, &oleevent_datatype, poleev);
|
2014-08-15 07:36:12 -04:00
|
|
|
poleev->dwCookie = 0;
|
|
|
|
poleev->pConnectionPoint = NULL;
|
|
|
|
poleev->event_id = 0;
|
2014-09-12 22:28:47 -04:00
|
|
|
poleev->pDispatch = NULL;
|
2014-08-15 07:36:12 -04:00
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
ev_advise(int argc, VALUE *argv, VALUE self)
|
|
|
|
{
|
|
|
|
|
|
|
|
VALUE ole, itf;
|
2014-12-01 06:03:13 -05:00
|
|
|
struct oledata *pole = NULL;
|
2014-08-15 07:36:12 -04:00
|
|
|
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)) {
|
2014-08-25 07:18:17 -04:00
|
|
|
pitf = StringValuePtr(itf);
|
2014-08-15 07:36:12 -04:00
|
|
|
if (rb_safe_level() > 0 && OBJ_TAINTED(itf)) {
|
2014-08-25 07:18:17 -04:00
|
|
|
rb_raise(rb_eSecurityError, "insecure event creation - `%s'",
|
2014-08-15 07:36:12 -04:00
|
|
|
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");
|
|
|
|
}
|
|
|
|
|
2014-12-01 06:03:13 -05:00
|
|
|
pole = oledata_get_struct(ole);
|
2014-08-15 07:36:12 -04:00
|
|
|
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");
|
|
|
|
}
|
|
|
|
|
2014-11-17 06:18:54 -05:00
|
|
|
TypedData_Get_Struct(self, struct oleeventdata, &oleevent_datatype, poleev);
|
2014-08-16 06:34:24 -04:00
|
|
|
pIEV->m_event_id = evs_length();
|
2014-08-15 07:36:12 -04:00
|
|
|
pIEV->pTypeInfo = pTypeInfo;
|
|
|
|
poleev->dwCookie = dwCookie;
|
|
|
|
poleev->pConnectionPoint = pConnectionPoint;
|
|
|
|
poleev->event_id = pIEV->m_event_id;
|
2014-09-12 22:28:47 -04:00
|
|
|
poleev->pDispatch = pDispatch;
|
|
|
|
OLE_ADDREF(pDispatch);
|
2014-08-15 07:36:12 -04:00
|
|
|
|
|
|
|
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
|
2014-10-04 19:29:24 -04:00
|
|
|
ole_msg_loop(void)
|
|
|
|
{
|
2014-08-15 07:36:12 -04:00
|
|
|
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;
|
2014-11-17 06:18:54 -05:00
|
|
|
TypedData_Get_Struct(self, struct oleeventdata, &oleevent_datatype, poleev);
|
2014-08-15 07:36:12 -04:00
|
|
|
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;
|
2014-11-17 06:18:54 -05:00
|
|
|
TypedData_Get_Struct(self, struct oleeventdata, &oleevent_datatype, poleev);
|
2014-08-15 07:36:12 -04:00
|
|
|
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;
|
|
|
|
}
|
2014-09-12 22:28:47 -04:00
|
|
|
OLE_FREE(poleev->pDispatch);
|
2014-08-15 07:36:12 -04:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2014-08-16 06:34:24 -04:00
|
|
|
static long
|
2014-08-15 07:36:12 -04:00
|
|
|
evs_length(void)
|
|
|
|
{
|
2014-08-16 06:34:24 -04:00
|
|
|
return RARRAY_LEN(ary_ole_event);
|
2014-08-15 07:36:12 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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
|
2014-09-30 01:25:32 -04:00
|
|
|
Init_win32ole_event(void)
|
2014-08-15 07:36:12 -04:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|