Skip to content

Instantly share code, notes, and snippets.

@chuckremes
Created March 24, 2010 20:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chuckremes/342716 to your computer and use it in GitHub Desktop.
Save chuckremes/342716 to your computer and use it in GitHub Desktop.
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, &paramS);
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