mirror of
https://github.com/php/php-src.git
synced 2025-08-19 08:49:28 +02:00
Added generic COM wrapper for PHP objects.
This commit is contained in:
parent
3b97fe3657
commit
fc964e88d3
8 changed files with 1234 additions and 12 deletions
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
547
ext/com/dispatch.c
Normal file
547
ext/com/dispatch.c
Normal file
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
547
ext/rpc/com/dispatch.c
Normal file
547
ext/rpc/com/dispatch.c
Normal file
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue