diff --git a/ext/com_dotnet/com_com.c b/ext/com_dotnet/com_com.c index c068f642f44a3..f6defb6e8051c 100644 --- a/ext/com_dotnet/com_com.c +++ b/ext/com_dotnet/com_com.c @@ -334,7 +334,7 @@ HRESULT php_com_invoke_helper(php_com_dotnet_object *obj, DISPID id_member, WORD flags, DISPPARAMS *disp_params, VARIANT *v, bool silent, bool allow_noarg) { HRESULT hr; - unsigned int arg_err; + unsigned int arg_err = (unsigned int)-1; EXCEPINFO e = {0}; hr = IDispatch_Invoke(V_DISPATCH(&obj->v), id_member, @@ -343,6 +343,9 @@ HRESULT php_com_invoke_helper(php_com_dotnet_object *obj, DISPID id_member, if (!silent && FAILED(hr)) { char *desc = NULL, *msg = NULL; + if(arg_err != (unsigned int)-1) + arg_err = disp_params->cArgs - arg_err; // We have reversed array of params + switch (hr) { case DISP_E_EXCEPTION: { zend_string *source = NULL, *desc_str = NULL; @@ -377,7 +380,7 @@ HRESULT php_com_invoke_helper(php_com_dotnet_object *obj, DISPID id_member, case DISP_E_PARAMNOTFOUND: case DISP_E_TYPEMISMATCH: desc = php_win32_error_to_msg(hr); - spprintf(&msg, 0, "Parameter %d: %s", arg_err, desc); + spprintf(&msg, 0, "Error [0x%08x] Parameter %d: %s", hr, arg_err, desc); php_win32_error_msg_free(desc); break; @@ -393,7 +396,10 @@ HRESULT php_com_invoke_helper(php_com_dotnet_object *obj, DISPID id_member, default: desc = php_win32_error_to_msg(hr); - spprintf(&msg, 0, "Error [0x%08x] %s", hr, desc); + if(arg_err != (unsigned int) -1) + spprintf(&msg, 0, "Error [0x%08x] Parameter %d: %s", hr, arg_err, desc); + else + spprintf(&msg, 0, "Error [0x%08x] %s", hr, desc); php_win32_error_msg_free(desc); break; } diff --git a/ext/com_dotnet/com_extension.c b/ext/com_dotnet/com_extension.c index 88feefa617b4b..5322bc38350de 100644 --- a/ext/com_dotnet/com_extension.c +++ b/ext/com_dotnet/com_extension.c @@ -219,6 +219,7 @@ PHP_MINIT_FUNCTION(com_dotnet) COM_CONST(VT_R4); COM_CONST(VT_R8); COM_CONST(VT_BOOL); + COM_CONST(VT_PTR); COM_CONST(VT_ERROR); COM_CONST(VT_CY); COM_CONST(VT_DATE); diff --git a/ext/com_dotnet/com_handlers.c b/ext/com_dotnet/com_handlers.c index fe1b6f257b283..4715fd78257b8 100644 --- a/ext/com_dotnet/com_handlers.c +++ b/ext/com_dotnet/com_handlers.c @@ -573,6 +573,7 @@ void php_com_object_free_storage(zend_object *object) } VariantClear(&obj->v); + zval_dtor(&obj->byref); if (obj->method_cache) { zend_hash_destroy(obj->method_cache); @@ -603,6 +604,9 @@ zend_object* php_com_object_clone(zend_object *object) * want to clone as much as possible */ VariantCopyInd(&cloneobj->v, &origobject->v); + ZVAL_NULL(&cloneobj->byref); + ZVAL_COPY(&cloneobj->byref, &origobject->byref); + if (cloneobj->typeinfo) { ITypeInfo_AddRef(cloneobj->typeinfo); } diff --git a/ext/com_dotnet/com_misc.c b/ext/com_dotnet/com_misc.c index b2920ddba62c2..2968ea5ca348f 100644 --- a/ext/com_dotnet/com_misc.c +++ b/ext/com_dotnet/com_misc.c @@ -121,7 +121,6 @@ PHP_COM_DOTNET_API bool php_com_safearray_get_elem(VARIANT *array, VARIANT *dest /* check bounds */ if (dim1 < lbound || dim1 > ubound) { - php_com_throw_exception(DISP_E_BADINDEX, "index out of bounds"); return 0; } diff --git a/ext/com_dotnet/com_variant.c b/ext/com_dotnet/com_variant.c index 40106e3d8f4ff..4472c7dc28ec1 100644 --- a/ext/com_dotnet/com_variant.c +++ b/ext/com_dotnet/com_variant.c @@ -128,9 +128,13 @@ PHP_COM_DOTNET_API void php_com_variant_from_zval(VARIANT *v, zval *z, int codep } V_DISPATCH(v) = V_DISPATCH(&obj->v); } else { - /* pass the variant by reference */ - V_VT(v) = VT_VARIANT | VT_BYREF; - V_VARIANTREF(v) = &obj->v; + if(V_VT(&obj->v) & VT_BYREF) { //Allready by reference + memcpy(v, &obj->v, sizeof(VARIANT)); + } else { + /* pass the variant by reference */ + V_VT(v) = VT_VARIANT | VT_BYREF; + V_VARIANTREF(v) = &obj->v; + } } } else { /* export the PHP object using our COM wrapper */ @@ -429,6 +433,7 @@ PHP_METHOD(variant, __construct) php_com_dotnet_object *obj; zval *zvalue = NULL; HRESULT res; + char *msg = NULL; if (ZEND_NUM_ARGS() == 0) { /* just leave things as-is - an empty variant */ @@ -451,36 +456,194 @@ PHP_METHOD(variant, __construct) php_com_variant_from_zval(&obj->v, zvalue, obj->code_page); } + if(ZEND_NUM_ARGS() >= 2 && vt == VT_ARRAY && V_VT(&obj->v) & VT_ARRAY) + vt = VT_EMPTY;//Prevent future VariantChangeType + /* Only perform conversion if variant not already of type passed */ if ((ZEND_NUM_ARGS() >= 2) && (vt != V_VT(&obj->v))) { - /* If already an array and VT_ARRAY is passed then: - - if only VT_ARRAY passed then do not perform a conversion - - if VT_ARRAY plus other type passed then perform conversion - but will probably fail (original behavior) - */ - if ((vt & VT_ARRAY) && (V_VT(&obj->v) & VT_ARRAY)) { - zend_long orig_vt = vt; - - vt &= ~VT_ARRAY; - if (vt) { - vt = orig_vt; + /** + * VT_BYREF, VT_VARIANT and VT_PTR functionality + * + * All of those types makes reference to another php_com_dotnet_object incrementing their refcounters. + * If you use VT_VARIANT then it will be VT_BYREF | VT_VARIANT. + * If you use VT_PTR then you get reference to VARIANTTAG. + * If you use VT_PTR | VT_BYREF then you get reference to VARIANTTAG's variable dependently of their type. + * + * For future: may be need to check if remote variable is changed. + */ + if((vt & VT_BYREF) || ((vt & ~VT_BYREF) == VT_VARIANT) || ((vt & ~VT_BYREF) == VT_PTR)) { + + if((vt & VT_BYREF) && (vt & ~VT_BYREF != VT_VARIANT) && (vt & ~VT_BYREF != VT_PTR)) { + spprintf(&msg, E_INVALIDARG, "VT_BYREF should be used alone or with VT_VARIANT or with VT_PTR"); + php_com_throw_exception(E_INVALIDARG, msg); + efree(msg); + return; + } + + ZVAL_DEREF(zvalue); + if(!php_com_is_valid_object(zvalue)) { + spprintf(&msg, 0, "To use VT_BYREF or VT_VARIANT you must have another VARIANT object as 'value'"); + php_com_throw_exception(E_INVALIDARG, msg); + efree(msg); + return; + } + + //Save a reference to original object + zval_dtor(&obj->byref); //Remove old value if exist + ZVAL_NEW_REF(&obj->byref, zvalue); //Set new reference to zvalue + Z_ADDREF_P(zvalue); //Increment their counter + + php_com_dotnet_object * src = CDNO_FETCH(zvalue); + VariantClear(&obj->v); //Remove our default value + + switch(V_VT(&src->v)) { + case VT_I2: + case VT_I4: + case VT_R4: + case VT_R8: + case VT_CY: + case VT_DATE: + case VT_BSTR: + case VT_DISPATCH: + case VT_ERROR: + case VT_BOOL: + case VT_UNKNOWN: + case VT_I1: + case VT_UI1: + case VT_UI2: + case VT_UI4: + case VT_I8: + case VT_UI8: + case VT_INT: + case VT_UINT: + case VT_VOID: + case VT_HRESULT: + case VT_PTR: + case VT_SAFEARRAY: + case VT_INT_PTR: + case VT_UINT_PTR: + obj->v.byref = &V_NONE(&src->v); + break; + case VT_DECIMAL: + obj->v.byref = &V_DECIMAL(&src->v); + break; + default: + obj->v.byref = &src->v; + V_VT(&obj->v) = VT_BYREF | VT_VARIANT; + break; + } + + switch(vt & ~VT_BYREF) { + case VT_PTR: + V_VT(&obj->v) = VT_PTR; + if(!(vt & VT_BYREF)) //If only VT_PTR is used, then ptr is referenced to VARIANT, otherway to value + obj->v.byref = &src->v; + break; + case VT_VARIANT: + V_VT(&obj->v) = vt; + V_VARIANTREF(&obj->v) = &src->v; + break; + default: + V_VT(&obj->v) = V_VT(&src->v) | VT_BYREF; + break; + } + vt = VT_EMPTY; //Prevent future VariantChangeType + } + + + /** + * VT_ARRAY functionality + * + * If already an array and VT_ARRAY is passed then: + * - if only VT_ARRAY passed then do not perform a conversion + * - if VT_ARRAY plus other type passed then perform conversion + */ + + res = S_OK; + SAFEARRAYBOUND Bound; + + if ((vt & ~VT_ARRAY) && //new variant have some type except VT_ARRAY + (V_VT(&obj->v) & VT_ARRAY) && //our variant is array + SafeArrayGetDim(V_ARRAY(&obj->v)) == 1 && //our array have one dimension + SUCCEEDED(res = SafeArrayGetLBound(V_ARRAY(&obj->v), 1, &Bound.lLbound)) && + SUCCEEDED(res = SafeArrayGetUBound(V_ARRAY(&obj->v), 1, &Bound.cElements))) + { + zend_long need_vt = vt & ~VT_ARRAY; + zend_long old_vt = V_VT(&obj->v) & ~VT_ARRAY; + + Bound.cElements -= Bound.lLbound - 1; + SAFEARRAY * newArray = SafeArrayCreate(need_vt, 1, &Bound); + + if (newArray) { + + for (LONG i = Bound.lLbound; i < Bound.lLbound + Bound.cElements; i++) { + VARIANT temp; + VariantInit(&temp); + if (old_vt != VT_VARIANT) + V_VT(&temp) = old_vt; + + switch (old_vt) { + case VT_VARIANT: + res = SafeArrayGetElement(V_ARRAY(&obj->v), &i, &temp); break; + default: + res = SafeArrayGetElement(V_ARRAY(&obj->v), &i, &V_I8(&temp)); break; + } + + if (FAILED(res)) + break; + + if (FAILED(res = VariantChangeType(&temp, &temp, 0, (VARTYPE)need_vt))) { + VariantClear(&temp); + break; + } + + switch (need_vt) { + case VT_VARIANT: + res = SafeArrayPutElement(newArray, &i, &temp); break; + case VT_DISPATCH: + case VT_UNKNOWN: + case VT_BSTR: + res = SafeArrayPutElement(newArray, &i, V_DISPATCH(&temp)); break; + default: + res = SafeArrayPutElement(newArray, &i, &V_I8(&temp)); + break; + } + + VariantClear(&temp); + if (FAILED(res)) + break; + } + + if (SUCCEEDED(res)) { + SafeArrayDestroy(V_ARRAY(&obj->v)); + V_ARRAY(&obj->v) = newArray; + V_VT(&obj->v) = vt | VT_ARRAY; //Set VT_ARRAY automatically + vt = VT_EMPTY;//No need change type any more + } + else + SafeArrayDestroy(newArray); } } - if (vt) { + /** Try to system conversion function if our array conversion + * function don't do conversion before (vt is not VT_EMPTY) + * and wasn't tried it (SUCCEEDED(res)) + */ + + if (SUCCEEDED(res) && vt) { res = VariantChangeType(&obj->v, &obj->v, 0, (VARTYPE)vt); + } - if (FAILED(res)) { - char *werr, *msg; + if (FAILED(res)) { + char *werr, *msg; werr = php_win32_error_to_msg(res); spprintf(&msg, 0, "Variant type conversion failed: %s", werr); php_win32_error_msg_free(werr); - php_com_throw_exception(res, msg); - efree(msg); - } + php_com_throw_exception(res, msg); + efree(msg); } } diff --git a/ext/com_dotnet/php_com_dotnet_internal.h b/ext/com_dotnet/php_com_dotnet_internal.h index f3405b5cb6a6b..87bd892016aa4 100644 --- a/ext/com_dotnet/php_com_dotnet_internal.h +++ b/ext/com_dotnet/php_com_dotnet_internal.h @@ -29,6 +29,7 @@ typedef struct _php_com_dotnet_object { zend_object zo; VARIANT v; + zval byref; bool modified; int code_page;