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