Port other major parts of PHP 4 COM extension into PHP 5 com_dotnet

extension.
This enables:
- iteration of SafeArray types via foreach()
- proxying of multi-dimensional SafeArray types so that multi-dimension
  array accesses work (untested!)
- Fix COM exceptions, and expose them as their own class of exception
  "com_exception"
- auto typelib file import (com.typelib_file ini option)
- event sinking
- wrapper to map PHP objects to COM
- fix mapping of variant values to PHP values

# Could someone please add com_saproxy.c and com_wrapper.c to the .dsp
# file?
This commit is contained in:
Wez Furlong 2004-01-07 21:00:07 +00:00
parent 48b96c10d2
commit e10c206dac
14 changed files with 1957 additions and 159 deletions

View file

@ -1,6 +1,6 @@
/*
+----------------------------------------------------------------------+
| PHP Version 4 |
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2003 The PHP Group |
+----------------------------------------------------------------------+
@ -64,7 +64,7 @@ PHP_FUNCTION(com_create_instance)
&module_name, &module_name_len, &server_params, &obj->code_page,
&typelib_name, &typelib_name_len)) {
php_com_throw_exception("Could not create COM object - invalid arguments!" TSRMLS_CC);
php_com_throw_exception(E_INVALIDARG, "Could not create COM object - invalid arguments!" TSRMLS_CC);
ZVAL_NULL(object);
return;
}
@ -113,7 +113,7 @@ PHP_FUNCTION(com_create_instance)
}
if (server_name && !COMG(allow_dcom)) {
php_com_throw_exception("DCOM has been disabled by your administrator [com.allow_dcom=0]" TSRMLS_CC);
php_com_throw_exception(E_ERROR, "DCOM has been disabled by your administrator [com.allow_dcom=0]" TSRMLS_CC);
return;
}
@ -227,7 +227,7 @@ PHP_FUNCTION(com_create_instance)
spprintf(&msg, 0, "Failed to create COM object `%s': %s", module_name, werr);
LocalFree(werr);
php_com_throw_exception(msg TSRMLS_CC);
php_com_throw_exception(res, msg TSRMLS_CC);
efree(msg);
ZVAL_NULL(object);
return;
@ -240,7 +240,7 @@ PHP_FUNCTION(com_create_instance)
/* load up the library from the named file */
int cached;
TL = php_com_load_typelib_via_cache(typelib_name, mode, obj->code_page, &cached TSRMLS_CC);
TL = php_com_load_typelib_via_cache(typelib_name, obj->code_page, &cached TSRMLS_CC);
if (TL) {
if (COMG(autoreg_on) && !cached) {
@ -341,7 +341,7 @@ HRESULT php_com_invoke_helper(php_com_dotnet_object *obj, DISPID id_member,
}
if (msg) {
php_com_throw_exception(msg TSRMLS_CC);
php_com_throw_exception(hr, msg TSRMLS_CC);
efree(msg);
}
}
@ -434,7 +434,7 @@ int php_com_do_invoke(php_com_dotnet_object *obj, char *name, int namelen,
winerr = php_win_err(hr);
spprintf(&msg, 0, "Unable to lookup `%s': %s", name, winerr);
LocalFree(winerr);
php_com_throw_exception(msg TSRMLS_CC);
php_com_throw_exception(hr, msg TSRMLS_CC);
efree(msg);
return FAILURE;
}
@ -442,6 +442,8 @@ int php_com_do_invoke(php_com_dotnet_object *obj, char *name, int namelen,
return php_com_do_invoke_by_id(obj, dispid, flags, v, nargs, args TSRMLS_CC);
}
/* {{{ proto string com_create_guid()
Generate a globally unique identifier (GUID) */
PHP_FUNCTION(com_create_guid)
{
GUID retval;
@ -460,4 +462,169 @@ PHP_FUNCTION(com_create_guid)
RETURN_FALSE;
}
}
/* }}} */
/* {{{ proto bool com_event_sink(object comobject, object sinkobject [, mixed sinkinterface])
Connect events from a COM object to a PHP object */
PHP_FUNCTION(com_event_sink)
{
zval *object, *sinkobject, *sink=NULL;
char *dispname = NULL, *typelibname = NULL;
zend_bool gotguid = 0;
php_com_dotnet_object *obj;
ITypeInfo *typeinfo = NULL;
RETVAL_FALSE;
if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Oo|z/",
&object, php_com_variant_class_entry, &sinkobject, &sink)) {
RETURN_FALSE;
}
obj = CDNO_FETCH(object);
if (sink && Z_TYPE_P(sink) == IS_ARRAY) {
/* 0 => typelibname, 1 => dispname */
zval **tmp;
if (zend_hash_index_find(Z_ARRVAL_P(sink), 0, (void**)&tmp) == SUCCESS)
typelibname = Z_STRVAL_PP(tmp);
if (zend_hash_index_find(Z_ARRVAL_P(sink), 1, (void**)&tmp) == SUCCESS)
dispname = Z_STRVAL_PP(tmp);
} else if (sink != NULL) {
convert_to_string(sink);
dispname = Z_STRVAL_P(sink);
}
typeinfo = php_com_locate_typeinfo(typelibname, obj, dispname, 1 TSRMLS_CC);
if (typeinfo) {
HashTable *id_to_name;
ALLOC_HASHTABLE(id_to_name);
if (php_com_process_typeinfo(typeinfo, id_to_name, 0, &obj->sink_id, obj->code_page TSRMLS_CC)) {
/* Create the COM wrapper for this sink */
obj->sink_dispatch = php_com_wrapper_export_as_sink(sinkobject, &obj->sink_id, id_to_name TSRMLS_CC);
/* Now hook it up to the source */
php_com_object_enable_event_sink(obj, TRUE TSRMLS_CC);
RETVAL_TRUE;
} else {
FREE_HASHTABLE(id_to_name);
}
}
if (typeinfo) {
ITypeInfo_Release(typeinfo);
}
}
/* }}} */
/* {{{ proto bool com_print_typeinfo(object comobject | string typelib, string dispinterface, bool wantsink)
Print out a PHP class definition for a dispatchable interface */
PHP_FUNCTION(com_print_typeinfo)
{
zval *arg1;
char *ifacename = NULL;
char *typelibname = NULL;
int ifacelen;
zend_bool wantsink = 0;
php_com_dotnet_object *obj = NULL;
ITypeInfo *typeinfo;
if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/|s!b", &arg1, &ifacename,
&ifacelen, &wantsink)) {
RETURN_FALSE;
}
if (Z_TYPE_P(arg1) == IS_OBJECT) {
CDNO_FETCH_VERIFY(obj, arg1);
} else {
convert_to_string(arg1);
typelibname = Z_STRVAL_P(arg1);
}
typeinfo = php_com_locate_typeinfo(typelibname, obj, ifacename, wantsink ? 1 : 0 TSRMLS_CC);
if (typeinfo) {
php_com_process_typeinfo(typeinfo, NULL, 1, NULL, obj ? obj->code_page : COMG(code_page) TSRMLS_CC);
ITypeInfo_Release(typeinfo);
RETURN_TRUE;
} else {
zend_error(E_WARNING, "Unable to find typeinfo using the parameters supplied");
}
RETURN_FALSE;
}
/* }}} */
/* {{{ proto bool com_message_pump([int timeoutms])
Process COM messages, sleeping for up to timeoutms milliseconds */
PHP_FUNCTION(com_message_pump)
{
long timeoutms = 0;
MSG msg;
DWORD result;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &timeoutms) == FAILURE)
RETURN_FALSE;
result = MsgWaitForMultipleObjects(0, NULL, FALSE, timeoutms, QS_ALLINPUT);
if (result == WAIT_OBJECT_0) {
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
/* we processed messages */
RETVAL_TRUE;
} else {
/* we did not process messages (timed out) */
RETVAL_FALSE;
}
}
/* }}} */
/* {{{ proto bool com_load_typelib(string typelib_name [, int case_insensitive])
Loads a Typelibrary and registers its constants */
PHP_FUNCTION(com_load_typelib)
{
char *name;
long namelen;
ITypeLib *pTL = NULL;
zend_bool cs = TRUE;
int codepage = COMG(code_page);
int cached = 0;
if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", &name, &namelen, &cs)) {
return;
}
RETVAL_FALSE;
pTL = php_com_load_typelib_via_cache(name, codepage, &cached TSRMLS_CC);
if (pTL) {
if (cached) {
RETVAL_TRUE;
} else if (php_com_import_typelib(pTL, cs ? CONST_CS : 0, codepage TSRMLS_CC) == SUCCESS) {
RETVAL_TRUE;
}
ITypeLib_Release(pTL);
pTL = NULL;
}
}
/* }}} */
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
*/

View file

@ -1,6 +1,6 @@
/*
+----------------------------------------------------------------------+
| PHP Version 4 |
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2003 The PHP Group |
+----------------------------------------------------------------------+
@ -109,7 +109,7 @@ PHP_FUNCTION(com_dotnet_create_instance)
if (COMG(dotnet_runtime_stuff) == NULL) {
if (FAILURE == dotnet_init(TSRMLS_C)) {
php_com_throw_exception("Failed to initialize .Net runtime" TSRMLS_CC);
php_com_throw_exception(E_ERROR, "Failed to initialize .Net runtime" TSRMLS_CC);
ZVAL_NULL(object);
return;
}
@ -123,7 +123,7 @@ PHP_FUNCTION(com_dotnet_create_instance)
&assembly_name, &assembly_name_len,
&datatype_name, &datatype_name_len,
&obj->code_page)) {
php_com_throw_exception("Could not create .Net object - invalid arguments!" TSRMLS_CC);
php_com_throw_exception(E_INVALIDARG, "Could not create .Net object - invalid arguments!" TSRMLS_CC);
ZVAL_NULL(object);
return;
}
@ -171,7 +171,7 @@ PHP_FUNCTION(com_dotnet_create_instance)
VariantClear(&vargs[1]);
if (ret == FAILURE) {
php_com_throw_exception("Failed to instantiate .Net object" TSRMLS_CC);
php_com_throw_exception(hr, "Failed to instantiate .Net object" TSRMLS_CC);
ZVAL_NULL(object);
return;
}

View file

@ -1,6 +1,6 @@
/*
+----------------------------------------------------------------------+
| PHP Version 4 |
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2003 The PHP Group |
+----------------------------------------------------------------------+
@ -31,7 +31,10 @@
ZEND_DECLARE_MODULE_GLOBALS(com_dotnet)
TsHashTable php_com_typelibraries;
zend_class_entry *php_com_variant_class_entry;
zend_class_entry
*php_com_variant_class_entry,
*php_com_exception_class_entry,
*php_com_saproxy_class_entry;
function_entry com_dotnet_functions[] = {
PHP_FE(variant_set, NULL)
@ -60,7 +63,12 @@ function_entry com_dotnet_functions[] = {
PHP_FE(variant_get_type, NULL)
PHP_FE(variant_set_type, NULL)
PHP_FE(variant_cast, NULL)
/* com_com.c */
PHP_FE(com_create_guid, NULL)
PHP_FE(com_event_sink, NULL)
PHP_FE(com_print_typeinfo, NULL)
PHP_FE(com_message_pump, NULL)
PHP_FE(com_load_typelib, NULL)
{ NULL, NULL, NULL }
};
@ -86,11 +94,77 @@ ZEND_GET_MODULE(com_dotnet)
/* {{{ PHP_INI
*/
/* com.typelib_file is the path to a file containing a
* list of typelibraries to register *persistently*.
* lines starting with ; are comments
* append #cis to end of typelib name to cause its constants
* to be loaded case insensitively */
static PHP_INI_MH(OnTypeLibFileUpdate)
{
FILE *typelib_file;
char *typelib_name_buffer;
char *strtok_buf = NULL;
int cached;
if (!new_value || (typelib_file = VCWD_FOPEN(new_value, "r"))==NULL) {
return FAILURE;
}
typelib_name_buffer = (char *) emalloc(sizeof(char)*1024);
while (fgets(typelib_name_buffer, 1024, typelib_file)) {
ITypeLib *pTL;
char *typelib_name;
char *modifier, *ptr;
int mode = CONST_CS | CONST_PERSISTENT; /* CONST_PERSISTENT is ok here */
if (typelib_name_buffer[0]==';') {
continue;
}
typelib_name = php_strtok_r(typelib_name_buffer, "\r\n", &strtok_buf); /* get rid of newlines */
if (typelib_name == NULL) {
continue;
}
typelib_name = php_strtok_r(typelib_name, "#", &strtok_buf);
modifier = php_strtok_r(NULL, "#", &strtok_buf);
if (modifier != NULL) {
if (!strcmp(modifier, "cis") || !strcmp(modifier, "case_insensitive")) {
mode &= ~CONST_CS;
}
}
/* Remove leading/training white spaces on search_string */
while (isspace(*typelib_name)) {/* Ends on '\0' in worst case */
typelib_name ++;
}
ptr = typelib_name + strlen(typelib_name) - 1;
while ((ptr != typelib_name) && isspace(*ptr)) {
*ptr = '\0';
ptr--;
}
if ((pTL = php_com_load_typelib_via_cache(typelib_name, COMG(code_page), &cached TSRMLS_CC)) != NULL) {
if (!cached) {
php_com_import_typelib(pTL, mode, COMG(code_page) TSRMLS_CC);
}
ITypeLib_Release(pTL);
}
}
efree(typelib_name_buffer);
fclose(typelib_file);
return SUCCESS;
}
PHP_INI_BEGIN()
STD_PHP_INI_ENTRY("com.allow_dcom", "0", PHP_INI_SYSTEM, OnUpdateBool, allow_dcom, zend_com_dotnet_globals, com_dotnet_globals)
STD_PHP_INI_ENTRY("com.autoregister_verbose", "0", PHP_INI_ALL, OnUpdateBool, autoreg_verbose, zend_com_dotnet_globals, com_dotnet_globals)
STD_PHP_INI_ENTRY("com.autoregister_typelib", "0", PHP_INI_ALL, OnUpdateBool, autoreg_on, zend_com_dotnet_globals, com_dotnet_globals)
STD_PHP_INI_ENTRY("com.autoregister_casesensitive", "0", PHP_INI_ALL, OnUpdateBool, autoreg_case_sensitive, zend_com_dotnet_globals, com_dotnet_globals)
STD_PHP_INI_ENTRY("com.autoregister_casesensitive", "1", PHP_INI_ALL, OnUpdateBool, autoreg_case_sensitive, zend_com_dotnet_globals, com_dotnet_globals)
STD_PHP_INI_ENTRY("com.code_page", "", PHP_INI_ALL, OnUpdateLong, code_page, zend_com_dotnet_globals, com_dotnet_globals)
PHP_INI_ENTRY("com.typelib_file", "", PHP_INI_SYSTEM, OnTypeLibFileUpdate)
PHP_INI_END()
/* }}} */
@ -99,6 +173,7 @@ PHP_INI_END()
static void php_com_dotnet_init_globals(zend_com_dotnet_globals *com_dotnet_globals)
{
memset(com_dotnet_globals, 0, sizeof(*com_dotnet_globals));
com_dotnet_globals->code_page = CP_ACP;
}
/* }}} */
@ -106,28 +181,44 @@ static void php_com_dotnet_init_globals(zend_com_dotnet_globals *com_dotnet_glob
*/
PHP_MINIT_FUNCTION(com_dotnet)
{
zend_class_entry ce;
zend_class_entry ce, *tmp;
ZEND_INIT_MODULE_GLOBALS(com_dotnet, php_com_dotnet_init_globals, NULL);
REGISTER_INI_ENTRIES();
php_com_wrapper_minit(INIT_FUNC_ARGS_PASSTHRU);
INIT_CLASS_ENTRY(ce, "com_exception", NULL);
php_com_exception_class_entry = zend_register_internal_class_ex(&ce, zend_exception_get_default(), NULL TSRMLS_CC);
php_com_exception_class_entry->ce_flags |= ZEND_ACC_FINAL;
php_com_exception_class_entry->constructor->common.fn_flags |= ZEND_ACC_PROTECTED;
INIT_CLASS_ENTRY(ce, "com_safearray_proxy", NULL);
php_com_saproxy_class_entry = zend_register_internal_class(&ce TSRMLS_CC);
php_com_saproxy_class_entry->ce_flags |= ZEND_ACC_FINAL;
// php_com_saproxy_class_entry->constructor->common.fn_flags |= ZEND_ACC_PROTECTED;
php_com_saproxy_class_entry->get_iterator = php_com_saproxy_iter_get;
INIT_CLASS_ENTRY(ce, "variant", NULL);
ce.create_object = php_com_object_new;
ce.get_iterator = php_com_iter_get;
// ce.get_iterator = php_com_iter_get;
php_com_variant_class_entry = zend_register_internal_class(&ce TSRMLS_CC);
php_com_variant_class_entry->get_iterator = php_com_iter_get;
INIT_CLASS_ENTRY(ce, "com", NULL);
ce.create_object = php_com_object_new;
ce.get_iterator = php_com_iter_get;
zend_register_internal_class_ex(&ce, php_com_variant_class_entry, "variant" TSRMLS_CC);
// ce.get_iterator = php_com_iter_get;
tmp = zend_register_internal_class_ex(&ce, php_com_variant_class_entry, "variant" TSRMLS_CC);
tmp->get_iterator = php_com_iter_get;
zend_ts_hash_init(&php_com_typelibraries, 0, NULL, php_com_typelibrary_dtor, 1);
#if HAVE_MSCOREE_H
INIT_CLASS_ENTRY(ce, "dotnet", NULL);
ce.create_object = php_com_object_new;
ce.get_iterator = php_com_iter_get;
zend_register_internal_class_ex(&ce, php_com_variant_class_entry, "variant" TSRMLS_CC);
// ce.get_iterator = php_com_iter_get;
tmp = zend_register_internal_class_ex(&ce, php_com_variant_class_entry, "variant" TSRMLS_CC);
tmp->get_iterator = php_com_iter_get;
#endif
#define COM_CONST(x) REGISTER_LONG_CONSTANT(#x, x, CONST_CS|CONST_PERSISTENT)

View file

@ -1,6 +1,6 @@
/*
+----------------------------------------------------------------------+
| PHP Version 4 |
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2003 The PHP Group |
+----------------------------------------------------------------------+
@ -51,7 +51,7 @@ static zval *com_property_read(zval *object, zval *member, zend_bool silent TSRM
}
} else {
if (!silent) {
php_com_throw_exception("this variant has no properties" TSRMLS_CC);
php_com_throw_exception(E_INVALIDARG, "this variant has no properties" TSRMLS_CC);
}
}
@ -74,7 +74,7 @@ static void com_property_write(zval *object, zval *member, zval *value TSRMLS_DC
VariantClear(&v);
}
} else {
php_com_throw_exception("this variant has no properties" TSRMLS_CC);
php_com_throw_exception(E_INVALIDARG, "this variant has no properties" TSRMLS_CC);
}
}
@ -115,7 +115,7 @@ static zval *com_read_dimension(zval *object, zval *offset TSRMLS_DC)
if (V_VT(&obj->v) == VT_DISPATCH) {
if (!obj->have_default_bind && !com_get_default_binding(obj TSRMLS_CC)) {
php_com_throw_exception("this COM object has no default property" TSRMLS_CC);
php_com_throw_exception(E_INVALIDARG, "this COM object has no default property" TSRMLS_CC);
return return_value;
}
@ -127,49 +127,19 @@ static zval *com_read_dimension(zval *object, zval *offset TSRMLS_DC)
VariantClear(&v);
}
} else if (V_ISARRAY(&obj->v)) {
SAFEARRAY *sa = V_ARRAY(&obj->v);
UINT dims;
VARTYPE vt;
LONG bound_low = 0, bound_high = 0;
LONG indices[1];
dims = SafeArrayGetDim(sa);
if (dims != 1) {
php_com_throw_exception("can only handle single dimension arrays" TSRMLS_CC);
return return_value;
}
if (FAILED(SafeArrayGetVartype(sa, &vt)) || vt == VT_EMPTY) {
vt = V_VT(&obj->v) & ~VT_ARRAY;
}
SafeArrayGetUBound(sa, 1, &bound_high);
SafeArrayGetLBound(sa, 1, &bound_low);
convert_to_long(offset);
/* check bounds */
if (Z_LVAL_P(offset) < bound_low || Z_LVAL_P(offset) > bound_high) {
php_com_throw_exception("index out of bounds" TSRMLS_CC);
return return_value;
if (SafeArrayGetDim(V_ARRAY(&obj->v)) == 1) {
if (php_com_safearray_get_elem(&obj->v, &v, Z_LVAL_P(offset) TSRMLS_CC)) {
php_com_wrap_variant(return_value, &v, obj->code_page TSRMLS_CC);
VariantClear(&v);
}
} else {
php_com_saproxy_create(object, return_value, Z_LVAL_P(offset) TSRMLS_CC);
}
indices[0] = Z_LVAL_P(offset);
VariantInit(&v);
V_VT(&v) = vt;
/* store the value into "lVal" member of the variant.
* This works because it is a union; since we know the variant
* type, we end up with a working variant */
SafeArrayGetElement(sa, indices, &v.lVal);
/* now we can set the return value from that element */
php_com_wrap_variant(return_value, &v, obj->code_page TSRMLS_CC);
VariantClear(&v);
} else {
php_com_throw_exception("this variant is not an array type" TSRMLS_CC);
php_com_throw_exception(E_INVALIDARG, "this variant is not an array type" TSRMLS_CC);
}
return return_value;
@ -185,7 +155,7 @@ static void com_write_dimension(zval *object, zval *offset, zval *value TSRMLS_D
if (V_VT(&obj->v) == VT_DISPATCH) {
if (!obj->have_default_bind && !com_get_default_binding(obj TSRMLS_CC)) {
php_com_throw_exception("this COM object has no default property" TSRMLS_CC);
php_com_throw_exception(E_INVALIDARG, "this COM object has no default property" TSRMLS_CC);
return;
}
@ -200,7 +170,7 @@ static void com_write_dimension(zval *object, zval *offset, zval *value TSRMLS_D
}
} else {
/* TODO: check for safearray */
php_com_throw_exception("this variant is not an array type" TSRMLS_CC);
php_com_throw_exception(E_INVALIDARG, "this variant is not an array type" TSRMLS_CC);
}
}
@ -345,7 +315,6 @@ static int com_call_method(char *method, INTERNAL_FUNCTION_PARAMETERS)
obj = CDNO_FETCH(getThis());
if (V_VT(&obj->v) != VT_DISPATCH) {
//php_com_throw_exception("call to member function of non-object");
return FAILURE;
}
@ -529,6 +498,30 @@ zend_object_handlers php_com_object_handlers = {
com_object_cast
};
void php_com_object_enable_event_sink(php_com_dotnet_object *obj, int enable TSRMLS_DC)
{
if (obj->sink_dispatch) {
IConnectionPointContainer *cont;
IConnectionPoint *point;
if (SUCCEEDED(IDispatch_QueryInterface(V_DISPATCH(&obj->v),
&IID_IConnectionPointContainer, (void**)&cont))) {
if (SUCCEEDED(IConnectionPointContainer_FindConnectionPoint(cont,
&obj->sink_id, &point))) {
if (enable) {
IConnectionPoint_Advise(point, (IUnknown*)obj->sink_dispatch, &obj->sink_cookie);
} else {
IConnectionPoint_Unadvise(point, obj->sink_cookie);
}
IConnectionPoint_Release(point);
}
IConnectionPointContainer_Release(cont);
}
}
}
void php_com_object_dtor(void *object, zend_object_handle handle TSRMLS_DC)
{
php_com_dotnet_object *obj = (php_com_dotnet_object*)object;
@ -538,8 +531,13 @@ void php_com_object_dtor(void *object, zend_object_handle handle TSRMLS_DC)
obj->typeinfo = NULL;
}
VariantClear(&obj->v);
if (obj->sink_dispatch) {
php_com_object_enable_event_sink(obj, FALSE TSRMLS_CC);
IDispatch_Release(obj->sink_dispatch);
obj->sink_dispatch = NULL;
}
VariantClear(&obj->v);
efree(obj);
}

View file

@ -1,6 +1,6 @@
/*
+----------------------------------------------------------------------+
| PHP Version 4 |
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2003 The PHP Group |
+----------------------------------------------------------------------+
@ -33,8 +33,11 @@ struct php_com_iterator {
zend_object_iterator iter;
IEnumVARIANT *ev;
ulong key;
VARIANT v;
VARIANT v; /* cached element */
int code_page;
VARIANT safe_array;
VARTYPE sa_type;
LONG sa_max;
};
static void com_iter_dtor(zend_object_iterator *iter TSRMLS_DC)
@ -45,6 +48,7 @@ static void com_iter_dtor(zend_object_iterator *iter TSRMLS_DC)
IEnumVARIANT_Release(I->ev);
}
VariantClear(&I->v);
VariantClear(&I->safe_array);
efree(I);
}
@ -71,7 +75,8 @@ static void com_iter_get_data(zend_object_iterator *iter, zval ***data TSRMLS_DC
}
MAKE_STD_ZVAL(ptr);
php_com_wrap_variant(ptr, &I->v, I->code_page TSRMLS_CC);
php_com_zval_from_variant(ptr, &I->v, I->code_page TSRMLS_CC);
/* php_com_wrap_variant(ptr, &I->v, I->code_page TSRMLS_CC); */
ptr_ptr = emalloc(sizeof(*ptr_ptr));
*ptr_ptr = ptr;
*data = ptr_ptr;
@ -98,15 +103,30 @@ static int com_iter_move_forwards(zend_object_iterator *iter TSRMLS_DC)
/* release current cached element */
VariantClear(&I->v);
/* Get the next element */
if (SUCCEEDED(IEnumVARIANT_Next(I->ev, 1, &I->v, &n_fetched)) && n_fetched > 0) {
I->key++;
return SUCCESS;
if (I->ev) {
/* Get the next element */
if (SUCCEEDED(IEnumVARIANT_Next(I->ev, 1, &I->v, &n_fetched)) && n_fetched > 0) {
I->key++;
return SUCCESS;
} else {
/* indicate that there are no more items */
I->key = (ulong)-1;
return FAILURE;
}
} else {
/* indicate that there are no more items */
I->key = (ulong)-1;
return FAILURE;
/* safe array */
if (I->key >= I->sa_max) {
I->key = (ulong)-1;
return FAILURE;
}
I->key++;
if (php_com_safearray_get_elem(&I->safe_array, &I->v, (LONG)I->key TSRMLS_CC)) {
return SUCCESS;
} else {
I->key = (ulong)-1;
return FAILURE;
}
}
}
@ -131,49 +151,89 @@ zend_object_iterator *php_com_iter_get(zend_class_entry *ce, zval *object TSRMLS
obj = CDNO_FETCH(object);
/* TODO: support enumerating through SafeArrays */
if (V_VT(&obj->v) != VT_DISPATCH) {
if (V_VT(&obj->v) != VT_DISPATCH && !V_ISARRAY(&obj->v)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "variant is not an object or array VT=%d", V_VT(&obj->v));
return NULL;
}
memset(&dp, 0, sizeof(dp));
VariantInit(&v);
/* can we enumerate it? */
if (FAILED(IDispatch_Invoke(V_DISPATCH(&obj->v), DISPID_NEWENUM,
&IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD|DISPATCH_PROPERTYGET,
&dp, &v, NULL, NULL))) {
return NULL;
}
/* get something useful out of it */
if (V_VT(&v) == VT_UNKNOWN) {
IUnknown_QueryInterface(V_UNKNOWN(&v), &IID_IEnumVARIANT, (void**)&iev);
} else if (V_VT(&v) == VT_DISPATCH) {
IDispatch_QueryInterface(V_DISPATCH(&v), &IID_IEnumVARIANT, (void**)&iev);
}
VariantClear(&v);
if (iev == NULL) {
return NULL;
}
I = (struct php_com_iterator*)ecalloc(1, sizeof(*I));
I->iter.funcs = &com_iter_funcs;
I->iter.data = I;
I->ev = iev;
I->code_page = obj->code_page;
VariantInit(&I->safe_array);
VariantInit(&I->v);
/* Get the first element now */
if (SUCCEEDED(IEnumVARIANT_Next(I->ev, 1, &I->v, &n_fetched)) && n_fetched > 0) {
/* indicate that we have element 0 */
I->key = 0;
if (V_ISARRAY(&obj->v)) {
LONG bound;
UINT dims;
dims = SafeArrayGetDim(V_ARRAY(&obj->v));
if (dims != 1) {
php_error_docref(NULL TSRMLS_CC, E_WARNING,
"Can only handle single dimension variant arrays (this array has %d)", dims);
goto fail;
}
/* same semantics as foreach on a PHP array;
* make a copy and enumerate that copy */
VariantCopy(&I->safe_array, &obj->v);
/* determine the key value for the array */
SafeArrayGetLBound(V_ARRAY(&I->safe_array), 1, &bound);
SafeArrayGetUBound(V_ARRAY(&I->safe_array), 1, &I->sa_max);
/* pre-fetch the element */
if (php_com_safearray_get_elem(&I->safe_array, &I->v, bound TSRMLS_CC)) {
I->key = bound;
} else {
I->key = (ulong)-1;
}
} else {
/* indicate that there are no more items */
I->key = (ulong)-1;
/* can we enumerate it? */
if (FAILED(IDispatch_Invoke(V_DISPATCH(&obj->v), DISPID_NEWENUM,
&IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD|DISPATCH_PROPERTYGET,
&dp, &v, NULL, NULL))) {
goto fail;
}
/* get something useful out of it */
if (V_VT(&v) == VT_UNKNOWN) {
IUnknown_QueryInterface(V_UNKNOWN(&v), &IID_IEnumVARIANT, (void**)&iev);
} else if (V_VT(&v) == VT_DISPATCH) {
IDispatch_QueryInterface(V_DISPATCH(&v), &IID_IEnumVARIANT, (void**)&iev);
}
VariantClear(&v);
if (iev == NULL) {
goto fail;
}
I->ev = iev;
/* Get the first element now */
if (SUCCEEDED(IEnumVARIANT_Next(I->ev, 1, &I->v, &n_fetched)) && n_fetched > 0) {
/* indicate that we have element 0 */
I->key = 0;
} else {
/* indicate that there are no more items */
I->key = (ulong)-1;
}
}
return &I->iter;
fail:
if (I) {
VariantClear(&I->safe_array);
VariantClear(&I->v);
free(I);
}
return NULL;
}

View file

@ -1,6 +1,6 @@
/*
+----------------------------------------------------------------------+
| PHP Version 4 |
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2003 The PHP Group |
+----------------------------------------------------------------------+
@ -29,31 +29,17 @@
#include "php_com_dotnet_internal.h"
#include "Zend/zend_default_classes.h"
zval *php_com_throw_exception(char *message TSRMLS_DC)
void php_com_throw_exception(HRESULT code, char *message TSRMLS_DC)
{
zval *e, *tmp;
ALLOC_ZVAL(e);
Z_TYPE_P(e) = IS_OBJECT;
object_init_ex(e, zend_exception_get_default());
e->refcount = 1;
e->is_ref = 1;
MAKE_STD_ZVAL(tmp);
ZVAL_STRING(tmp, message, 1);
zend_hash_update(Z_OBJPROP_P(e), "message", sizeof("message"), (void**)&tmp, sizeof(zval*), NULL);
MAKE_STD_ZVAL(tmp);
ZVAL_STRING(tmp, zend_get_executed_filename(TSRMLS_C), 1);
zend_hash_update(Z_OBJPROP_P(e), "file", sizeof("file"), (void**)&tmp, sizeof(zval*), NULL);
MAKE_STD_ZVAL(tmp);
ZVAL_LONG(tmp, zend_get_executed_lineno(TSRMLS_C));
zend_hash_update(Z_OBJPROP_P(e), "line", sizeof("line"), (void**)&tmp, sizeof(zval*), NULL);
EG(exception) = e;
return e;
int free_msg = 0;
if (message == NULL) {
message = php_win_err(code);
free_msg = 1;
}
zend_throw_exception(php_com_exception_class_entry, message, (long)code TSRMLS_CC);
if (free_msg) {
efree(message);
}
}
PHPAPI void php_com_wrap_dispatch(zval *z, IDispatch *disp,
@ -89,7 +75,7 @@ PHPAPI void php_com_wrap_variant(zval *z, VARIANT *v,
obj->ce = php_com_variant_class_entry;
VariantInit(&obj->v);
VariantCopy(&obj->v, v);
VariantCopyInd(&obj->v, v);
if (V_VT(&obj->v) == VT_DISPATCH) {
IDispatch_GetTypeInfo(V_DISPATCH(&obj->v), 0, LANG_NEUTRAL, &obj->typeinfo);
@ -100,3 +86,56 @@ PHPAPI void php_com_wrap_variant(zval *z, VARIANT *v,
z->value.obj.handle = zend_objects_store_put(obj, php_com_object_dtor, php_com_object_clone TSRMLS_CC);
z->value.obj.handlers = &php_com_object_handlers;
}
/* this is a convenience function for fetching a particular
* element from a (possibly multi-dimensional) safe array */
PHPAPI int php_com_safearray_get_elem(VARIANT *array, VARIANT *dest, LONG dim1 TSRMLS_DC)
{
UINT dims;
LONG lbound, ubound;
LONG indices[1];
VARTYPE vt;
if (!V_ISARRAY(array)) {
return 0;
}
dims = SafeArrayGetDim(V_ARRAY(array));
if (dims != 1) {
php_error_docref(NULL TSRMLS_CC, E_WARNING,
"Can only handle single dimension variant arrays (this array has %d)", dims);
return 0;
}
if (FAILED(SafeArrayGetVartype(V_ARRAY(array), &vt)) || vt == VT_EMPTY) {
vt = V_VT(array) & ~VT_ARRAY;
}
/* determine the bounds */
SafeArrayGetLBound(V_ARRAY(array), 1, &lbound);
SafeArrayGetUBound(V_ARRAY(array), 1, &ubound);
/* check bounds */
if (dim1 < lbound || dim1 > ubound) {
php_com_throw_exception(E_INVALIDARG, "index out of bounds" TSRMLS_CC);
return 0;
}
/* now fetch that element */
VariantInit(dest);
indices[0] = dim1;
if (vt == VT_VARIANT) {
SafeArrayGetElement(V_ARRAY(array), indices, dest);
} else {
V_VT(dest) = vt;
/* store the value into "lVal" member of the variant.
* This works because it is a union; since we know the variant
* type, we end up with a working variant */
SafeArrayGetElement(V_ARRAY(array), indices, &dest->lVal);
}
return 1;
}

View file

@ -1,6 +1,6 @@
/*
+----------------------------------------------------------------------+
| PHP Version 4 |
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2003 The PHP Group |
+----------------------------------------------------------------------+
@ -100,4 +100,4 @@ PHPAPI char *php_com_olestring_to_string(OLECHAR *olestring, uint *string_len, i
}
return string;
}
}

View file

@ -0,0 +1,430 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2003 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.0 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_0.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 implements a SafeArray proxy which is used internally
* by the engine when resolving multi-dimensional array accesses on
* SafeArray types
* */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "php_com_dotnet.h"
#include "php_com_dotnet_internal.h"
#include "Zend/zend_default_classes.h"
typedef struct {
/* the object we a proxying for; we hold a refcount to it */
zval *zobj;
php_com_dotnet_object *obj;
/* how many dimensions we are indirecting to get into this element */
LONG dimensions;
/* this is an array whose size_is(dimensions) */
LONG *indices;
} php_com_saproxy;
typedef struct {
zend_object_iterator iter;
zval *proxy_obj;
php_com_saproxy *proxy;
LONG key;
LONG imin, imax;
LONG *indices;
} php_com_saproxy_iter;
#define SA_FETCH(zv) (php_com_saproxy*)zend_object_store_get_object(zv TSRMLS_CC)
static zval *saproxy_property_read(zval *object, zval *member, zend_bool silent TSRMLS_DC)
{
zval *return_value;
MAKE_STD_ZVAL(return_value);
ZVAL_NULL(return_value);
if (!silent) {
php_com_throw_exception(E_INVALIDARG, "safearray has no properties" TSRMLS_CC);
}
return return_value;
}
static void saproxy_property_write(zval *object, zval *member, zval *value TSRMLS_DC)
{
php_com_throw_exception(E_INVALIDARG, "safearray has no properties" TSRMLS_CC);
}
static zval *saproxy_read_dimension(zval *object, zval *offset TSRMLS_DC)
{
php_com_saproxy *proxy = SA_FETCH(object);
zval *return_value;
UINT dims;
SAFEARRAY *sa;
LONG ubound, lbound;
MAKE_STD_ZVAL(return_value);
ZVAL_NULL(return_value);
if (!V_ISARRAY(&proxy->obj->v)) {
php_com_throw_exception(E_INVALIDARG, "proxied object is no longer a safe array!" TSRMLS_CC);
return return_value;
}
/* offset/index must be an integer */
convert_to_long(offset);
sa = V_ARRAY(&proxy->obj->v);
dims = SafeArrayGetDim(sa);
if (proxy->dimensions >= dims) {
/* too many dimensions */
php_com_throw_exception(E_INVALIDARG, "too many dimensions!" TSRMLS_CC);
return return_value;
}
/* bounds check */
SafeArrayGetLBound(sa, proxy->dimensions, &lbound);
SafeArrayGetUBound(sa, proxy->dimensions, &ubound);
if (Z_LVAL_P(offset) < lbound || Z_LVAL_P(offset) > ubound) {
php_com_throw_exception(E_INVALIDARG, "index out of bounds" TSRMLS_CC);
return return_value;
}
if (dims - 1 == proxy->dimensions) {
LONG *indices;
VARTYPE vt;
VARIANT v;
VariantInit(&v);
/* we can return a real value */
indices = do_alloca(dims * sizeof(LONG));
/* copy indices from proxy */
memcpy(indices, proxy->indices, (dims-1) * sizeof(LONG));
/* add user-supplied index */
indices[dims-1] = Z_LVAL_P(offset);
/* now fetch the value */
if (FAILED(SafeArrayGetVartype(sa, &vt)) || vt == VT_EMPTY) {
vt = V_VT(&proxy->obj->v) & ~VT_ARRAY;
}
if (vt == VT_VARIANT) {
SafeArrayGetElement(sa, indices, &v);
} else {
V_VT(&v) = vt;
SafeArrayGetElement(sa, indices, &v.lVal);
}
free_alloca(indices);
php_com_wrap_variant(return_value, &v, proxy->obj->code_page TSRMLS_CC);
VariantClear(&v);
} else {
/* return another proxy */
php_com_saproxy_create(object, return_value, Z_LVAL_P(offset) TSRMLS_CC);
}
return return_value;
}
static void saproxy_write_dimension(zval *object, zval *offset, zval *value TSRMLS_DC)
{
php_com_throw_exception(E_NOTIMPL, "writing to safearray not yet implemented" TSRMLS_CC);
}
static void saproxy_object_set(zval **property, zval *value TSRMLS_DC)
{
}
static zval *saproxy_object_get(zval *property TSRMLS_DC)
{
/* Not yet implemented in the engine */
return NULL;
}
static int saproxy_property_exists(zval *object, zval *member, int check_empty TSRMLS_DC)
{
/* no properties */
return 0;
}
static int saproxy_dimension_exists(zval *object, zval *member, int check_empty TSRMLS_DC)
{
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Operation not yet supported on a COM object");
return 0;
}
static void saproxy_property_delete(zval *object, zval *member TSRMLS_DC)
{
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot delete properties from a COM object");
}
static void saproxy_dimension_delete(zval *object, zval *offset TSRMLS_DC)
{
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot delete properties from a COM object");
}
static HashTable *saproxy_properties_get(zval *object TSRMLS_DC)
{
/* no properties */
return NULL;
}
static union _zend_function *saproxy_method_get(zval *object, char *name, int len TSRMLS_DC)
{
/* no methods */
return NULL;
}
static int saproxy_call_method(char *method, INTERNAL_FUNCTION_PARAMETERS)
{
return FAILURE;
}
static union _zend_function *saproxy_constructor_get(zval *object TSRMLS_DC)
{
/* user cannot instanciate */
return NULL;
}
static zend_class_entry *saproxy_class_entry_get(zval *object TSRMLS_DC)
{
return php_com_saproxy_class_entry;
}
static int saproxy_class_name_get(zval *object, char **class_name, zend_uint *class_name_len, int parent TSRMLS_DC)
{
*class_name = estrndup(php_com_saproxy_class_entry->name, php_com_saproxy_class_entry->name_length);
*class_name_len = php_com_saproxy_class_entry->name_length;
return 0;
}
static int saproxy_objects_compare(zval *object1, zval *object2 TSRMLS_DC)
{
return -1;
}
static int saproxy_object_cast(zval *readobj, zval *writeobj, int type, int should_free TSRMLS_DC)
{
return FAILURE;
}
zend_object_handlers php_com_saproxy_handlers = {
ZEND_OBJECTS_STORE_HANDLERS,
saproxy_property_read,
saproxy_property_write,
saproxy_read_dimension,
saproxy_write_dimension,
NULL,
saproxy_object_get,
saproxy_object_set,
saproxy_property_exists,
saproxy_property_delete,
saproxy_dimension_exists,
saproxy_dimension_delete,
saproxy_properties_get,
saproxy_method_get,
saproxy_call_method,
saproxy_constructor_get,
saproxy_class_entry_get,
saproxy_class_name_get,
saproxy_objects_compare,
saproxy_object_cast
};
static void saproxy_dtor(void *object, zend_object_handle handle TSRMLS_DC)
{
php_com_saproxy *proxy = (php_com_saproxy *)object;
ZVAL_DELREF(proxy->zobj);
efree(proxy->indices);
efree(proxy);
}
static void saproxy_clone(void *object, void **clone_ptr TSRMLS_DC)
{
php_com_saproxy *proxy = (php_com_saproxy *)object;
php_com_saproxy *cloneproxy;
cloneproxy = emalloc(sizeof(*cloneproxy));
memcpy(cloneproxy, proxy, sizeof(*cloneproxy));
ZVAL_ADDREF(cloneproxy->zobj);
cloneproxy->indices = safe_emalloc(cloneproxy->dimensions, sizeof(LONG), 0);
memcpy(cloneproxy->indices, proxy->indices, cloneproxy->dimensions * sizeof(LONG));
*clone_ptr = cloneproxy;
}
int php_com_saproxy_create(zval *com_object, zval *proxy_out, long index TSRMLS_DC)
{
php_com_saproxy *proxy, *rel = NULL;
php_com_dotnet_object *obj;
proxy = ecalloc(1, sizeof(*proxy));
proxy->dimensions = 1;
if (Z_OBJCE_P(com_object) == php_com_saproxy_class_entry) {
rel = SA_FETCH(com_object);
obj = rel->obj;
proxy->zobj = rel->zobj;
proxy->dimensions += rel->dimensions;
} else {
obj = CDNO_FETCH(com_object);
proxy->zobj = com_object;
}
ZVAL_ADDREF(proxy->zobj);
proxy->indices = safe_emalloc(proxy->dimensions, sizeof(LONG), 0);
if (rel) {
memcpy(proxy->indices, rel->indices, (proxy->dimensions-1) * sizeof(LONG));
}
proxy->indices[proxy->dimensions-1] = index;
Z_TYPE_P(proxy_out) = IS_OBJECT;
Z_OBJ_HANDLE_P(proxy_out) = zend_objects_store_put(proxy, saproxy_dtor, saproxy_clone TSRMLS_CC);
Z_OBJ_HT_P(proxy_out) = &php_com_saproxy_handlers;
return 1;
}
/* iterator */
static void saproxy_iter_dtor(zend_object_iterator *iter TSRMLS_DC)
{
php_com_saproxy_iter *I = (php_com_saproxy_iter*)iter->data;
ZVAL_DELREF(I->proxy_obj);
efree(I->indices);
efree(I);
}
static int saproxy_iter_has_more(zend_object_iterator *iter TSRMLS_DC)
{
php_com_saproxy_iter *I = (php_com_saproxy_iter*)iter->data;
return (I->key < I->imax) ? SUCCESS : FAILURE;
}
static void saproxy_iter_get_data(zend_object_iterator *iter, zval ***data TSRMLS_DC)
{
php_com_saproxy_iter *I = (php_com_saproxy_iter*)iter->data;
VARIANT v;
VARTYPE vt;
zval *return_value, **ptr_ptr;
SAFEARRAY *sa;
I->indices[I->proxy->dimensions-1] = I->key;
sa = V_ARRAY(&I->proxy->obj->v);
if (FAILED(SafeArrayGetVartype(sa, &vt)) || vt == VT_EMPTY) {
vt = V_VT(&I->proxy->obj->v) & ~VT_ARRAY;
}
VariantInit(&v);
if (vt == VT_VARIANT) {
SafeArrayGetElement(sa, I->indices, &v);
} else {
V_VT(&v) = vt;
SafeArrayGetElement(sa, I->indices, &v.lVal);
}
MAKE_STD_ZVAL(return_value);
php_com_wrap_variant(return_value, &v, I->proxy->obj->code_page TSRMLS_CC);
VariantClear(&v);
ptr_ptr = emalloc(sizeof(*ptr_ptr));
*ptr_ptr = return_value;
*data = ptr_ptr;
}
static int saproxy_iter_get_key(zend_object_iterator *iter, char **str_key, uint *str_key_len,
ulong *int_key TSRMLS_DC)
{
php_com_saproxy_iter *I = (php_com_saproxy_iter*)iter->data;
if (I->key == -1) {
return HASH_KEY_NON_EXISTANT;
}
*int_key = (ulong)I->key;
return HASH_KEY_IS_LONG;
}
static int saproxy_iter_move_forwards(zend_object_iterator *iter TSRMLS_DC)
{
php_com_saproxy_iter *I = (php_com_saproxy_iter*)iter->data;
if (++I->key >= I->imax) {
I->key = -1;
return FAILURE;
}
return SUCCESS;
}
static zend_object_iterator_funcs saproxy_iter_funcs = {
saproxy_iter_dtor,
saproxy_iter_has_more,
saproxy_iter_get_data,
saproxy_iter_get_key,
saproxy_iter_move_forwards,
NULL
};
zend_object_iterator *php_com_saproxy_iter_get(zend_class_entry *ce, zval *object TSRMLS_DC)
{
php_com_saproxy *proxy = SA_FETCH(object);
php_com_saproxy_iter *I;
I = ecalloc(1, sizeof(*I));
I->iter.funcs = &saproxy_iter_funcs;
I->iter.data = I;
I->proxy = proxy;
I->proxy_obj = object;
ZVAL_ADDREF(I->proxy_obj);
I->indices = safe_emalloc(proxy->dimensions + 1, sizeof(LONG), 0);
memcpy(I->indices, proxy->indices, proxy->dimensions * sizeof(LONG));
SafeArrayGetLBound(V_ARRAY(&proxy->obj->v), proxy->dimensions, &I->imin);
SafeArrayGetUBound(V_ARRAY(&proxy->obj->v), proxy->dimensions, &I->imax);
I->key = I->imin;
return &I->iter;
}

View file

@ -1,6 +1,6 @@
/*
+----------------------------------------------------------------------+
| PHP Version 4 |
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2003 The PHP Group |
+----------------------------------------------------------------------+
@ -35,8 +35,7 @@
* b) a CLSID, major, minor e.g. "{00000200-0000-0010-8000-00AA006D2EA4},2,0"
* c) a Type Library name e.g. "Microsoft OLE DB ActiveX Data Objects 1.0 Library"
*/
PHPAPI ITypeLib *php_com_load_typelib(char *search_string, int mode,
int codepage TSRMLS_DC)
PHPAPI ITypeLib *php_com_load_typelib(char *search_string, int codepage TSRMLS_DC)
{
ITypeLib *TL = NULL;
char *strtok_buf, *major, *minor;
@ -119,7 +118,7 @@ PHPAPI ITypeLib *php_com_load_typelib(char *search_string, int mode,
/* get the default value for this key and compare */
libnamelen = strlen(search_string)+1;
if (ERROR_SUCCESS == RegQueryValue(hsubkey, version, libname, &libnamelen)) {
if (0 == ((mode & CONST_CS) ? strcmp(libname, search_string) : stricmp(libname, search_string))) {
if (0 == stricmp(libname, search_string)) {
char *str = NULL;
int major, minor;
@ -130,7 +129,7 @@ PHPAPI ITypeLib *php_com_load_typelib(char *search_string, int mode,
}
spprintf(&str, 0, "%s,%d,%d", keyname, major, minor);
/* recurse */
TL = php_com_load_typelib(str, mode, codepage TSRMLS_CC);
TL = php_com_load_typelib(str, codepage TSRMLS_CC);
efree(str);
break;
@ -226,7 +225,7 @@ void php_com_typelibrary_dtor(void *pDest)
}
PHPAPI ITypeLib *php_com_load_typelib_via_cache(char *search_string,
int mode, int codepage, int *cached TSRMLS_DC)
int codepage, int *cached TSRMLS_DC)
{
ITypeLib **TL;
char *name_dup;
@ -244,7 +243,7 @@ PHPAPI ITypeLib *php_com_load_typelib_via_cache(char *search_string,
*cached = 0;
name_dup = estrndup(search_string, l);
*TL = php_com_load_typelib(name_dup, mode, codepage TSRMLS_CC);
*TL = php_com_load_typelib(name_dup, codepage TSRMLS_CC);
efree(name_dup);
if (*TL) {
@ -257,3 +256,346 @@ PHPAPI ITypeLib *php_com_load_typelib_via_cache(char *search_string,
return *TL;
}
ITypeInfo *php_com_locate_typeinfo(char *typelibname, php_com_dotnet_object *obj, char *dispname, int sink TSRMLS_DC)
{
ITypeInfo *typeinfo = NULL;
ITypeLib *typelib = NULL;
int gotguid = 0;
GUID iid;
if (obj) {
if (dispname == NULL && sink) {
IProvideClassInfo2 *pci2;
IProvideClassInfo *pci;
if (SUCCEEDED(IDispatch_QueryInterface(V_DISPATCH(&obj->v), &IID_IProvideClassInfo2, (void**)&pci2))) {
gotguid = SUCCEEDED(IProvideClassInfo2_GetGUID(pci2, GUIDKIND_DEFAULT_SOURCE_DISP_IID, &iid));
IProvideClassInfo2_Release(pci2);
}
if (!gotguid && SUCCEEDED(IDispatch_QueryInterface(V_DISPATCH(&obj->v), &IID_IProvideClassInfo, (void**)&pci))) {
/* examine the available interfaces */
/* TODO: write some code here */
php_error_docref(NULL TSRMLS_CC, E_WARNING, "IProvideClassInfo: this code not yet written!");
IProvideClassInfo_Release(pci);
}
} else if (dispname == NULL) {
if (obj->typeinfo) {
ITypeInfo_AddRef(obj->typeinfo);
return obj->typeinfo;
} else {
IDispatch_GetTypeInfo(V_DISPATCH(&obj->v), 0, LANG_NEUTRAL, &typeinfo);
if (typeinfo) {
return typeinfo;
}
}
} else if (dispname && obj->typeinfo) {
unsigned int idx;
/* get the library from the object; the rest will be dealt with later */
ITypeInfo_GetContainingTypeLib(obj->typeinfo, &typelib, &idx);
} else if (typelibname == NULL) {
IDispatch_GetTypeInfo(V_DISPATCH(&obj->v), 0, LANG_NEUTRAL, &typeinfo);
if (dispname) {
unsigned int idx;
/* get the library from the object; the rest will be dealt with later */
ITypeInfo_GetContainingTypeLib(typeinfo, &typelib, &idx);
if (typelib) {
ITypeInfo_Release(typeinfo);
typeinfo = NULL;
}
}
}
} else if (typelibname) {
/* Fetch the typelibrary and use that to look things up */
typelib = php_com_load_typelib(typelibname, obj->code_page TSRMLS_CC);
}
if (!gotguid && dispname && typelib) {
unsigned short cfound;
MEMBERID memid;
OLECHAR *olename = php_com_string_to_olestring(dispname, strlen(dispname), CP_ACP TSRMLS_CC);
cfound = 1;
if (FAILED(ITypeLib_FindName(typelib, olename, 0, &typeinfo, &memid, &cfound)) || cfound == 0) {
CLSID coclass;
ITypeInfo *coinfo;
/* assume that it might be a progid instead */
if (SUCCEEDED(CLSIDFromProgID(olename, &coclass)) &&
SUCCEEDED(ITypeLib_GetTypeInfoOfGuid(typelib, &coclass, &coinfo))) {
/* enumerate implemented interfaces and pick the one as indicated by sink */
TYPEATTR *attr;
int i;
ITypeInfo_GetTypeAttr(coinfo, &attr);
for (i = 0; i < attr->cImplTypes; i++) {
HREFTYPE rt;
int tf;
if (FAILED(ITypeInfo_GetImplTypeFlags(coinfo, i, &tf))) {
continue;
}
if ((sink && tf == (IMPLTYPEFLAG_FSOURCE|IMPLTYPEFLAG_FDEFAULT)) ||
(!sink && (tf & IMPLTYPEFLAG_FSOURCE) == 0)) {
/* flags match what we are looking for */
if (SUCCEEDED(ITypeInfo_GetRefTypeOfImplType(coinfo, i, &rt)))
if (SUCCEEDED(ITypeInfo_GetRefTypeInfo(coinfo, rt, &typeinfo)))
break;
}
}
ITypeInfo_ReleaseTypeAttr(coinfo, attr);
ITypeInfo_Release(coinfo);
}
}
efree(olename);
} else if (gotguid) {
ITypeLib_GetTypeInfoOfGuid(typelib, &iid, &typeinfo);
}
if (typelib) {
ITypeLib_Release(typelib);
}
return typeinfo;
}
static const struct {
VARTYPE vt;
const char *name;
} vt_names[] = {
{ VT_NULL, "VT_NULL" },
{ VT_EMPTY, "VT_EMPTY" },
{ VT_UI1, "VT_UI1" },
{ VT_I2, "VT_I2" },
{ VT_I4, "VT_I4" },
{ VT_R4, "VT_R4" },
{ VT_R8, "VT_R8" },
{ VT_BOOL, "VT_BOOL" },
{ VT_ERROR, "VT_ERROR" },
{ VT_CY, "VT_CY" },
{ VT_DATE, "VT_DATE" },
{ VT_BSTR, "VT_BSTR" },
{ VT_DECIMAL, "VT_DECIMAL" },
{ VT_UNKNOWN, "VT_UNKNOWN" },
{ VT_DISPATCH, "VT_DISPATCH" },
{ VT_VARIANT, "VT_VARIANT" },
{ VT_I1, "VT_I1" },
{ VT_UI2, "VT_UI2" },
{ VT_UI4, "VT_UI4" },
{ VT_INT, "VT_INT" },
{ VT_UINT, "VT_UINT" },
{ VT_ARRAY, "VT_ARRAY" },
{ VT_BYREF, "VT_BYREF" },
{ VT_VOID, "VT_VOID" },
{ VT_PTR, "VT_PTR" },
{ VT_HRESULT, "VT_HRESULT" },
{ 0, NULL }
};
static inline const char *vt_to_string(VARTYPE vt)
{
int i;
for (i = 0; vt_names[i].name != NULL; i++) {
if (vt_names[i].vt == vt)
return vt_names[i].name;
}
return "?";
}
static char *php_com_string_from_clsid(const CLSID *clsid, int codepage TSRMLS_DC)
{
LPOLESTR ole_clsid;
char *clsid_str;
StringFromCLSID(clsid, &ole_clsid);
clsid_str = php_com_olestring_to_string(ole_clsid, NULL, codepage TSRMLS_CC);
LocalFree(ole_clsid);
return clsid_str;
}
int php_com_process_typeinfo(ITypeInfo *typeinfo, HashTable *id_to_name, int printdef, GUID *guid, int codepage TSRMLS_DC)
{
TYPEATTR *attr;
FUNCDESC *func;
int i;
OLECHAR *olename;
char *ansiname = NULL;
unsigned int ansinamelen;
int ret = 0;
if (FAILED(ITypeInfo_GetTypeAttr(typeinfo, &attr))) {
return 0;
}
/* verify that it is suitable */
if (id_to_name == NULL || attr->typekind == TKIND_DISPATCH) {
if (guid) {
memcpy(guid, &attr->guid, sizeof(GUID));
}
if (printdef) {
char *guidstring;
ITypeInfo_GetDocumentation(typeinfo, MEMBERID_NIL, &olename, NULL, NULL, NULL);
ansiname = php_com_olestring_to_string(olename, &ansinamelen, codepage TSRMLS_CC);
SysFreeString(olename);
guidstring = php_com_string_from_clsid(&attr->guid, codepage TSRMLS_CC);
php_printf("class %s { /* GUID=%s */\n", ansiname, guidstring);
efree(guidstring);
efree(ansiname);
}
if (id_to_name) {
zend_hash_init(id_to_name, 0, NULL, ZVAL_PTR_DTOR, 0);
}
/* So we've got the dispatch interface; lets list the event methods */
for (i = 0; i < attr->cFuncs; i++) {
zval *tmp;
DISPID lastid = 0; /* for props */
int isprop;
if (FAILED(ITypeInfo_GetFuncDesc(typeinfo, i, &func)))
break;
isprop = (func->invkind & DISPATCH_PROPERTYGET || func->invkind & DISPATCH_PROPERTYPUT);
if (!isprop || lastid != func->memid) {
lastid = func->memid;
ITypeInfo_GetDocumentation(typeinfo, func->memid, &olename, NULL, NULL, NULL);
ansiname = php_com_olestring_to_string(olename, &ansinamelen, codepage TSRMLS_CC);
SysFreeString(olename);
if (printdef) {
int j;
char *funcdesc;
unsigned int funcdesclen, cnames = 0;
BSTR *names;
names = (BSTR*)emalloc((func->cParams + 1) * sizeof(BSTR));
ITypeInfo_GetNames(typeinfo, func->memid, names, func->cParams + 1, &cnames);
/* first element is the function name */
SysFreeString(names[0]);
php_printf("\t/* DISPID=%d */\n", func->memid);
if (func->elemdescFunc.tdesc.vt != VT_VOID) {
php_printf("\t/* %s [%d] */\n",
vt_to_string(func->elemdescFunc.tdesc.vt),
func->elemdescFunc.tdesc.vt
);
}
if (isprop) {
ITypeInfo_GetDocumentation(typeinfo, func->memid, NULL, &olename, NULL, NULL);
if (olename) {
funcdesc = php_com_olestring_to_string(olename, &funcdesclen, codepage TSRMLS_CC);
SysFreeString(olename);
php_printf("\t/* %s */\n", funcdesc);
efree(funcdesc);
}
php_printf("\tvar $%s;\n\n", ansiname);
} else {
/* a function */
php_printf("\tfunction %s(\n", ansiname);
for (j = 0; j < func->cParams; j++) {
ELEMDESC *elem = &func->lprgelemdescParam[j];
php_printf("\t\t/* %s [%d] ", vt_to_string(elem->tdesc.vt), elem->tdesc.vt);
if (elem->paramdesc.wParamFlags & PARAMFLAG_FIN)
php_printf("[in]");
if (elem->paramdesc.wParamFlags & PARAMFLAG_FOUT)
php_printf("[out]");
if (elem->tdesc.vt == VT_PTR) {
/* what does it point to ? */
php_printf(" --> %s [%d] ",
vt_to_string(elem->tdesc.lptdesc->vt),
elem->tdesc.lptdesc->vt
);
}
/* when we handle prop put and get, this will look nicer */
if (j+1 < (int)cnames) {
funcdesc = php_com_olestring_to_string(names[j+1], &funcdesclen, codepage TSRMLS_CC);
SysFreeString(names[j+1]);
} else {
funcdesc = "???";
}
php_printf(" */ %s%s%c\n",
elem->tdesc.vt == VT_PTR ? "&$" : "$",
funcdesc,
j == func->cParams - 1 ? ' ' : ','
);
if (j+1 < (int)cnames) {
efree(funcdesc);
}
}
php_printf("\t\t)\n\t{\n");
ITypeInfo_GetDocumentation(typeinfo, func->memid, NULL, &olename, NULL, NULL);
if (olename) {
funcdesc = php_com_olestring_to_string(olename, &funcdesclen, codepage TSRMLS_CC);
SysFreeString(olename);
php_printf("\t\t/* %s */\n", funcdesc);
efree(funcdesc);
}
php_printf("\t}\n");
}
efree(names);
}
if (id_to_name) {
zend_str_tolower(ansiname, ansinamelen);
MAKE_STD_ZVAL(tmp);
ZVAL_STRINGL(tmp, ansiname, ansinamelen, 0);
zend_hash_index_update(id_to_name, func->memid, (void*)&tmp, sizeof(zval *), NULL);
}
}
ITypeInfo_ReleaseFuncDesc(typeinfo, func);
}
if (printdef) {
php_printf("}\n");
}
ret = 1;
} else {
zend_error(E_WARNING, "That's not a dispatchable interface!! type kind = %08x", attr->typekind);
}
ITypeInfo_ReleaseTypeAttr(typeinfo, attr);
return ret;
}

View file

@ -1,6 +1,6 @@
/*
+----------------------------------------------------------------------+
| PHP Version 4 |
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2003 The PHP Group |
+----------------------------------------------------------------------+
@ -59,8 +59,9 @@ PHPAPI void php_com_variant_from_zval(VARIANT *v, zval *z, int codepage TSRMLS_D
V_VARIANTREF(v) = &obj->v;
}
} else {
/* TODO: export the object using our COM wrapper */
V_VT(v) = VT_NULL;
/* export the PHP object using our COM wrapper */
V_VT(v) = VT_DISPATCH;
V_DISPATCH(v) = php_com_wrapper_export(z TSRMLS_CC);
}
break;
@ -168,6 +169,9 @@ PHPAPI int php_com_zval_from_variant(zval *z, VARIANT *v, int codepage TSRMLS_DC
break;
case VT_VARIANT:
/* points to another variant */
return php_com_zval_from_variant(z, V_VARIANTREF(v), codepage TSRMLS_CC);
default:
php_com_wrap_variant(z, v, codepage TSRMLS_CC);
}
@ -202,7 +206,7 @@ PHP_FUNCTION(com_variant_create_instance)
if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
"z!|ll", &zvalue, &vt, &codepage)) {
php_com_throw_exception("Invalid arguments" TSRMLS_CC);
php_com_throw_exception(E_INVALIDARG, "Invalid arguments" TSRMLS_CC);
return;
}
@ -223,7 +227,7 @@ PHP_FUNCTION(com_variant_create_instance)
spprintf(&msg, 0, "Variant type conversion failed: %s", werr);
LocalFree(werr);
php_com_throw_exception(msg TSRMLS_CC);
php_com_throw_exception(res, msg TSRMLS_CC);
efree(msg);
}
}
@ -254,6 +258,11 @@ PHP_FUNCTION(variant_set)
ITypeInfo_Release(obj->typeinfo);
obj->typeinfo = NULL;
}
if (obj->sink_dispatch) {
php_com_object_enable_event_sink(obj, FALSE TSRMLS_CC);
IDispatch_Release(obj->sink_dispatch);
obj->sink_dispatch = NULL;
}
VariantClear(&obj->v);
@ -779,7 +788,7 @@ PHP_FUNCTION(variant_set_type)
spprintf(&msg, 0, "Variant type conversion failed: %s", werr);
LocalFree(werr);
php_com_throw_exception(msg TSRMLS_CC);
php_com_throw_exception(res, msg TSRMLS_CC);
efree(msg);
}
}
@ -813,7 +822,7 @@ PHP_FUNCTION(variant_cast)
spprintf(&msg, 0, "Variant type conversion failed: %s", werr);
LocalFree(werr);
php_com_throw_exception(msg TSRMLS_CC);
php_com_throw_exception(res, msg TSRMLS_CC);
efree(msg);
}

View file

@ -0,0 +1,640 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2003 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.0 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_0.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 exports a PHP object as a COM object by wrapping it
* using IDispatchEx */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "php_com_dotnet.h"
#include "php_com_dotnet_internal.h"
typedef struct {
/* This first part MUST match the declaration
* of interface IDispatchEx */
CONST_VTBL struct IDispatchExVtbl *lpVtbl;
/* now the PHP stuff */
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 */
GUID sinkid; /* iid that we "implement" for event sinking */
int id;
} php_dispatchex;
static int le_dispatch;
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);
}
int php_com_wrapper_minit(INIT_FUNC_ARGS)
{
le_dispatch = zend_register_list_destructors_ex(dispatch_dtor,
NULL, "com_dotnet_dispatch_wrapper", module_number);
return le_dispatch;
}
/* {{{ trace */
static inline void trace(char *fmt, ...)
{
va_list ap;
char buf[4096];
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)
{
TSRMLS_FETCH();
FETCH_DISP("QueryInterface");
if (IsEqualGUID(&IID_IUnknown, riid) ||
IsEqualGUID(&IID_IDispatch, riid) ||
IsEqualGUID(&IID_IDispatchEx, riid) ||
IsEqualGUID(&disp->sinkid, riid)) {
*ppvObject = This;
InterlockedIncrement(&disp->refcount);
return S_OK;
}
*ppvObject = NULL;
return E_NOINTERFACE;
}
static ULONG STDMETHODCALLTYPE disp_addref(IDispatchEx *This)
{
TSRMLS_FETCH();
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)
{
TSRMLS_FETCH();
FETCH_DISP("GetTypeInfoCount");
*pctinfo = 0;
return S_OK;
}
static HRESULT STDMETHODCALLTYPE disp_gettypeinfo(
IDispatchEx *This,
/* [in] */ UINT iTInfo,
/* [in] */ LCID lcid,
/* [out] */ ITypeInfo **ppTInfo)
{
TSRMLS_FETCH();
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_com_olestring_to_string(rgszNames[i], &namelen, COMG(code_page) 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_PP(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_com_olestring_to_string(bstrName, &namelen, COMG(code_page) 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_PP(tmp);
ret = S_OK;
}
efree(name);
return ret;
}
static HRESULT STDMETHODCALLTYPE disp_invokeex(
IDispatchEx *This,
/* [in] */ DISPID id,
/* [in] */ LCID lcid,
/* [in] */ WORD wFlags,
/* [in] */ DISPPARAMS *pdp,
/* [out] */ VARIANT *pvarRes,
/* [out] */ EXCEPINFO *pei,
/* [unique][in] */ IServiceProvider *pspCaller)
{
zval **name;
UINT i;
zval *retval = NULL;
zval ***params = NULL;
HRESULT ret = DISP_E_MEMBERNOTFOUND;
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 %20s flags=%08x args=%d\n", id, Z_STRVAL_PP(name), wFlags, pdp->cArgs);
/* convert args into zvals.
* Args are in reverse order */
params = (zval ***)emalloc(sizeof(zval **) * pdp->cArgs);
for (i = 0; i < pdp->cArgs; i++) {
VARIANT *arg;
zval *zarg;
arg = &pdp->rgvarg[ pdp->cArgs - 1 - i];
trace("alloc zval for arg %d VT=%08x\n", i, V_VT(arg));
ALLOC_INIT_ZVAL(zarg);
php_com_wrap_variant(zarg, arg, COMG(code_page) TSRMLS_CC);
params[i] = &zarg;
}
trace("arguments processed, prepare to do some work\n");
/* TODO: if PHP raises an exception here, we should catch it
* and expose it as a COM exception */
if (wFlags & DISPATCH_PROPERTYGET) {
retval = zend_read_property(Z_OBJCE_P(disp->object), disp->object, Z_STRVAL_PP(name), Z_STRLEN_PP(name)+1, 1 TSRMLS_CC);
} else if (wFlags & DISPATCH_PROPERTYPUT) {
zend_update_property(Z_OBJCE_P(disp->object), disp->object, Z_STRVAL_PP(name), Z_STRLEN_PP(name)+1, *params[0] TSRMLS_CC);
} else if (wFlags & DISPATCH_METHOD) {
if (SUCCESS == call_user_function_ex(EG(function_table), &disp->object, *name,
&retval, pdp->cArgs, params, 1, NULL TSRMLS_CC)) {
ret = S_OK;
} else {
ret = DISP_E_EXCEPTION;
}
} else {
trace("Don't know how to handle this invocation %08x\n", wFlags);
}
/* release arguments */
for (i = 0; i < pdp->cArgs; i++)
zval_ptr_dtor(params[i]);
efree(params);
/* return value */
if (retval) {
if (pvarRes) {
if (Z_TYPE_P(retval) == IS_OBJECT) {
/* export the object using a dispatch like ourselves */
VariantInit(pvarRes);
V_VT(pvarRes) = VT_DISPATCH;
V_DISPATCH(pvarRes) = php_com_wrapper_export(retval TSRMLS_CC);
} else {
php_com_variant_from_zval(pvarRes, retval, COMG(code_page) TSRMLS_CC);
}
}
zval_ptr_dtor(&retval);
} else if (pvarRes) {
VariantInit(pvarRes);
}
} else {
trace("InvokeEx: I don't support DISPID=%d\n", id);
}
return ret;
}
static HRESULT STDMETHODCALLTYPE disp_deletememberbyname(
IDispatchEx *This,
/* [in] */ BSTR bstrName,
/* [in] */ DWORD grfdex)
{
TSRMLS_FETCH();
FETCH_DISP("DeleteMemberByName");
return S_FALSE;
}
static HRESULT STDMETHODCALLTYPE disp_deletememberbydispid(
IDispatchEx *This,
/* [in] */ DISPID id)
{
TSRMLS_FETCH();
FETCH_DISP("DeleteMemberByDispID");
return S_FALSE;
}
static HRESULT STDMETHODCALLTYPE disp_getmemberproperties(
IDispatchEx *This,
/* [in] */ DISPID id,
/* [in] */ DWORD grfdexFetch,
/* [out] */ DWORD *pgrfdex)
{
TSRMLS_FETCH();
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_com_string_to_olestring(Z_STRVAL_P(name), Z_STRLEN_P(name), COMG(code_page) 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;
TSRMLS_FETCH();
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)
{
TSRMLS_FETCH();
FETCH_DISP("GetNameSpaceParent");
*ppunk = NULL;
return E_NOTIMPL;
}
static struct IDispatchExVtbl php_dispatch_vtbl = {
disp_queryinterface,
disp_addref,
disp_release,
disp_gettypeinfocount,
disp_gettypeinfo,
disp_getidsofnames,
disp_invoke,
disp_getdispid,
disp_invokeex,
disp_deletememberbyname,
disp_deletememberbydispid,
disp_getmemberproperties,
disp_getmembername,
disp_getnextdispid,
disp_getnamespaceparent
};
/* enumerate functions and properties of the object and assign
* dispatch ids */
static void generate_dispids(php_dispatchex *disp TSRMLS_DC)
{
HashPosition pos;
char *name = NULL;
zval *tmp;
int namelen;
int keytype;
ulong pid;
if (disp->dispid_to_name == NULL) {
ALLOC_HASHTABLE(disp->dispid_to_name);
ALLOC_HASHTABLE(disp->name_to_dispid);
zend_hash_init(disp->name_to_dispid, 0, NULL, ZVAL_PTR_DTOR, 0);
zend_hash_init(disp->dispid_to_name, 0, NULL, ZVAL_PTR_DTOR, 0);
}
/* properties */
if (Z_OBJPROP_P(disp->object)) {
zend_hash_internal_pointer_reset_ex(Z_OBJPROP_P(disp->object), &pos);
while (HASH_KEY_NON_EXISTANT != (keytype =
zend_hash_get_current_key_ex(Z_OBJPROP_P(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_P(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 */
if (Z_OBJCE_P(disp->object)) {
zend_hash_internal_pointer_reset_ex(&Z_OBJCE_P(disp->object)->function_table, &pos);
while (HASH_KEY_NON_EXISTANT != (keytype =
zend_hash_get_current_key_ex(&Z_OBJCE_P(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_P(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));
trace("constructing a COM proxy\n");
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;
if (object)
ZVAL_ADDREF(object);
disp->object = object;
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);
FREE_HASHTABLE(disp->dispid_to_name);
FREE_HASHTABLE(disp->name_to_dispid);
if (disp->object)
zval_ptr_dtor(&disp->object);
CoTaskMemFree(disp);
}
PHPAPI IDispatch *php_com_wrapper_export_as_sink(zval *val, GUID *sinkid,
HashTable *id_to_name TSRMLS_DC)
{
php_dispatchex *disp = disp_constructor(val TSRMLS_CC);
HashPosition pos;
char *name = NULL;
zval *tmp, **ntmp;
int namelen;
int keytype;
ulong pid;
disp->dispid_to_name = id_to_name;
memcpy(&disp->sinkid, sinkid, sizeof(disp->sinkid));
/* build up the reverse mapping */
ALLOC_HASHTABLE(disp->name_to_dispid);
zend_hash_init(disp->name_to_dispid, 0, NULL, ZVAL_PTR_DTOR, 0);
zend_hash_internal_pointer_reset_ex(id_to_name, &pos);
while (HASH_KEY_NON_EXISTANT != (keytype =
zend_hash_get_current_key_ex(id_to_name, &name, &namelen, &pid, 0, &pos))) {
if (keytype == HASH_KEY_IS_LONG) {
zend_hash_get_current_data_ex(id_to_name, (void**)&ntmp, &pos);
MAKE_STD_ZVAL(tmp);
ZVAL_LONG(tmp, pid);
zend_hash_update(disp->name_to_dispid, Z_STRVAL_PP(ntmp),
Z_STRLEN_PP(ntmp)+1, (void*)&tmp, sizeof(zval *), NULL);
}
zend_hash_move_forward_ex(id_to_name, &pos);
}
return (IDispatch*)disp;
}
PHPAPI IDispatch *php_com_wrapper_export(zval *val TSRMLS_DC)
{
php_dispatchex *disp = NULL;
if (Z_TYPE_P(val) != IS_OBJECT)
return NULL;
if (php_com_is_valid_object(val TSRMLS_CC)) {
/* pass back its IDispatch directly */
php_com_dotnet_object *obj = CDNO_FETCH(val);
if (obj == NULL)
return NULL;
if (V_VT(&obj->v) == VT_DISPATCH && V_DISPATCH(&obj->v)) {
IDispatch_AddRef(V_DISPATCH(&obj->v));
return V_DISPATCH(&obj->v);
}
return NULL;
}
disp = disp_constructor(val TSRMLS_CC);
generate_dispids(disp TSRMLS_CC);
return (IDispatch*)disp;
}

View file

@ -7,7 +7,7 @@ if (PHP_COM_DOTNET == "yes") {
CHECK_LIB('oleaut32.lib', 'com_dotnet');
EXTENSION("com_dotnet", "com_com.c com_dotnet.c com_extension.c \
com_handlers.c com_iterator.c com_misc.c com_olechar.c \
com_typeinfo.c com_variant.c");
com_typeinfo.c com_variant.c com_wrapper.c com_saproxy.c");
AC_DEFINE('HAVE_COM_DOTNET', 1, 'Have COM_DOTNET support');
CHECK_HEADER_ADD_INCLUDE('mscoree.h', 'CFLAGS_COM_DOTNET');
}

View file

@ -1,6 +1,6 @@
/*
+----------------------------------------------------------------------+
| PHP Version 4 |
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2003 The PHP Group |
+----------------------------------------------------------------------+
@ -40,14 +40,13 @@ PHP_RINIT_FUNCTION(com_dotnet);
PHP_RSHUTDOWN_FUNCTION(com_dotnet);
PHP_MINFO_FUNCTION(com_dotnet);
PHP_FUNCTION(com_create_guid);
ZEND_BEGIN_MODULE_GLOBALS(com_dotnet)
zend_bool allow_dcom;
zend_bool autoreg_verbose;
zend_bool autoreg_on;
zend_bool autoreg_case_sensitive;
void *dotnet_runtime_stuff; /* opaque to avoid cluttering up other modules */
int code_page; /* default code_page if left unspecified */
ZEND_END_MODULE_GLOBALS(com_dotnet)
#ifdef ZTS

View file

@ -1,6 +1,6 @@
/*
+----------------------------------------------------------------------+
| PHP Version 4 |
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2003 The PHP Group |
+----------------------------------------------------------------------+
@ -41,6 +41,11 @@ typedef struct _php_com_dotnet_object {
zend_class_entry *ce;
DISPID default_bind; /* default property for array accesses */
/* associated event sink */
IDispatch *sink_dispatch;
GUID sink_id;
DWORD sink_cookie;
} php_com_dotnet_object;
static inline int php_com_is_valid_object(zval *zv TSRMLS_DC)
@ -54,7 +59,7 @@ static inline int php_com_is_valid_object(zval *zv TSRMLS_DC)
#define CDNO_FETCH(zv) (php_com_dotnet_object*)zend_object_store_get_object(zv TSRMLS_CC)
#define CDNO_FETCH_VERIFY(obj, zv) do { \
if (!php_com_is_valid_object(zv TSRMLS_CC)) { \
php_com_throw_exception("expected a variant object" TSRMLS_CC); \
php_com_throw_exception(E_UNEXPECTED, "expected a variant object" TSRMLS_CC); \
return; \
} \
obj = (php_com_dotnet_object*)zend_object_store_get_object(zv TSRMLS_CC); \
@ -62,13 +67,18 @@ static inline int php_com_is_valid_object(zval *zv TSRMLS_DC)
/* com_extension.c */
TsHashTable php_com_typelibraries;
zend_class_entry *php_com_variant_class_entry;
zend_class_entry *php_com_variant_class_entry, *php_com_exception_class_entry, *php_com_saproxy_class_entry;
/* com_handlers.c */
zend_object_value php_com_object_new(zend_class_entry *ce TSRMLS_DC);
void php_com_object_clone(void *object, void **clone_ptr TSRMLS_DC);
void php_com_object_dtor(void *object, zend_object_handle handle TSRMLS_DC);
zend_object_handlers php_com_object_handlers;
void php_com_object_enable_event_sink(php_com_dotnet_object *obj, int enable TSRMLS_DC);
/* com_saproxy.c */
zend_object_iterator *php_com_saproxy_iter_get(zend_class_entry *ce, zval *object TSRMLS_DC);
int php_com_saproxy_create(zval *com_object, zval *proxy_out, long index TSRMLS_DC);
/* com_olechar.c */
PHPAPI char *php_com_olestring_to_string(OLECHAR *olestring,
@ -79,6 +89,12 @@ PHPAPI OLECHAR *php_com_string_to_olestring(char *string,
/* com_com.c */
PHP_FUNCTION(com_create_instance);
PHP_FUNCTION(com_event_sink);
PHP_FUNCTION(com_create_guid);
PHP_FUNCTION(com_print_typeinfo);
PHP_FUNCTION(com_message_pump);
PHP_FUNCTION(com_load_typelib);
HRESULT php_com_invoke_helper(php_com_dotnet_object *obj, DISPID id_member,
WORD flags, DISPPARAMS *disp_params, VARIANT *v TSRMLS_DC);
HRESULT php_com_get_id_of_name(php_com_dotnet_object *obj, char *name,
@ -88,6 +104,11 @@ int php_com_do_invoke_by_id(php_com_dotnet_object *obj, DISPID dispid,
int php_com_do_invoke(php_com_dotnet_object *obj, char *name, int namelen,
WORD flags, VARIANT *v, int nargs, zval **args TSRMLS_DC);
/* com_wrapper.c */
int php_com_wrapper_minit(INIT_FUNC_ARGS);
PHPAPI IDispatch *php_com_wrapper_export_as_sink(zval *val, GUID *sinkid, HashTable *id_to_name TSRMLS_DC);
PHPAPI IDispatch *php_com_wrapper_export(zval *val TSRMLS_DC);
/* com_variant.c */
PHP_FUNCTION(com_variant_create_instance);
PHP_FUNCTION(variant_set);
@ -127,20 +148,22 @@ void php_com_dotnet_rshutdown(TSRMLS_D);
void php_com_dotnet_mshutdown(TSRMLS_D);
/* com_misc.c */
zval *php_com_throw_exception(char *message TSRMLS_DC);
void php_com_throw_exception(HRESULT code, char *message TSRMLS_DC);
PHPAPI void php_com_wrap_dispatch(zval *z, IDispatch *disp,
int codepage TSRMLS_DC);
PHPAPI void php_com_wrap_variant(zval *z, VARIANT *v,
int codepage TSRMLS_DC);
PHPAPI int php_com_safearray_get_elem(VARIANT *array, VARIANT *dest, LONG dim1 TSRMLS_DC);
/* com_typeinfo.c */
PHPAPI ITypeLib *php_com_load_typelib_via_cache(char *search_string,
int mode, int codepage, int *cached TSRMLS_DC);
PHPAPI ITypeLib *php_com_load_typelib(char *search_string, int mode,
int codepage TSRMLS_DC);
int codepage, int *cached TSRMLS_DC);
PHPAPI ITypeLib *php_com_load_typelib(char *search_string, int codepage TSRMLS_DC);
PHPAPI int php_com_import_typelib(ITypeLib *TL, int mode,
int codepage TSRMLS_DC);
void php_com_typelibrary_dtor(void *pDest);
ITypeInfo *php_com_locate_typeinfo(char *typelibname, php_com_dotnet_object *obj, char *dispname, int sink TSRMLS_DC);
int php_com_process_typeinfo(ITypeInfo *typeinfo, HashTable *id_to_name, int printdef, GUID *guid, int codepage TSRMLS_DC);
/* com_iterator.c */
zend_object_iterator *php_com_iter_get(zend_class_entry *ce, zval *object TSRMLS_DC);