/* map an ID to a name */ HRESULT php_com_get_id_of_name(php_com_dotnet_object *obj, char *name, size_t namelen, DISPID *dispid) { OLECHAR *olename; HRESULT hr; zval *tmp; if (namelen == -1) { namelen = strlen(name); } if (obj->id_of_name_cache && NULL != (tmp = zend_hash_str_find(obj->id_of_name_cache, name, namelen))) { *dispid = (DISPID)Z_LVAL_P(tmp); return S_OK; } olename = php_com_string_to_olestring(name, namelen, obj->code_page); if (obj->typeinfo) { hr = ITypeInfo_GetIDsOfNames(obj->typeinfo, &olename, 1, dispid); if (FAILED(hr)) { hr = IDispatch_GetIDsOfNames(V_DISPATCH(&obj->v), &IID_NULL, &olename, 1, LOCALE_SYSTEM_DEFAULT, dispid); if (SUCCEEDED(hr)) { /* fall back on IDispatch direct */ ITypeInfo_Release(obj->typeinfo); obj->typeinfo = NULL; } } } else { hr = IDispatch_GetIDsOfNames(V_DISPATCH(&obj->v), &IID_NULL, &olename, 1, LOCALE_SYSTEM_DEFAULT, dispid); } efree(olename); if (SUCCEEDED(hr)) { zval tmp; /* cache the mapping */ if (!obj->id_of_name_cache) { ALLOC_HASHTABLE(obj->id_of_name_cache); zend_hash_init(obj->id_of_name_cache, 2, NULL, NULL, 0); } ZVAL_LONG(&tmp, *dispid); zend_hash_str_update(obj->id_of_name_cache, name, namelen, &tmp); } return hr; }
/* The search string can be either: * a) a file name * b) a CLSID, major, minor e.g. "{00000200-0000-0010-8000-00AA006D2EA4},2,0" * c) a Type Library name e.g. "Microsoft OLE DB ActiveX Data Objects 1.0 Library" */ PHP_COM_DOTNET_API ITypeLib *php_com_load_typelib(char *search_string, int codepage TSRMLS_DC) { ITypeLib *TL = NULL; char *strtok_buf, *major, *minor; CLSID clsid; OLECHAR *p; HRESULT hr; search_string = php_strtok_r(search_string, ",", &strtok_buf); if (search_string == NULL) { return NULL; } major = php_strtok_r(NULL, ",", &strtok_buf); minor = php_strtok_r(NULL, ",", &strtok_buf); p = php_com_string_to_olestring(search_string, strlen(search_string), codepage TSRMLS_CC); if (SUCCEEDED(CLSIDFromString(p, &clsid))) { WORD major_i = 1, minor_i = 0; /* pick up the major/minor numbers; if none specified, default to 1,0 */ if (major && minor) { major_i = (WORD)atoi(major); minor_i = (WORD)atoi(minor); } /* Load the TypeLib by GUID */ hr = LoadRegTypeLib((REFGUID)&clsid, major_i, minor_i, LANG_NEUTRAL, &TL); /* if that failed, assumed that the GUID is actually a CLSID and * attemp to get the library via an instance of that class */ if (FAILED(hr) && (major == NULL || minor == NULL)) { IDispatch *disp = NULL; ITypeInfo *info = NULL; int idx; if (SUCCEEDED(hr = CoCreateInstance(&clsid, NULL, CLSCTX_SERVER, &IID_IDispatch, (LPVOID*)&disp)) && SUCCEEDED(hr = IDispatch_GetTypeInfo(disp, 0, LANG_NEUTRAL, &info))) { hr = ITypeInfo_GetContainingTypeLib(info, &TL, &idx); } if (info) { ITypeInfo_Release(info); } if (disp) { IDispatch_Release(disp); } } } else { /* Try to load it from a file; if it fails, do a really painful search of * the registry */ if (FAILED(LoadTypeLib(p, &TL))) { HKEY hkey, hsubkey; DWORD SubKeys, MaxSubKeyLength; char *keyname; unsigned int i, j; DWORD VersionCount; char version[20]; char *libname; DWORD libnamelen; if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CLASSES_ROOT, "TypeLib", 0, KEY_READ, &hkey) && ERROR_SUCCESS == RegQueryInfoKey(hkey, NULL, NULL, NULL, &SubKeys, &MaxSubKeyLength, NULL, NULL, NULL, NULL, NULL, NULL)) { MaxSubKeyLength++; /* make room for NUL */ keyname = emalloc(MaxSubKeyLength); libname = emalloc(strlen(search_string) + 1); for (i = 0; i < SubKeys && TL == NULL; i++) { if (ERROR_SUCCESS == RegEnumKey(hkey, i, keyname, MaxSubKeyLength) && ERROR_SUCCESS == RegOpenKeyEx(hkey, keyname, 0, KEY_READ, &hsubkey)) { if (ERROR_SUCCESS == RegQueryInfoKey(hsubkey, NULL, NULL, NULL, &VersionCount, NULL, NULL, NULL, NULL, NULL, NULL, NULL)) { for (j = 0; j < VersionCount; j++) { if (ERROR_SUCCESS != RegEnumKey(hsubkey, j, version, sizeof(version))) { continue; } /* get the default value for this key and compare */ libnamelen = strlen(search_string)+1; if (ERROR_SUCCESS == RegQueryValue(hsubkey, version, libname, &libnamelen)) { if (0 == stricmp(libname, search_string)) { char *str = NULL; int major_tmp, minor_tmp; /* fetch the GUID and add the version numbers */ if (2 != sscanf(version, "%d.%d", &major_tmp, &minor_tmp)) { major_tmp = 1; minor_tmp = 0; } spprintf(&str, 0, "%s,%d,%d", keyname, major_tmp, minor_tmp); /* recurse */ TL = php_com_load_typelib(str, codepage TSRMLS_CC); efree(str); break; } } } } RegCloseKey(hsubkey); } } RegCloseKey(hkey); efree(keyname); efree(libname); } } } efree(p); return TL; }
ITypeInfo *php_com_locate_typeinfo(char *typelibname, php_com_dotnet_object *obj, char *dispname, int sink) { ITypeInfo *typeinfo = NULL; ITypeLib *typelib = NULL; int gotguid = 0; GUID iid; if (obj) { if (dispname == NULL && sink) { IProvideClassInfo2 *pci2; IProvideClassInfo *pci; if (SUCCEEDED(IDispatch_QueryInterface(V_DISPATCH(&obj->v), &IID_IProvideClassInfo2, (void**)&pci2))) { gotguid = SUCCEEDED(IProvideClassInfo2_GetGUID(pci2, GUIDKIND_DEFAULT_SOURCE_DISP_IID, &iid)); IProvideClassInfo2_Release(pci2); } if (!gotguid && SUCCEEDED(IDispatch_QueryInterface(V_DISPATCH(&obj->v), &IID_IProvideClassInfo, (void**)&pci))) { /* examine the available interfaces */ /* TODO: write some code here */ php_error_docref(NULL, E_WARNING, "IProvideClassInfo: this code not yet written!"); IProvideClassInfo_Release(pci); } } else if (dispname == NULL) { if (obj->typeinfo) { ITypeInfo_AddRef(obj->typeinfo); return obj->typeinfo; } else { IDispatch_GetTypeInfo(V_DISPATCH(&obj->v), 0, LANG_NEUTRAL, &typeinfo); if (typeinfo) { return typeinfo; } } } else if (dispname && obj->typeinfo) { unsigned int idx; /* get the library from the object; the rest will be dealt with later */ ITypeInfo_GetContainingTypeLib(obj->typeinfo, &typelib, &idx); } else if (typelibname == NULL) { IDispatch_GetTypeInfo(V_DISPATCH(&obj->v), 0, LANG_NEUTRAL, &typeinfo); if (dispname) { unsigned int idx; /* get the library from the object; the rest will be dealt with later */ ITypeInfo_GetContainingTypeLib(typeinfo, &typelib, &idx); if (typelib) { ITypeInfo_Release(typeinfo); typeinfo = NULL; } } } } else if (typelibname) { /* Fetch the typelibrary and use that to look things up */ typelib = php_com_load_typelib(typelibname, CP_THREAD_ACP); } if (!gotguid && dispname && typelib) { unsigned short cfound; MEMBERID memid; OLECHAR *olename = php_com_string_to_olestring(dispname, strlen(dispname), CP_ACP); cfound = 1; if (FAILED(ITypeLib_FindName(typelib, olename, 0, &typeinfo, &memid, &cfound)) || cfound == 0) { CLSID coclass; ITypeInfo *coinfo; /* assume that it might be a progid instead */ if (SUCCEEDED(CLSIDFromProgID(olename, &coclass)) && SUCCEEDED(ITypeLib_GetTypeInfoOfGuid(typelib, &coclass, &coinfo))) { /* enumerate implemented interfaces and pick the one as indicated by sink */ TYPEATTR *attr; int i; ITypeInfo_GetTypeAttr(coinfo, &attr); for (i = 0; i < attr->cImplTypes; i++) { HREFTYPE rt; int tf; if (FAILED(ITypeInfo_GetImplTypeFlags(coinfo, i, &tf))) { continue; } if ((sink && tf == (IMPLTYPEFLAG_FSOURCE|IMPLTYPEFLAG_FDEFAULT)) || (!sink && (tf & IMPLTYPEFLAG_FSOURCE) == 0)) { /* flags match what we are looking for */ if (SUCCEEDED(ITypeInfo_GetRefTypeOfImplType(coinfo, i, &rt))) if (SUCCEEDED(ITypeInfo_GetRefTypeInfo(coinfo, rt, &typeinfo))) break; } } ITypeInfo_ReleaseTypeAttr(coinfo, attr); ITypeInfo_Release(coinfo); } } efree(olename); } else if (gotguid) { ITypeLib_GetTypeInfoOfGuid(typelib, &iid, &typeinfo); } if (typelib) { ITypeLib_Release(typelib); } return typeinfo; }
PHP_COM_DOTNET_API void php_com_variant_from_zval(VARIANT *v, zval *z, int codepage TSRMLS_DC) { OLECHAR *olestring; php_com_dotnet_object *obj; switch (Z_TYPE_P(z)) { case IS_NULL: V_VT(v) = VT_NULL; break; case IS_BOOL: V_VT(v) = VT_BOOL; V_BOOL(v) = Z_BVAL_P(z) ? VARIANT_TRUE : VARIANT_FALSE; break; case IS_OBJECT: if (php_com_is_valid_object(z TSRMLS_CC)) { obj = CDNO_FETCH(z); if (V_VT(&obj->v) == VT_DISPATCH) { /* pass the underlying object */ V_VT(v) = VT_DISPATCH; if (V_DISPATCH(&obj->v)) { IDispatch_AddRef(V_DISPATCH(&obj->v)); } 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; } } else { /* export the PHP object using our COM wrapper */ V_VT(v) = VT_DISPATCH; V_DISPATCH(v) = php_com_wrapper_export(z TSRMLS_CC); } break; case IS_ARRAY: /* map as safe array */ safe_array_from_zval(v, z, codepage TSRMLS_CC); break; case IS_LONG: V_VT(v) = VT_I4; V_I4(v) = Z_LVAL_P(z); break; case IS_DOUBLE: V_VT(v) = VT_R8; V_R8(v) = Z_DVAL_P(z); break; case IS_STRING: V_VT(v) = VT_BSTR; olestring = php_com_string_to_olestring(Z_STRVAL_P(z), Z_STRLEN_P(z), codepage TSRMLS_CC); V_BSTR(v) = SysAllocStringByteLen((char*)olestring, Z_STRLEN_P(z) * sizeof(OLECHAR)); efree(olestring); break; case IS_RESOURCE: case IS_CONSTANT: case IS_CONSTANT_ARRAY: default: V_VT(v) = VT_NULL; break; } }
static union _zend_function *com_method_get(zend_object **object_ptr, zend_string *name, const zval *key) { zend_internal_function f, *fptr = NULL; union _zend_function *func; DISPID dummy; php_com_dotnet_object *obj = (php_com_dotnet_object*)*object_ptr; if (V_VT(&obj->v) != VT_DISPATCH) { return NULL; } if (FAILED(php_com_get_id_of_name(obj, name->val, name->len, &dummy))) { return NULL; } /* check cache */ if (obj->method_cache == NULL || NULL == (fptr = zend_hash_find_ptr(obj->method_cache, name))) { f.type = ZEND_OVERLOADED_FUNCTION; f.num_args = 0; f.arg_info = NULL; f.scope = obj->ce; f.fn_flags = ZEND_ACC_CALL_VIA_HANDLER; f.function_name = zend_string_copy(name); f.handler = PHP_FN(com_method_handler); fptr = &f; if (obj->typeinfo) { /* look for byref params */ ITypeComp *comp; ITypeInfo *TI = NULL; DESCKIND kind; BINDPTR bindptr; OLECHAR *olename; ULONG lhash; int i; 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); if (SUCCEEDED(ITypeComp_Bind(comp, olename, lhash, INVOKE_FUNC, &TI, &kind, &bindptr))) { switch (kind) { case DESCKIND_FUNCDESC: f.arg_info = ecalloc(bindptr.lpfuncdesc->cParams, sizeof(zend_arg_info)); for (i = 0; i < bindptr.lpfuncdesc->cParams; i++) { f.arg_info[i].allow_null = 1; if (bindptr.lpfuncdesc->lprgelemdescParam[i].paramdesc.wParamFlags & PARAMFLAG_FOUT) { f.arg_info[i].pass_by_reference = ZEND_SEND_BY_REF; } } f.num_args = bindptr.lpfuncdesc->cParams; ITypeInfo_ReleaseFuncDesc(TI, bindptr.lpfuncdesc); break; /* these should not happen, but *might* happen if the user * screws up; lets avoid a leak in that case */ case DESCKIND_VARDESC: ITypeInfo_ReleaseVarDesc(TI, bindptr.lpvardesc); break; case DESCKIND_TYPECOMP: ITypeComp_Release(bindptr.lptcomp); break; case DESCKIND_NONE: break; } if (TI) { ITypeInfo_Release(TI); } } ITypeComp_Release(comp); efree(olename); } } zend_set_function_arg_flags((zend_function*)&f); /* save this method in the cache */ if (!obj->method_cache) { ALLOC_HASHTABLE(obj->method_cache); zend_hash_init(obj->method_cache, 2, NULL, function_dtor, 0); } zend_hash_update_mem(obj->method_cache, name, &f, sizeof(f)); } if (fptr) { /* duplicate this into a new chunk of emalloc'd memory, * since the engine will efree it */ func = emalloc(sizeof(*fptr)); memcpy(func, fptr, sizeof(*fptr)); return func; } return NULL; }
PHP_COM_DOTNET_API void php_com_variant_from_zval(VARIANT *v, zval *z, int codepage) { OLECHAR *olestring; php_com_dotnet_object *obj; zend_uchar ztype = IS_NULL; if (z) { ZVAL_DEREF(z); ztype = Z_TYPE_P(z); } switch (ztype) { case IS_NULL: V_VT(v) = VT_NULL; break; case IS_FALSE: V_VT(v) = VT_BOOL; V_BOOL(v) = VARIANT_FALSE; break; case IS_TRUE: V_VT(v) = VT_BOOL; V_BOOL(v) = VARIANT_TRUE; break; case IS_OBJECT: if (php_com_is_valid_object(z)) { obj = CDNO_FETCH(z); if (V_VT(&obj->v) == VT_DISPATCH) { /* pass the underlying object */ V_VT(v) = VT_DISPATCH; if (V_DISPATCH(&obj->v)) { IDispatch_AddRef(V_DISPATCH(&obj->v)); } 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; } } else { /* export the PHP object using our COM wrapper */ V_VT(v) = VT_DISPATCH; V_DISPATCH(v) = php_com_wrapper_export(z); } break; case IS_ARRAY: /* map as safe array */ safe_array_from_zval(v, z, codepage); break; case IS_LONG: #if SIZEOF_ZEND_LONG == 4 V_VT(v) = VT_I4; V_I4(v) = Z_LVAL_P(z); #else V_VT(v) = VT_I8; V_I8(v) = Z_LVAL_P(z); #endif break; case IS_DOUBLE: V_VT(v) = VT_R8; V_R8(v) = Z_DVAL_P(z); break; case IS_STRING: V_VT(v) = VT_BSTR; olestring = php_com_string_to_olestring(Z_STRVAL_P(z), Z_STRLEN_P(z), codepage); if (CP_UTF8 == codepage) { V_BSTR(v) = SysAllocStringByteLen((char*)olestring, (UINT)(wcslen(olestring) * sizeof(OLECHAR))); } else { V_BSTR(v) = SysAllocStringByteLen((char*)olestring, (UINT)(Z_STRLEN_P(z) * sizeof(OLECHAR))); } efree(olestring); break; case IS_RESOURCE: case IS_CONSTANT_AST: default: V_VT(v) = VT_NULL; break; } }