Hi all,
Playing further with my VB program and native oleaut32 I've hit an 'interesting issue' with the Microsoft Date/Time picker.
typelib.c in dlls\oleaut32 assumes all parms are dwords and picks the lval of a variant (which for most of the types of variant is the dword containing the important information). However as shown by the following trace:
trace:ole:ITypeInfo_fnInvoke (0x404560ac)(0x40418fac,id=20,flags=0x00000004,0x405c5d84,(nil),0x405c5d64,0 x405c5d94) partial stub! trace:ole:dump_DispParms args=1 named args=1 trace:ole:dump_Variant (0x405c5dec) trace:ole:dump_Variant VARTYPE: VT_DATE trace:ole:dump_Variant (?)-882854389 L"Value"(1) parm0: L"\00e5MSComCtl2WWW" memid is 00000014 Param 0: tdesc.vartype 12 (VT_VARIANT) u.parmadesc.flags 1 u.parmadesc.lpex (nil) funckind: 1 (pure virtual) invkind: 4 (property put) callconv: 4 (stdcall) oVft: 224 cParamsOpt: 0 wFlags: 3c helpstring: L"Returns/sets the current date." entry: (null) 08072560:Call ntdll.RtlAllocateHeap(40370000,00000000,00000008) ret=408bfcc3 08072560:Ret ntdll.RtlAllocateHeap() retval=4041a160 ret=408bfcc3 08072560:Call ntdll.RtlAllocateHeap(40370000,00000008,00000004) ret=408bfce4 08072560:Ret ntdll.RtlAllocateHeap() retval=4041a180 ret=408bfce4 trace:ole:ITypeInfo_fnInvoke set 0 to disparg type 7 vs 12
This shows that the function takes one parm and that parm is a variant. We pass the value contained in the parm and it fails. We need to ensure the whole variant is put on the stack for the called function. Since we currently only call with dword parms this wont work.
I have managed to get further by a dirty hack:
DWORD numArgWords = 0;
args[0] = (DWORD)pIUnk;
for (i=0;i<pFDesc->funcdesc.cParams;i++) { if (i<pDispParams->cArgs) { TRACE("set %d to disparg type %d vs %d\n",i, V_VT(&pDispParams->rgvarg[pDispParams->cArgs-i-1]), pFDesc->funcdesc.lprgelemdescParam[i].tdesc.vt );
if (pFDesc->funcdesc.lprgelemdescParam[i].tdesc.vt != VT_VARIANT) { args[i+1] = V_UNION(&pDispParams->rgvarg[pDispParams->cArgs-i-1],lVal); numArgWords = numArgWords+1; } else { TRACE("Faking variant on stack as more args \n"); memcpy(&args[i+1], &pDispParams->rgvarg[pDispParams->cArgs-i-1], sizeof(VARIANT)); numArgWords = numArgWords + sizeof(VARIANT)/4; }
} else { TYPEDESC *tdesc = &(pFDesc->funcdesc.lprgelemdescParam[i].tdesc); TRACE("set %d to pointer for get (type is %d)\n",i,tdesc->vt); /*FIXME: give pointers for the rest, so propertyget works*/ args[i+1] = (DWORD)&args2[i]; numArgWords = numArgWords+1;
/* If pointer to variant, pass reference to variant * in result variant array. */ if ((tdesc->vt == VT_PTR) && (tdesc->u.lptdesc->vt == VT_VARIANT) && pVarResult ) args[i+1] = (DWORD)(pVarResult+(i-pDispParams->cArgs)); } } TRACE("_invoke with %ld args\n", numArgWords+1); res = _invoke((*(DWORD***)pIUnk)[pFDesc->funcdesc.oVft/4], pFDesc->funcdesc.callconv, numArgWords+1, args );
This hack takes the parm and puts the variant into that arg and subsequent ones, meaning the stack to the called function should look as it would be. Unfortunately as it stands this would only work with one parm, so my question is how it is supposed to be fixed. I could potentially change args[i+1] to args[numArgWords+1] but it still feels very hacky.
If I have been ignorant of something please bear with me - I've never touched com programming before!
Regards, Jason