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