PHPAPI OLECHAR *php_com_string_to_olestring(char *string, uint string_len, int codepage TSRMLS_DC) { OLECHAR *olestring = NULL; DWORD flags = codepage == CP_UTF8 ? 0 : MB_PRECOMPOSED | MB_ERR_INVALID_CHARS; BOOL ok; if (string_len == -1) { /* determine required length for the buffer (includes NUL terminator) */ string_len = MultiByteToWideChar(codepage, flags, string, -1, NULL, 0); } else { /* allow room for NUL terminator */ string_len++; } if (string_len > 0) { olestring = (OLECHAR*)safe_emalloc(string_len, sizeof(OLECHAR), 0); ok = MultiByteToWideChar(codepage, flags, string, string_len, olestring, string_len); } else { ok = FALSE; olestring = (OLECHAR*)emalloc(sizeof(OLECHAR)); *olestring = 0; } if (!ok) { char *msg = php_win32_error_to_msg(GetLastError()); php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not convert string to unicode: `%s'", msg); LocalFree(msg); } return olestring; }
PHPAPI char *php_com_olestring_to_string(OLECHAR *olestring, uint *string_len, int codepage TSRMLS_DC) { char *string; uint length = 0; BOOL ok; length = WideCharToMultiByte(codepage, 0, olestring, -1, NULL, 0, NULL, NULL); if (length) { string = (char*)safe_emalloc(length, sizeof(char), 0); length = WideCharToMultiByte(codepage, 0, olestring, -1, string, length, NULL, NULL); ok = length > 0; } else { string = (char*)emalloc(sizeof(char)); *string = '\0'; ok = FALSE; length = 0; } if (!ok) { char *msg = php_win32_error_to_msg(GetLastError()); php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not convert string from unicode: `%s'", msg); LocalFree(msg); } if (string_len) { *string_len = length-1; } return string; }
void php_com_throw_exception(HRESULT code, char *message) { int free_msg = 0; if (message == NULL) { message = php_win32_error_to_msg(code); free_msg = 1; } #if SIZEOF_ZEND_LONG == 8 zend_throw_exception(php_com_exception_class_entry, message, (zend_long)(uint32_t)code); #else zend_throw_exception(php_com_exception_class_entry, message, (zend_long)code); #endif if (free_msg) { LocalFree(message); } }
int php_com_do_invoke(php_com_dotnet_object *obj, char *name, size_t namelen, WORD flags, VARIANT *v, int nargs, zval *args, int allow_noarg) { DISPID dispid; HRESULT hr; char *winerr = NULL; char *msg = NULL; hr = php_com_get_id_of_name(obj, name, namelen, &dispid); if (FAILED(hr)) { winerr = php_win32_error_to_msg(hr); spprintf(&msg, 0, "Unable to lookup `%s': %s", name, winerr); LocalFree(winerr); php_com_throw_exception(hr, msg); efree(msg); return FAILURE; } return php_com_do_invoke_by_id(obj, dispid, flags, v, nargs, args, 0, allow_noarg); }
PHP_COM_DOTNET_API OLECHAR *php_com_string_to_olestring(char *string, size_t string_len, int codepage) { OLECHAR *olestring = NULL; DWORD flags = codepage == CP_UTF8 ? 0 : MB_PRECOMPOSED | MB_ERR_INVALID_CHARS; BOOL ok; if (string_len == -1) { /* determine required length for the buffer (includes NUL terminator) */ string_len = MultiByteToWideChar(codepage, flags, string, -1, NULL, 0); } else { /* allow room for NUL terminator */ string_len++; } if (string_len > 0) { olestring = (OLECHAR*)safe_emalloc(string_len, sizeof(OLECHAR), 0); /* XXX if that's a real multibyte string, olestring is obviously allocated excessively. This should be fixed by reallocating the olestring, but as emalloc is used, that doesn't matter much. */ ok = MultiByteToWideChar(codepage, flags, string, (int)string_len, olestring, (int)string_len); if (ok > 0 && ok < string_len) { olestring[ok] = '\0'; } } else { ok = FALSE; olestring = (OLECHAR*)emalloc(sizeof(OLECHAR)); *olestring = 0; } if (!ok) { char *msg = php_win32_error_to_msg(GetLastError()); php_error_docref(NULL, E_WARNING, "Could not convert string to unicode: `%s'", msg); LocalFree(msg); } return olestring; }
/* Performs an Invoke on the given com object. * returns a failure code and creates an exception if there was an error */ HRESULT php_com_invoke_helper(php_com_dotnet_object *obj, DISPID id_member, WORD flags, DISPPARAMS *disp_params, VARIANT *v, int silent, int allow_noarg TSRMLS_DC) { HRESULT hr; unsigned int arg_err; EXCEPINFO e = {0}; hr = IDispatch_Invoke(V_DISPATCH(&obj->v), id_member, &IID_NULL, LOCALE_SYSTEM_DEFAULT, flags, disp_params, v, &e, &arg_err); if (silent == 0 && FAILED(hr)) { char *source = NULL, *desc = NULL, *msg = NULL; int source_len, desc_len; switch (hr) { case DISP_E_EXCEPTION: if (e.bstrSource) { source = php_com_olestring_to_string(e.bstrSource, &source_len, obj->code_page TSRMLS_CC); SysFreeString(e.bstrSource); } if (e.bstrDescription) { desc = php_com_olestring_to_string(e.bstrDescription, &desc_len, obj->code_page TSRMLS_CC); SysFreeString(e.bstrDescription); } if (PG(html_errors)) { spprintf(&msg, 0, "<b>Source:</b> %s<br/><b>Description:</b> %s", source ? source : "Unknown", desc ? desc : "Unknown"); } else { spprintf(&msg, 0, "Source: %s\nDescription: %s", source ? source : "Unknown", desc ? desc : "Unknown"); } if (desc) { efree(desc); } if (source) { efree(source); } if (e.bstrHelpFile) { SysFreeString(e.bstrHelpFile); } break; case DISP_E_PARAMNOTFOUND: case DISP_E_TYPEMISMATCH: desc = php_win32_error_to_msg(hr); spprintf(&msg, 0, "Parameter %d: %s", arg_err, desc); LocalFree(desc); break; case DISP_E_BADPARAMCOUNT: if ((disp_params->cArgs + disp_params->cNamedArgs == 0) && (allow_noarg == 1)) { /* if getting a property and they are missing all parameters, * we want to create a proxy object for them; so lets not create an * exception here */ msg = NULL; break; } /* else fall through */ default: desc = php_win32_error_to_msg(hr); spprintf(&msg, 0, "Error [0x%08x] %s", hr, desc); LocalFree(desc); break; } if (msg) { php_com_throw_exception(hr, msg TSRMLS_CC); efree(msg); } } return hr; }
/* the core of COM */ int php_com_do_invoke_byref(php_com_dotnet_object *obj, zend_internal_function *f, WORD flags, VARIANT *v, int nargs, zval *args) { DISPID dispid, altdispid; DISPPARAMS disp_params; HRESULT hr; VARIANT *vargs = NULL, *byref_vals = NULL; int i, byref_count = 0, j; /* assumption: that the active function (f) is the function we generated for the engine */ if (!f) { return FAILURE; } hr = php_com_get_id_of_name(obj, f->function_name->val, f->function_name->len, &dispid); if (FAILED(hr)) { char *winerr = NULL; char *msg = NULL; winerr = php_win32_error_to_msg(hr); spprintf(&msg, 0, "Unable to lookup `%s': %s", f->function_name->val, winerr); LocalFree(winerr); php_com_throw_exception(hr, msg); efree(msg); return FAILURE; } if (nargs) { vargs = (VARIANT*)safe_emalloc(sizeof(VARIANT), nargs, 0); } if (f->arg_info) { for (i = 0; i < nargs; i++) { if (f->arg_info[nargs - i - 1].pass_by_reference) { byref_count++; } } } if (byref_count) { byref_vals = (VARIANT*)safe_emalloc(sizeof(VARIANT), byref_count, 0); for (j = 0, i = 0; i < nargs; i++) { if (f->arg_info[nargs - i - 1].pass_by_reference) { /* put the value into byref_vals instead */ php_com_variant_from_zval(&byref_vals[j], &args[nargs - i - 1], obj->code_page); /* if it is already byref, "move" it into the vargs array, otherwise * make vargs a reference to this value */ if (V_VT(&byref_vals[j]) & VT_BYREF) { memcpy(&vargs[i], &byref_vals[j], sizeof(vargs[i])); VariantInit(&byref_vals[j]); /* leave the variant slot empty to simplify cleanup */ } else { VariantInit(&vargs[i]); V_VT(&vargs[i]) = V_VT(&byref_vals[j]) | VT_BYREF; /* union magic ensures that this works out */ vargs[i].byref = &V_UINT(&byref_vals[j]); } j++; } else { php_com_variant_from_zval(&vargs[i], &args[nargs - i - 1], obj->code_page); } } } else { /* Invoke'd args are in reverse order */ for (i = 0; i < nargs; i++) { php_com_variant_from_zval(&vargs[i], &args[nargs - i - 1], obj->code_page); } } disp_params.cArgs = nargs; disp_params.cNamedArgs = 0; disp_params.rgvarg = vargs; disp_params.rgdispidNamedArgs = NULL; if (flags & DISPATCH_PROPERTYPUT) { altdispid = DISPID_PROPERTYPUT; disp_params.rgdispidNamedArgs = &altdispid; disp_params.cNamedArgs = 1; } /* this will create an exception if needed */ hr = php_com_invoke_helper(obj, dispid, flags, &disp_params, v, 0, 0); /* release variants */ if (vargs) { if (f && f->arg_info) { for (i = 0, j = 0; i < nargs; i++) { /* if this was byref, update the zval */ if (f->arg_info[nargs - i - 1].pass_by_reference) { SEPARATE_ZVAL_IF_NOT_REF(&args[nargs - i - 1]); /* if the variant is pointing at the byref_vals, we need to map * the pointee value as a zval; otherwise, the value is pointing * into an existing PHP variant record */ if (V_VT(&vargs[i]) & VT_BYREF) { if (vargs[i].byref == &V_UINT(&byref_vals[j])) { /* copy that value */ php_com_zval_from_variant(&args[nargs - i - 1], &byref_vals[j], obj->code_page); } } else { /* not sure if this can ever happen; the variant we marked as BYREF * is no longer BYREF - copy its value */ php_com_zval_from_variant(&args[nargs - i - 1], &vargs[i], obj->code_page); } VariantClear(&byref_vals[j]); j++; } VariantClear(&vargs[i]); } } else { for (i = 0, j = 0; i < nargs; i++) { VariantClear(&vargs[i]); } } efree(vargs); } return SUCCEEDED(hr) ? SUCCESS : FAILURE; }