xref: /PHP-7.3/ext/com_dotnet/com_handlers.c (revision 45a77232)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2018 The PHP Group                                |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.01 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_01.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    +----------------------------------------------------------------------+
17  */
18 
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22 
23 #include "php.h"
24 #include "php_ini.h"
25 #include "ext/standard/info.h"
26 #include "php_com_dotnet.h"
27 #include "php_com_dotnet_internal.h"
28 #include "Zend/zend_exceptions.h"
29 
com_property_read(zval * object,zval * member,int type,void ** cahce_slot,zval * rv)30 static zval *com_property_read(zval *object, zval *member, int type, void **cahce_slot, zval *rv)
31 {
32 	php_com_dotnet_object *obj;
33 	VARIANT v;
34 	HRESULT res;
35 
36 	ZVAL_NULL(rv);
37 
38 	obj = CDNO_FETCH(object);
39 
40 	if (V_VT(&obj->v) == VT_DISPATCH) {
41 		VariantInit(&v);
42 
43 		convert_to_string_ex(member);
44 
45 		res = php_com_do_invoke(obj, Z_STRVAL_P(member), Z_STRLEN_P(member),
46 				DISPATCH_METHOD|DISPATCH_PROPERTYGET, &v, 0, NULL, 1);
47 
48 		if (res == SUCCESS) {
49 			php_com_zval_from_variant(rv, &v, obj->code_page);
50 			VariantClear(&v);
51 		} else if (res == DISP_E_BADPARAMCOUNT) {
52 			php_com_saproxy_create(object, rv, member);
53 		}
54 	} else {
55 		php_com_throw_exception(E_INVALIDARG, "this variant has no properties");
56 	}
57 
58 	return rv;
59 }
60 
com_property_write(zval * object,zval * member,zval * value,void ** cache_slot)61 static void com_property_write(zval *object, zval *member, zval *value, void **cache_slot)
62 {
63 	php_com_dotnet_object *obj;
64 	VARIANT v;
65 
66 	obj = CDNO_FETCH(object);
67 
68 	if (V_VT(&obj->v) == VT_DISPATCH) {
69 		VariantInit(&v);
70 
71 		convert_to_string_ex(member);
72 		if (SUCCESS == php_com_do_invoke(obj, Z_STRVAL_P(member), Z_STRLEN_P(member),
73 				DISPATCH_PROPERTYPUT|DISPATCH_PROPERTYPUTREF, &v, 1, value, 0)) {
74 			VariantClear(&v);
75 		}
76 	} else {
77 		php_com_throw_exception(E_INVALIDARG, "this variant has no properties");
78 	}
79 }
80 
com_read_dimension(zval * object,zval * offset,int type,zval * rv)81 static zval *com_read_dimension(zval *object, zval *offset, int type, zval *rv)
82 {
83 	php_com_dotnet_object *obj;
84 	VARIANT v;
85 
86 	ZVAL_NULL(rv);
87 
88 	obj = CDNO_FETCH(object);
89 
90 	if (V_VT(&obj->v) == VT_DISPATCH) {
91 		VariantInit(&v);
92 
93 		if (SUCCESS == php_com_do_invoke_by_id(obj, DISPID_VALUE,
94 				DISPATCH_METHOD|DISPATCH_PROPERTYGET, &v, 1, offset, 0, 0)) {
95 			php_com_zval_from_variant(rv, &v, obj->code_page);
96 			VariantClear(&v);
97 		}
98 	} else if (V_ISARRAY(&obj->v)) {
99 		convert_to_long(offset);
100 
101 		if (SafeArrayGetDim(V_ARRAY(&obj->v)) == 1) {
102 			if (php_com_safearray_get_elem(&obj->v, &v, (LONG)Z_LVAL_P(offset))) {
103 				php_com_wrap_variant(rv, &v, obj->code_page);
104 				VariantClear(&v);
105 			}
106 		} else {
107 			php_com_saproxy_create(object, rv, offset);
108 		}
109 
110 	} else {
111 		php_com_throw_exception(E_INVALIDARG, "this variant is not an array type");
112 	}
113 
114 	return rv;
115 }
116 
com_write_dimension(zval * object,zval * offset,zval * value)117 static void com_write_dimension(zval *object, zval *offset, zval *value)
118 {
119 	php_com_dotnet_object *obj;
120 	zval args[2];
121 	VARIANT v;
122 	HRESULT res;
123 
124 	obj = CDNO_FETCH(object);
125 
126 	if (offset == NULL) {
127 		php_com_throw_exception(DISP_E_BADINDEX, "appending to variants is not supported");
128 		return;
129 	}
130 
131 	if (V_VT(&obj->v) == VT_DISPATCH) {
132 		ZVAL_COPY_VALUE(&args[0], offset);
133 		ZVAL_COPY_VALUE(&args[1], value);
134 
135 		VariantInit(&v);
136 
137 		if (SUCCESS == php_com_do_invoke_by_id(obj, DISPID_VALUE,
138 				DISPATCH_METHOD|DISPATCH_PROPERTYPUT, &v, 2, args, 0, 0)) {
139 			VariantClear(&v);
140 		}
141 	} else if (V_ISARRAY(&obj->v)) {
142 		LONG indices = 0;
143 		VARTYPE vt;
144 
145 		if (SafeArrayGetDim(V_ARRAY(&obj->v)) == 1) {
146 			if (FAILED(SafeArrayGetVartype(V_ARRAY(&obj->v), &vt)) || vt == VT_EMPTY) {
147 				vt = V_VT(&obj->v) & ~VT_ARRAY;
148 			}
149 
150 			convert_to_long(offset);
151 			indices = (LONG)Z_LVAL_P(offset);
152 
153 			VariantInit(&v);
154 			php_com_variant_from_zval(&v, value, obj->code_page);
155 
156 			if (V_VT(&v) != vt) {
157 				VariantChangeType(&v, &v, 0, vt);
158 			}
159 
160 			if (vt == VT_VARIANT) {
161 				res = SafeArrayPutElement(V_ARRAY(&obj->v), &indices, &v);
162 			} else {
163 				res = SafeArrayPutElement(V_ARRAY(&obj->v), &indices, &v.lVal);
164 			}
165 
166 			VariantClear(&v);
167 
168 			if (FAILED(res)) {
169 				php_com_throw_exception(res, NULL);
170 			}
171 
172 		} else {
173 			php_com_throw_exception(DISP_E_BADINDEX, "this variant has multiple dimensions; you can't set a new value without specifying *all* dimensions");
174 		}
175 
176 	} else {
177 		php_com_throw_exception(E_INVALIDARG, "this variant is not an array type");
178 	}
179 }
180 
181 #if 0
182 static void com_object_set(zval **property, zval *value)
183 {
184 	/* Not yet implemented in the engine */
185 }
186 
187 static zval *com_object_get(zval *property)
188 {
189 	/* Not yet implemented in the engine */
190 	return NULL;
191 }
192 #endif
193 
com_property_exists(zval * object,zval * member,int check_empty,void ** cache_slot)194 static int com_property_exists(zval *object, zval *member, int check_empty, void **cache_slot)
195 {
196 	DISPID dispid;
197 	php_com_dotnet_object *obj;
198 
199 	obj = CDNO_FETCH(object);
200 
201 	if (V_VT(&obj->v) == VT_DISPATCH) {
202 		convert_to_string_ex(member);
203 		if (SUCCEEDED(php_com_get_id_of_name(obj, Z_STRVAL_P(member), Z_STRLEN_P(member), &dispid))) {
204 			/* TODO: distinguish between property and method! */
205 			return 1;
206 		}
207 	} else {
208 		/* TODO: check for safearray */
209 	}
210 
211 	return 0;
212 }
213 
com_dimension_exists(zval * object,zval * member,int check_empty)214 static int com_dimension_exists(zval *object, zval *member, int check_empty)
215 {
216 	php_error_docref(NULL, E_WARNING, "Operation not yet supported on a COM object");
217 	return 0;
218 }
219 
com_property_delete(zval * object,zval * member,void ** cache_slot)220 static void com_property_delete(zval *object, zval *member, void **cache_slot)
221 {
222 	php_error_docref(NULL, E_WARNING, "Cannot delete properties from a COM object");
223 }
224 
com_dimension_delete(zval * object,zval * offset)225 static void com_dimension_delete(zval *object, zval *offset)
226 {
227 	php_error_docref(NULL, E_WARNING, "Cannot delete properties from a COM object");
228 }
229 
com_properties_get(zval * object)230 static HashTable *com_properties_get(zval *object)
231 {
232 	/* TODO: use type-info to get all the names and values ?
233 	 * DANGER: if we do that, there is a strong possibility for
234 	 * infinite recursion when the hash is displayed via var_dump().
235 	 * Perhaps it is best to leave it un-implemented.
236 	 */
237 	return &zend_empty_array;
238 }
239 
function_dtor(zval * zv)240 static void function_dtor(zval *zv)
241 {
242 	zend_internal_function *f = (zend_internal_function*)Z_PTR_P(zv);
243 
244 	zend_string_release_ex(f->function_name, 0);
245 	if (f->arg_info) {
246 		efree(f->arg_info);
247 	}
248 	efree(f);
249 }
250 
PHP_FUNCTION(com_method_handler)251 static PHP_FUNCTION(com_method_handler)
252 {
253 	zval *object = getThis();
254 
255 	Z_OBJ_HANDLER_P(object, call_method)(
256 			((zend_internal_function*)EX(func))->function_name,
257 			Z_OBJ_P(object),
258 			INTERNAL_FUNCTION_PARAM_PASSTHRU);
259 }
260 
com_method_get(zend_object ** object_ptr,zend_string * name,const zval * key)261 static union _zend_function *com_method_get(zend_object **object_ptr, zend_string *name, const zval *key)
262 {
263 	zend_internal_function f, *fptr = NULL;
264 	union _zend_function *func;
265 	DISPID dummy;
266 	php_com_dotnet_object *obj = (php_com_dotnet_object*)*object_ptr;
267 
268 	if (V_VT(&obj->v) != VT_DISPATCH) {
269 		return NULL;
270 	}
271 
272 	if (FAILED(php_com_get_id_of_name(obj, name->val, name->len, &dummy))) {
273 		return NULL;
274 	}
275 
276 	/* check cache */
277 	if (obj->method_cache == NULL || NULL == (fptr = zend_hash_find_ptr(obj->method_cache, name))) {
278 		f.type = ZEND_OVERLOADED_FUNCTION;
279 		f.num_args = 0;
280 		f.arg_info = NULL;
281 		f.scope = obj->ce;
282 		f.fn_flags = ZEND_ACC_CALL_VIA_HANDLER;
283 		f.function_name = zend_string_copy(name);
284 		f.handler = PHP_FN(com_method_handler);
285 
286 		fptr = &f;
287 
288 		if (obj->typeinfo) {
289 			/* look for byref params */
290 			ITypeComp *comp;
291 			ITypeInfo *TI = NULL;
292 			DESCKIND kind;
293 			BINDPTR bindptr;
294 			OLECHAR *olename;
295 			ULONG lhash;
296 			int i;
297 
298 			if (SUCCEEDED(ITypeInfo_GetTypeComp(obj->typeinfo, &comp))) {
299 				olename = php_com_string_to_olestring(name->val, name->len, obj->code_page);
300 				lhash = LHashValOfNameSys(SYS_WIN32, LOCALE_SYSTEM_DEFAULT, olename);
301 
302 				if (SUCCEEDED(ITypeComp_Bind(comp, olename, lhash, INVOKE_FUNC, &TI, &kind, &bindptr))) {
303 					switch (kind) {
304 						case DESCKIND_FUNCDESC:
305 							f.arg_info = ecalloc(bindptr.lpfuncdesc->cParams, sizeof(zend_arg_info));
306 
307 							for (i = 0; i < bindptr.lpfuncdesc->cParams; i++) {
308 								f.arg_info[i].type = ZEND_TYPE_ENCODE(0,1);
309 								if (bindptr.lpfuncdesc->lprgelemdescParam[i].paramdesc.wParamFlags & PARAMFLAG_FOUT) {
310 									f.arg_info[i].pass_by_reference = ZEND_SEND_BY_REF;
311 								}
312 							}
313 
314 							f.num_args = bindptr.lpfuncdesc->cParams;
315 
316 							ITypeInfo_ReleaseFuncDesc(TI, bindptr.lpfuncdesc);
317 							break;
318 
319 							/* these should not happen, but *might* happen if the user
320 							 * screws up; lets avoid a leak in that case */
321 						case DESCKIND_VARDESC:
322 							ITypeInfo_ReleaseVarDesc(TI, bindptr.lpvardesc);
323 							break;
324 						case DESCKIND_TYPECOMP:
325 							ITypeComp_Release(bindptr.lptcomp);
326 							break;
327 
328 						case DESCKIND_NONE:
329 							break;
330 					}
331 					if (TI) {
332 						ITypeInfo_Release(TI);
333 					}
334 				}
335 				ITypeComp_Release(comp);
336 				efree(olename);
337 			}
338 		}
339 
340 		zend_set_function_arg_flags((zend_function*)&f);
341 		/* save this method in the cache */
342 		if (!obj->method_cache) {
343 			ALLOC_HASHTABLE(obj->method_cache);
344 			zend_hash_init(obj->method_cache, 2, NULL, function_dtor, 0);
345 		}
346 
347 		zend_hash_update_mem(obj->method_cache, name, &f, sizeof(f));
348 	}
349 
350 	if (fptr) {
351 		/* duplicate this into a new chunk of emalloc'd memory,
352 		 * since the engine will efree it */
353 		func = emalloc(sizeof(*fptr));
354 		memcpy(func, fptr, sizeof(*fptr));
355 
356 		return func;
357 	}
358 
359 	return NULL;
360 }
361 
com_call_method(zend_string * method,zend_object * object,INTERNAL_FUNCTION_PARAMETERS)362 static int com_call_method(zend_string *method, zend_object *object, INTERNAL_FUNCTION_PARAMETERS)
363 {
364 	zval *args = NULL;
365 	php_com_dotnet_object *obj = (php_com_dotnet_object*)object;
366 	int nargs;
367 	VARIANT v;
368 	int ret = FAILURE;
369 
370 	if (V_VT(&obj->v) != VT_DISPATCH) {
371 		return FAILURE;
372 	}
373 
374 	nargs = ZEND_NUM_ARGS();
375 
376 	if (nargs) {
377 		args = (zval *)safe_emalloc(sizeof(zval), nargs, 0);
378 		zend_get_parameters_array_ex(nargs, args);
379 	}
380 
381 	VariantInit(&v);
382 
383 	if (SUCCESS == php_com_do_invoke_byref(obj, (zend_internal_function*)EX(func), DISPATCH_METHOD|DISPATCH_PROPERTYGET, &v, nargs, args)) {
384 		php_com_zval_from_variant(return_value, &v, obj->code_page);
385 		ret = SUCCESS;
386 		VariantClear(&v);
387 	}
388 
389 	if (args) {
390 		efree(args);
391 	}
392 
393 	return ret;
394 }
395 
com_constructor_get(zend_object * object)396 static union _zend_function *com_constructor_get(zend_object *object)
397 {
398 	php_com_dotnet_object *obj = (php_com_dotnet_object *) object;
399 	static zend_internal_function c, d, v;
400 
401 #define POPULATE_CTOR(f, fn)	\
402 	f.type = ZEND_INTERNAL_FUNCTION; \
403 	f.function_name = obj->ce->name; \
404 	f.scope = obj->ce; \
405 	f.arg_info = NULL; \
406 	f.num_args = 0; \
407 	f.fn_flags = 0; \
408 	f.handler = ZEND_FN(fn); \
409 	return (union _zend_function*)&f;
410 
411 	switch (obj->ce->name->val[0]) {
412 #if HAVE_MSCOREE_H
413 		case 'd':
414 			POPULATE_CTOR(d, com_dotnet_create_instance);
415 #endif
416 
417 		case 'c':
418 			POPULATE_CTOR(c, com_create_instance);
419 
420 		case 'v':
421 			POPULATE_CTOR(v, com_variant_create_instance);
422 
423 		default:
424 			return NULL;
425 	}
426 }
427 
com_class_name_get(const zend_object * object)428 static zend_string* com_class_name_get(const zend_object *object)
429 {
430 	php_com_dotnet_object *obj = (php_com_dotnet_object *)object;
431 
432 	return zend_string_copy(obj->ce->name);
433 }
434 
435 /* This compares two variants for equality */
com_objects_compare(zval * object1,zval * object2)436 static int com_objects_compare(zval *object1, zval *object2)
437 {
438 	php_com_dotnet_object *obja, *objb;
439 	int ret;
440 	/* strange header bug problem here... the headers define the proto without the
441 	 * flags parameter.  However, the MSDN docs state that there is a flags parameter,
442 	 * and my VC6 won't link unless the code uses the version with 4 parameters.
443 	 * So, we have this declaration here to fix it */
444 	STDAPI VarCmp(LPVARIANT pvarLeft, LPVARIANT pvarRight, LCID lcid, DWORD flags);
445 
446 	obja = CDNO_FETCH(object1);
447 	objb = CDNO_FETCH(object2);
448 
449 	switch (VarCmp(&obja->v, &objb->v, LOCALE_SYSTEM_DEFAULT, 0)) {
450 		case VARCMP_LT:
451 			ret = -1;
452 			break;
453 		case VARCMP_GT:
454 			ret = 1;
455 			break;
456 		case VARCMP_EQ:
457 			ret = 0;
458 			break;
459 		default:
460 			/* either or both operands are NULL...
461 			 * not 100% sure how to handle this */
462 			ret = -2;
463 	}
464 
465 	return ret;
466 }
467 
com_object_cast(zval * readobj,zval * writeobj,int type)468 static int com_object_cast(zval *readobj, zval *writeobj, int type)
469 {
470 	php_com_dotnet_object *obj;
471 	VARIANT v;
472 	VARTYPE vt = VT_EMPTY;
473 	HRESULT res = S_OK;
474 
475 	obj = CDNO_FETCH(readobj);
476 	ZVAL_NULL(writeobj);
477 	VariantInit(&v);
478 
479 	if (V_VT(&obj->v) == VT_DISPATCH) {
480 		if (SUCCESS != php_com_do_invoke_by_id(obj, DISPID_VALUE,
481 				DISPATCH_METHOD|DISPATCH_PROPERTYGET, &v, 0, NULL, 1, 0)) {
482 			VariantCopy(&v, &obj->v);
483 		}
484 	} else {
485 		VariantCopy(&v, &obj->v);
486 	}
487 
488 	switch(type) {
489 		case IS_LONG:
490 		case _IS_NUMBER:
491 			vt = VT_INT;
492 			break;
493 		case IS_DOUBLE:
494 			vt = VT_R8;
495 			break;
496 		case IS_FALSE:
497 		case IS_TRUE:
498 		case _IS_BOOL:
499 			vt = VT_BOOL;
500 			break;
501 		case IS_STRING:
502 			vt = VT_BSTR;
503 			break;
504 		default:
505 			;
506 	}
507 
508 	if (vt != VT_EMPTY && vt != V_VT(&v)) {
509 		res = VariantChangeType(&v, &v, 0, vt);
510 	}
511 
512 	if (SUCCEEDED(res)) {
513 		php_com_zval_from_variant(writeobj, &v, obj->code_page);
514 	}
515 
516 	VariantClear(&v);
517 
518 	if (SUCCEEDED(res)) {
519 		return SUCCESS;
520 	}
521 
522 	return zend_std_cast_object_tostring(readobj, writeobj, type);
523 }
524 
com_object_count(zval * object,zend_long * count)525 static int com_object_count(zval *object, zend_long *count)
526 {
527 	php_com_dotnet_object *obj;
528 	LONG ubound = 0, lbound = 0;
529 
530 	obj = CDNO_FETCH(object);
531 
532 	if (!V_ISARRAY(&obj->v)) {
533 		return FAILURE;
534 	}
535 
536 	SafeArrayGetLBound(V_ARRAY(&obj->v), 1, &lbound);
537 	SafeArrayGetUBound(V_ARRAY(&obj->v), 1, &ubound);
538 
539 	*count = ubound - lbound + 1;
540 
541 	return SUCCESS;
542 }
543 
544 zend_object_handlers php_com_object_handlers = {
545 	0,
546 	php_com_object_free_storage,
547 	zend_objects_destroy_object,
548 	php_com_object_clone,
549 	com_property_read,
550 	com_property_write,
551 	com_read_dimension,
552 	com_write_dimension,
553 	NULL,
554 	NULL, /* com_object_get, */
555 	NULL, /* com_object_set, */
556 	com_property_exists,
557 	com_property_delete,
558 	com_dimension_exists,
559 	com_dimension_delete,
560 	com_properties_get,
561 	com_method_get,
562 	com_call_method,
563 	com_constructor_get,
564 	com_class_name_get,
565 	com_objects_compare,
566 	com_object_cast,
567 	com_object_count,
568 	NULL,									/* get_debug_info */
569 	NULL,									/* get_closure */
570 	NULL,									/* get_gc */
571 };
572 
php_com_object_enable_event_sink(php_com_dotnet_object * obj,int enable)573 void php_com_object_enable_event_sink(php_com_dotnet_object *obj, int enable)
574 {
575 	if (obj->sink_dispatch) {
576 		IConnectionPointContainer *cont;
577 		IConnectionPoint *point;
578 
579 		if (SUCCEEDED(IDispatch_QueryInterface(V_DISPATCH(&obj->v),
580 				&IID_IConnectionPointContainer, (void**)&cont))) {
581 
582 			if (SUCCEEDED(IConnectionPointContainer_FindConnectionPoint(cont,
583 					&obj->sink_id, &point))) {
584 
585 				if (enable) {
586 					IConnectionPoint_Advise(point, (IUnknown*)obj->sink_dispatch, &obj->sink_cookie);
587 				} else {
588 					IConnectionPoint_Unadvise(point, obj->sink_cookie);
589 				}
590 				IConnectionPoint_Release(point);
591 			}
592 			IConnectionPointContainer_Release(cont);
593 		}
594 	}
595 }
596 
php_com_object_free_storage(zend_object * object)597 void php_com_object_free_storage(zend_object *object)
598 {
599 	php_com_dotnet_object *obj = (php_com_dotnet_object*)object;
600 
601 	if (obj->typeinfo) {
602 		ITypeInfo_Release(obj->typeinfo);
603 		obj->typeinfo = NULL;
604 	}
605 
606 	if (obj->sink_dispatch) {
607 		php_com_object_enable_event_sink(obj, FALSE);
608 		IDispatch_Release(obj->sink_dispatch);
609 		obj->sink_dispatch = NULL;
610 	}
611 
612 	VariantClear(&obj->v);
613 
614 	if (obj->method_cache) {
615 		zend_hash_destroy(obj->method_cache);
616 		FREE_HASHTABLE(obj->method_cache);
617 	}
618 	if (obj->id_of_name_cache) {
619 		zend_hash_destroy(obj->id_of_name_cache);
620 		FREE_HASHTABLE(obj->id_of_name_cache);
621 	}
622 }
623 
php_com_object_clone(zval * object)624 zend_object* php_com_object_clone(zval *object)
625 {
626 	php_com_dotnet_object *cloneobj, *origobject;
627 
628 	origobject = (php_com_dotnet_object*)Z_OBJ_P(object);
629 	cloneobj = (php_com_dotnet_object*)emalloc(sizeof(php_com_dotnet_object));
630 
631 	memcpy(cloneobj, origobject, sizeof(*cloneobj));
632 
633 	/* VariantCopy will perform VariantClear; we don't want to clobber
634 	 * the IDispatch that we memcpy'd, so we init a new variant in the
635 	 * clone structure */
636 	VariantInit(&cloneobj->v);
637 	/* We use the Indirection-following version of the API since we
638 	 * want to clone as much as possible */
639 	VariantCopyInd(&cloneobj->v, &origobject->v);
640 
641 	if (cloneobj->typeinfo) {
642 		ITypeInfo_AddRef(cloneobj->typeinfo);
643 	}
644 
645 	return (zend_object*)cloneobj;
646 }
647 
php_com_object_new(zend_class_entry * ce)648 zend_object* php_com_object_new(zend_class_entry *ce)
649 {
650 	php_com_dotnet_object *obj;
651 
652 	php_com_initialize();
653 	obj = emalloc(sizeof(*obj));
654 	memset(obj, 0, sizeof(*obj));
655 
656 	VariantInit(&obj->v);
657 	obj->code_page = CP_ACP;
658 	obj->ce = ce;
659 
660 	zend_object_std_init(&obj->zo, ce);
661 	obj->zo.handlers = &php_com_object_handlers;
662 
663 	obj->typeinfo = NULL;
664 
665 	return (zend_object*)obj;
666 }
667