Created
March 24, 2010 20:06
-
-
Save chuckremes/342716 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
static VALUE | |
ole_invoke(int argc, VALUE *argv, VALUE self, USHORT wFlags, BOOL is_bracket) | |
{ | |
LCID lcid = cWIN32OLE_lcid; | |
struct oledata *pole; | |
HRESULT hr; | |
VALUE cmd; | |
VALUE paramS; | |
VALUE param; | |
VALUE obj; | |
VALUE v; | |
BSTR wcmdname; | |
DISPID DispID; | |
DISPID* pDispID; | |
EXCEPINFO excepinfo; | |
VARIANT result; | |
VARIANTARG* realargs = NULL; | |
unsigned int argErr = 0; | |
unsigned int i; | |
unsigned int cNamedArgs; | |
int n; | |
struct oleparam op; | |
struct olevariantdata *pvar; | |
memset(&excepinfo, 0, sizeof(EXCEPINFO)); | |
VariantInit(&result); | |
op.dp.rgvarg = NULL; | |
op.dp.rgdispidNamedArgs = NULL; | |
op.dp.cNamedArgs = 0; | |
op.dp.cArgs = 0; | |
rb_scan_args(argc, argv, "1*", &cmd, ¶mS); | |
if(TYPE(cmd) != T_STRING && TYPE(cmd) != T_SYMBOL && !is_bracket) { | |
rb_raise(rb_eTypeError, "method is wrong type (expected String or Symbol)"); | |
} | |
if (TYPE(cmd) == T_SYMBOL) { | |
cmd = rb_sym_to_s(cmd); | |
} | |
OLEData_Get_Struct(self, pole); | |
if(!pole->pDispatch) { | |
rb_raise(rb_eRuntimeError, "failed to get dispatch interface"); | |
} | |
if (is_bracket) { | |
DispID = DISPID_VALUE; | |
argc += 1; | |
rb_ary_unshift(paramS, cmd); | |
} else { | |
wcmdname = ole_vstr2wc(cmd); | |
hr = pole->pDispatch->lpVtbl->GetIDsOfNames( pole->pDispatch, &IID_NULL, | |
&wcmdname, 1, lcid, &DispID); | |
SysFreeString(wcmdname); | |
if(FAILED(hr)) { | |
ole_raise(hr, eWIN32OLERuntimeError, | |
"unknown property or method: `%s'", | |
StringValuePtr(cmd)); | |
} | |
} | |
/* pick up last argument of method */ | |
param = rb_ary_entry(paramS, argc-2); | |
op.dp.cNamedArgs = 0; | |
/* if last arg is hash object */ | |
if(TYPE(param) == T_HASH) { | |
/*------------------------------------------ | |
hash object ==> named dispatch parameters | |
--------------------------------------------*/ | |
cNamedArgs = NUM2INT(rb_funcall(param, rb_intern("length"), 0)); | |
op.dp.cArgs = cNamedArgs + argc - 2; | |
op.pNamedArgs = ALLOCA_N(OLECHAR*, cNamedArgs + 1); | |
op.dp.rgvarg = ALLOCA_N(VARIANTARG, op.dp.cArgs); | |
rb_block_call(param, rb_intern("each"), 0, 0, hash2named_arg, (VALUE)&op); | |
pDispID = ALLOCA_N(DISPID, cNamedArgs + 1); | |
op.pNamedArgs[0] = ole_vstr2wc(cmd); | |
hr = pole->pDispatch->lpVtbl->GetIDsOfNames(pole->pDispatch, | |
&IID_NULL, | |
op.pNamedArgs, | |
op.dp.cNamedArgs + 1, | |
lcid, pDispID); | |
for(i = 0; i < op.dp.cNamedArgs + 1; i++) { | |
SysFreeString(op.pNamedArgs[i]); | |
op.pNamedArgs[i] = NULL; | |
} | |
if(FAILED(hr)) { | |
/* clear dispatch parameters */ | |
for(i = 0; i < op.dp.cArgs; i++ ) { | |
VariantClear(&op.dp.rgvarg[i]); | |
} | |
ole_raise(hr, eWIN32OLERuntimeError, | |
"failed to get named argument info: `%s'", | |
StringValuePtr(cmd)); | |
} | |
op.dp.rgdispidNamedArgs = &(pDispID[1]); | |
} | |
else { | |
cNamedArgs = 0; | |
op.dp.cArgs = argc - 1; | |
op.pNamedArgs = ALLOCA_N(OLECHAR*, cNamedArgs + 1); | |
if (op.dp.cArgs > 0) { | |
op.dp.rgvarg = ALLOCA_N(VARIANTARG, op.dp.cArgs); | |
} | |
} | |
/*-------------------------------------- | |
non hash args ==> dispatch parameters | |
----------------------------------------*/ | |
if(op.dp.cArgs > cNamedArgs) { | |
realargs = ALLOCA_N(VARIANTARG, op.dp.cArgs-cNamedArgs+1); | |
for(i = cNamedArgs; i < op.dp.cArgs; i++) { | |
n = op.dp.cArgs - i + cNamedArgs - 1; | |
VariantInit(&realargs[n]); | |
VariantInit(&op.dp.rgvarg[n]); | |
param = rb_ary_entry(paramS, i-cNamedArgs); | |
// makes a copy of each parameter OR records a reference to the object | |
if (rb_obj_is_kind_of(param, cWIN32OLE_VARIANT)) { | |
Data_Get_Struct(param, struct olevariantdata, pvar); | |
VariantCopy(&op.dp.rgvarg[n], &(pvar->var)); | |
} else { | |
ole_val2variant(param, &realargs[n]); | |
V_VT(&op.dp.rgvarg[n]) = VT_VARIANT | VT_BYREF; | |
V_VARIANTREF(&op.dp.rgvarg[n]) = &realargs[n]; | |
} | |
} | |
} | |
/* apparent you need to call propput, you need this */ | |
if (wFlags & DISPATCH_PROPERTYPUT) { | |
if (op.dp.cArgs == 0) | |
ole_raise(ResultFromScode(E_INVALIDARG), eWIN32OLERuntimeError, "argument error"); | |
op.dp.cNamedArgs = 1; | |
op.dp.rgdispidNamedArgs = ALLOCA_N( DISPID, 1 ); | |
op.dp.rgdispidNamedArgs[0] = DISPID_PROPERTYPUT; | |
} | |
hr = pole->pDispatch->lpVtbl->Invoke(pole->pDispatch, DispID, | |
&IID_NULL, lcid, wFlags, &op.dp, | |
&result, &excepinfo, &argErr); | |
if (FAILED(hr)) { | |
/* retry to call args by value */ | |
if(op.dp.cArgs >= cNamedArgs) { | |
for(i = cNamedArgs; i < op.dp.cArgs; i++) { | |
n = op.dp.cArgs - i + cNamedArgs - 1; | |
param = rb_ary_entry(paramS, i-cNamedArgs); | |
ole_val2variant(param, &op.dp.rgvarg[n]); | |
} | |
if (hr == DISP_E_EXCEPTION) { | |
ole_freeexceptinfo(&excepinfo); | |
} | |
memset(&excepinfo, 0, sizeof(EXCEPINFO)); | |
VariantInit(&result); | |
hr = pole->pDispatch->lpVtbl->Invoke(pole->pDispatch, DispID, | |
&IID_NULL, lcid, wFlags, | |
&op.dp, &result, | |
&excepinfo, &argErr); | |
/* mega kludge. if a method in WORD is called and we ask | |
* for a result when one is not returned then | |
* hResult == DISP_E_EXCEPTION. this only happens on | |
* functions whose DISPID > 0x8000 */ | |
if ((hr == DISP_E_EXCEPTION || hr == DISP_E_MEMBERNOTFOUND) && DispID > 0x8000) { | |
if (hr == DISP_E_EXCEPTION) { | |
ole_freeexceptinfo(&excepinfo); | |
} | |
memset(&excepinfo, 0, sizeof(EXCEPINFO)); | |
hr = pole->pDispatch->lpVtbl->Invoke(pole->pDispatch, DispID, | |
&IID_NULL, lcid, wFlags, | |
&op.dp, NULL, | |
&excepinfo, &argErr); | |
} | |
for(i = cNamedArgs; i < op.dp.cArgs; i++) { | |
n = op.dp.cArgs - i + cNamedArgs - 1; | |
VariantClear(&op.dp.rgvarg[n]); | |
} | |
} | |
if (FAILED(hr)) { | |
/* retry after converting nil to VT_EMPTY */ | |
if (op.dp.cArgs > cNamedArgs) { | |
for(i = cNamedArgs; i < op.dp.cArgs; i++) { | |
n = op.dp.cArgs - i + cNamedArgs - 1; | |
param = rb_ary_entry(paramS, i-cNamedArgs); | |
ole_val2variant2(param, &op.dp.rgvarg[n]); | |
} | |
if (hr == DISP_E_EXCEPTION) { | |
ole_freeexceptinfo(&excepinfo); | |
} | |
memset(&excepinfo, 0, sizeof(EXCEPINFO)); | |
VariantInit(&result); | |
hr = pole->pDispatch->lpVtbl->Invoke(pole->pDispatch, DispID, | |
&IID_NULL, lcid, wFlags, | |
&op.dp, &result, | |
&excepinfo, &argErr); | |
for(i = cNamedArgs; i < op.dp.cArgs; i++) { | |
n = op.dp.cArgs - i + cNamedArgs - 1; | |
VariantClear(&op.dp.rgvarg[n]); | |
} | |
} | |
} | |
} | |
/* clear dispatch parameter */ | |
if(op.dp.cArgs > cNamedArgs) { | |
for(i = cNamedArgs; i < op.dp.cArgs; i++) { | |
n = op.dp.cArgs - i + cNamedArgs - 1; | |
param = rb_ary_entry(paramS, i-cNamedArgs); | |
if (rb_obj_is_kind_of(param, cWIN32OLE_VARIANT)) { | |
ole_val2variant(param, &realargs[n]); | |
} | |
// Why aren't we calling VariantClear() here? Won't this leak the memory allocated to the parameters? | |
} | |
set_argv(realargs, cNamedArgs, op.dp.cArgs); | |
} | |
else { | |
for(i = 0; i < op.dp.cArgs; i++) { | |
VariantClear(&op.dp.rgvarg[i]); | |
} | |
} | |
if (FAILED(hr)) { | |
v = ole_excepinfo2msg(&excepinfo); | |
ole_raise(hr, eWIN32OLERuntimeError, "(in OLE method `%s': )%s", | |
StringValuePtr(cmd), | |
StringValuePtr(v)); | |
} | |
obj = ole_variant2val(&result); | |
VariantClear(&result); | |
return obj; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment