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