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