From a5e8d3cb7e58d3eb56fa20369d673893e9f94c1d Mon Sep 17 00:00:00 2001 From: DmitryMaksimov Date: Mon, 25 Oct 2021 11:40:44 +0500 Subject: [PATCH 1/7] using SUBLANG_DEFAULT is not best choise --- win32/winutil.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/win32/winutil.c b/win32/winutil.c index 5e959307d7365..3d41873310f6a 100644 --- a/win32/winutil.c +++ b/win32/winutil.c @@ -29,7 +29,7 @@ PHP_WINUTIL_API char *php_win32_error_to_msg(HRESULT error) DWORD ret = FormatMessageW( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&bufw, 0, NULL + NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), (LPWSTR)&bufw, 0, NULL ); if (!ret || !bufw) { From 6718780342c6113496e1df2c8acab4614a85f2f1 Mon Sep 17 00:00:00 2001 From: DmitryMaksimov Date: Mon, 25 Oct 2021 12:24:38 +0500 Subject: [PATCH 2/7] LOCALE_SYSTEM_DEFAULT is not the best choise --- ext/com_dotnet/com_extension.c | 1 + ext/com_dotnet/com_handlers.c | 4 ++-- ext/com_dotnet/com_iterator.c | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/ext/com_dotnet/com_extension.c b/ext/com_dotnet/com_extension.c index 55b3c282aee37..88feefa617b4b 100644 --- a/ext/com_dotnet/com_extension.c +++ b/ext/com_dotnet/com_extension.c @@ -245,6 +245,7 @@ PHP_MINIT_FUNCTION(com_dotnet) COM_CONST(VARCMP_GT); COM_CONST(VARCMP_NULL); COM_CONST(LOCALE_SYSTEM_DEFAULT); + COM_CONST(LOCALE_NEUTRAL); COM_CONST(NORM_IGNORECASE); COM_CONST(NORM_IGNORENONSPACE); diff --git a/ext/com_dotnet/com_handlers.c b/ext/com_dotnet/com_handlers.c index 7f8f0e8614a79..fe1b6f257b283 100644 --- a/ext/com_dotnet/com_handlers.c +++ b/ext/com_dotnet/com_handlers.c @@ -326,7 +326,7 @@ static zend_function *com_method_get(zend_object **object_ptr, zend_string *name if (SUCCEEDED(ITypeInfo_GetTypeComp(obj->typeinfo, &comp))) { olename = php_com_string_to_olestring(name->val, name->len, obj->code_page); - lhash = LHashValOfNameSys(SYS_WIN32, LOCALE_SYSTEM_DEFAULT, olename); + lhash = LHashValOfNameSys(SYS_WIN32, LOCALE_NEUTRAL, olename); if (SUCCEEDED(ITypeComp_Bind(comp, olename, lhash, INVOKE_FUNC, &TI, &kind, &bindptr))) { switch (kind) { @@ -410,7 +410,7 @@ static int com_objects_compare(zval *object1, zval *object2) obja = CDNO_FETCH(object1); objb = CDNO_FETCH(object2); - switch (VarCmp(&obja->v, &objb->v, LOCALE_SYSTEM_DEFAULT, 0)) { + switch (VarCmp(&obja->v, &objb->v, LOCALE_NEUTRAL, 0)) { case VARCMP_LT: ret = -1; break; diff --git a/ext/com_dotnet/com_iterator.c b/ext/com_dotnet/com_iterator.c index 15e9b5df840c5..0bf4789b0ce41 100644 --- a/ext/com_dotnet/com_iterator.c +++ b/ext/com_dotnet/com_iterator.c @@ -200,7 +200,7 @@ zend_object_iterator *php_com_iter_get(zend_class_entry *ce, zval *object, int b } else { /* can we enumerate it? */ if (FAILED(IDispatch_Invoke(V_DISPATCH(&obj->v), DISPID_NEWENUM, - &IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD|DISPATCH_PROPERTYGET, + &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD|DISPATCH_PROPERTYGET, &dp, &v, NULL, NULL))) { goto fail; } From 0cf2c1b3adcb95b3bdeee82bbb384d83c23a74b0 Mon Sep 17 00:00:00 2001 From: DmitryMaksimov Date: Mon, 25 Oct 2021 16:01:20 +0500 Subject: [PATCH 3/7] feature (new Variant) Type conversions for one dimension arrays --- ext/com_dotnet/com_variant.c | 88 +++++++++++++++++++++++++++++++----- 1 file changed, 76 insertions(+), 12 deletions(-) diff --git a/ext/com_dotnet/com_variant.c b/ext/com_dotnet/com_variant.c index 40106e3d8f4ff..243c27ae326d4 100644 --- a/ext/com_dotnet/com_variant.c +++ b/ext/com_dotnet/com_variant.c @@ -457,30 +457,94 @@ PHP_METHOD(variant, __construct) /* 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; + res = S_OK; + SAFEARRAYBOUND Bound; + + if (!(V_VT(&obj->v) & VT_BYREF) && //not supported + (vt & ~VT_ARRAY) && //new variant have some type except VT_ARRAY + (V_VT(&obj->v) & VT_ARRAY) && //old variant is array + SafeArrayGetDim(V_ARRAY(&obj->v)) == 1 && //old 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); } } From 959549eec7dd89b7d38b0aa835ea4625c86aaa24 Mon Sep 17 00:00:00 2001 From: DmitryMaksimov Date: Sat, 6 Nov 2021 19:30:18 +0500 Subject: [PATCH 4/7] Feature (ext/com_dotnet): VT_ARRAY, VT_BYREF, VT_PTR --- ext/com_dotnet/com_com.c | 12 ++- ext/com_dotnet/com_handlers.c | 4 + ext/com_dotnet/com_variant.c | 120 ++++++++++++++++++++--- ext/com_dotnet/php_com_dotnet_internal.h | 1 + 4 files changed, 122 insertions(+), 15 deletions(-) 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_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_variant.c b/ext/com_dotnet/com_variant.c index 243c27ae326d4..4d2412155a4f2 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 */ @@ -454,22 +459,113 @@ PHP_METHOD(variant, __construct) /* 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 - */ + /** + * 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(res, 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 (!(V_VT(&obj->v) & VT_BYREF) && //not supported - (vt & ~VT_ARRAY) && //new variant have some type except VT_ARRAY - (V_VT(&obj->v) & VT_ARRAY) && //old variant is array - SafeArrayGetDim(V_ARRAY(&obj->v)) == 1 && //old array have one dimension + 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; 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; From fcc0540b0e1917093b9bacd6380dd576b90d591c Mon Sep 17 00:00:00 2001 From: DmitryMaksimov Date: Sun, 7 Nov 2021 11:08:14 +0500 Subject: [PATCH 5/7] Feature (ext/com_dotnet) VT_PTR added to constant's list --- ext/com_dotnet/com_extension.c | 1 + 1 file changed, 1 insertion(+) 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); From bc6f2706b8886806afcf572b40b3293976e72aaf Mon Sep 17 00:00:00 2001 From: DmitryMaksimov Date: Mon, 8 Nov 2021 10:13:45 +0500 Subject: [PATCH 6/7] uninitialized local variable 'res' used --- ext/com_dotnet/com_variant.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/com_dotnet/com_variant.c b/ext/com_dotnet/com_variant.c index 4d2412155a4f2..c2878a9ad1239 100644 --- a/ext/com_dotnet/com_variant.c +++ b/ext/com_dotnet/com_variant.c @@ -473,7 +473,7 @@ PHP_METHOD(variant, __construct) 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(res, msg); + php_com_throw_exception(E_INVALIDARG, msg); efree(msg); return; } From f6870d2f87ed688d88f6f45f7592d8e5f26e6ea7 Mon Sep 17 00:00:00 2001 From: DmitryMaksimov Date: Tue, 9 Nov 2021 12:15:32 +0500 Subject: [PATCH 7/7] Bugfix #79248 and #39596 --- ext/com_dotnet/com_misc.c | 1 - ext/com_dotnet/com_variant.c | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) 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 c2878a9ad1239..4472c7dc28ec1 100644 --- a/ext/com_dotnet/com_variant.c +++ b/ext/com_dotnet/com_variant.c @@ -456,6 +456,9 @@ 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))) {