Back to index

php5  5.3.10
com_wrapper.c
Go to the documentation of this file.
00001 /*
00002    +----------------------------------------------------------------------+
00003    | PHP Version 5                                                        |
00004    +----------------------------------------------------------------------+
00005    | Copyright (c) 1997-2012 The PHP Group                                |
00006    +----------------------------------------------------------------------+
00007    | This source file is subject to version 3.01 of the PHP license,      |
00008    | that is bundled with this package in the file LICENSE, and is        |
00009    | available through the world-wide-web at the following url:           |
00010    | http://www.php.net/license/3_01.txt                                  |
00011    | If you did not receive a copy of the PHP license and are unable to   |
00012    | obtain it through the world-wide-web, please send a note to          |
00013    | license@php.net so we can mail you a copy immediately.               |
00014    +----------------------------------------------------------------------+
00015    | Author: Wez Furlong  <wez@thebrainroom.com>                          |
00016    +----------------------------------------------------------------------+
00017  */
00018 
00019 /* $Id: com_wrapper.c 321634 2012-01-01 13:15:04Z felipe $ */
00020 
00021 /* This module exports a PHP object as a COM object by wrapping it
00022  * using IDispatchEx */
00023 
00024 #ifdef HAVE_CONFIG_H
00025 #include "config.h"
00026 #endif
00027 
00028 #include "php.h"
00029 #include "php_ini.h"
00030 #include "ext/standard/info.h"
00031 #include "php_com_dotnet.h"
00032 #include "php_com_dotnet_internal.h"
00033 
00034 typedef struct {
00035        /* This first part MUST match the declaration
00036         * of interface IDispatchEx */
00037        CONST_VTBL struct IDispatchExVtbl *lpVtbl;
00038 
00039        /* now the PHP stuff */
00040        
00041        DWORD engine_thread; /* for sanity checking */
00042        zval *object;               /* the object exported */
00043        LONG refcount;                     /* COM reference count */
00044 
00045        HashTable *dispid_to_name;  /* keep track of dispid -> name mappings */
00046        HashTable *name_to_dispid;  /* keep track of name -> dispid mappings */
00047 
00048        GUID sinkid;  /* iid that we "implement" for event sinking */
00049        
00050        int id;
00051 } php_dispatchex;
00052 
00053 static int le_dispatch;
00054 
00055 static void disp_destructor(php_dispatchex *disp);
00056 
00057 static void dispatch_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
00058 {
00059        php_dispatchex *disp = (php_dispatchex *)rsrc->ptr;
00060        disp_destructor(disp);
00061 }
00062 
00063 int php_com_wrapper_minit(INIT_FUNC_ARGS)
00064 {
00065        le_dispatch = zend_register_list_destructors_ex(dispatch_dtor,
00066               NULL, "com_dotnet_dispatch_wrapper", module_number);
00067        return le_dispatch;
00068 }
00069 
00070 
00071 /* {{{ trace */
00072 static inline void trace(char *fmt, ...)
00073 {
00074        va_list ap;
00075        char buf[4096];
00076 
00077        snprintf(buf, sizeof(buf), "T=%08x ", GetCurrentThreadId());
00078        OutputDebugString(buf);
00079        
00080        va_start(ap, fmt);
00081        vsnprintf(buf, sizeof(buf), fmt, ap);
00082 
00083        OutputDebugString(buf);
00084        
00085        va_end(ap);
00086 }
00087 /* }}} */
00088 
00089 #ifdef ZTS
00090 # define TSRMLS_FIXED()     TSRMLS_FETCH();
00091 #else
00092 # define TSRMLS_FIXED()
00093 #endif
00094 
00095 #define FETCH_DISP(methname)                                                                                                                                     \
00096        TSRMLS_FIXED()                                                                                                                                                          \
00097        php_dispatchex *disp = (php_dispatchex*)This;                                                                                               \
00098        if (COMG(rshutdown_started)) {                                                                                                                            \
00099               trace(" PHP Object:%p (name:unknown) %s\n", disp->object,  methname);                                                  \
00100        } else {                                                                                                                                                                \
00101               trace(" PHP Object:%p (name:%s) %s\n", disp->object, Z_OBJCE_P(disp->object)->name, methname);    \
00102        }                                                                                                                                                                              \
00103        if (GetCurrentThreadId() != disp->engine_thread) {                                                                                          \
00104               return RPC_E_WRONG_THREAD;                                                                                                                         \
00105        }
00106 
00107 static HRESULT STDMETHODCALLTYPE disp_queryinterface( 
00108        IDispatchEx *This,
00109        /* [in] */ REFIID riid,
00110        /* [iid_is][out] */ void **ppvObject)
00111 {
00112        FETCH_DISP("QueryInterface");
00113 
00114        if (IsEqualGUID(&IID_IUnknown, riid) ||
00115                      IsEqualGUID(&IID_IDispatch, riid) ||
00116                      IsEqualGUID(&IID_IDispatchEx, riid) ||
00117                      IsEqualGUID(&disp->sinkid, riid)) {
00118               *ppvObject = This;
00119               InterlockedIncrement(&disp->refcount);
00120               return S_OK;
00121        }
00122 
00123        *ppvObject = NULL;
00124        return E_NOINTERFACE;
00125 }
00126         
00127 static ULONG STDMETHODCALLTYPE disp_addref(IDispatchEx *This)
00128 {
00129        FETCH_DISP("AddRef");
00130 
00131        return InterlockedIncrement(&disp->refcount);
00132 }
00133         
00134 static ULONG STDMETHODCALLTYPE disp_release(IDispatchEx *This)
00135 {
00136        ULONG ret;
00137        FETCH_DISP("Release");
00138 
00139        ret = InterlockedDecrement(&disp->refcount);
00140        trace("-- refcount now %d\n", ret);
00141        if (ret == 0) {
00142               /* destroy it */
00143               if (disp->id)
00144                      zend_list_delete(disp->id);
00145        }
00146        return ret;
00147 }
00148 
00149 static HRESULT STDMETHODCALLTYPE disp_gettypeinfocount( 
00150        IDispatchEx *This,
00151        /* [out] */ UINT *pctinfo)
00152 {
00153        FETCH_DISP("GetTypeInfoCount");
00154 
00155        *pctinfo = 0;
00156        return S_OK;
00157 }
00158         
00159 static HRESULT STDMETHODCALLTYPE disp_gettypeinfo( 
00160        IDispatchEx *This,
00161        /* [in] */ UINT iTInfo,
00162        /* [in] */ LCID lcid,
00163        /* [out] */ ITypeInfo **ppTInfo)
00164 {
00165        FETCH_DISP("GetTypeInfo");
00166        
00167        *ppTInfo = NULL;
00168        return DISP_E_BADINDEX;
00169 }
00170 
00171 static HRESULT STDMETHODCALLTYPE disp_getidsofnames( 
00172        IDispatchEx *This,
00173        /* [in] */ REFIID riid,
00174        /* [size_is][in] */ LPOLESTR *rgszNames,
00175        /* [in] */ UINT cNames,
00176        /* [in] */ LCID lcid,
00177        /* [size_is][out] */ DISPID *rgDispId)
00178 {
00179        UINT i;
00180        HRESULT ret = S_OK;
00181        FETCH_DISP("GetIDsOfNames");
00182 
00183        for (i = 0; i < cNames; i++) {
00184               char *name;
00185               unsigned int namelen;
00186               zval **tmp;
00187               
00188               name = php_com_olestring_to_string(rgszNames[i], &namelen, COMG(code_page) TSRMLS_CC);
00189               
00190               /* Lookup the name in the hash */
00191               if (zend_hash_find(disp->name_to_dispid, name, namelen+1, (void**)&tmp) == FAILURE) {
00192                      ret = DISP_E_UNKNOWNNAME;
00193                      rgDispId[i] = 0;
00194               } else {
00195                      rgDispId[i] = Z_LVAL_PP(tmp);
00196               }
00197 
00198               efree(name);
00199 
00200        }
00201        
00202        return ret;
00203 }
00204 
00205 static HRESULT STDMETHODCALLTYPE disp_invoke( 
00206        IDispatchEx *This,
00207        /* [in] */ DISPID dispIdMember,
00208        /* [in] */ REFIID riid,
00209        /* [in] */ LCID lcid,
00210        /* [in] */ WORD wFlags,
00211        /* [out][in] */ DISPPARAMS *pDispParams,
00212        /* [out] */ VARIANT *pVarResult,
00213        /* [out] */ EXCEPINFO *pExcepInfo,
00214        /* [out] */ UINT *puArgErr)
00215 {
00216        return This->lpVtbl->InvokeEx(This, dispIdMember,
00217                      lcid, wFlags, pDispParams,
00218                      pVarResult, pExcepInfo, NULL);
00219 }
00220 
00221 static HRESULT STDMETHODCALLTYPE disp_getdispid( 
00222        IDispatchEx *This,
00223        /* [in] */ BSTR bstrName,
00224        /* [in] */ DWORD grfdex,
00225        /* [out] */ DISPID *pid)
00226 {
00227        HRESULT ret = DISP_E_UNKNOWNNAME;
00228        char *name;
00229        unsigned int namelen;
00230        zval **tmp;
00231        FETCH_DISP("GetDispID");
00232 
00233        name = php_com_olestring_to_string(bstrName, &namelen, COMG(code_page) TSRMLS_CC);
00234 
00235        trace("Looking for %s, namelen=%d in %p\n", name, namelen, disp->name_to_dispid);
00236        
00237        /* Lookup the name in the hash */
00238        if (zend_hash_find(disp->name_to_dispid, name, namelen+1, (void**)&tmp) == SUCCESS) {
00239               trace("found it\n");
00240               *pid = Z_LVAL_PP(tmp);
00241               ret = S_OK;
00242        }
00243 
00244        efree(name);
00245        
00246        return ret;
00247 }
00248 
00249 static HRESULT STDMETHODCALLTYPE disp_invokeex( 
00250        IDispatchEx *This,
00251        /* [in] */ DISPID id,
00252        /* [in] */ LCID lcid,
00253        /* [in] */ WORD wFlags,
00254        /* [in] */ DISPPARAMS *pdp,
00255        /* [out] */ VARIANT *pvarRes,
00256        /* [out] */ EXCEPINFO *pei,
00257        /* [unique][in] */ IServiceProvider *pspCaller)
00258 {
00259        zval **name;
00260        UINT i;
00261        zval *retval = NULL;
00262        zval ***params = NULL;
00263        HRESULT ret = DISP_E_MEMBERNOTFOUND;
00264        FETCH_DISP("InvokeEx");
00265 
00266        if (SUCCESS == zend_hash_index_find(disp->dispid_to_name, id, (void**)&name)) {
00267               /* TODO: add support for overloaded objects */
00268 
00269               trace("-- Invoke: %d %20s [%d] flags=%08x args=%d\n", id, Z_STRVAL_PP(name), Z_STRLEN_PP(name), wFlags, pdp->cArgs);
00270               
00271               /* convert args into zvals.
00272                * Args are in reverse order */
00273               if (pdp->cArgs) {
00274                      params = (zval ***)safe_emalloc(sizeof(zval **), pdp->cArgs, 0);
00275                      for (i = 0; i < pdp->cArgs; i++) {
00276                             VARIANT *arg;
00277                             zval *zarg;
00278 
00279                             arg = &pdp->rgvarg[ pdp->cArgs - 1 - i];
00280 
00281                             trace("alloc zval for arg %d VT=%08x\n", i, V_VT(arg));
00282 
00283                             ALLOC_INIT_ZVAL(zarg);
00284                             php_com_wrap_variant(zarg, arg, COMG(code_page) TSRMLS_CC);
00285                             params[i] = (zval**)emalloc(sizeof(zval**));
00286                             *params[i] = zarg;
00287                      }
00288               }
00289 
00290               trace("arguments processed, prepare to do some work\n");       
00291        
00292               /* TODO: if PHP raises an exception here, we should catch it
00293                * and expose it as a COM exception */
00294               
00295               if (wFlags & DISPATCH_PROPERTYGET) {
00296                      retval = zend_read_property(Z_OBJCE_P(disp->object), disp->object, Z_STRVAL_PP(name), Z_STRLEN_PP(name)+1, 1 TSRMLS_CC);
00297               } else if (wFlags & DISPATCH_PROPERTYPUT) {
00298                      zend_update_property(Z_OBJCE_P(disp->object), disp->object, Z_STRVAL_PP(name), Z_STRLEN_PP(name)+1, *params[0] TSRMLS_CC);
00299               } else if (wFlags & DISPATCH_METHOD) {
00300                      zend_try {
00301                             if (SUCCESS == call_user_function_ex(EG(function_table), &disp->object, *name,
00302                                                  &retval, pdp->cArgs, params, 1, NULL TSRMLS_CC)) {
00303                                    ret = S_OK;
00304                                    trace("function called ok\n");
00305 
00306                                    /* Copy any modified values to callers copy of variant*/
00307                                    for (i = 0; i < pdp->cArgs; i++) {
00308                                           php_com_dotnet_object *obj = CDNO_FETCH(*params[i]);
00309                                           VARIANT *srcvar = &obj->v;
00310                                           VARIANT *dstvar = &pdp->rgvarg[ pdp->cArgs - 1 - i];
00311                                           if ((V_VT(dstvar) & VT_BYREF) && obj->modified ) {
00312                                                  trace("percolate modified value for arg %d VT=%08x\n", i, V_VT(dstvar));
00313                                                  php_com_copy_variant(dstvar, srcvar TSRMLS_CC);   
00314                                           }
00315                                    }
00316                             } else {
00317                                    trace("failed to call func\n");
00318                                    ret = DISP_E_EXCEPTION;
00319                             }
00320                      } zend_catch {
00321                             trace("something blew up\n");
00322                             ret = DISP_E_EXCEPTION;
00323                      } zend_end_try();
00324               } else {
00325                      trace("Don't know how to handle this invocation %08x\n", wFlags);
00326               }
00327        
00328               /* release arguments */
00329               if (params) {
00330                      for (i = 0; i < pdp->cArgs; i++) {
00331                             zval_ptr_dtor(params[i]);
00332                             efree(params[i]);
00333                      }
00334                      efree(params);
00335               }
00336               
00337               /* return value */
00338               if (retval) {
00339                      if (pvarRes) {
00340                             VariantInit(pvarRes);
00341                             php_com_variant_from_zval(pvarRes, retval, COMG(code_page) TSRMLS_CC);
00342                      }
00343                      zval_ptr_dtor(&retval);
00344               } else if (pvarRes) {
00345                      VariantInit(pvarRes);
00346               }
00347               
00348        } else {
00349               trace("InvokeEx: I don't support DISPID=%d\n", id);
00350        }
00351 
00352        return ret;
00353 }
00354 
00355 static HRESULT STDMETHODCALLTYPE disp_deletememberbyname( 
00356        IDispatchEx *This,
00357        /* [in] */ BSTR bstrName,
00358        /* [in] */ DWORD grfdex)
00359 {
00360        FETCH_DISP("DeleteMemberByName");
00361 
00362        /* TODO: unset */
00363 
00364        return S_FALSE;
00365 }
00366 
00367 static HRESULT STDMETHODCALLTYPE disp_deletememberbydispid( 
00368        IDispatchEx *This,
00369        /* [in] */ DISPID id)
00370 {
00371        FETCH_DISP("DeleteMemberByDispID");
00372        
00373        /* TODO: unset */
00374        
00375        return S_FALSE;
00376 }
00377 
00378 static HRESULT STDMETHODCALLTYPE disp_getmemberproperties( 
00379        IDispatchEx *This,
00380        /* [in] */ DISPID id,
00381        /* [in] */ DWORD grfdexFetch,
00382        /* [out] */ DWORD *pgrfdex)
00383 {
00384        FETCH_DISP("GetMemberProperties");
00385 
00386        return DISP_E_UNKNOWNNAME;
00387 }
00388 
00389 static HRESULT STDMETHODCALLTYPE disp_getmembername( 
00390        IDispatchEx *This,
00391        /* [in] */ DISPID id,
00392        /* [out] */ BSTR *pbstrName)
00393 {
00394        zval *name;
00395        FETCH_DISP("GetMemberName");
00396 
00397        if (SUCCESS == zend_hash_index_find(disp->dispid_to_name, id, (void**)&name)) {
00398               OLECHAR *olestr = php_com_string_to_olestring(Z_STRVAL_P(name), Z_STRLEN_P(name), COMG(code_page) TSRMLS_CC);
00399               *pbstrName = SysAllocString(olestr);
00400               efree(olestr);
00401               return S_OK;
00402        } else {
00403               return DISP_E_UNKNOWNNAME;
00404        }
00405 }
00406 
00407 static HRESULT STDMETHODCALLTYPE disp_getnextdispid( 
00408        IDispatchEx *This,
00409        /* [in] */ DWORD grfdex,
00410        /* [in] */ DISPID id,
00411        /* [out] */ DISPID *pid)
00412 {
00413        ulong next = id+1;
00414        FETCH_DISP("GetNextDispID");
00415 
00416        while(!zend_hash_index_exists(disp->dispid_to_name, next))
00417               next++;
00418 
00419        if (zend_hash_index_exists(disp->dispid_to_name, next)) {
00420               *pid = next;
00421               return S_OK;
00422        }
00423        return S_FALSE;
00424 }
00425 
00426 static HRESULT STDMETHODCALLTYPE disp_getnamespaceparent( 
00427        IDispatchEx *This,
00428        /* [out] */ IUnknown **ppunk)
00429 {
00430        FETCH_DISP("GetNameSpaceParent");
00431 
00432        *ppunk = NULL;
00433        return E_NOTIMPL;
00434 }
00435         
00436 static struct IDispatchExVtbl php_dispatch_vtbl = {
00437        disp_queryinterface,
00438        disp_addref,
00439        disp_release,
00440        disp_gettypeinfocount,
00441        disp_gettypeinfo,
00442        disp_getidsofnames,
00443        disp_invoke,
00444        disp_getdispid,
00445        disp_invokeex,
00446        disp_deletememberbyname,
00447        disp_deletememberbydispid,
00448        disp_getmemberproperties,
00449        disp_getmembername,
00450        disp_getnextdispid,
00451        disp_getnamespaceparent
00452 };
00453 
00454 
00455 /* enumerate functions and properties of the object and assign
00456  * dispatch ids */
00457 static void generate_dispids(php_dispatchex *disp TSRMLS_DC)
00458 {
00459        HashPosition pos;
00460        char *name = NULL;
00461        zval *tmp;
00462        int namelen;
00463        int keytype;
00464        ulong pid;
00465 
00466        if (disp->dispid_to_name == NULL) {
00467               ALLOC_HASHTABLE(disp->dispid_to_name);
00468               ALLOC_HASHTABLE(disp->name_to_dispid);
00469               zend_hash_init(disp->name_to_dispid, 0, NULL, ZVAL_PTR_DTOR, 0);
00470               zend_hash_init(disp->dispid_to_name, 0, NULL, ZVAL_PTR_DTOR, 0);
00471        }
00472 
00473        /* properties */
00474        if (Z_OBJPROP_P(disp->object)) {
00475               zend_hash_internal_pointer_reset_ex(Z_OBJPROP_P(disp->object), &pos);
00476               while (HASH_KEY_NON_EXISTANT != (keytype =
00477                             zend_hash_get_current_key_ex(Z_OBJPROP_P(disp->object), &name,
00478                             &namelen, &pid, 0, &pos))) {
00479                      char namebuf[32];
00480                      if (keytype == HASH_KEY_IS_LONG) {
00481                             snprintf(namebuf, sizeof(namebuf), "%d", pid);
00482                             name = namebuf;
00483                             namelen = strlen(namebuf)+1;
00484                      }
00485 
00486                      zend_hash_move_forward_ex(Z_OBJPROP_P(disp->object), &pos);
00487 
00488                      /* Find the existing id */
00489                      if (zend_hash_find(disp->name_to_dispid, name, namelen, (void**)&tmp) == SUCCESS)
00490                             continue;
00491 
00492                      /* add the mappings */
00493                      MAKE_STD_ZVAL(tmp);
00494                      ZVAL_STRINGL(tmp, name, namelen-1, 1);
00495                      pid = zend_hash_next_free_element(disp->dispid_to_name);
00496                      zend_hash_index_update(disp->dispid_to_name, pid, (void*)&tmp, sizeof(zval *), NULL);
00497 
00498                      MAKE_STD_ZVAL(tmp);
00499                      ZVAL_LONG(tmp, pid);
00500                      zend_hash_update(disp->name_to_dispid, name, namelen, (void*)&tmp, sizeof(zval *), NULL);
00501               }
00502        }
00503        
00504        /* functions */
00505        if (Z_OBJCE_P(disp->object)) {
00506               zend_hash_internal_pointer_reset_ex(&Z_OBJCE_P(disp->object)->function_table, &pos);
00507               while (HASH_KEY_NON_EXISTANT != (keytype =
00508                             zend_hash_get_current_key_ex(&Z_OBJCE_P(disp->object)->function_table,
00509                             &name, &namelen, &pid, 0, &pos))) {
00510 
00511                      char namebuf[32];
00512                      if (keytype == HASH_KEY_IS_LONG) {
00513                             snprintf(namebuf, sizeof(namebuf), "%d", pid);
00514                             name = namebuf;
00515                             namelen = strlen(namebuf) + 1;
00516                      }
00517 
00518                      zend_hash_move_forward_ex(Z_OBJPROP_P(disp->object), &pos);
00519 
00520                      /* Find the existing id */
00521                      if (zend_hash_find(disp->name_to_dispid, name, namelen, (void**)&tmp) == SUCCESS)
00522                             continue;
00523 
00524                      /* add the mappings */
00525                      MAKE_STD_ZVAL(tmp);
00526                      ZVAL_STRINGL(tmp, name, namelen-1, 1);
00527                      pid = zend_hash_next_free_element(disp->dispid_to_name);
00528                      zend_hash_index_update(disp->dispid_to_name, pid, (void*)&tmp, sizeof(zval *), NULL);
00529 
00530                      MAKE_STD_ZVAL(tmp);
00531                      ZVAL_LONG(tmp, pid);
00532                      zend_hash_update(disp->name_to_dispid, name, namelen, (void*)&tmp, sizeof(zval *), NULL);
00533               }
00534        }
00535 }
00536 
00537 static php_dispatchex *disp_constructor(zval *object TSRMLS_DC)
00538 {
00539        php_dispatchex *disp = (php_dispatchex*)CoTaskMemAlloc(sizeof(php_dispatchex));
00540 
00541        trace("constructing a COM wrapper for PHP object %p (%s)\n", object, Z_OBJCE_P(object)->name);
00542        
00543        if (disp == NULL)
00544               return NULL;
00545 
00546        memset(disp, 0, sizeof(php_dispatchex));
00547 
00548        disp->engine_thread = GetCurrentThreadId();
00549        disp->lpVtbl = &php_dispatch_vtbl;
00550        disp->refcount = 1;
00551 
00552 
00553        if (object)
00554               Z_ADDREF_P(object);
00555        disp->object = object;
00556 
00557        disp->id = zend_list_insert(disp, le_dispatch);
00558        
00559        return disp;
00560 }
00561 
00562 static void disp_destructor(php_dispatchex *disp)
00563 {
00564        TSRMLS_FETCH();
00565        
00566        /* Object store not available during request shutdown */
00567        if (COMG(rshutdown_started)) {
00568               trace("destroying COM wrapper for PHP object %p (name:unknown)\n", disp->object);
00569        } else {
00570               trace("destroying COM wrapper for PHP object %p (name:%s)\n", disp->object, Z_OBJCE_P(disp->object)->name);
00571        }
00572        
00573        disp->id = 0;
00574        
00575        if (disp->refcount > 0)
00576               CoDisconnectObject((IUnknown*)disp, 0);
00577 
00578        zend_hash_destroy(disp->dispid_to_name);
00579        zend_hash_destroy(disp->name_to_dispid);
00580        FREE_HASHTABLE(disp->dispid_to_name);
00581        FREE_HASHTABLE(disp->name_to_dispid);
00582                      
00583        if (disp->object)
00584               zval_ptr_dtor(&disp->object);
00585 
00586        CoTaskMemFree(disp);
00587 }
00588 
00589 PHPAPI IDispatch *php_com_wrapper_export_as_sink(zval *val, GUID *sinkid,
00590           HashTable *id_to_name TSRMLS_DC)
00591 {
00592        php_dispatchex *disp = disp_constructor(val TSRMLS_CC);
00593        HashPosition pos;
00594        char *name = NULL;
00595        zval *tmp, **ntmp;
00596        int namelen;
00597        int keytype;
00598        ulong pid;
00599 
00600        disp->dispid_to_name = id_to_name;
00601 
00602        memcpy(&disp->sinkid, sinkid, sizeof(disp->sinkid));
00603        
00604        /* build up the reverse mapping */
00605        ALLOC_HASHTABLE(disp->name_to_dispid);
00606        zend_hash_init(disp->name_to_dispid, 0, NULL, ZVAL_PTR_DTOR, 0);
00607        
00608        zend_hash_internal_pointer_reset_ex(id_to_name, &pos);
00609        while (HASH_KEY_NON_EXISTANT != (keytype =
00610                             zend_hash_get_current_key_ex(id_to_name, &name, &namelen, &pid, 0, &pos))) {
00611 
00612               if (keytype == HASH_KEY_IS_LONG) {
00613 
00614                      zend_hash_get_current_data_ex(id_to_name, (void**)&ntmp, &pos);
00615                      
00616                      MAKE_STD_ZVAL(tmp);
00617                      ZVAL_LONG(tmp, pid);
00618                      zend_hash_update(disp->name_to_dispid, Z_STRVAL_PP(ntmp),
00619                             Z_STRLEN_PP(ntmp)+1, (void*)&tmp, sizeof(zval *), NULL);
00620               }
00621 
00622               zend_hash_move_forward_ex(id_to_name, &pos);
00623        }
00624 
00625        return (IDispatch*)disp;
00626 }
00627 
00628 PHPAPI IDispatch *php_com_wrapper_export(zval *val TSRMLS_DC)
00629 {
00630        php_dispatchex *disp = NULL;
00631 
00632        if (Z_TYPE_P(val) != IS_OBJECT) {
00633               return NULL;
00634        }
00635 
00636        if (php_com_is_valid_object(val TSRMLS_CC)) {
00637               /* pass back its IDispatch directly */
00638               php_com_dotnet_object *obj = CDNO_FETCH(val);
00639               
00640               if (obj == NULL)
00641                      return NULL;
00642 
00643               if (V_VT(&obj->v) == VT_DISPATCH && V_DISPATCH(&obj->v)) {
00644                      IDispatch_AddRef(V_DISPATCH(&obj->v));
00645                      return V_DISPATCH(&obj->v);
00646               }
00647                      
00648               return NULL;
00649        }
00650 
00651        disp = disp_constructor(val TSRMLS_CC);
00652        generate_dispids(disp TSRMLS_CC);
00653 
00654        return (IDispatch*)disp;
00655 }
00656 
00657