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