]> granicus.if.org Git - php/commitdiff
Added generic COM wrapper for PHP objects.
authorWez Furlong <wez@php.net>
Mon, 20 May 2002 01:31:48 +0000 (01:31 +0000)
committerWez Furlong <wez@php.net>
Mon, 20 May 2002 01:31:48 +0000 (01:31 +0000)
ext/com/COM.c
ext/com/conversion.c
ext/com/dispatch.c [new file with mode: 0644]
ext/com/php_COM.h
ext/rpc/com/com_wrapper.c
ext/rpc/com/conversion.c
ext/rpc/com/dispatch.c [new file with mode: 0644]
ext/rpc/com/php_com.h

index cb42bfc4f726bcbff39130516febbb2548d4db1f..f8149faf7cbfaf08a48e93466d3a924496662af8 100644 (file)
@@ -65,7 +65,6 @@
 #include "php_VARIANT.h"
 
 static ITypeLib *php_COM_find_typelib(char *search_string, int mode TSRMLS_DC);
-static int php_COM_load_typelib(ITypeLib *TypeLib, int mode TSRMLS_DC);
 static int do_COM_offget(VARIANT *result, comval *array, pval *property, int cleanup TSRMLS_DC);
 static int do_COM_propget(VARIANT *var_result, comval *obj, pval *arg_property, int cleanup TSRMLS_DC);
 static void php_register_COM_class(TSRMLS_D);
@@ -1424,6 +1423,32 @@ PHPAPI int php_COM_set_property_handler(zend_property_reference *property_refere
        return SUCCESS;
 }
 
+/* create an overloaded COM object from a dispatch pointer */
+PHPAPI zval *php_COM_object_from_dispatch(IDispatch *disp, zval *val TSRMLS_DC)
+{
+       comval *obj;
+       long rid;
+       zval *zobj;
+       
+       ALLOC_COM(obj);
+       C_DISPATCH(obj) = disp;
+       php_COM_set(obj, &C_DISPATCH(obj), FALSE TSRMLS_CC);
+       
+       /* resource */
+       rid = zend_list_insert(obj, IS_COM);
+
+       if (val == NULL)
+               MAKE_STD_ZVAL(val);
+       ZVAL_RESOURCE(val, rid);
+
+       /* now we want an object */
+       MAKE_STD_ZVAL(zobj);
+       object_init_ex(zobj, &COM_class_entry);
+       zend_hash_index_update(Z_OBJPROP_P(zobj), 0, &val, sizeof(zval *), NULL);       
+
+       return zobj;
+}
+
 
 PHPAPI void php_COM_call_function_handler(INTERNAL_FUNCTION_PARAMETERS, zend_property_reference *property_reference)
 {
@@ -1672,7 +1697,7 @@ static ITypeLib *php_COM_find_typelib(char *search_string, int mode TSRMLS_DC)
 }
 
 
-static int php_COM_load_typelib(ITypeLib *TypeLib, int mode TSRMLS_DC)
+PHPAPI int php_COM_load_typelib(ITypeLib *TypeLib, int mode TSRMLS_DC)
 {
        ITypeComp *TypeComp;
        int i;
@@ -1800,11 +1825,18 @@ static void php_COM_init(int module_number TSRMLS_DC)
        php_register_COM_class(TSRMLS_C);
 }
 
+PHPAPI ZEND_DECLARE_MODULE_GLOBALS(com)
+
+static void php_com_init_globals(zend_com_globals *com_globals)
+{
+}
 
 PHP_MINIT_FUNCTION(COM)
 {
+       ZEND_INIT_MODULE_GLOBALS(com, php_com_init_globals, NULL);
        php_COM_init(module_number TSRMLS_CC);
        php_VARIANT_init(module_number TSRMLS_CC);
+       php_COM_dispatch_init(module_number TSRMLS_CC);
 
        REGISTER_LONG_CONSTANT("CLSCTX_INPROC_SERVER",  CLSCTX_INPROC_SERVER, CONST_CS | CONST_PERSISTENT);
        REGISTER_LONG_CONSTANT("CLSCTX_INPROC_HANDLER", CLSCTX_INPROC_HANDLER, CONST_CS | CONST_PERSISTENT);
@@ -1818,7 +1850,6 @@ PHP_MINIT_FUNCTION(COM)
        return SUCCESS;
 }
 
-
 PHP_MSHUTDOWN_FUNCTION(COM)
 {
        UNREGISTER_INI_ENTRIES();
@@ -1835,7 +1866,7 @@ zend_module_entry COM_module_entry = {
     PHP_MSHUTDOWN(COM),
     NULL,
     NULL,
-    PHP_MINFO(COM),
+       PHP_MINFO(COM),
     NO_VERSION_YET,
     STANDARD_MODULE_PROPERTIES
 };
index 0adcefdf47e5cc410821413a2fda996a32c33118..130885edd95302f3954ece1cffe370fd62e37195 100644 (file)
@@ -58,6 +58,8 @@ PHPAPI void php_pval_to_variant(pval *pval_arg, VARIANT *var_arg, int codepage T
                                type = VT_VARIANT|VT_BYREF;
                        } else if (!strcmp(Z_OBJCE_P(pval_arg)->name, "COM")) {
                                type = VT_DISPATCH;
+                       } else {
+                               type = VT_DISPATCH;
                        }
                        break;
 
@@ -155,6 +157,12 @@ PHPAPI void php_pval_to_variant_ex2(pval *pval_arg, VARIANT *var_arg, int type,
                }
        } else {
                switch (V_VT(var_arg)) {
+
+                       case VT_NULL:
+                       case VT_VOID:
+                               ZVAL_NULL(pval_arg);
+                               break; 
+                       
                        case VT_UI1:
                                convert_to_long_ex(&pval_arg);
                                V_UI1(var_arg) = (unsigned char) Z_LVAL_P(pval_arg);
@@ -211,6 +219,7 @@ PHPAPI void php_pval_to_variant_ex2(pval *pval_arg, VARIANT *var_arg, int type,
 
                                                /** @todo
                                                case IS_STRING:
+                                               */
                                                        /* string representation of a time value */
 
                                                default:
@@ -262,7 +271,13 @@ PHPAPI void php_pval_to_variant_ex2(pval *pval_arg, VARIANT *var_arg, int type,
                                break;
 
                        case VT_DISPATCH:
-                               comval_to_variant(pval_arg, var_arg TSRMLS_CC);
+                               if (!strcmp(Z_OBJCE_P(pval_arg)->name, "COM")) {
+                                       comval_to_variant(pval_arg, var_arg TSRMLS_CC);
+                               } else {
+                                       V_DISPATCH(var_arg) = php_COM_export_object(pval_arg TSRMLS_CC);
+                                       if (V_DISPATCH(var_arg))
+                                               V_VT(var_arg) = VT_DISPATCH;
+                               }
                                if (V_VT(var_arg) != VT_DISPATCH) {
                                        VariantInit(var_arg);
                                }
@@ -437,7 +452,7 @@ PHPAPI void php_pval_to_variant_ex2(pval *pval_arg, VARIANT *var_arg, int type,
                                break;
 
                        default:
-                               php_error(E_WARNING, "Type not supported or not yet implemented.");
+                               php_error(E_WARNING,"Unsupported variant type: %d (0x%X)", V_VT(var_arg), V_VT(var_arg));
                }
        }
 }
diff --git a/ext/com/dispatch.c b/ext/com/dispatch.c
new file mode 100644 (file)
index 0000000..802c4f8
--- /dev/null
@@ -0,0 +1,547 @@
+/*
+   +----------------------------------------------------------------------+
+   | PHP Version 4                                                        |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1997-2002 The PHP Group                                |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 2.02 of the PHP license,      |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available at through the world-wide-web at                           |
+   | http://www.php.net/license/2_02.txt.                                 |
+   | If you did not receive a copy of the PHP license and are unable to   |
+   | obtain it through the world-wide-web, please send a note to          |
+   | license@php.net so we can mail you a copy immediately.               |
+   +----------------------------------------------------------------------+
+   | Author: Wez Furlong <wez@thebrainroom.com>                           |
+   +----------------------------------------------------------------------+
+ */
+
+/* $Id$ */
+
+/* 
+ * This module is used to export PHP objects to COM and DOTNET by exposing
+ * them as objects implementing IDispatch.
+ * */
+
+#include "php.h"
+#include "php_COM.h"
+#include "php_VARIANT.h"
+#include "conversion.h"
+#include "variant.h"
+
+#define COBJMACROS
+#include <unknwn.h> /* IDispatch */
+#include <dispex.h> /* IDispatchEx */
+
+
+typedef struct {
+       /* This first part MUST match the declaration
+        * of interface IDispatchEx */
+       CONST_VTBL struct IDispatchExVtbl *lpVtbl;
+
+       /* now the PHP stuff */
+       
+       THREAD_T engine_thread; /* for sanity checking */
+       zval *object;                   /* the object exported */
+       LONG refcount;                  /* COM reference count */
+
+       HashTable dispid_to_name;       /* keep track of dispid -> name mappings */
+       HashTable name_to_dispid;       /* keep track of name -> dispid mappings */
+
+       int id;
+} php_dispatchex;
+
+static void disp_destructor(php_dispatchex *disp);
+
+static void dispatch_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
+{
+       php_dispatchex *disp = (php_dispatchex *)rsrc->ptr;
+       disp_destructor(disp);
+}
+
+static int le_dispatch;
+int php_COM_dispatch_init(int module_number TSRMLS_DC)
+{
+       le_dispatch = zend_register_list_destructors_ex(dispatch_dtor, NULL, "COM:Dispatch", module_number);
+       return le_dispatch;
+}
+
+
+/* {{{ trace */
+static inline void trace(char *fmt, ...)
+{
+       va_list ap;
+       char buf[4096];
+
+       sprintf(buf, "T=%08x ", tsrm_thread_id());
+       OutputDebugString(buf);
+       
+       va_start(ap, fmt);
+       vsnprintf(buf, sizeof(buf), fmt, ap);
+
+       OutputDebugString(buf);
+       
+       va_end(ap);
+}
+/* }}} */
+
+#define FETCH_DISP(methname)   \
+       php_dispatchex *disp = (php_dispatchex*)This; \
+       trace(" PHP:%s %s\n", Z_OBJCE_P(disp->object)->name, methname); \
+       if (tsrm_thread_id() != disp->engine_thread) \
+               return E_UNEXPECTED;
+
+
+static HRESULT STDMETHODCALLTYPE disp_queryinterface( 
+       IDispatchEx *This,
+       /* [in] */ REFIID riid,
+       /* [iid_is][out] */ void **ppvObject)
+{
+       FETCH_DISP("QueryInterface");
+
+       if (IsEqualGUID(&IID_IUnknown, riid) ||
+                       IsEqualGUID(&IID_IDispatch, riid) ||
+                       IsEqualGUID(&IID_IDispatchEx, riid)) {
+               *ppvObject = This;
+               InterlockedIncrement(&disp->refcount);
+               return S_OK;
+       }
+
+       *ppvObject = NULL;
+       return E_NOINTERFACE;
+}
+        
+static ULONG STDMETHODCALLTYPE disp_addref(IDispatchEx *This)
+{
+       FETCH_DISP("AddRef");
+
+       return InterlockedIncrement(&disp->refcount);
+}
+        
+static ULONG STDMETHODCALLTYPE disp_release(IDispatchEx *This)
+{
+       ULONG ret;
+       TSRMLS_FETCH();
+       FETCH_DISP("Release");
+
+       ret = InterlockedDecrement(&disp->refcount);
+       trace("-- refcount now %d\n", ret);
+       if (ret == 0) {
+               /* destroy it */
+               if (disp->id)
+                       zend_list_delete(disp->id);
+       }
+       return ret;
+}
+
+static HRESULT STDMETHODCALLTYPE disp_gettypeinfocount( 
+       IDispatchEx *This,
+       /* [out] */ UINT *pctinfo)
+{
+       FETCH_DISP("GetTypeInfoCount");
+
+       *pctinfo = 0;
+       return S_OK;
+}
+        
+static HRESULT STDMETHODCALLTYPE disp_gettypeinfo( 
+       IDispatchEx *This,
+       /* [in] */ UINT iTInfo,
+       /* [in] */ LCID lcid,
+       /* [out] */ ITypeInfo **ppTInfo)
+{
+       FETCH_DISP("GetTypeInfo");
+       
+       *ppTInfo = NULL;
+       return DISP_E_BADINDEX;
+}
+
+static HRESULT STDMETHODCALLTYPE disp_getidsofnames( 
+       IDispatchEx *This,
+       /* [in] */ REFIID riid,
+       /* [size_is][in] */ LPOLESTR *rgszNames,
+       /* [in] */ UINT cNames,
+       /* [in] */ LCID lcid,
+       /* [size_is][out] */ DISPID *rgDispId)
+{
+       UINT i;
+       HRESULT ret = S_OK;
+       TSRMLS_FETCH();
+       FETCH_DISP("GetIDsOfNames");
+
+       for (i = 0; i < cNames; i++) {
+               char *name;
+               unsigned int namelen;
+               zval *tmp;
+               
+               name = php_OLECHAR_to_char(rgszNames[i], &namelen, CP_ACP TSRMLS_CC);
+               
+               /* Lookup the name in the hash */
+               if (zend_hash_find(&disp->name_to_dispid, name, namelen+1, (void**)&tmp) == FAILURE) {
+                       ret = DISP_E_UNKNOWNNAME;
+                       rgDispId[i] = 0;
+               } else {
+                       rgDispId[i] = Z_LVAL_P(tmp);
+               }
+
+               efree(name);
+
+       }
+       
+       return ret;
+}
+
+static HRESULT STDMETHODCALLTYPE disp_invoke( 
+       IDispatchEx *This,
+       /* [in] */ DISPID dispIdMember,
+       /* [in] */ REFIID riid,
+       /* [in] */ LCID lcid,
+       /* [in] */ WORD wFlags,
+       /* [out][in] */ DISPPARAMS *pDispParams,
+       /* [out] */ VARIANT *pVarResult,
+       /* [out] */ EXCEPINFO *pExcepInfo,
+       /* [out] */ UINT *puArgErr)
+{
+       return This->lpVtbl->InvokeEx(This, dispIdMember,
+                       lcid, wFlags, pDispParams,
+                       pVarResult, pExcepInfo, NULL);
+}
+
+static HRESULT STDMETHODCALLTYPE disp_getdispid( 
+       IDispatchEx *This,
+       /* [in] */ BSTR bstrName,
+       /* [in] */ DWORD grfdex,
+       /* [out] */ DISPID *pid)
+{
+       HRESULT ret = DISP_E_UNKNOWNNAME;
+       char *name;
+       unsigned int namelen;
+       zval *tmp;
+       TSRMLS_FETCH();
+       FETCH_DISP("GetDispID");
+
+       name = php_OLECHAR_to_char(bstrName, &namelen, CP_ACP TSRMLS_CC);
+
+       /* Lookup the name in the hash */
+       if (zend_hash_find(&disp->name_to_dispid, name, namelen+1, (void**)&tmp) == SUCCESS) {
+               *pid = Z_LVAL_P(tmp);
+               ret = S_OK;
+       }
+
+       efree(name);
+       
+       return ret;
+}
+
+static HRESULT STDMETHODCALLTYPE disp_invokeex( 
+       IDispatchEx *This,
+       /* [in] */ DISPID id,
+       /* [in] */ LCID lcid,
+       /* [in] */ WORD wFlags,
+       /* [in] */ DISPPARAMS *pdp,
+       /* [out] */ VARIANT *pvarRes,
+       /* [out] */ EXCEPINFO *pei,
+       /* [unique][in] */ IServiceProvider *pspCaller)
+{
+       zval *name;
+       UINT i;
+       int codepage = CP_ACP;
+       zval **retval = NULL;
+       zval ***params = NULL;
+       TSRMLS_FETCH();
+       FETCH_DISP("InvokeEx");
+
+       if (SUCCESS == zend_hash_index_find(&disp->dispid_to_name, id, (void**)&name)) {
+               /* TODO: add support for overloaded objects */
+
+               trace("-- Invoke: %d %s\n", id, Z_STRVAL_P(name));
+               
+               /* convert args into zvals.
+                * Args are in reverse order */
+               params = (zval ***)emalloc(sizeof(zval **) * pdp->cArgs);
+               for (i = 0; i < pdp->cArgs; i++) {
+                       ALLOC_INIT_ZVAL(*params[i]);
+
+                       if (V_VT(&pdp->rgvarg[pdp->cArgs-i]) == VT_DISPATCH) {
+                               php_COM_object_from_dispatch(V_DISPATCH(&pdp->rgvarg[pdp->cArgs-i]), *params[i] TSRMLS_CC);
+                       } else {
+                               ZVAL_VARIANT(*params[i], &pdp->rgvarg[pdp->cArgs-i]);
+                       }
+               }
+               
+               if (wFlags & DISPATCH_PROPERTYGET) {
+                       zend_hash_find(Z_OBJPROP_P(disp->object), Z_STRVAL_P(name), Z_STRLEN_P(name)+1, (void**)&retval);
+               } else if (wFlags & DISPATCH_PROPERTYPUT) {
+                       add_property_zval(disp->object, Z_STRVAL_P(name), *params[0]);
+               } else if (wFlags & DISPATCH_METHOD) {
+                       
+                       call_user_function_ex(EG(function_table), &disp->object, name,
+                                       retval, pdp->cArgs, params, 1, NULL TSRMLS_CC);
+               }
+       
+               /* release arguments */
+               for (i = 0; i < pdp->cArgs; i++)
+                       zval_ptr_dtor(params[i]);
+               
+               /* return value */
+               if (retval) {
+                       if (pvarRes) {
+                               if (Z_TYPE_PP(retval) == IS_OBJECT) {
+                                       /* export the object using a dispatch like ourselves */
+                                       VariantInit(pvarRes);
+                                       V_VT(pvarRes) = VT_DISPATCH;
+                                       V_DISPATCH(pvarRes) = php_COM_export_object(*retval TSRMLS_CC);
+                               } else {
+                                       php_pval_to_variant(*retval, pvarRes, codepage TSRMLS_CC);
+                               }
+                       }
+                       zval_ptr_dtor(retval);
+               } else if (pvarRes) {
+                       VariantInit(pvarRes);
+               }
+               
+               return S_OK;
+       } else {
+               trace("InvokeEx: I don't support DISPID=%d\n", id);
+       }
+
+       return DISP_E_MEMBERNOTFOUND;
+}
+
+static HRESULT STDMETHODCALLTYPE disp_deletememberbyname( 
+       IDispatchEx *This,
+       /* [in] */ BSTR bstrName,
+       /* [in] */ DWORD grfdex)
+{
+       FETCH_DISP("DeleteMemberByName");
+
+       return S_FALSE;
+}
+
+static HRESULT STDMETHODCALLTYPE disp_deletememberbydispid( 
+       IDispatchEx *This,
+       /* [in] */ DISPID id)
+{
+       FETCH_DISP("DeleteMemberByDispID");
+       
+       return S_FALSE;
+}
+
+static HRESULT STDMETHODCALLTYPE disp_getmemberproperties( 
+       IDispatchEx *This,
+       /* [in] */ DISPID id,
+       /* [in] */ DWORD grfdexFetch,
+       /* [out] */ DWORD *pgrfdex)
+{
+       FETCH_DISP("GetMemberProperties");
+
+       return DISP_E_UNKNOWNNAME;
+}
+
+static HRESULT STDMETHODCALLTYPE disp_getmembername( 
+       IDispatchEx *This,
+       /* [in] */ DISPID id,
+       /* [out] */ BSTR *pbstrName)
+{
+       zval *name;
+       TSRMLS_FETCH();
+       FETCH_DISP("GetMemberName");
+
+       if (SUCCESS == zend_hash_index_find(&disp->dispid_to_name, id, (void**)&name)) {
+               OLECHAR *olestr = php_char_to_OLECHAR(Z_STRVAL_P(name), Z_STRLEN_P(name), CP_ACP TSRMLS_CC);
+               *pbstrName = SysAllocString(olestr);
+               efree(olestr);
+               return S_OK;
+       } else {
+               return DISP_E_UNKNOWNNAME;
+       }
+}
+
+static HRESULT STDMETHODCALLTYPE disp_getnextdispid( 
+       IDispatchEx *This,
+       /* [in] */ DWORD grfdex,
+       /* [in] */ DISPID id,
+       /* [out] */ DISPID *pid)
+{
+       ulong next = id+1;
+       FETCH_DISP("GetNextDispID");
+
+       while(!zend_hash_index_exists(&disp->dispid_to_name, next))
+               next++;
+
+       if (zend_hash_index_exists(&disp->dispid_to_name, next)) {
+               *pid = next;
+               return S_OK;
+       }
+       return S_FALSE;
+}
+
+static HRESULT STDMETHODCALLTYPE disp_getnamespaceparent( 
+       IDispatchEx *This,
+       /* [out] */ IUnknown **ppunk)
+{
+       FETCH_DISP("GetNameSpaceParent");
+
+       *ppunk = NULL;
+       return E_NOTIMPL;
+}
+        
+static struct IDispatchExVtbl php_dispatch_vtbl = {
+       disp_queryinterface,
+       disp_addref,
+       disp_release,
+       disp_gettypeinfocount,
+       disp_gettypeinfo,
+       disp_getidsofnames,
+       disp_invoke,
+       disp_getdispid,
+       disp_invokeex,
+       disp_deletememberbyname,
+       disp_deletememberbydispid,
+       disp_getmemberproperties,
+       disp_getmembername,
+       disp_getnextdispid,
+       disp_getnamespaceparent
+};
+
+
+/* enumerate functions and properties of the object and assign
+ * dispatch ids */
+static void update_dispids(php_dispatchex *disp TSRMLS_DC)
+{
+       HashPosition pos;
+       char *name = NULL;
+       zval *tmp;
+       int namelen;
+       int keytype;
+       ulong pid;
+
+       /* properties */
+       zend_hash_internal_pointer_reset_ex(Z_OBJPROP_PP(&disp->object), &pos);
+       while (HASH_KEY_NON_EXISTANT != (keytype =
+                               zend_hash_get_current_key_ex(Z_OBJPROP_PP(&disp->object), &name, &namelen, &pid, 0, &pos))) {
+               char namebuf[32];
+               if (keytype == HASH_KEY_IS_LONG) {
+                       sprintf(namebuf, "%d", pid);
+                       name = namebuf;
+                       namelen = strlen(namebuf);
+               }
+
+               zend_hash_move_forward_ex(Z_OBJPROP_PP(&disp->object), &pos);
+               
+               /* Find the existing id */
+               if (zend_hash_find(&disp->name_to_dispid, name, namelen+1, (void**)&tmp) == SUCCESS)
+                       continue;
+               
+               /* add the mappings */
+               MAKE_STD_ZVAL(tmp);
+               ZVAL_STRINGL(tmp, name, namelen, 1);
+               zend_hash_index_update(&disp->dispid_to_name, pid, (void*)&tmp, sizeof(zval *), NULL);
+
+               MAKE_STD_ZVAL(tmp);
+               ZVAL_LONG(tmp, pid);
+               zend_hash_update(&disp->name_to_dispid, name, namelen+1, (void*)&tmp, sizeof(zval *), NULL);
+
+       }
+       
+       /* functions */
+       zend_hash_internal_pointer_reset_ex(&Z_OBJCE_PP(&disp->object)->function_table, &pos);
+       while (HASH_KEY_NON_EXISTANT != (keytype =
+                               zend_hash_get_current_key_ex(&Z_OBJCE_PP(&disp->object)->function_table, &name, &namelen, &pid, 0, &pos))) {
+
+               char namebuf[32];
+               if (keytype == HASH_KEY_IS_LONG) {
+                       sprintf(namebuf, "%d", pid);
+                       name = namebuf;
+                       namelen = strlen(namebuf);
+               }
+
+               zend_hash_move_forward_ex(Z_OBJPROP_PP(&disp->object), &pos);
+               
+               /* Find the existing id */
+               if (zend_hash_find(&disp->name_to_dispid, name, namelen+1, (void**)&tmp) == SUCCESS)
+                       continue;
+               
+               /* add the mappings */
+               MAKE_STD_ZVAL(tmp);
+               ZVAL_STRINGL(tmp, name, namelen, 1);
+               zend_hash_index_update(&disp->dispid_to_name, pid, (void*)&tmp, sizeof(zval *), NULL);
+
+               MAKE_STD_ZVAL(tmp);
+               ZVAL_LONG(tmp, pid);
+               zend_hash_update(&disp->name_to_dispid, name, namelen+1, (void*)&tmp, sizeof(zval *), NULL);
+       }
+}
+
+static php_dispatchex *disp_constructor(zval *object TSRMLS_DC)
+{
+       php_dispatchex *disp = (php_dispatchex*)CoTaskMemAlloc(sizeof(php_dispatchex));
+       
+       if (disp == NULL)
+               return NULL;
+
+       memset(disp, 0, sizeof(php_dispatchex));
+
+       disp->engine_thread = tsrm_thread_id();
+       disp->lpVtbl = &php_dispatch_vtbl;
+       disp->refcount = 1;
+
+       zend_hash_init(&disp->dispid_to_name, 0, NULL, NULL, TRUE);
+       zend_hash_init(&disp->name_to_dispid, 0, NULL, NULL, TRUE);
+
+       if (object)
+               ZVAL_ADDREF(object);
+       disp->object = object;
+
+       update_dispids(disp TSRMLS_CC);
+
+       disp->id = zend_list_insert(disp, le_dispatch);
+       
+       return disp;
+}
+
+static void disp_destructor(php_dispatchex *disp)
+{
+       TSRMLS_FETCH();
+       
+       trace("destroying COM wrapper for PHP object %s\n", Z_OBJCE_P(disp->object)->name);
+
+       disp->id = 0;
+       
+       if (disp->refcount > 0)
+               CoDisconnectObject((IUnknown*)disp, 0);
+
+       zend_hash_destroy(&disp->dispid_to_name);
+       zend_hash_destroy(&disp->name_to_dispid);
+
+       if (disp->object)
+               zval_ptr_dtor(&disp->object);
+
+
+       CoTaskMemFree(disp);
+}
+
+
+PHPAPI IDispatch *php_COM_export_object(zval *val TSRMLS_DC)
+{
+       if (Z_TYPE_P(val) != IS_OBJECT)
+               return NULL;
+
+       if (Z_OBJCE_P(val) == &COM_class_entry) {
+               /* pass back it's IDispatch directly */
+               zval **tmp;
+               comval *obj;
+               int type;
+               
+               zend_hash_index_find(Z_OBJPROP_P(val), 0, (void**)&tmp);
+               obj = (comval *)zend_list_find(Z_LVAL_PP(tmp), &type);
+               if (type != IS_COM)
+                       return NULL;
+
+               C_DISPATCH(obj)->lpVtbl->AddRef(C_DISPATCH(obj));
+               return C_DISPATCH(obj);
+       }
+       return (IDispatch*)disp_constructor(val TSRMLS_CC);
+}
+
+
index 5c0155515bb89a7018aecef6336503583d347d1d..396df9c93c6126de57196f44b113164513b6cc0d 100644 (file)
@@ -31,6 +31,12 @@ PHPAPI int php_COM_get_le_comval();
 PHPAPI int php_COM_set_property_handler(zend_property_reference *property_reference, pval *value);
 PHPAPI pval php_COM_get_property_handler(zend_property_reference *property_reference);
 PHPAPI void php_COM_call_function_handler(INTERNAL_FUNCTION_PARAMETERS, zend_property_reference *property_reference);
+PHPAPI zval *php_COM_object_from_dispatch(IDispatch *disp, zval *val TSRMLS_DC);
+PHPAPI int php_COM_load_typelib(ITypeLib *TypeLib, int mode TSRMLS_DC);
+
+/* dispatch.c */
+PHPAPI IDispatch *php_COM_export_object(zval *val TSRMLS_DC);
+int php_COM_dispatch_init(int module_number TSRMLS_DC);
 
 zend_module_entry COM_module_entry;
 zend_class_entry COM_class_entry;
@@ -43,6 +49,18 @@ END_EXTERN_C()
 
 #define phpext_com_ptr &COM_module_entry
 
+ZEND_BEGIN_MODULE_GLOBALS(com)
+       int nothing;
+ZEND_END_MODULE_GLOBALS(com)
+
+PHPAPI ZEND_EXTERN_MODULE_GLOBALS(com);
+       
+#ifdef ZTS
+#define COMG(v)        TSRMG(com_globals_id, zend_com_globals *, v)
+#else
+#define COMG(v)        (com_globals.v)
+#endif
+       
 #else
 
 #define phpext_com_ptr NULL
index cb42bfc4f726bcbff39130516febbb2548d4db1f..f8149faf7cbfaf08a48e93466d3a924496662af8 100644 (file)
@@ -65,7 +65,6 @@
 #include "php_VARIANT.h"
 
 static ITypeLib *php_COM_find_typelib(char *search_string, int mode TSRMLS_DC);
-static int php_COM_load_typelib(ITypeLib *TypeLib, int mode TSRMLS_DC);
 static int do_COM_offget(VARIANT *result, comval *array, pval *property, int cleanup TSRMLS_DC);
 static int do_COM_propget(VARIANT *var_result, comval *obj, pval *arg_property, int cleanup TSRMLS_DC);
 static void php_register_COM_class(TSRMLS_D);
@@ -1424,6 +1423,32 @@ PHPAPI int php_COM_set_property_handler(zend_property_reference *property_refere
        return SUCCESS;
 }
 
+/* create an overloaded COM object from a dispatch pointer */
+PHPAPI zval *php_COM_object_from_dispatch(IDispatch *disp, zval *val TSRMLS_DC)
+{
+       comval *obj;
+       long rid;
+       zval *zobj;
+       
+       ALLOC_COM(obj);
+       C_DISPATCH(obj) = disp;
+       php_COM_set(obj, &C_DISPATCH(obj), FALSE TSRMLS_CC);
+       
+       /* resource */
+       rid = zend_list_insert(obj, IS_COM);
+
+       if (val == NULL)
+               MAKE_STD_ZVAL(val);
+       ZVAL_RESOURCE(val, rid);
+
+       /* now we want an object */
+       MAKE_STD_ZVAL(zobj);
+       object_init_ex(zobj, &COM_class_entry);
+       zend_hash_index_update(Z_OBJPROP_P(zobj), 0, &val, sizeof(zval *), NULL);       
+
+       return zobj;
+}
+
 
 PHPAPI void php_COM_call_function_handler(INTERNAL_FUNCTION_PARAMETERS, zend_property_reference *property_reference)
 {
@@ -1672,7 +1697,7 @@ static ITypeLib *php_COM_find_typelib(char *search_string, int mode TSRMLS_DC)
 }
 
 
-static int php_COM_load_typelib(ITypeLib *TypeLib, int mode TSRMLS_DC)
+PHPAPI int php_COM_load_typelib(ITypeLib *TypeLib, int mode TSRMLS_DC)
 {
        ITypeComp *TypeComp;
        int i;
@@ -1800,11 +1825,18 @@ static void php_COM_init(int module_number TSRMLS_DC)
        php_register_COM_class(TSRMLS_C);
 }
 
+PHPAPI ZEND_DECLARE_MODULE_GLOBALS(com)
+
+static void php_com_init_globals(zend_com_globals *com_globals)
+{
+}
 
 PHP_MINIT_FUNCTION(COM)
 {
+       ZEND_INIT_MODULE_GLOBALS(com, php_com_init_globals, NULL);
        php_COM_init(module_number TSRMLS_CC);
        php_VARIANT_init(module_number TSRMLS_CC);
+       php_COM_dispatch_init(module_number TSRMLS_CC);
 
        REGISTER_LONG_CONSTANT("CLSCTX_INPROC_SERVER",  CLSCTX_INPROC_SERVER, CONST_CS | CONST_PERSISTENT);
        REGISTER_LONG_CONSTANT("CLSCTX_INPROC_HANDLER", CLSCTX_INPROC_HANDLER, CONST_CS | CONST_PERSISTENT);
@@ -1818,7 +1850,6 @@ PHP_MINIT_FUNCTION(COM)
        return SUCCESS;
 }
 
-
 PHP_MSHUTDOWN_FUNCTION(COM)
 {
        UNREGISTER_INI_ENTRIES();
@@ -1835,7 +1866,7 @@ zend_module_entry COM_module_entry = {
     PHP_MSHUTDOWN(COM),
     NULL,
     NULL,
-    PHP_MINFO(COM),
+       PHP_MINFO(COM),
     NO_VERSION_YET,
     STANDARD_MODULE_PROPERTIES
 };
index 0adcefdf47e5cc410821413a2fda996a32c33118..130885edd95302f3954ece1cffe370fd62e37195 100644 (file)
@@ -58,6 +58,8 @@ PHPAPI void php_pval_to_variant(pval *pval_arg, VARIANT *var_arg, int codepage T
                                type = VT_VARIANT|VT_BYREF;
                        } else if (!strcmp(Z_OBJCE_P(pval_arg)->name, "COM")) {
                                type = VT_DISPATCH;
+                       } else {
+                               type = VT_DISPATCH;
                        }
                        break;
 
@@ -155,6 +157,12 @@ PHPAPI void php_pval_to_variant_ex2(pval *pval_arg, VARIANT *var_arg, int type,
                }
        } else {
                switch (V_VT(var_arg)) {
+
+                       case VT_NULL:
+                       case VT_VOID:
+                               ZVAL_NULL(pval_arg);
+                               break; 
+                       
                        case VT_UI1:
                                convert_to_long_ex(&pval_arg);
                                V_UI1(var_arg) = (unsigned char) Z_LVAL_P(pval_arg);
@@ -211,6 +219,7 @@ PHPAPI void php_pval_to_variant_ex2(pval *pval_arg, VARIANT *var_arg, int type,
 
                                                /** @todo
                                                case IS_STRING:
+                                               */
                                                        /* string representation of a time value */
 
                                                default:
@@ -262,7 +271,13 @@ PHPAPI void php_pval_to_variant_ex2(pval *pval_arg, VARIANT *var_arg, int type,
                                break;
 
                        case VT_DISPATCH:
-                               comval_to_variant(pval_arg, var_arg TSRMLS_CC);
+                               if (!strcmp(Z_OBJCE_P(pval_arg)->name, "COM")) {
+                                       comval_to_variant(pval_arg, var_arg TSRMLS_CC);
+                               } else {
+                                       V_DISPATCH(var_arg) = php_COM_export_object(pval_arg TSRMLS_CC);
+                                       if (V_DISPATCH(var_arg))
+                                               V_VT(var_arg) = VT_DISPATCH;
+                               }
                                if (V_VT(var_arg) != VT_DISPATCH) {
                                        VariantInit(var_arg);
                                }
@@ -437,7 +452,7 @@ PHPAPI void php_pval_to_variant_ex2(pval *pval_arg, VARIANT *var_arg, int type,
                                break;
 
                        default:
-                               php_error(E_WARNING, "Type not supported or not yet implemented.");
+                               php_error(E_WARNING,"Unsupported variant type: %d (0x%X)", V_VT(var_arg), V_VT(var_arg));
                }
        }
 }
diff --git a/ext/rpc/com/dispatch.c b/ext/rpc/com/dispatch.c
new file mode 100644 (file)
index 0000000..802c4f8
--- /dev/null
@@ -0,0 +1,547 @@
+/*
+   +----------------------------------------------------------------------+
+   | PHP Version 4                                                        |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1997-2002 The PHP Group                                |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 2.02 of the PHP license,      |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available at through the world-wide-web at                           |
+   | http://www.php.net/license/2_02.txt.                                 |
+   | If you did not receive a copy of the PHP license and are unable to   |
+   | obtain it through the world-wide-web, please send a note to          |
+   | license@php.net so we can mail you a copy immediately.               |
+   +----------------------------------------------------------------------+
+   | Author: Wez Furlong <wez@thebrainroom.com>                           |
+   +----------------------------------------------------------------------+
+ */
+
+/* $Id$ */
+
+/* 
+ * This module is used to export PHP objects to COM and DOTNET by exposing
+ * them as objects implementing IDispatch.
+ * */
+
+#include "php.h"
+#include "php_COM.h"
+#include "php_VARIANT.h"
+#include "conversion.h"
+#include "variant.h"
+
+#define COBJMACROS
+#include <unknwn.h> /* IDispatch */
+#include <dispex.h> /* IDispatchEx */
+
+
+typedef struct {
+       /* This first part MUST match the declaration
+        * of interface IDispatchEx */
+       CONST_VTBL struct IDispatchExVtbl *lpVtbl;
+
+       /* now the PHP stuff */
+       
+       THREAD_T engine_thread; /* for sanity checking */
+       zval *object;                   /* the object exported */
+       LONG refcount;                  /* COM reference count */
+
+       HashTable dispid_to_name;       /* keep track of dispid -> name mappings */
+       HashTable name_to_dispid;       /* keep track of name -> dispid mappings */
+
+       int id;
+} php_dispatchex;
+
+static void disp_destructor(php_dispatchex *disp);
+
+static void dispatch_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
+{
+       php_dispatchex *disp = (php_dispatchex *)rsrc->ptr;
+       disp_destructor(disp);
+}
+
+static int le_dispatch;
+int php_COM_dispatch_init(int module_number TSRMLS_DC)
+{
+       le_dispatch = zend_register_list_destructors_ex(dispatch_dtor, NULL, "COM:Dispatch", module_number);
+       return le_dispatch;
+}
+
+
+/* {{{ trace */
+static inline void trace(char *fmt, ...)
+{
+       va_list ap;
+       char buf[4096];
+
+       sprintf(buf, "T=%08x ", tsrm_thread_id());
+       OutputDebugString(buf);
+       
+       va_start(ap, fmt);
+       vsnprintf(buf, sizeof(buf), fmt, ap);
+
+       OutputDebugString(buf);
+       
+       va_end(ap);
+}
+/* }}} */
+
+#define FETCH_DISP(methname)   \
+       php_dispatchex *disp = (php_dispatchex*)This; \
+       trace(" PHP:%s %s\n", Z_OBJCE_P(disp->object)->name, methname); \
+       if (tsrm_thread_id() != disp->engine_thread) \
+               return E_UNEXPECTED;
+
+
+static HRESULT STDMETHODCALLTYPE disp_queryinterface( 
+       IDispatchEx *This,
+       /* [in] */ REFIID riid,
+       /* [iid_is][out] */ void **ppvObject)
+{
+       FETCH_DISP("QueryInterface");
+
+       if (IsEqualGUID(&IID_IUnknown, riid) ||
+                       IsEqualGUID(&IID_IDispatch, riid) ||
+                       IsEqualGUID(&IID_IDispatchEx, riid)) {
+               *ppvObject = This;
+               InterlockedIncrement(&disp->refcount);
+               return S_OK;
+       }
+
+       *ppvObject = NULL;
+       return E_NOINTERFACE;
+}
+        
+static ULONG STDMETHODCALLTYPE disp_addref(IDispatchEx *This)
+{
+       FETCH_DISP("AddRef");
+
+       return InterlockedIncrement(&disp->refcount);
+}
+        
+static ULONG STDMETHODCALLTYPE disp_release(IDispatchEx *This)
+{
+       ULONG ret;
+       TSRMLS_FETCH();
+       FETCH_DISP("Release");
+
+       ret = InterlockedDecrement(&disp->refcount);
+       trace("-- refcount now %d\n", ret);
+       if (ret == 0) {
+               /* destroy it */
+               if (disp->id)
+                       zend_list_delete(disp->id);
+       }
+       return ret;
+}
+
+static HRESULT STDMETHODCALLTYPE disp_gettypeinfocount( 
+       IDispatchEx *This,
+       /* [out] */ UINT *pctinfo)
+{
+       FETCH_DISP("GetTypeInfoCount");
+
+       *pctinfo = 0;
+       return S_OK;
+}
+        
+static HRESULT STDMETHODCALLTYPE disp_gettypeinfo( 
+       IDispatchEx *This,
+       /* [in] */ UINT iTInfo,
+       /* [in] */ LCID lcid,
+       /* [out] */ ITypeInfo **ppTInfo)
+{
+       FETCH_DISP("GetTypeInfo");
+       
+       *ppTInfo = NULL;
+       return DISP_E_BADINDEX;
+}
+
+static HRESULT STDMETHODCALLTYPE disp_getidsofnames( 
+       IDispatchEx *This,
+       /* [in] */ REFIID riid,
+       /* [size_is][in] */ LPOLESTR *rgszNames,
+       /* [in] */ UINT cNames,
+       /* [in] */ LCID lcid,
+       /* [size_is][out] */ DISPID *rgDispId)
+{
+       UINT i;
+       HRESULT ret = S_OK;
+       TSRMLS_FETCH();
+       FETCH_DISP("GetIDsOfNames");
+
+       for (i = 0; i < cNames; i++) {
+               char *name;
+               unsigned int namelen;
+               zval *tmp;
+               
+               name = php_OLECHAR_to_char(rgszNames[i], &namelen, CP_ACP TSRMLS_CC);
+               
+               /* Lookup the name in the hash */
+               if (zend_hash_find(&disp->name_to_dispid, name, namelen+1, (void**)&tmp) == FAILURE) {
+                       ret = DISP_E_UNKNOWNNAME;
+                       rgDispId[i] = 0;
+               } else {
+                       rgDispId[i] = Z_LVAL_P(tmp);
+               }
+
+               efree(name);
+
+       }
+       
+       return ret;
+}
+
+static HRESULT STDMETHODCALLTYPE disp_invoke( 
+       IDispatchEx *This,
+       /* [in] */ DISPID dispIdMember,
+       /* [in] */ REFIID riid,
+       /* [in] */ LCID lcid,
+       /* [in] */ WORD wFlags,
+       /* [out][in] */ DISPPARAMS *pDispParams,
+       /* [out] */ VARIANT *pVarResult,
+       /* [out] */ EXCEPINFO *pExcepInfo,
+       /* [out] */ UINT *puArgErr)
+{
+       return This->lpVtbl->InvokeEx(This, dispIdMember,
+                       lcid, wFlags, pDispParams,
+                       pVarResult, pExcepInfo, NULL);
+}
+
+static HRESULT STDMETHODCALLTYPE disp_getdispid( 
+       IDispatchEx *This,
+       /* [in] */ BSTR bstrName,
+       /* [in] */ DWORD grfdex,
+       /* [out] */ DISPID *pid)
+{
+       HRESULT ret = DISP_E_UNKNOWNNAME;
+       char *name;
+       unsigned int namelen;
+       zval *tmp;
+       TSRMLS_FETCH();
+       FETCH_DISP("GetDispID");
+
+       name = php_OLECHAR_to_char(bstrName, &namelen, CP_ACP TSRMLS_CC);
+
+       /* Lookup the name in the hash */
+       if (zend_hash_find(&disp->name_to_dispid, name, namelen+1, (void**)&tmp) == SUCCESS) {
+               *pid = Z_LVAL_P(tmp);
+               ret = S_OK;
+       }
+
+       efree(name);
+       
+       return ret;
+}
+
+static HRESULT STDMETHODCALLTYPE disp_invokeex( 
+       IDispatchEx *This,
+       /* [in] */ DISPID id,
+       /* [in] */ LCID lcid,
+       /* [in] */ WORD wFlags,
+       /* [in] */ DISPPARAMS *pdp,
+       /* [out] */ VARIANT *pvarRes,
+       /* [out] */ EXCEPINFO *pei,
+       /* [unique][in] */ IServiceProvider *pspCaller)
+{
+       zval *name;
+       UINT i;
+       int codepage = CP_ACP;
+       zval **retval = NULL;
+       zval ***params = NULL;
+       TSRMLS_FETCH();
+       FETCH_DISP("InvokeEx");
+
+       if (SUCCESS == zend_hash_index_find(&disp->dispid_to_name, id, (void**)&name)) {
+               /* TODO: add support for overloaded objects */
+
+               trace("-- Invoke: %d %s\n", id, Z_STRVAL_P(name));
+               
+               /* convert args into zvals.
+                * Args are in reverse order */
+               params = (zval ***)emalloc(sizeof(zval **) * pdp->cArgs);
+               for (i = 0; i < pdp->cArgs; i++) {
+                       ALLOC_INIT_ZVAL(*params[i]);
+
+                       if (V_VT(&pdp->rgvarg[pdp->cArgs-i]) == VT_DISPATCH) {
+                               php_COM_object_from_dispatch(V_DISPATCH(&pdp->rgvarg[pdp->cArgs-i]), *params[i] TSRMLS_CC);
+                       } else {
+                               ZVAL_VARIANT(*params[i], &pdp->rgvarg[pdp->cArgs-i]);
+                       }
+               }
+               
+               if (wFlags & DISPATCH_PROPERTYGET) {
+                       zend_hash_find(Z_OBJPROP_P(disp->object), Z_STRVAL_P(name), Z_STRLEN_P(name)+1, (void**)&retval);
+               } else if (wFlags & DISPATCH_PROPERTYPUT) {
+                       add_property_zval(disp->object, Z_STRVAL_P(name), *params[0]);
+               } else if (wFlags & DISPATCH_METHOD) {
+                       
+                       call_user_function_ex(EG(function_table), &disp->object, name,
+                                       retval, pdp->cArgs, params, 1, NULL TSRMLS_CC);
+               }
+       
+               /* release arguments */
+               for (i = 0; i < pdp->cArgs; i++)
+                       zval_ptr_dtor(params[i]);
+               
+               /* return value */
+               if (retval) {
+                       if (pvarRes) {
+                               if (Z_TYPE_PP(retval) == IS_OBJECT) {
+                                       /* export the object using a dispatch like ourselves */
+                                       VariantInit(pvarRes);
+                                       V_VT(pvarRes) = VT_DISPATCH;
+                                       V_DISPATCH(pvarRes) = php_COM_export_object(*retval TSRMLS_CC);
+                               } else {
+                                       php_pval_to_variant(*retval, pvarRes, codepage TSRMLS_CC);
+                               }
+                       }
+                       zval_ptr_dtor(retval);
+               } else if (pvarRes) {
+                       VariantInit(pvarRes);
+               }
+               
+               return S_OK;
+       } else {
+               trace("InvokeEx: I don't support DISPID=%d\n", id);
+       }
+
+       return DISP_E_MEMBERNOTFOUND;
+}
+
+static HRESULT STDMETHODCALLTYPE disp_deletememberbyname( 
+       IDispatchEx *This,
+       /* [in] */ BSTR bstrName,
+       /* [in] */ DWORD grfdex)
+{
+       FETCH_DISP("DeleteMemberByName");
+
+       return S_FALSE;
+}
+
+static HRESULT STDMETHODCALLTYPE disp_deletememberbydispid( 
+       IDispatchEx *This,
+       /* [in] */ DISPID id)
+{
+       FETCH_DISP("DeleteMemberByDispID");
+       
+       return S_FALSE;
+}
+
+static HRESULT STDMETHODCALLTYPE disp_getmemberproperties( 
+       IDispatchEx *This,
+       /* [in] */ DISPID id,
+       /* [in] */ DWORD grfdexFetch,
+       /* [out] */ DWORD *pgrfdex)
+{
+       FETCH_DISP("GetMemberProperties");
+
+       return DISP_E_UNKNOWNNAME;
+}
+
+static HRESULT STDMETHODCALLTYPE disp_getmembername( 
+       IDispatchEx *This,
+       /* [in] */ DISPID id,
+       /* [out] */ BSTR *pbstrName)
+{
+       zval *name;
+       TSRMLS_FETCH();
+       FETCH_DISP("GetMemberName");
+
+       if (SUCCESS == zend_hash_index_find(&disp->dispid_to_name, id, (void**)&name)) {
+               OLECHAR *olestr = php_char_to_OLECHAR(Z_STRVAL_P(name), Z_STRLEN_P(name), CP_ACP TSRMLS_CC);
+               *pbstrName = SysAllocString(olestr);
+               efree(olestr);
+               return S_OK;
+       } else {
+               return DISP_E_UNKNOWNNAME;
+       }
+}
+
+static HRESULT STDMETHODCALLTYPE disp_getnextdispid( 
+       IDispatchEx *This,
+       /* [in] */ DWORD grfdex,
+       /* [in] */ DISPID id,
+       /* [out] */ DISPID *pid)
+{
+       ulong next = id+1;
+       FETCH_DISP("GetNextDispID");
+
+       while(!zend_hash_index_exists(&disp->dispid_to_name, next))
+               next++;
+
+       if (zend_hash_index_exists(&disp->dispid_to_name, next)) {
+               *pid = next;
+               return S_OK;
+       }
+       return S_FALSE;
+}
+
+static HRESULT STDMETHODCALLTYPE disp_getnamespaceparent( 
+       IDispatchEx *This,
+       /* [out] */ IUnknown **ppunk)
+{
+       FETCH_DISP("GetNameSpaceParent");
+
+       *ppunk = NULL;
+       return E_NOTIMPL;
+}
+        
+static struct IDispatchExVtbl php_dispatch_vtbl = {
+       disp_queryinterface,
+       disp_addref,
+       disp_release,
+       disp_gettypeinfocount,
+       disp_gettypeinfo,
+       disp_getidsofnames,
+       disp_invoke,
+       disp_getdispid,
+       disp_invokeex,
+       disp_deletememberbyname,
+       disp_deletememberbydispid,
+       disp_getmemberproperties,
+       disp_getmembername,
+       disp_getnextdispid,
+       disp_getnamespaceparent
+};
+
+
+/* enumerate functions and properties of the object and assign
+ * dispatch ids */
+static void update_dispids(php_dispatchex *disp TSRMLS_DC)
+{
+       HashPosition pos;
+       char *name = NULL;
+       zval *tmp;
+       int namelen;
+       int keytype;
+       ulong pid;
+
+       /* properties */
+       zend_hash_internal_pointer_reset_ex(Z_OBJPROP_PP(&disp->object), &pos);
+       while (HASH_KEY_NON_EXISTANT != (keytype =
+                               zend_hash_get_current_key_ex(Z_OBJPROP_PP(&disp->object), &name, &namelen, &pid, 0, &pos))) {
+               char namebuf[32];
+               if (keytype == HASH_KEY_IS_LONG) {
+                       sprintf(namebuf, "%d", pid);
+                       name = namebuf;
+                       namelen = strlen(namebuf);
+               }
+
+               zend_hash_move_forward_ex(Z_OBJPROP_PP(&disp->object), &pos);
+               
+               /* Find the existing id */
+               if (zend_hash_find(&disp->name_to_dispid, name, namelen+1, (void**)&tmp) == SUCCESS)
+                       continue;
+               
+               /* add the mappings */
+               MAKE_STD_ZVAL(tmp);
+               ZVAL_STRINGL(tmp, name, namelen, 1);
+               zend_hash_index_update(&disp->dispid_to_name, pid, (void*)&tmp, sizeof(zval *), NULL);
+
+               MAKE_STD_ZVAL(tmp);
+               ZVAL_LONG(tmp, pid);
+               zend_hash_update(&disp->name_to_dispid, name, namelen+1, (void*)&tmp, sizeof(zval *), NULL);
+
+       }
+       
+       /* functions */
+       zend_hash_internal_pointer_reset_ex(&Z_OBJCE_PP(&disp->object)->function_table, &pos);
+       while (HASH_KEY_NON_EXISTANT != (keytype =
+                               zend_hash_get_current_key_ex(&Z_OBJCE_PP(&disp->object)->function_table, &name, &namelen, &pid, 0, &pos))) {
+
+               char namebuf[32];
+               if (keytype == HASH_KEY_IS_LONG) {
+                       sprintf(namebuf, "%d", pid);
+                       name = namebuf;
+                       namelen = strlen(namebuf);
+               }
+
+               zend_hash_move_forward_ex(Z_OBJPROP_PP(&disp->object), &pos);
+               
+               /* Find the existing id */
+               if (zend_hash_find(&disp->name_to_dispid, name, namelen+1, (void**)&tmp) == SUCCESS)
+                       continue;
+               
+               /* add the mappings */
+               MAKE_STD_ZVAL(tmp);
+               ZVAL_STRINGL(tmp, name, namelen, 1);
+               zend_hash_index_update(&disp->dispid_to_name, pid, (void*)&tmp, sizeof(zval *), NULL);
+
+               MAKE_STD_ZVAL(tmp);
+               ZVAL_LONG(tmp, pid);
+               zend_hash_update(&disp->name_to_dispid, name, namelen+1, (void*)&tmp, sizeof(zval *), NULL);
+       }
+}
+
+static php_dispatchex *disp_constructor(zval *object TSRMLS_DC)
+{
+       php_dispatchex *disp = (php_dispatchex*)CoTaskMemAlloc(sizeof(php_dispatchex));
+       
+       if (disp == NULL)
+               return NULL;
+
+       memset(disp, 0, sizeof(php_dispatchex));
+
+       disp->engine_thread = tsrm_thread_id();
+       disp->lpVtbl = &php_dispatch_vtbl;
+       disp->refcount = 1;
+
+       zend_hash_init(&disp->dispid_to_name, 0, NULL, NULL, TRUE);
+       zend_hash_init(&disp->name_to_dispid, 0, NULL, NULL, TRUE);
+
+       if (object)
+               ZVAL_ADDREF(object);
+       disp->object = object;
+
+       update_dispids(disp TSRMLS_CC);
+
+       disp->id = zend_list_insert(disp, le_dispatch);
+       
+       return disp;
+}
+
+static void disp_destructor(php_dispatchex *disp)
+{
+       TSRMLS_FETCH();
+       
+       trace("destroying COM wrapper for PHP object %s\n", Z_OBJCE_P(disp->object)->name);
+
+       disp->id = 0;
+       
+       if (disp->refcount > 0)
+               CoDisconnectObject((IUnknown*)disp, 0);
+
+       zend_hash_destroy(&disp->dispid_to_name);
+       zend_hash_destroy(&disp->name_to_dispid);
+
+       if (disp->object)
+               zval_ptr_dtor(&disp->object);
+
+
+       CoTaskMemFree(disp);
+}
+
+
+PHPAPI IDispatch *php_COM_export_object(zval *val TSRMLS_DC)
+{
+       if (Z_TYPE_P(val) != IS_OBJECT)
+               return NULL;
+
+       if (Z_OBJCE_P(val) == &COM_class_entry) {
+               /* pass back it's IDispatch directly */
+               zval **tmp;
+               comval *obj;
+               int type;
+               
+               zend_hash_index_find(Z_OBJPROP_P(val), 0, (void**)&tmp);
+               obj = (comval *)zend_list_find(Z_LVAL_PP(tmp), &type);
+               if (type != IS_COM)
+                       return NULL;
+
+               C_DISPATCH(obj)->lpVtbl->AddRef(C_DISPATCH(obj));
+               return C_DISPATCH(obj);
+       }
+       return (IDispatch*)disp_constructor(val TSRMLS_CC);
+}
+
+
index 5c0155515bb89a7018aecef6336503583d347d1d..396df9c93c6126de57196f44b113164513b6cc0d 100644 (file)
@@ -31,6 +31,12 @@ PHPAPI int php_COM_get_le_comval();
 PHPAPI int php_COM_set_property_handler(zend_property_reference *property_reference, pval *value);
 PHPAPI pval php_COM_get_property_handler(zend_property_reference *property_reference);
 PHPAPI void php_COM_call_function_handler(INTERNAL_FUNCTION_PARAMETERS, zend_property_reference *property_reference);
+PHPAPI zval *php_COM_object_from_dispatch(IDispatch *disp, zval *val TSRMLS_DC);
+PHPAPI int php_COM_load_typelib(ITypeLib *TypeLib, int mode TSRMLS_DC);
+
+/* dispatch.c */
+PHPAPI IDispatch *php_COM_export_object(zval *val TSRMLS_DC);
+int php_COM_dispatch_init(int module_number TSRMLS_DC);
 
 zend_module_entry COM_module_entry;
 zend_class_entry COM_class_entry;
@@ -43,6 +49,18 @@ END_EXTERN_C()
 
 #define phpext_com_ptr &COM_module_entry
 
+ZEND_BEGIN_MODULE_GLOBALS(com)
+       int nothing;
+ZEND_END_MODULE_GLOBALS(com)
+
+PHPAPI ZEND_EXTERN_MODULE_GLOBALS(com);
+       
+#ifdef ZTS
+#define COMG(v)        TSRMG(com_globals_id, zend_com_globals *, v)
+#else
+#define COMG(v)        (com_globals.v)
+#endif
+       
 #else
 
 #define phpext_com_ptr NULL