123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643 |
- /*
- +----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
- +----------------------------------------------------------------------+
- | This source file is subject to version 3.01 of the PHP license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | https://www.php.net/license/3_01.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> |
- +----------------------------------------------------------------------+
- */
- /* This module exports a PHP object as a COM object by wrapping it
- * using IDispatchEx */
- #ifdef HAVE_CONFIG_H
- #include "config.h"
- #endif
- #include "php.h"
- #include "php_ini.h"
- #include "ext/standard/info.h"
- #include "php_com_dotnet.h"
- #include "php_com_dotnet_internal.h"
- typedef struct {
- /* This first part MUST match the declaration
- * of interface IDispatchEx */
- CONST_VTBL struct IDispatchExVtbl *lpVtbl;
- /* now the PHP stuff */
- DWORD 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 */
- GUID sinkid; /* iid that we "implement" for event sinking */
- zend_resource *res;
- } php_dispatchex;
- static int le_dispatch;
- static void disp_destructor(php_dispatchex *disp);
- static void dispatch_dtor(zend_resource *rsrc)
- {
- php_dispatchex *disp = (php_dispatchex *)rsrc->ptr;
- disp_destructor(disp);
- }
- int php_com_wrapper_minit(INIT_FUNC_ARGS)
- {
- le_dispatch = zend_register_list_destructors_ex(dispatch_dtor,
- NULL, "com_dotnet_dispatch_wrapper", module_number);
- return le_dispatch;
- }
- /* {{{ trace */
- static inline void trace(char *fmt, ...)
- {
- va_list ap;
- char buf[4096];
- snprintf(buf, sizeof(buf), "T=%08x ", GetCurrentThreadId());
- 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; \
- if (COMG(rshutdown_started)) { \
- trace(" PHP Object:%p (name:unknown) %s\n", Z_OBJ(disp->object), methname); \
- } else { \
- trace(" PHP Object:%p (name:%s) %s\n", Z_OBJ(disp->object), Z_OBJCE(disp->object)->name->val, methname); \
- } \
- if (GetCurrentThreadId() != disp->engine_thread) { \
- return RPC_E_WRONG_THREAD; \
- }
- 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) ||
- IsEqualGUID(&disp->sinkid, 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;
- FETCH_DISP("Release");
- ret = InterlockedDecrement(&disp->refcount);
- trace("-- refcount now %d\n", ret);
- if (ret == 0) {
- /* destroy it */
- if (disp->res)
- zend_list_delete(disp->res);
- }
- 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;
- FETCH_DISP("GetIDsOfNames");
- for (i = 0; i < cNames; i++) {
- char *name;
- size_t namelen;
- zval *tmp;
- name = php_com_olestring_to_string(rgszNames[i], &namelen, COMG(code_page));
- /* Lookup the name in the hash */
- if ((tmp = zend_hash_str_find(disp->name_to_dispid, name, namelen)) == NULL) {
- ret = DISP_E_UNKNOWNNAME;
- rgDispId[i] = 0;
- } else {
- rgDispId[i] = (DISPID)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;
- size_t namelen;
- zval *tmp;
- FETCH_DISP("GetDispID");
- name = php_com_olestring_to_string(bstrName, &namelen, COMG(code_page));
- trace("Looking for %s, namelen=%d in %p\n", name, namelen, disp->name_to_dispid);
- /* Lookup the name in the hash */
- if ((tmp = zend_hash_str_find(disp->name_to_dispid, name, namelen)) != NULL) {
- trace("found it\n");
- *pid = (DISPID)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;
- zval rv, *retval = NULL;
- zval *params = NULL;
- HRESULT ret = DISP_E_MEMBERNOTFOUND;
- FETCH_DISP("InvokeEx");
- if (NULL != (name = zend_hash_index_find(disp->dispid_to_name, id))) {
- /* TODO: add support for overloaded objects */
- trace("-- Invoke: %d %20s [%d] flags=%08x args=%d\n", id, Z_STRVAL_P(name), Z_STRLEN_P(name), wFlags, pdp->cArgs);
- /* convert args into zvals.
- * Args are in reverse order */
- if (pdp->cArgs) {
- params = (zval *)safe_emalloc(sizeof(zval), pdp->cArgs, 0);
- for (i = 0; i < pdp->cArgs; i++) {
- VARIANT *arg;
- arg = &pdp->rgvarg[ pdp->cArgs - 1 - i];
- trace("alloc zval for arg %d VT=%08x\n", i, V_VT(arg));
- php_com_wrap_variant(¶ms[i], arg, COMG(code_page));
- }
- }
- trace("arguments processed, prepare to do some work\n");
- /* TODO: if PHP raises an exception here, we should catch it
- * and expose it as a COM exception */
- if (wFlags & DISPATCH_PROPERTYGET) {
- retval = zend_read_property(Z_OBJCE(disp->object), Z_OBJ(disp->object), Z_STRVAL_P(name), Z_STRLEN_P(name)+1, 1, &rv);
- } else if (wFlags & DISPATCH_PROPERTYPUT) {
- zend_update_property(Z_OBJCE(disp->object), Z_OBJ(disp->object), Z_STRVAL_P(name), Z_STRLEN_P(name), ¶ms[0]);
- } else if (wFlags & DISPATCH_METHOD) {
- zend_try {
- retval = &rv;
- if (SUCCESS == call_user_function(NULL, &disp->object, name,
- retval, pdp->cArgs, params)) {
- ret = S_OK;
- trace("function called ok\n");
- /* Copy any modified values to callers copy of variant*/
- for (i = 0; i < pdp->cArgs; i++) {
- php_com_dotnet_object *obj = CDNO_FETCH(¶ms[i]);
- VARIANT *srcvar = &obj->v;
- VARIANT *dstvar = &pdp->rgvarg[ pdp->cArgs - 1 - i];
- if ((V_VT(dstvar) & VT_BYREF) && obj->modified ) {
- trace("percolate modified value for arg %d VT=%08x\n", i, V_VT(dstvar));
- php_com_copy_variant(dstvar, srcvar);
- }
- }
- } else {
- trace("failed to call func\n");
- ret = DISP_E_EXCEPTION;
- }
- } zend_catch {
- trace("something blew up\n");
- ret = DISP_E_EXCEPTION;
- } zend_end_try();
- } else {
- trace("Don't know how to handle this invocation %08x\n", wFlags);
- }
- /* release arguments */
- if (params) {
- for (i = 0; i < pdp->cArgs; i++) {
- zval_ptr_dtor(¶ms[i]);
- }
- efree(params);
- }
- /* return value */
- if (retval) {
- if (pvarRes) {
- VariantInit(pvarRes);
- php_com_variant_from_zval(pvarRes, retval, COMG(code_page));
- }
- zval_ptr_dtor(retval);
- } else if (pvarRes) {
- VariantInit(pvarRes);
- }
- } else {
- trace("InvokeEx: I don't support DISPID=%d\n", id);
- }
- return ret;
- }
- static HRESULT STDMETHODCALLTYPE disp_deletememberbyname(
- IDispatchEx *This,
- /* [in] */ BSTR bstrName,
- /* [in] */ DWORD grfdex)
- {
- FETCH_DISP("DeleteMemberByName");
- /* TODO: unset */
- return S_FALSE;
- }
- static HRESULT STDMETHODCALLTYPE disp_deletememberbydispid(
- IDispatchEx *This,
- /* [in] */ DISPID id)
- {
- FETCH_DISP("DeleteMemberByDispID");
- /* TODO: unset */
- 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;
- FETCH_DISP("GetMemberName");
- if (NULL != (name = zend_hash_index_find(disp->dispid_to_name, id))) {
- OLECHAR *olestr = php_com_string_to_olestring(Z_STRVAL_P(name), Z_STRLEN_P(name), COMG(code_page));
- *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)
- {
- zend_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 generate_dispids(php_dispatchex *disp)
- {
- HashPosition pos;
- zend_string *name = NULL;
- zval *tmp, tmp2;
- int keytype;
- zend_ulong pid;
- if (disp->dispid_to_name == NULL) {
- ALLOC_HASHTABLE(disp->dispid_to_name);
- ALLOC_HASHTABLE(disp->name_to_dispid);
- zend_hash_init(disp->name_to_dispid, 0, NULL, ZVAL_PTR_DTOR, 0);
- zend_hash_init(disp->dispid_to_name, 0, NULL, ZVAL_PTR_DTOR, 0);
- }
- /* properties */
- if (Z_OBJPROP(disp->object)) {
- zend_hash_internal_pointer_reset_ex(Z_OBJPROP(disp->object), &pos);
- while (HASH_KEY_NON_EXISTENT != (keytype =
- zend_hash_get_current_key_ex(Z_OBJPROP(disp->object), &name,
- &pid, &pos))) {
- char namebuf[32];
- if (keytype == HASH_KEY_IS_LONG) {
- snprintf(namebuf, sizeof(namebuf), ZEND_ULONG_FMT, pid);
- name = zend_string_init(namebuf, strlen(namebuf), 0);
- } else {
- zend_string_addref(name);
- }
- zend_hash_move_forward_ex(Z_OBJPROP(disp->object), &pos);
- /* Find the existing id */
- if ((tmp = zend_hash_find(disp->name_to_dispid, name)) != NULL) {
- zend_string_release_ex(name, 0);
- continue;
- }
- /* add the mappings */
- ZVAL_STR_COPY(&tmp2, name);
- pid = zend_hash_next_free_element(disp->dispid_to_name);
- zend_hash_index_update(disp->dispid_to_name, pid, &tmp2);
- ZVAL_LONG(&tmp2, pid);
- zend_hash_update(disp->name_to_dispid, name, &tmp2);
- zend_string_release_ex(name, 0);
- }
- }
- /* functions */
- if (Z_OBJCE(disp->object)) {
- zend_hash_internal_pointer_reset_ex(&Z_OBJCE(disp->object)->function_table, &pos);
- while (HASH_KEY_NON_EXISTENT != (keytype =
- zend_hash_get_current_key_ex(&Z_OBJCE(disp->object)->function_table,
- &name, &pid, &pos))) {
- char namebuf[32];
- if (keytype == HASH_KEY_IS_LONG) {
- snprintf(namebuf, sizeof(namebuf), ZEND_ULONG_FMT, pid);
- name = zend_string_init(namebuf, strlen(namebuf), 0);
- } else {
- zend_string_addref(name);
- }
- zend_hash_move_forward_ex(&Z_OBJCE(disp->object)->function_table, &pos);
- /* Find the existing id */
- if ((tmp = zend_hash_find(disp->name_to_dispid, name)) != NULL) {
- zend_string_release_ex(name, 0);
- continue;
- }
- /* add the mappings */
- ZVAL_STR_COPY(&tmp2, name);
- pid = zend_hash_next_free_element(disp->dispid_to_name);
- zend_hash_index_update(disp->dispid_to_name, pid, &tmp2);
- ZVAL_LONG(&tmp2, pid);
- zend_hash_update(disp->name_to_dispid, name, &tmp2);
- zend_string_release_ex(name, 0);
- }
- }
- }
- static php_dispatchex *disp_constructor(zval *object)
- {
- php_dispatchex *disp = (php_dispatchex*)CoTaskMemAlloc(sizeof(php_dispatchex));
- zval *tmp;
- trace("constructing a COM wrapper for PHP object %p (%s)\n", object, Z_OBJCE_P(object)->name);
- if (disp == NULL)
- return NULL;
- memset(disp, 0, sizeof(php_dispatchex));
- disp->engine_thread = GetCurrentThreadId();
- disp->lpVtbl = &php_dispatch_vtbl;
- disp->refcount = 1;
- if (object) {
- ZVAL_COPY(&disp->object, object);
- } else {
- ZVAL_UNDEF(&disp->object);
- }
- tmp = zend_list_insert(disp, le_dispatch);
- disp->res = Z_RES_P(tmp);
- return disp;
- }
- static void disp_destructor(php_dispatchex *disp)
- {
- /* Object store not available during request shutdown */
- if (COMG(rshutdown_started)) {
- trace("destroying COM wrapper for PHP object %p (name:unknown)\n", Z_OBJ(disp->object));
- } else {
- trace("destroying COM wrapper for PHP object %p (name:%s)\n", Z_OBJ(disp->object), Z_OBJCE(disp->object)->name->val);
- }
- disp->res = NULL;
- if (disp->refcount > 0)
- CoDisconnectObject((IUnknown*)disp, 0);
- zend_hash_destroy(disp->dispid_to_name);
- zend_hash_destroy(disp->name_to_dispid);
- FREE_HASHTABLE(disp->dispid_to_name);
- FREE_HASHTABLE(disp->name_to_dispid);
- zval_ptr_dtor(&disp->object);
- CoTaskMemFree(disp);
- }
- PHP_COM_DOTNET_API IDispatch *php_com_wrapper_export_as_sink(zval *val, GUID *sinkid,
- HashTable *id_to_name)
- {
- php_dispatchex *disp = disp_constructor(val);
- HashPosition pos;
- zend_string *name = NULL;
- zval tmp, *ntmp;
- int keytype;
- zend_ulong pid;
- disp->dispid_to_name = id_to_name;
- memcpy(&disp->sinkid, sinkid, sizeof(disp->sinkid));
- /* build up the reverse mapping */
- ALLOC_HASHTABLE(disp->name_to_dispid);
- zend_hash_init(disp->name_to_dispid, 0, NULL, ZVAL_PTR_DTOR, 0);
- zend_hash_internal_pointer_reset_ex(id_to_name, &pos);
- while (HASH_KEY_NON_EXISTENT != (keytype =
- zend_hash_get_current_key_ex(id_to_name, &name, &pid, &pos))) {
- if (keytype == HASH_KEY_IS_LONG) {
- ntmp = zend_hash_get_current_data_ex(id_to_name, &pos);
- ZVAL_LONG(&tmp, pid);
- zend_hash_update(disp->name_to_dispid, Z_STR_P(ntmp), &tmp);
- }
- zend_hash_move_forward_ex(id_to_name, &pos);
- }
- return (IDispatch*)disp;
- }
- PHP_COM_DOTNET_API IDispatch *php_com_wrapper_export(zval *val)
- {
- php_dispatchex *disp = NULL;
- if (Z_TYPE_P(val) != IS_OBJECT) {
- return NULL;
- }
- if (php_com_is_valid_object(val)) {
- /* pass back its IDispatch directly */
- php_com_dotnet_object *obj = CDNO_FETCH(val);
- if (obj == NULL)
- return NULL;
- if (V_VT(&obj->v) == VT_DISPATCH && V_DISPATCH(&obj->v)) {
- IDispatch_AddRef(V_DISPATCH(&obj->v));
- return V_DISPATCH(&obj->v);
- }
- return NULL;
- }
- disp = disp_constructor(val);
- generate_dispids(disp);
- return (IDispatch*)disp;
- }
|