xref: /PHP-7.3/ext/com_dotnet/com_typeinfo.c (revision 7424bfc7)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2018 The PHP Group                                |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.01 of the PHP license,      |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.php.net/license/3_01.txt                                  |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Author: Wez Furlong <wez@thebrainroom.com>                           |
16    |         Harald Radi <h.radi@nme.at>                                  |
17    +----------------------------------------------------------------------+
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 
24 #include "php.h"
25 #include "php_ini.h"
26 #include "ext/standard/info.h"
27 #include "php_com_dotnet.h"
28 #include "php_com_dotnet_internal.h"
29 
30 
31 /* The search string can be either:
32  * a) a file name
33  * b) a CLSID, major, minor e.g. "{00000200-0000-0010-8000-00AA006D2EA4},2,0"
34  * c) a Type Library name e.g. "Microsoft OLE DB ActiveX Data Objects 1.0 Library"
35  */
php_com_load_typelib(char * search_string,int codepage)36 PHP_COM_DOTNET_API ITypeLib *php_com_load_typelib(char *search_string, int codepage)
37 {
38 	ITypeLib *TL = NULL;
39 	char *strtok_buf, *major, *minor;
40 	CLSID clsid;
41 	OLECHAR *p;
42 	HRESULT hr;
43 
44 	search_string = php_strtok_r(search_string, ",", &strtok_buf);
45 
46 	if (search_string == NULL) {
47 		return NULL;
48 	}
49 
50 	major = php_strtok_r(NULL, ",", &strtok_buf);
51 	minor = php_strtok_r(NULL, ",", &strtok_buf);
52 
53 	p = php_com_string_to_olestring(search_string, strlen(search_string), codepage);
54 
55 	if (SUCCEEDED(CLSIDFromString(p, &clsid))) {
56 		WORD major_i = 1, minor_i = 0;
57 
58 		/* pick up the major/minor numbers; if none specified, default to 1,0 */
59 		if (major && minor) {
60 			major_i = (WORD)atoi(major);
61 			minor_i = (WORD)atoi(minor);
62 		}
63 
64 		/* Load the TypeLib by GUID */
65 		hr = LoadRegTypeLib((REFGUID)&clsid, major_i, minor_i, LANG_NEUTRAL, &TL);
66 
67 		/* if that failed, assumed that the GUID is actually a CLSID and
68 		 * attempt to get the library via an instance of that class */
69 		if (FAILED(hr) && (major == NULL || minor == NULL)) {
70 			IDispatch *disp = NULL;
71 			ITypeInfo *info = NULL;
72 			UINT idx;
73 
74 			if (SUCCEEDED(hr = CoCreateInstance(&clsid, NULL, CLSCTX_SERVER, &IID_IDispatch, (LPVOID*)&disp)) &&
75 					SUCCEEDED(hr = IDispatch_GetTypeInfo(disp, 0, LANG_NEUTRAL, &info))) {
76 				hr = ITypeInfo_GetContainingTypeLib(info, &TL, &idx);
77 			}
78 
79 			if (info) {
80 				ITypeInfo_Release(info);
81 			}
82 			if (disp) {
83 				IDispatch_Release(disp);
84 			}
85 		}
86 	} else {
87 		/* Try to load it from a file; if it fails, do a really painful search of
88 		 * the registry */
89 		if (FAILED(LoadTypeLib(p, &TL))) {
90 			HKEY hkey, hsubkey;
91 			DWORD SubKeys, MaxSubKeyLength;
92 			char *keyname;
93 			unsigned int i, j;
94 			DWORD VersionCount;
95 			char version[20];
96 			char *libname;
97 			long libnamelen;
98 
99 			if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CLASSES_ROOT, "TypeLib", 0, KEY_READ, &hkey) &&
100 					ERROR_SUCCESS == RegQueryInfoKey(hkey, NULL, NULL, NULL, &SubKeys,
101 					&MaxSubKeyLength, NULL, NULL, NULL, NULL, NULL, NULL)) {
102 
103 				MaxSubKeyLength++; /* make room for NUL */
104 				keyname = emalloc(MaxSubKeyLength);
105 				libname = emalloc(strlen(search_string) + 1);
106 
107 				for (i = 0; i < SubKeys && TL == NULL; i++) {
108 					if (ERROR_SUCCESS == RegEnumKey(hkey, i, keyname, MaxSubKeyLength) &&
109 							ERROR_SUCCESS == RegOpenKeyEx(hkey, keyname, 0, KEY_READ, &hsubkey)) {
110 						if (ERROR_SUCCESS == RegQueryInfoKey(hsubkey, NULL, NULL, NULL, &VersionCount,
111 								NULL, NULL, NULL, NULL, NULL, NULL, NULL)) {
112 							for (j = 0; j < VersionCount; j++) {
113 								if (ERROR_SUCCESS != RegEnumKey(hsubkey, j, version, sizeof(version))) {
114 									continue;
115 								}
116 								/* get the default value for this key and compare */
117 								libnamelen = (long)strlen(search_string)+1;
118 								if (ERROR_SUCCESS == RegQueryValue(hsubkey, version, libname, &libnamelen)) {
119 									if (0 == stricmp(libname, search_string)) {
120 										char *str = NULL;
121 										int major_tmp, minor_tmp;
122 
123 										/* fetch the GUID and add the version numbers */
124 										if (2 != sscanf(version, "%d.%d", &major_tmp, &minor_tmp)) {
125 											major_tmp = 1;
126 											minor_tmp = 0;
127 										}
128 										spprintf(&str, 0, "%s,%d,%d", keyname, major_tmp, minor_tmp);
129 										/* recurse */
130 										TL = php_com_load_typelib(str, codepage);
131 
132 										efree(str);
133 										break;
134 									}
135 								}
136 							}
137 						}
138 						RegCloseKey(hsubkey);
139 					}
140 				}
141 				RegCloseKey(hkey);
142 				efree(keyname);
143 				efree(libname);
144 			}
145 		}
146 	}
147 
148 	efree(p);
149 
150 	return TL;
151 }
152 
153 /* Given a type-library, merge it into the current engine state */
php_com_import_typelib(ITypeLib * TL,int mode,int codepage)154 PHP_COM_DOTNET_API int php_com_import_typelib(ITypeLib *TL, int mode, int codepage)
155 {
156 	int i, j, interfaces;
157 	TYPEKIND pTKind;
158 	ITypeInfo *TypeInfo;
159 	VARDESC *pVarDesc;
160 	UINT NameCount;
161 	BSTR bstr_ids;
162 	zend_constant c;
163 	zval *exists, results, value;
164 	char *const_name;
165 	size_t len;
166 
167 	if (TL == NULL) {
168 		return FAILURE;
169 	}
170 
171 	interfaces = ITypeLib_GetTypeInfoCount(TL);
172 	for (i = 0; i < interfaces; i++) {
173 		ITypeLib_GetTypeInfoType(TL, i, &pTKind);
174 		if (pTKind == TKIND_ENUM) {
175 			ITypeLib_GetTypeInfo(TL, i, &TypeInfo);
176 			for (j = 0; ; j++) {
177 				if (FAILED(ITypeInfo_GetVarDesc(TypeInfo, j, &pVarDesc))) {
178 					break;
179 				}
180 				ITypeInfo_GetNames(TypeInfo, pVarDesc->memid, &bstr_ids, 1, &NameCount);
181 				if (NameCount != 1) {
182 					ITypeInfo_ReleaseVarDesc(TypeInfo, pVarDesc);
183 					continue;
184 				}
185 
186 				const_name = php_com_olestring_to_string(bstr_ids, &len, codepage);
187 				c.name = zend_string_init(const_name, len, mode & CONST_PERSISTENT);
188 				// TODO: avoid reallocation???
189 				efree(const_name);
190 				if(c.name == NULL) {
191 					ITypeInfo_ReleaseVarDesc(TypeInfo, pVarDesc);
192 					continue;
193 				}
194 //???				c.name_len++; /* include NUL */
195 				SysFreeString(bstr_ids);
196 
197 				/* sanity check for the case where the constant is already defined */
198 				php_com_zval_from_variant(&value, pVarDesc->lpvarValue, codepage);
199 				if ((exists = zend_get_constant(c.name)) != NULL) {
200 					if (COMG(autoreg_verbose) && !compare_function(&results, &value, exists)) {
201 						php_error_docref(NULL, E_WARNING, "Type library constant %s is already defined", ZSTR_VAL(c.name));
202 					}
203 					zend_string_release_ex(c.name, mode & CONST_PERSISTENT);
204 					ITypeInfo_ReleaseVarDesc(TypeInfo, pVarDesc);
205 					continue;
206 				}
207 
208 				/* register the constant */
209 				if (Z_TYPE(value) == IS_LONG) {
210 					ZEND_CONSTANT_SET_FLAGS(&c, mode, 0);
211 					ZVAL_LONG(&c.value, Z_LVAL(value));
212 					zend_register_constant(&c);
213 				}
214 				ITypeInfo_ReleaseVarDesc(TypeInfo, pVarDesc);
215 			}
216 			ITypeInfo_Release(TypeInfo);
217 		}
218 	}
219 	return SUCCESS;
220 }
221 
222 /* Type-library stuff */
php_com_typelibrary_dtor(zval * pDest)223 void php_com_typelibrary_dtor(zval *pDest)
224 {
225 	ITypeLib *Lib = (ITypeLib*)Z_PTR_P(pDest);
226 	ITypeLib_Release(Lib);
227 }
228 
php_com_load_typelib_via_cache(char * search_string,int codepage,int * cached)229 PHP_COM_DOTNET_API ITypeLib *php_com_load_typelib_via_cache(char *search_string,
230 	int codepage, int *cached)
231 {
232 	ITypeLib *TL;
233 	char *name_dup;
234 	size_t l;
235 
236 	l = strlen(search_string);
237 
238 	if ((TL = zend_ts_hash_str_find_ptr(&php_com_typelibraries, search_string, l)) != NULL) {
239 		*cached = 1;
240 		/* add a reference for the caller */
241 		ITypeLib_AddRef(TL);
242 		return TL;
243 	}
244 
245 	*cached = 0;
246 	name_dup = estrndup(search_string, l);
247 	TL = php_com_load_typelib(name_dup, codepage);
248 	efree(name_dup);
249 
250 	if (TL) {
251 		if (NULL != zend_ts_hash_str_update_ptr(&php_com_typelibraries,
252 				search_string, l, TL)) {
253 			/* add a reference for the hash table */
254 			ITypeLib_AddRef(TL);
255 		}
256 	}
257 
258 	return TL;
259 }
260 
php_com_locate_typeinfo(char * typelibname,php_com_dotnet_object * obj,char * dispname,int sink)261 ITypeInfo *php_com_locate_typeinfo(char *typelibname, php_com_dotnet_object *obj, char *dispname, int sink)
262 {
263 	ITypeInfo *typeinfo = NULL;
264 	ITypeLib *typelib = NULL;
265 	int gotguid = 0;
266 	GUID iid;
267 
268 	if (obj) {
269 		if (dispname == NULL && sink) {
270 			if (V_VT(&obj->v) == VT_DISPATCH) {
271 				IProvideClassInfo2 *pci2;
272 				IProvideClassInfo *pci;
273 
274 				if (SUCCEEDED(IDispatch_QueryInterface(V_DISPATCH(&obj->v), &IID_IProvideClassInfo2, (void**)&pci2))) {
275 					gotguid = SUCCEEDED(IProvideClassInfo2_GetGUID(pci2, GUIDKIND_DEFAULT_SOURCE_DISP_IID, &iid));
276 					IProvideClassInfo2_Release(pci2);
277 				}
278 				if (!gotguid && SUCCEEDED(IDispatch_QueryInterface(V_DISPATCH(&obj->v), &IID_IProvideClassInfo, (void**)&pci))) {
279 					/* examine the available interfaces */
280 					/* TODO: write some code here */
281 					php_error_docref(NULL, E_WARNING, "IProvideClassInfo: this code not yet written!");
282 					IProvideClassInfo_Release(pci);
283 				}
284 			}
285 		} else if (dispname == NULL) {
286 			if (obj->typeinfo) {
287 				ITypeInfo_AddRef(obj->typeinfo);
288 				return obj->typeinfo;
289 			} else {
290 				IDispatch_GetTypeInfo(V_DISPATCH(&obj->v), 0, LANG_NEUTRAL, &typeinfo);
291 				if (typeinfo) {
292 					return typeinfo;
293 				}
294 			}
295 		} else if (dispname && obj->typeinfo) {
296 			unsigned int idx;
297 			/* get the library from the object; the rest will be dealt with later */
298 			ITypeInfo_GetContainingTypeLib(obj->typeinfo, &typelib, &idx);
299 		} else if (typelibname == NULL) {
300 			if (V_VT(&obj->v) == VT_DISPATCH) {
301 				IDispatch_GetTypeInfo(V_DISPATCH(&obj->v), 0, LANG_NEUTRAL, &typeinfo);
302 				if (dispname) {
303 					unsigned int idx;
304 					/* get the library from the object; the rest will be dealt with later */
305 					ITypeInfo_GetContainingTypeLib(typeinfo, &typelib, &idx);
306 
307 					if (typelib) {
308 						ITypeInfo_Release(typeinfo);
309 						typeinfo = NULL;
310 					}
311 				}
312 			}
313 		}
314 	} else if (typelibname) {
315 		/* Fetch the typelibrary and use that to look things up */
316 		typelib = php_com_load_typelib(typelibname, CP_THREAD_ACP);
317 	}
318 
319 	if (!gotguid && dispname && typelib) {
320 		unsigned short cfound;
321 		MEMBERID memid;
322 		OLECHAR *olename = php_com_string_to_olestring(dispname, strlen(dispname), CP_ACP);
323 
324 		cfound = 1;
325 		if (FAILED(ITypeLib_FindName(typelib, olename, 0, &typeinfo, &memid, &cfound)) || cfound == 0) {
326 			CLSID coclass;
327 			ITypeInfo *coinfo;
328 
329 			/* assume that it might be a progid instead */
330 			if (SUCCEEDED(CLSIDFromProgID(olename, &coclass)) &&
331 					SUCCEEDED(ITypeLib_GetTypeInfoOfGuid(typelib, &coclass, &coinfo))) {
332 
333 				/* enumerate implemented interfaces and pick the one as indicated by sink */
334 				TYPEATTR *attr;
335 				int i;
336 
337 				ITypeInfo_GetTypeAttr(coinfo, &attr);
338 
339 				for (i = 0; i < attr->cImplTypes; i++) {
340 					HREFTYPE rt;
341 					int tf;
342 
343 					if (FAILED(ITypeInfo_GetImplTypeFlags(coinfo, i, &tf))) {
344 						continue;
345 					}
346 
347 					if ((sink && tf == (IMPLTYPEFLAG_FSOURCE|IMPLTYPEFLAG_FDEFAULT)) ||
348 						(!sink && (tf & IMPLTYPEFLAG_FSOURCE) == 0)) {
349 
350 						/* flags match what we are looking for */
351 
352 						if (SUCCEEDED(ITypeInfo_GetRefTypeOfImplType(coinfo, i, &rt)))
353 							if (SUCCEEDED(ITypeInfo_GetRefTypeInfo(coinfo, rt, &typeinfo)))
354 								break;
355 
356 					}
357 				}
358 
359 				ITypeInfo_ReleaseTypeAttr(coinfo, attr);
360 				ITypeInfo_Release(coinfo);
361 			}
362 		}
363 
364 
365 		efree(olename);
366 	} else if (gotguid) {
367 		ITypeLib_GetTypeInfoOfGuid(typelib, &iid, &typeinfo);
368 	}
369 
370 	if (typelib) {
371 		ITypeLib_Release(typelib);
372 	}
373 
374 	return typeinfo;
375 }
376 
377 static const struct {
378 	VARTYPE vt;
379 	const char *name;
380 } vt_names[] = {
381 	{ VT_NULL,		"VT_NULL" },
382 	{ VT_EMPTY,		"VT_EMPTY" },
383 	{ VT_UI1,		"VT_UI1" },
384 	{ VT_I2,		"VT_I2" },
385 	{ VT_I4,		"VT_I4" },
386 	{ VT_R4,		"VT_R4" },
387 	{ VT_R8,		"VT_R8" },
388 	{ VT_BOOL,		"VT_BOOL" },
389 	{ VT_ERROR,		"VT_ERROR" },
390 	{ VT_CY,		"VT_CY" },
391 	{ VT_DATE,		"VT_DATE" },
392 	{ VT_BSTR,		"VT_BSTR" },
393 	{ VT_DECIMAL,	"VT_DECIMAL" },
394 	{ VT_UNKNOWN,	"VT_UNKNOWN" },
395 	{ VT_DISPATCH,	"VT_DISPATCH" },
396 	{ VT_VARIANT,	"VT_VARIANT" },
397 	{ VT_I1,		"VT_I1" },
398 	{ VT_UI2,		"VT_UI2" },
399 	{ VT_UI4,		"VT_UI4" },
400 	{ VT_INT,		"VT_INT" },
401 	{ VT_UINT,		"VT_UINT" },
402 	{ VT_ARRAY,		"VT_ARRAY" },
403 	{ VT_BYREF,		"VT_BYREF" },
404 	{ VT_VOID,		"VT_VOID" },
405 	{ VT_PTR,		"VT_PTR" },
406 	{ VT_HRESULT,	"VT_HRESULT" },
407 	{ VT_SAFEARRAY, "VT_SAFEARRAY" },
408 	{ 0, NULL }
409 };
410 
vt_to_string(VARTYPE vt)411 static inline const char *vt_to_string(VARTYPE vt)
412 {
413 	int i;
414 	for (i = 0; vt_names[i].name != NULL; i++) {
415 		if (vt_names[i].vt == vt)
416 			return vt_names[i].name;
417 	}
418 	return "?";
419 }
420 
php_com_string_from_clsid(const CLSID * clsid,int codepage)421 static char *php_com_string_from_clsid(const CLSID *clsid, int codepage)
422 {
423 	LPOLESTR ole_clsid;
424 	char *clsid_str;
425 
426 	StringFromCLSID(clsid, &ole_clsid);
427 	clsid_str = php_com_olestring_to_string(ole_clsid, NULL, codepage);
428 	LocalFree(ole_clsid);
429 
430 	return clsid_str;
431 }
432 
433 
php_com_process_typeinfo(ITypeInfo * typeinfo,HashTable * id_to_name,int printdef,GUID * guid,int codepage)434 int php_com_process_typeinfo(ITypeInfo *typeinfo, HashTable *id_to_name, int printdef, GUID *guid, int codepage)
435 {
436 	TYPEATTR *attr;
437 	FUNCDESC *func;
438 	int i;
439 	OLECHAR *olename;
440 	char *ansiname = NULL;
441 	size_t ansinamelen;
442 	int ret = 0;
443 	DISPID lastid = 0;	/* for props */
444 
445 	if (FAILED(ITypeInfo_GetTypeAttr(typeinfo, &attr))) {
446 		return 0;
447 	}
448 
449 	/* verify that it is suitable */
450 	if (id_to_name == NULL || attr->typekind == TKIND_DISPATCH) {
451 
452 		if (guid) {
453 			memcpy(guid, &attr->guid, sizeof(GUID));
454 		}
455 
456 		if (printdef) {
457 			char *guidstring;
458 
459 			ITypeInfo_GetDocumentation(typeinfo, MEMBERID_NIL, &olename, NULL, NULL, NULL);
460 			ansiname = php_com_olestring_to_string(olename, &ansinamelen, codepage);
461 			SysFreeString(olename);
462 
463 			guidstring = php_com_string_from_clsid(&attr->guid, codepage);
464 			php_printf("class %s { /* GUID=%s */\n", ansiname, guidstring);
465 			efree(guidstring);
466 
467 			efree(ansiname);
468 		}
469 
470 		if (id_to_name) {
471 			zend_hash_init(id_to_name, 0, NULL, ZVAL_PTR_DTOR, 0);
472 		}
473 
474 		/* So we've got the dispatch interface; lets list the event methods */
475 		for (i = 0; i < attr->cFuncs; i++) {
476 			zval tmp;
477 			int isprop;
478 
479 			if (FAILED(ITypeInfo_GetFuncDesc(typeinfo, i, &func)))
480 				break;
481 
482 			isprop = (func->invkind & DISPATCH_PROPERTYGET || func->invkind & DISPATCH_PROPERTYPUT);
483 
484 			if (!isprop || lastid != func->memid) {
485 
486 				lastid = func->memid;
487 
488 				ITypeInfo_GetDocumentation(typeinfo, func->memid, &olename, NULL, NULL, NULL);
489 				ansiname = php_com_olestring_to_string(olename, &ansinamelen, codepage);
490 				SysFreeString(olename);
491 
492 				if (printdef) {
493 					int j;
494 					char *funcdesc;
495 					size_t funcdesclen;
496 					unsigned int cnames = 0;
497 					BSTR *names;
498 
499 					names = (BSTR*)safe_emalloc((func->cParams + 1), sizeof(BSTR), 0);
500 
501 					ITypeInfo_GetNames(typeinfo, func->memid, names, func->cParams + 1, &cnames);
502 					/* first element is the function name */
503 					SysFreeString(names[0]);
504 
505 					php_printf("\t/* DISPID=%d */\n", func->memid);
506 
507 					if (func->elemdescFunc.tdesc.vt != VT_VOID) {
508 						php_printf("\t/* %s [%d] */\n",
509 								vt_to_string(func->elemdescFunc.tdesc.vt),
510 								func->elemdescFunc.tdesc.vt
511 								);
512 					}
513 
514 					if (isprop) {
515 
516 						ITypeInfo_GetDocumentation(typeinfo, func->memid, NULL, &olename, NULL, NULL);
517 						if (olename) {
518 							funcdesc = php_com_olestring_to_string(olename, &funcdesclen, codepage);
519 							SysFreeString(olename);
520 							php_printf("\t/* %s */\n", funcdesc);
521 							efree(funcdesc);
522 						}
523 
524 						php_printf("\tvar $%s;\n\n", ansiname);
525 
526 					} else {
527 						/* a function */
528 
529 						php_printf("\tfunction %s(\n", ansiname);
530 
531 						for (j = 0; j < func->cParams; j++) {
532 							ELEMDESC *elem = &func->lprgelemdescParam[j];
533 
534 							php_printf("\t\t/* %s [%d] ", vt_to_string(elem->tdesc.vt), elem->tdesc.vt);
535 
536 							if (elem->paramdesc.wParamFlags & PARAMFLAG_FIN)
537 								php_printf("[in]");
538 							if (elem->paramdesc.wParamFlags & PARAMFLAG_FOUT)
539 								php_printf("[out]");
540 
541 							if (elem->tdesc.vt == VT_PTR) {
542 								/* what does it point to ? */
543 								php_printf(" --> %s [%d] ",
544 										vt_to_string(elem->tdesc.lptdesc->vt),
545 										elem->tdesc.lptdesc->vt
546 										);
547 							}
548 
549 							/* when we handle prop put and get, this will look nicer */
550 							if (j+1 < (int)cnames) {
551 								funcdesc = php_com_olestring_to_string(names[j+1], &funcdesclen, codepage);
552 								SysFreeString(names[j+1]);
553 							} else {
554 								funcdesc = "???";
555 							}
556 
557 							php_printf(" */ %s%s%c\n",
558 									elem->tdesc.vt == VT_PTR ? "&$" : "$",
559 									funcdesc,
560 									j == func->cParams - 1 ? ' ' : ','
561 									);
562 
563 							if (j+1 < (int)cnames) {
564 								efree(funcdesc);
565 							}
566 						}
567 
568 						php_printf("\t\t)\n\t{\n");
569 
570 						ITypeInfo_GetDocumentation(typeinfo, func->memid, NULL, &olename, NULL, NULL);
571 						if (olename) {
572 							funcdesc = php_com_olestring_to_string(olename, &funcdesclen, codepage);
573 							SysFreeString(olename);
574 							php_printf("\t\t/* %s */\n", funcdesc);
575 							efree(funcdesc);
576 						}
577 
578 						php_printf("\t}\n");
579 					}
580 
581 					efree(names);
582 				}
583 
584 				if (id_to_name) {
585 					zend_str_tolower(ansiname, ansinamelen);
586 					ZVAL_STRINGL(&tmp, ansiname, ansinamelen);
587 					zend_hash_index_update(id_to_name, func->memid, &tmp);
588 					// TODO: avoid reallocation???
589 				}
590 				efree(ansiname);
591 			}
592 			ITypeInfo_ReleaseFuncDesc(typeinfo, func);
593 		}
594 
595 		if (printdef) {
596 			php_printf("}\n");
597 		}
598 
599 		ret = 1;
600 	} else {
601 		zend_error(E_WARNING, "That's not a dispatchable interface!! type kind = %08x", attr->typekind);
602 	}
603 
604 	ITypeInfo_ReleaseTypeAttr(typeinfo, attr);
605 
606 	return ret;
607 }
608