xref: /PHP-5.3/ext/com_dotnet/com_wrapper.c (revision a2045ff3)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 5                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2013 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    +----------------------------------------------------------------------+
17  */
18 
19 /* $Id$ */
20 
21 /* This module exports a PHP object as a COM object by wrapping it
22  * using IDispatchEx */
23 
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27 
28 #include "php.h"
29 #include "php_ini.h"
30 #include "ext/standard/info.h"
31 #include "php_com_dotnet.h"
32 #include "php_com_dotnet_internal.h"
33 
34 typedef struct {
35 	/* This first part MUST match the declaration
36 	 * of interface IDispatchEx */
37 	CONST_VTBL struct IDispatchExVtbl *lpVtbl;
38 
39 	/* now the PHP stuff */
40 
41 	DWORD engine_thread; /* for sanity checking */
42 	zval *object;			/* the object exported */
43 	LONG refcount;			/* COM reference count */
44 
45 	HashTable *dispid_to_name;	/* keep track of dispid -> name mappings */
46 	HashTable *name_to_dispid;	/* keep track of name -> dispid mappings */
47 
48 	GUID sinkid;	/* iid that we "implement" for event sinking */
49 
50 	int id;
51 } php_dispatchex;
52 
53 static int le_dispatch;
54 
55 static void disp_destructor(php_dispatchex *disp);
56 
dispatch_dtor(zend_rsrc_list_entry * rsrc TSRMLS_DC)57 static void dispatch_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
58 {
59 	php_dispatchex *disp = (php_dispatchex *)rsrc->ptr;
60 	disp_destructor(disp);
61 }
62 
php_com_wrapper_minit(INIT_FUNC_ARGS)63 int php_com_wrapper_minit(INIT_FUNC_ARGS)
64 {
65 	le_dispatch = zend_register_list_destructors_ex(dispatch_dtor,
66 		NULL, "com_dotnet_dispatch_wrapper", module_number);
67 	return le_dispatch;
68 }
69 
70 
71 /* {{{ trace */
trace(char * fmt,...)72 static inline void trace(char *fmt, ...)
73 {
74 	va_list ap;
75 	char buf[4096];
76 
77 	snprintf(buf, sizeof(buf), "T=%08x ", GetCurrentThreadId());
78 	OutputDebugString(buf);
79 
80 	va_start(ap, fmt);
81 	vsnprintf(buf, sizeof(buf), fmt, ap);
82 
83 	OutputDebugString(buf);
84 
85 	va_end(ap);
86 }
87 /* }}} */
88 
89 #ifdef ZTS
90 # define TSRMLS_FIXED()	TSRMLS_FETCH();
91 #else
92 # define TSRMLS_FIXED()
93 #endif
94 
95 #define FETCH_DISP(methname)																			\
96 	TSRMLS_FIXED() 																						\
97 	php_dispatchex *disp = (php_dispatchex*)This; 														\
98 	if (COMG(rshutdown_started)) {																		\
99 		trace(" PHP Object:%p (name:unknown) %s\n", disp->object,  methname); 							\
100 	} else {																							\
101 		trace(" PHP Object:%p (name:%s) %s\n", disp->object, Z_OBJCE_P(disp->object)->name, methname); 	\
102 	}																									\
103 	if (GetCurrentThreadId() != disp->engine_thread) {													\
104 		return RPC_E_WRONG_THREAD;																		\
105 	}
106 
disp_queryinterface(IDispatchEx * This,REFIID riid,void ** ppvObject)107 static HRESULT STDMETHODCALLTYPE disp_queryinterface(
108 	IDispatchEx *This,
109 	/* [in] */ REFIID riid,
110 	/* [iid_is][out] */ void **ppvObject)
111 {
112 	FETCH_DISP("QueryInterface");
113 
114 	if (IsEqualGUID(&IID_IUnknown, riid) ||
115 			IsEqualGUID(&IID_IDispatch, riid) ||
116 			IsEqualGUID(&IID_IDispatchEx, riid) ||
117 			IsEqualGUID(&disp->sinkid, riid)) {
118 		*ppvObject = This;
119 		InterlockedIncrement(&disp->refcount);
120 		return S_OK;
121 	}
122 
123 	*ppvObject = NULL;
124 	return E_NOINTERFACE;
125 }
126 
disp_addref(IDispatchEx * This)127 static ULONG STDMETHODCALLTYPE disp_addref(IDispatchEx *This)
128 {
129 	FETCH_DISP("AddRef");
130 
131 	return InterlockedIncrement(&disp->refcount);
132 }
133 
disp_release(IDispatchEx * This)134 static ULONG STDMETHODCALLTYPE disp_release(IDispatchEx *This)
135 {
136 	ULONG ret;
137 	FETCH_DISP("Release");
138 
139 	ret = InterlockedDecrement(&disp->refcount);
140 	trace("-- refcount now %d\n", ret);
141 	if (ret == 0) {
142 		/* destroy it */
143 		if (disp->id)
144 			zend_list_delete(disp->id);
145 	}
146 	return ret;
147 }
148 
disp_gettypeinfocount(IDispatchEx * This,UINT * pctinfo)149 static HRESULT STDMETHODCALLTYPE disp_gettypeinfocount(
150 	IDispatchEx *This,
151 	/* [out] */ UINT *pctinfo)
152 {
153 	FETCH_DISP("GetTypeInfoCount");
154 
155 	*pctinfo = 0;
156 	return S_OK;
157 }
158 
disp_gettypeinfo(IDispatchEx * This,UINT iTInfo,LCID lcid,ITypeInfo ** ppTInfo)159 static HRESULT STDMETHODCALLTYPE disp_gettypeinfo(
160 	IDispatchEx *This,
161 	/* [in] */ UINT iTInfo,
162 	/* [in] */ LCID lcid,
163 	/* [out] */ ITypeInfo **ppTInfo)
164 {
165 	FETCH_DISP("GetTypeInfo");
166 
167 	*ppTInfo = NULL;
168 	return DISP_E_BADINDEX;
169 }
170 
disp_getidsofnames(IDispatchEx * This,REFIID riid,LPOLESTR * rgszNames,UINT cNames,LCID lcid,DISPID * rgDispId)171 static HRESULT STDMETHODCALLTYPE disp_getidsofnames(
172 	IDispatchEx *This,
173 	/* [in] */ REFIID riid,
174 	/* [size_is][in] */ LPOLESTR *rgszNames,
175 	/* [in] */ UINT cNames,
176 	/* [in] */ LCID lcid,
177 	/* [size_is][out] */ DISPID *rgDispId)
178 {
179 	UINT i;
180 	HRESULT ret = S_OK;
181 	FETCH_DISP("GetIDsOfNames");
182 
183 	for (i = 0; i < cNames; i++) {
184 		char *name;
185 		unsigned int namelen;
186 		zval **tmp;
187 
188 		name = php_com_olestring_to_string(rgszNames[i], &namelen, COMG(code_page) TSRMLS_CC);
189 
190 		/* Lookup the name in the hash */
191 		if (zend_hash_find(disp->name_to_dispid, name, namelen+1, (void**)&tmp) == FAILURE) {
192 			ret = DISP_E_UNKNOWNNAME;
193 			rgDispId[i] = 0;
194 		} else {
195 			rgDispId[i] = Z_LVAL_PP(tmp);
196 		}
197 
198 		efree(name);
199 
200 	}
201 
202 	return ret;
203 }
204 
disp_invoke(IDispatchEx * This,DISPID dispIdMember,REFIID riid,LCID lcid,WORD wFlags,DISPPARAMS * pDispParams,VARIANT * pVarResult,EXCEPINFO * pExcepInfo,UINT * puArgErr)205 static HRESULT STDMETHODCALLTYPE disp_invoke(
206 	IDispatchEx *This,
207 	/* [in] */ DISPID dispIdMember,
208 	/* [in] */ REFIID riid,
209 	/* [in] */ LCID lcid,
210 	/* [in] */ WORD wFlags,
211 	/* [out][in] */ DISPPARAMS *pDispParams,
212 	/* [out] */ VARIANT *pVarResult,
213 	/* [out] */ EXCEPINFO *pExcepInfo,
214 	/* [out] */ UINT *puArgErr)
215 {
216 	return This->lpVtbl->InvokeEx(This, dispIdMember,
217 			lcid, wFlags, pDispParams,
218 			pVarResult, pExcepInfo, NULL);
219 }
220 
disp_getdispid(IDispatchEx * This,BSTR bstrName,DWORD grfdex,DISPID * pid)221 static HRESULT STDMETHODCALLTYPE disp_getdispid(
222 	IDispatchEx *This,
223 	/* [in] */ BSTR bstrName,
224 	/* [in] */ DWORD grfdex,
225 	/* [out] */ DISPID *pid)
226 {
227 	HRESULT ret = DISP_E_UNKNOWNNAME;
228 	char *name;
229 	unsigned int namelen;
230 	zval **tmp;
231 	FETCH_DISP("GetDispID");
232 
233 	name = php_com_olestring_to_string(bstrName, &namelen, COMG(code_page) TSRMLS_CC);
234 
235 	trace("Looking for %s, namelen=%d in %p\n", name, namelen, disp->name_to_dispid);
236 
237 	/* Lookup the name in the hash */
238 	if (zend_hash_find(disp->name_to_dispid, name, namelen+1, (void**)&tmp) == SUCCESS) {
239 		trace("found it\n");
240 		*pid = Z_LVAL_PP(tmp);
241 		ret = S_OK;
242 	}
243 
244 	efree(name);
245 
246 	return ret;
247 }
248 
disp_invokeex(IDispatchEx * This,DISPID id,LCID lcid,WORD wFlags,DISPPARAMS * pdp,VARIANT * pvarRes,EXCEPINFO * pei,IServiceProvider * pspCaller)249 static HRESULT STDMETHODCALLTYPE disp_invokeex(
250 	IDispatchEx *This,
251 	/* [in] */ DISPID id,
252 	/* [in] */ LCID lcid,
253 	/* [in] */ WORD wFlags,
254 	/* [in] */ DISPPARAMS *pdp,
255 	/* [out] */ VARIANT *pvarRes,
256 	/* [out] */ EXCEPINFO *pei,
257 	/* [unique][in] */ IServiceProvider *pspCaller)
258 {
259 	zval **name;
260 	UINT i;
261 	zval *retval = NULL;
262 	zval ***params = NULL;
263 	HRESULT ret = DISP_E_MEMBERNOTFOUND;
264 	FETCH_DISP("InvokeEx");
265 
266 	if (SUCCESS == zend_hash_index_find(disp->dispid_to_name, id, (void**)&name)) {
267 		/* TODO: add support for overloaded objects */
268 
269 		trace("-- Invoke: %d %20s [%d] flags=%08x args=%d\n", id, Z_STRVAL_PP(name), Z_STRLEN_PP(name), wFlags, pdp->cArgs);
270 
271 		/* convert args into zvals.
272 		 * Args are in reverse order */
273 		if (pdp->cArgs) {
274 			params = (zval ***)safe_emalloc(sizeof(zval **), pdp->cArgs, 0);
275 			for (i = 0; i < pdp->cArgs; i++) {
276 				VARIANT *arg;
277 				zval *zarg;
278 
279 				arg = &pdp->rgvarg[ pdp->cArgs - 1 - i];
280 
281 				trace("alloc zval for arg %d VT=%08x\n", i, V_VT(arg));
282 
283 				ALLOC_INIT_ZVAL(zarg);
284 				php_com_wrap_variant(zarg, arg, COMG(code_page) TSRMLS_CC);
285 				params[i] = (zval**)emalloc(sizeof(zval**));
286 				*params[i] = zarg;
287 			}
288 		}
289 
290 		trace("arguments processed, prepare to do some work\n");
291 
292 		/* TODO: if PHP raises an exception here, we should catch it
293 		 * and expose it as a COM exception */
294 
295 		if (wFlags & DISPATCH_PROPERTYGET) {
296 			retval = zend_read_property(Z_OBJCE_P(disp->object), disp->object, Z_STRVAL_PP(name), Z_STRLEN_PP(name)+1, 1 TSRMLS_CC);
297 		} else if (wFlags & DISPATCH_PROPERTYPUT) {
298 			zend_update_property(Z_OBJCE_P(disp->object), disp->object, Z_STRVAL_PP(name), Z_STRLEN_PP(name)+1, *params[0] TSRMLS_CC);
299 		} else if (wFlags & DISPATCH_METHOD) {
300 			zend_try {
301 				if (SUCCESS == call_user_function_ex(EG(function_table), &disp->object, *name,
302 							&retval, pdp->cArgs, params, 1, NULL TSRMLS_CC)) {
303 					ret = S_OK;
304 					trace("function called ok\n");
305 
306 					/* Copy any modified values to callers copy of variant*/
307 					for (i = 0; i < pdp->cArgs; i++) {
308 						php_com_dotnet_object *obj = CDNO_FETCH(*params[i]);
309 						VARIANT *srcvar = &obj->v;
310 						VARIANT *dstvar = &pdp->rgvarg[ pdp->cArgs - 1 - i];
311 						if ((V_VT(dstvar) & VT_BYREF) && obj->modified ) {
312 							trace("percolate modified value for arg %d VT=%08x\n", i, V_VT(dstvar));
313 							php_com_copy_variant(dstvar, srcvar TSRMLS_CC);
314 						}
315 					}
316 				} else {
317 					trace("failed to call func\n");
318 					ret = DISP_E_EXCEPTION;
319 				}
320 			} zend_catch {
321 				trace("something blew up\n");
322 				ret = DISP_E_EXCEPTION;
323 			} zend_end_try();
324 		} else {
325 			trace("Don't know how to handle this invocation %08x\n", wFlags);
326 		}
327 
328 		/* release arguments */
329 		if (params) {
330 			for (i = 0; i < pdp->cArgs; i++) {
331 				zval_ptr_dtor(params[i]);
332 				efree(params[i]);
333 			}
334 			efree(params);
335 		}
336 
337 		/* return value */
338 		if (retval) {
339 			if (pvarRes) {
340 				VariantInit(pvarRes);
341 				php_com_variant_from_zval(pvarRes, retval, COMG(code_page) TSRMLS_CC);
342 			}
343 			zval_ptr_dtor(&retval);
344 		} else if (pvarRes) {
345 			VariantInit(pvarRes);
346 		}
347 
348 	} else {
349 		trace("InvokeEx: I don't support DISPID=%d\n", id);
350 	}
351 
352 	return ret;
353 }
354 
disp_deletememberbyname(IDispatchEx * This,BSTR bstrName,DWORD grfdex)355 static HRESULT STDMETHODCALLTYPE disp_deletememberbyname(
356 	IDispatchEx *This,
357 	/* [in] */ BSTR bstrName,
358 	/* [in] */ DWORD grfdex)
359 {
360 	FETCH_DISP("DeleteMemberByName");
361 
362 	/* TODO: unset */
363 
364 	return S_FALSE;
365 }
366 
disp_deletememberbydispid(IDispatchEx * This,DISPID id)367 static HRESULT STDMETHODCALLTYPE disp_deletememberbydispid(
368 	IDispatchEx *This,
369 	/* [in] */ DISPID id)
370 {
371 	FETCH_DISP("DeleteMemberByDispID");
372 
373 	/* TODO: unset */
374 
375 	return S_FALSE;
376 }
377 
disp_getmemberproperties(IDispatchEx * This,DISPID id,DWORD grfdexFetch,DWORD * pgrfdex)378 static HRESULT STDMETHODCALLTYPE disp_getmemberproperties(
379 	IDispatchEx *This,
380 	/* [in] */ DISPID id,
381 	/* [in] */ DWORD grfdexFetch,
382 	/* [out] */ DWORD *pgrfdex)
383 {
384 	FETCH_DISP("GetMemberProperties");
385 
386 	return DISP_E_UNKNOWNNAME;
387 }
388 
disp_getmembername(IDispatchEx * This,DISPID id,BSTR * pbstrName)389 static HRESULT STDMETHODCALLTYPE disp_getmembername(
390 	IDispatchEx *This,
391 	/* [in] */ DISPID id,
392 	/* [out] */ BSTR *pbstrName)
393 {
394 	zval *name;
395 	FETCH_DISP("GetMemberName");
396 
397 	if (SUCCESS == zend_hash_index_find(disp->dispid_to_name, id, (void**)&name)) {
398 		OLECHAR *olestr = php_com_string_to_olestring(Z_STRVAL_P(name), Z_STRLEN_P(name), COMG(code_page) TSRMLS_CC);
399 		*pbstrName = SysAllocString(olestr);
400 		efree(olestr);
401 		return S_OK;
402 	} else {
403 		return DISP_E_UNKNOWNNAME;
404 	}
405 }
406 
disp_getnextdispid(IDispatchEx * This,DWORD grfdex,DISPID id,DISPID * pid)407 static HRESULT STDMETHODCALLTYPE disp_getnextdispid(
408 	IDispatchEx *This,
409 	/* [in] */ DWORD grfdex,
410 	/* [in] */ DISPID id,
411 	/* [out] */ DISPID *pid)
412 {
413 	ulong next = id+1;
414 	FETCH_DISP("GetNextDispID");
415 
416 	while(!zend_hash_index_exists(disp->dispid_to_name, next))
417 		next++;
418 
419 	if (zend_hash_index_exists(disp->dispid_to_name, next)) {
420 		*pid = next;
421 		return S_OK;
422 	}
423 	return S_FALSE;
424 }
425 
disp_getnamespaceparent(IDispatchEx * This,IUnknown ** ppunk)426 static HRESULT STDMETHODCALLTYPE disp_getnamespaceparent(
427 	IDispatchEx *This,
428 	/* [out] */ IUnknown **ppunk)
429 {
430 	FETCH_DISP("GetNameSpaceParent");
431 
432 	*ppunk = NULL;
433 	return E_NOTIMPL;
434 }
435 
436 static struct IDispatchExVtbl php_dispatch_vtbl = {
437 	disp_queryinterface,
438 	disp_addref,
439 	disp_release,
440 	disp_gettypeinfocount,
441 	disp_gettypeinfo,
442 	disp_getidsofnames,
443 	disp_invoke,
444 	disp_getdispid,
445 	disp_invokeex,
446 	disp_deletememberbyname,
447 	disp_deletememberbydispid,
448 	disp_getmemberproperties,
449 	disp_getmembername,
450 	disp_getnextdispid,
451 	disp_getnamespaceparent
452 };
453 
454 
455 /* enumerate functions and properties of the object and assign
456  * dispatch ids */
generate_dispids(php_dispatchex * disp TSRMLS_DC)457 static void generate_dispids(php_dispatchex *disp TSRMLS_DC)
458 {
459 	HashPosition pos;
460 	char *name = NULL;
461 	zval *tmp;
462 	int namelen;
463 	int keytype;
464 	ulong pid;
465 
466 	if (disp->dispid_to_name == NULL) {
467 		ALLOC_HASHTABLE(disp->dispid_to_name);
468 		ALLOC_HASHTABLE(disp->name_to_dispid);
469 		zend_hash_init(disp->name_to_dispid, 0, NULL, ZVAL_PTR_DTOR, 0);
470 		zend_hash_init(disp->dispid_to_name, 0, NULL, ZVAL_PTR_DTOR, 0);
471 	}
472 
473 	/* properties */
474 	if (Z_OBJPROP_P(disp->object)) {
475 		zend_hash_internal_pointer_reset_ex(Z_OBJPROP_P(disp->object), &pos);
476 		while (HASH_KEY_NON_EXISTANT != (keytype =
477 				zend_hash_get_current_key_ex(Z_OBJPROP_P(disp->object), &name,
478 			   	&namelen, &pid, 0, &pos))) {
479 			char namebuf[32];
480 			if (keytype == HASH_KEY_IS_LONG) {
481 				snprintf(namebuf, sizeof(namebuf), "%d", pid);
482 				name = namebuf;
483 				namelen = strlen(namebuf)+1;
484 			}
485 
486 			zend_hash_move_forward_ex(Z_OBJPROP_P(disp->object), &pos);
487 
488 			/* Find the existing id */
489 			if (zend_hash_find(disp->name_to_dispid, name, namelen, (void**)&tmp) == SUCCESS)
490 				continue;
491 
492 			/* add the mappings */
493 			MAKE_STD_ZVAL(tmp);
494 			ZVAL_STRINGL(tmp, name, namelen-1, 1);
495 			pid = zend_hash_next_free_element(disp->dispid_to_name);
496 			zend_hash_index_update(disp->dispid_to_name, pid, (void*)&tmp, sizeof(zval *), NULL);
497 
498 			MAKE_STD_ZVAL(tmp);
499 			ZVAL_LONG(tmp, pid);
500 			zend_hash_update(disp->name_to_dispid, name, namelen, (void*)&tmp, sizeof(zval *), NULL);
501 		}
502 	}
503 
504 	/* functions */
505 	if (Z_OBJCE_P(disp->object)) {
506 		zend_hash_internal_pointer_reset_ex(&Z_OBJCE_P(disp->object)->function_table, &pos);
507 		while (HASH_KEY_NON_EXISTANT != (keytype =
508 				zend_hash_get_current_key_ex(&Z_OBJCE_P(disp->object)->function_table,
509 			 	&name, &namelen, &pid, 0, &pos))) {
510 
511 			char namebuf[32];
512 			if (keytype == HASH_KEY_IS_LONG) {
513 				snprintf(namebuf, sizeof(namebuf), "%d", pid);
514 				name = namebuf;
515 				namelen = strlen(namebuf) + 1;
516 			}
517 
518 			zend_hash_move_forward_ex(Z_OBJPROP_P(disp->object), &pos);
519 
520 			/* Find the existing id */
521 			if (zend_hash_find(disp->name_to_dispid, name, namelen, (void**)&tmp) == SUCCESS)
522 				continue;
523 
524 			/* add the mappings */
525 			MAKE_STD_ZVAL(tmp);
526 			ZVAL_STRINGL(tmp, name, namelen-1, 1);
527 			pid = zend_hash_next_free_element(disp->dispid_to_name);
528 			zend_hash_index_update(disp->dispid_to_name, pid, (void*)&tmp, sizeof(zval *), NULL);
529 
530 			MAKE_STD_ZVAL(tmp);
531 			ZVAL_LONG(tmp, pid);
532 			zend_hash_update(disp->name_to_dispid, name, namelen, (void*)&tmp, sizeof(zval *), NULL);
533 		}
534 	}
535 }
536 
disp_constructor(zval * object TSRMLS_DC)537 static php_dispatchex *disp_constructor(zval *object TSRMLS_DC)
538 {
539 	php_dispatchex *disp = (php_dispatchex*)CoTaskMemAlloc(sizeof(php_dispatchex));
540 
541 	trace("constructing a COM wrapper for PHP object %p (%s)\n", object, Z_OBJCE_P(object)->name);
542 
543 	if (disp == NULL)
544 		return NULL;
545 
546 	memset(disp, 0, sizeof(php_dispatchex));
547 
548 	disp->engine_thread = GetCurrentThreadId();
549 	disp->lpVtbl = &php_dispatch_vtbl;
550 	disp->refcount = 1;
551 
552 
553 	if (object)
554 		Z_ADDREF_P(object);
555 	disp->object = object;
556 
557 	disp->id = zend_list_insert(disp, le_dispatch);
558 
559 	return disp;
560 }
561 
disp_destructor(php_dispatchex * disp)562 static void disp_destructor(php_dispatchex *disp)
563 {
564 	TSRMLS_FETCH();
565 
566 	/* Object store not available during request shutdown */
567 	if (COMG(rshutdown_started)) {
568 		trace("destroying COM wrapper for PHP object %p (name:unknown)\n", disp->object);
569 	} else {
570 		trace("destroying COM wrapper for PHP object %p (name:%s)\n", disp->object, Z_OBJCE_P(disp->object)->name);
571 	}
572 
573 	disp->id = 0;
574 
575 	if (disp->refcount > 0)
576 		CoDisconnectObject((IUnknown*)disp, 0);
577 
578 	zend_hash_destroy(disp->dispid_to_name);
579 	zend_hash_destroy(disp->name_to_dispid);
580 	FREE_HASHTABLE(disp->dispid_to_name);
581 	FREE_HASHTABLE(disp->name_to_dispid);
582 
583 	if (disp->object)
584 		zval_ptr_dtor(&disp->object);
585 
586 	CoTaskMemFree(disp);
587 }
588 
php_com_wrapper_export_as_sink(zval * val,GUID * sinkid,HashTable * id_to_name TSRMLS_DC)589 PHP_COM_DOTNET_API IDispatch *php_com_wrapper_export_as_sink(zval *val, GUID *sinkid,
590 	   HashTable *id_to_name TSRMLS_DC)
591 {
592 	php_dispatchex *disp = disp_constructor(val TSRMLS_CC);
593 	HashPosition pos;
594 	char *name = NULL;
595 	zval *tmp, **ntmp;
596 	int namelen;
597 	int keytype;
598 	ulong pid;
599 
600 	disp->dispid_to_name = id_to_name;
601 
602 	memcpy(&disp->sinkid, sinkid, sizeof(disp->sinkid));
603 
604 	/* build up the reverse mapping */
605 	ALLOC_HASHTABLE(disp->name_to_dispid);
606 	zend_hash_init(disp->name_to_dispid, 0, NULL, ZVAL_PTR_DTOR, 0);
607 
608 	zend_hash_internal_pointer_reset_ex(id_to_name, &pos);
609 	while (HASH_KEY_NON_EXISTANT != (keytype =
610 				zend_hash_get_current_key_ex(id_to_name, &name, &namelen, &pid, 0, &pos))) {
611 
612 		if (keytype == HASH_KEY_IS_LONG) {
613 
614 			zend_hash_get_current_data_ex(id_to_name, (void**)&ntmp, &pos);
615 
616 			MAKE_STD_ZVAL(tmp);
617 			ZVAL_LONG(tmp, pid);
618 			zend_hash_update(disp->name_to_dispid, Z_STRVAL_PP(ntmp),
619 				Z_STRLEN_PP(ntmp)+1, (void*)&tmp, sizeof(zval *), NULL);
620 		}
621 
622 		zend_hash_move_forward_ex(id_to_name, &pos);
623 	}
624 
625 	return (IDispatch*)disp;
626 }
627 
php_com_wrapper_export(zval * val TSRMLS_DC)628 PHP_COM_DOTNET_API IDispatch *php_com_wrapper_export(zval *val TSRMLS_DC)
629 {
630 	php_dispatchex *disp = NULL;
631 
632 	if (Z_TYPE_P(val) != IS_OBJECT) {
633 		return NULL;
634 	}
635 
636 	if (php_com_is_valid_object(val TSRMLS_CC)) {
637 		/* pass back its IDispatch directly */
638 		php_com_dotnet_object *obj = CDNO_FETCH(val);
639 
640 		if (obj == NULL)
641 			return NULL;
642 
643 		if (V_VT(&obj->v) == VT_DISPATCH && V_DISPATCH(&obj->v)) {
644 			IDispatch_AddRef(V_DISPATCH(&obj->v));
645 			return V_DISPATCH(&obj->v);
646 		}
647 
648 		return NULL;
649 	}
650 
651 	disp = disp_constructor(val TSRMLS_CC);
652 	generate_dispids(disp TSRMLS_CC);
653 
654 	return (IDispatch*)disp;
655 }
656 
657 
658