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