2
+----------------------------------------------------------------------+
4
+----------------------------------------------------------------------+
5
| Copyright (c) 1997-2004 The PHP Group |
6
+----------------------------------------------------------------------+
7
| This source file is subject to version 3.0 of the PHP license, |
8
| that is bundled with this package in the file LICENSE, and is |
9
| available through the world-wide-web at the following url: |
10
| http://www.php.net/license/3_0.txt. |
11
| If you did not receive a copy of the PHP license and are unable to |
12
| obtain it through the world-wide-web, please send a note to |
13
| license@php.net so we can mail you a copy immediately. |
14
+----------------------------------------------------------------------+
15
| Author: Wez Furlong <wez@thebrainroom.com> |
16
+----------------------------------------------------------------------+
19
/* $Id: com_wrapper.c,v 1.4.2.1 2004/07/28 23:48:26 wez Exp $ */
21
/* This module exports a PHP object as a COM object by wrapping it
22
* using IDispatchEx */
30
#include "ext/standard/info.h"
31
#include "php_com_dotnet.h"
32
#include "php_com_dotnet_internal.h"
35
/* This first part MUST match the declaration
36
* of interface IDispatchEx */
37
CONST_VTBL struct IDispatchExVtbl *lpVtbl;
39
/* now the PHP stuff */
41
THREAD_T engine_thread; /* for sanity checking */
42
zval *object; /* the object exported */
43
LONG refcount; /* COM reference count */
45
HashTable *dispid_to_name; /* keep track of dispid -> name mappings */
46
HashTable *name_to_dispid; /* keep track of name -> dispid mappings */
48
GUID sinkid; /* iid that we "implement" for event sinking */
53
static int le_dispatch;
55
static void disp_destructor(php_dispatchex *disp);
57
static void dispatch_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
59
php_dispatchex *disp = (php_dispatchex *)rsrc->ptr;
60
disp_destructor(disp);
63
int php_com_wrapper_minit(INIT_FUNC_ARGS)
65
le_dispatch = zend_register_list_destructors_ex(dispatch_dtor,
66
NULL, "com_dotnet_dispatch_wrapper", module_number);
72
static inline void trace(char *fmt, ...)
77
sprintf(buf, "T=%08x ", tsrm_thread_id());
78
OutputDebugString(buf);
81
vsnprintf(buf, sizeof(buf), fmt, ap);
83
OutputDebugString(buf);
89
#define FETCH_DISP(methname) \
90
php_dispatchex *disp = (php_dispatchex*)This; \
91
trace(" PHP:%s %s\n", Z_OBJCE_P(disp->object)->name, methname); \
92
if (tsrm_thread_id() != disp->engine_thread) \
96
static HRESULT STDMETHODCALLTYPE disp_queryinterface(
98
/* [in] */ REFIID riid,
99
/* [iid_is][out] */ void **ppvObject)
103
FETCH_DISP("QueryInterface");
105
if (IsEqualGUID(&IID_IUnknown, riid) ||
106
IsEqualGUID(&IID_IDispatch, riid) ||
107
IsEqualGUID(&IID_IDispatchEx, riid) ||
108
IsEqualGUID(&disp->sinkid, riid)) {
110
InterlockedIncrement(&disp->refcount);
115
return E_NOINTERFACE;
118
static ULONG STDMETHODCALLTYPE disp_addref(IDispatchEx *This)
122
FETCH_DISP("AddRef");
124
return InterlockedIncrement(&disp->refcount);
127
static ULONG STDMETHODCALLTYPE disp_release(IDispatchEx *This)
132
FETCH_DISP("Release");
134
ret = InterlockedDecrement(&disp->refcount);
135
trace("-- refcount now %d\n", ret);
139
zend_list_delete(disp->id);
144
static HRESULT STDMETHODCALLTYPE disp_gettypeinfocount(
146
/* [out] */ UINT *pctinfo)
150
FETCH_DISP("GetTypeInfoCount");
156
static HRESULT STDMETHODCALLTYPE disp_gettypeinfo(
158
/* [in] */ UINT iTInfo,
159
/* [in] */ LCID lcid,
160
/* [out] */ ITypeInfo **ppTInfo)
164
FETCH_DISP("GetTypeInfo");
167
return DISP_E_BADINDEX;
170
static HRESULT STDMETHODCALLTYPE disp_getidsofnames(
172
/* [in] */ REFIID riid,
173
/* [size_is][in] */ LPOLESTR *rgszNames,
174
/* [in] */ UINT cNames,
175
/* [in] */ LCID lcid,
176
/* [size_is][out] */ DISPID *rgDispId)
182
FETCH_DISP("GetIDsOfNames");
184
for (i = 0; i < cNames; i++) {
186
unsigned int namelen;
189
name = php_com_olestring_to_string(rgszNames[i], &namelen, COMG(code_page) TSRMLS_CC);
191
/* Lookup the name in the hash */
192
if (zend_hash_find(disp->name_to_dispid, name, namelen+1, (void**)&tmp) == FAILURE) {
193
ret = DISP_E_UNKNOWNNAME;
196
rgDispId[i] = Z_LVAL_PP(tmp);
206
static HRESULT STDMETHODCALLTYPE disp_invoke(
208
/* [in] */ DISPID dispIdMember,
209
/* [in] */ REFIID riid,
210
/* [in] */ LCID lcid,
211
/* [in] */ WORD wFlags,
212
/* [out][in] */ DISPPARAMS *pDispParams,
213
/* [out] */ VARIANT *pVarResult,
214
/* [out] */ EXCEPINFO *pExcepInfo,
215
/* [out] */ UINT *puArgErr)
217
return This->lpVtbl->InvokeEx(This, dispIdMember,
218
lcid, wFlags, pDispParams,
219
pVarResult, pExcepInfo, NULL);
222
static HRESULT STDMETHODCALLTYPE disp_getdispid(
224
/* [in] */ BSTR bstrName,
225
/* [in] */ DWORD grfdex,
226
/* [out] */ DISPID *pid)
228
HRESULT ret = DISP_E_UNKNOWNNAME;
230
unsigned int namelen;
234
FETCH_DISP("GetDispID");
236
name = php_com_olestring_to_string(bstrName, &namelen, COMG(code_page) TSRMLS_CC);
238
/* Lookup the name in the hash */
239
if (zend_hash_find(disp->name_to_dispid, name, namelen+1, (void**)&tmp) == SUCCESS) {
240
*pid = Z_LVAL_PP(tmp);
249
static HRESULT STDMETHODCALLTYPE disp_invokeex(
251
/* [in] */ DISPID id,
252
/* [in] */ LCID lcid,
253
/* [in] */ WORD wFlags,
254
/* [in] */ DISPPARAMS *pdp,
255
/* [out] */ VARIANT *pvarRes,
256
/* [out] */ EXCEPINFO *pei,
257
/* [unique][in] */ IServiceProvider *pspCaller)
262
zval ***params = NULL;
263
HRESULT ret = DISP_E_MEMBERNOTFOUND;
266
FETCH_DISP("InvokeEx");
268
if (SUCCESS == zend_hash_index_find(disp->dispid_to_name, id, (void**)&name)) {
269
/* TODO: add support for overloaded objects */
271
trace("-- Invoke: %d %20s flags=%08x args=%d\n", id, Z_STRVAL_PP(name), wFlags, pdp->cArgs);
273
/* convert args into zvals.
274
* Args are in reverse order */
275
params = (zval ***)safe_emalloc(sizeof(zval **), pdp->cArgs, 0);
276
for (i = 0; i < pdp->cArgs; i++) {
280
arg = &pdp->rgvarg[ pdp->cArgs - 1 - i];
282
trace("alloc zval for arg %d VT=%08x\n", i, V_VT(arg));
284
ALLOC_INIT_ZVAL(zarg);
285
php_com_wrap_variant(zarg, arg, COMG(code_page) TSRMLS_CC);
289
trace("arguments processed, prepare to do some work\n");
291
/* TODO: if PHP raises an exception here, we should catch it
292
* and expose it as a COM exception */
294
if (wFlags & DISPATCH_PROPERTYGET) {
295
retval = zend_read_property(Z_OBJCE_P(disp->object), disp->object, Z_STRVAL_PP(name), Z_STRLEN_PP(name)+1, 1 TSRMLS_CC);
296
} else if (wFlags & DISPATCH_PROPERTYPUT) {
297
zend_update_property(Z_OBJCE_P(disp->object), disp->object, Z_STRVAL_PP(name), Z_STRLEN_PP(name)+1, *params[0] TSRMLS_CC);
298
} else if (wFlags & DISPATCH_METHOD) {
300
if (SUCCESS == call_user_function_ex(EG(function_table), &disp->object, *name,
301
&retval, pdp->cArgs, params, 1, NULL TSRMLS_CC)) {
304
ret = DISP_E_EXCEPTION;
307
ret = DISP_E_EXCEPTION;
310
trace("Don't know how to handle this invocation %08x\n", wFlags);
313
/* release arguments */
314
for (i = 0; i < pdp->cArgs; i++)
315
zval_ptr_dtor(params[i]);
321
VariantInit(pvarRes);
322
php_com_variant_from_zval(pvarRes, retval, COMG(code_page) TSRMLS_CC);
324
zval_ptr_dtor(&retval);
325
} else if (pvarRes) {
326
VariantInit(pvarRes);
330
trace("InvokeEx: I don't support DISPID=%d\n", id);
336
static HRESULT STDMETHODCALLTYPE disp_deletememberbyname(
338
/* [in] */ BSTR bstrName,
339
/* [in] */ DWORD grfdex)
343
FETCH_DISP("DeleteMemberByName");
350
static HRESULT STDMETHODCALLTYPE disp_deletememberbydispid(
352
/* [in] */ DISPID id)
356
FETCH_DISP("DeleteMemberByDispID");
363
static HRESULT STDMETHODCALLTYPE disp_getmemberproperties(
365
/* [in] */ DISPID id,
366
/* [in] */ DWORD grfdexFetch,
367
/* [out] */ DWORD *pgrfdex)
371
FETCH_DISP("GetMemberProperties");
373
return DISP_E_UNKNOWNNAME;
376
static HRESULT STDMETHODCALLTYPE disp_getmembername(
378
/* [in] */ DISPID id,
379
/* [out] */ BSTR *pbstrName)
384
FETCH_DISP("GetMemberName");
386
if (SUCCESS == zend_hash_index_find(disp->dispid_to_name, id, (void**)&name)) {
387
OLECHAR *olestr = php_com_string_to_olestring(Z_STRVAL_P(name), Z_STRLEN_P(name), COMG(code_page) TSRMLS_CC);
388
*pbstrName = SysAllocString(olestr);
392
return DISP_E_UNKNOWNNAME;
396
static HRESULT STDMETHODCALLTYPE disp_getnextdispid(
398
/* [in] */ DWORD grfdex,
399
/* [in] */ DISPID id,
400
/* [out] */ DISPID *pid)
405
FETCH_DISP("GetNextDispID");
407
while(!zend_hash_index_exists(disp->dispid_to_name, next))
410
if (zend_hash_index_exists(disp->dispid_to_name, next)) {
417
static HRESULT STDMETHODCALLTYPE disp_getnamespaceparent(
419
/* [out] */ IUnknown **ppunk)
423
FETCH_DISP("GetNameSpaceParent");
429
static struct IDispatchExVtbl php_dispatch_vtbl = {
433
disp_gettypeinfocount,
439
disp_deletememberbyname,
440
disp_deletememberbydispid,
441
disp_getmemberproperties,
444
disp_getnamespaceparent
448
/* enumerate functions and properties of the object and assign
450
static void generate_dispids(php_dispatchex *disp TSRMLS_DC)
459
if (disp->dispid_to_name == NULL) {
460
ALLOC_HASHTABLE(disp->dispid_to_name);
461
ALLOC_HASHTABLE(disp->name_to_dispid);
462
zend_hash_init(disp->name_to_dispid, 0, NULL, ZVAL_PTR_DTOR, 0);
463
zend_hash_init(disp->dispid_to_name, 0, NULL, ZVAL_PTR_DTOR, 0);
467
if (Z_OBJPROP_P(disp->object)) {
468
zend_hash_internal_pointer_reset_ex(Z_OBJPROP_P(disp->object), &pos);
469
while (HASH_KEY_NON_EXISTANT != (keytype =
470
zend_hash_get_current_key_ex(Z_OBJPROP_P(disp->object), &name,
471
&namelen, &pid, 0, &pos))) {
473
if (keytype == HASH_KEY_IS_LONG) {
474
sprintf(namebuf, "%d", pid);
476
namelen = strlen(namebuf);
479
zend_hash_move_forward_ex(Z_OBJPROP_P(disp->object), &pos);
481
/* Find the existing id */
482
if (zend_hash_find(disp->name_to_dispid, name, namelen+1, (void**)&tmp) == SUCCESS)
485
/* add the mappings */
487
ZVAL_STRINGL(tmp, name, namelen, 1);
488
zend_hash_index_update(disp->dispid_to_name, pid, (void*)&tmp, sizeof(zval *), NULL);
492
zend_hash_update(disp->name_to_dispid, name, namelen+1, (void*)&tmp, sizeof(zval *), NULL);
497
if (Z_OBJCE_P(disp->object)) {
498
zend_hash_internal_pointer_reset_ex(&Z_OBJCE_P(disp->object)->function_table, &pos);
499
while (HASH_KEY_NON_EXISTANT != (keytype =
500
zend_hash_get_current_key_ex(&Z_OBJCE_P(disp->object)->function_table,
501
&name, &namelen, &pid, 0, &pos))) {
504
if (keytype == HASH_KEY_IS_LONG) {
505
sprintf(namebuf, "%d", pid);
507
namelen = strlen(namebuf);
510
zend_hash_move_forward_ex(Z_OBJPROP_P(disp->object), &pos);
512
/* Find the existing id */
513
if (zend_hash_find(disp->name_to_dispid, name, namelen+1, (void**)&tmp) == SUCCESS)
516
/* add the mappings */
518
ZVAL_STRINGL(tmp, name, namelen, 1);
519
zend_hash_index_update(disp->dispid_to_name, pid, (void*)&tmp, sizeof(zval *), NULL);
523
zend_hash_update(disp->name_to_dispid, name, namelen+1, (void*)&tmp, sizeof(zval *), NULL);
528
static php_dispatchex *disp_constructor(zval *object TSRMLS_DC)
530
php_dispatchex *disp = (php_dispatchex*)CoTaskMemAlloc(sizeof(php_dispatchex));
532
trace("constructing a COM proxy\n");
537
memset(disp, 0, sizeof(php_dispatchex));
539
disp->engine_thread = tsrm_thread_id();
540
disp->lpVtbl = &php_dispatch_vtbl;
546
disp->object = object;
548
disp->id = zend_list_insert(disp, le_dispatch);
553
static void disp_destructor(php_dispatchex *disp)
557
trace("destroying COM wrapper for PHP object %s\n", Z_OBJCE_P(disp->object)->name);
561
if (disp->refcount > 0)
562
CoDisconnectObject((IUnknown*)disp, 0);
564
zend_hash_destroy(disp->dispid_to_name);
565
zend_hash_destroy(disp->name_to_dispid);
566
FREE_HASHTABLE(disp->dispid_to_name);
567
FREE_HASHTABLE(disp->name_to_dispid);
570
zval_ptr_dtor(&disp->object);
575
PHPAPI IDispatch *php_com_wrapper_export_as_sink(zval *val, GUID *sinkid,
576
HashTable *id_to_name TSRMLS_DC)
578
php_dispatchex *disp = disp_constructor(val TSRMLS_CC);
586
disp->dispid_to_name = id_to_name;
588
memcpy(&disp->sinkid, sinkid, sizeof(disp->sinkid));
590
/* build up the reverse mapping */
591
ALLOC_HASHTABLE(disp->name_to_dispid);
592
zend_hash_init(disp->name_to_dispid, 0, NULL, ZVAL_PTR_DTOR, 0);
594
zend_hash_internal_pointer_reset_ex(id_to_name, &pos);
595
while (HASH_KEY_NON_EXISTANT != (keytype =
596
zend_hash_get_current_key_ex(id_to_name, &name, &namelen, &pid, 0, &pos))) {
598
if (keytype == HASH_KEY_IS_LONG) {
600
zend_hash_get_current_data_ex(id_to_name, (void**)&ntmp, &pos);
604
zend_hash_update(disp->name_to_dispid, Z_STRVAL_PP(ntmp),
605
Z_STRLEN_PP(ntmp)+1, (void*)&tmp, sizeof(zval *), NULL);
608
zend_hash_move_forward_ex(id_to_name, &pos);
611
return (IDispatch*)disp;
614
PHPAPI IDispatch *php_com_wrapper_export(zval *val TSRMLS_DC)
616
php_dispatchex *disp = NULL;
618
if (Z_TYPE_P(val) != IS_OBJECT)
621
if (php_com_is_valid_object(val TSRMLS_CC)) {
622
/* pass back its IDispatch directly */
623
php_com_dotnet_object *obj = CDNO_FETCH(val);
628
if (V_VT(&obj->v) == VT_DISPATCH && V_DISPATCH(&obj->v)) {
629
IDispatch_AddRef(V_DISPATCH(&obj->v));
630
return V_DISPATCH(&obj->v);
636
disp = disp_constructor(val TSRMLS_CC);
637
generate_dispids(disp TSRMLS_CC);
639
return (IDispatch*)disp;