summaryrefslogtreecommitdiff
path: root/ext/com_dotnet
diff options
context:
space:
mode:
Diffstat (limited to 'ext/com_dotnet')
-rw-r--r--ext/com_dotnet/CREDITS2
-rw-r--r--ext/com_dotnet/README71
-rw-r--r--ext/com_dotnet/com_com.c844
-rw-r--r--ext/com_dotnet/com_dotnet.c322
-rw-r--r--ext/com_dotnet/com_extension.c520
-rw-r--r--ext/com_dotnet/com_handlers.c683
-rw-r--r--ext/com_dotnet/com_iterator.c250
-rw-r--r--ext/com_dotnet/com_misc.c144
-rw-r--r--ext/com_dotnet/com_olechar.c101
-rw-r--r--ext/com_dotnet/com_persist.c777
-rw-r--r--ext/com_dotnet/com_saproxy.c586
-rw-r--r--ext/com_dotnet/com_typeinfo.c607
-rw-r--r--ext/com_dotnet/com_variant.c1075
-rw-r--r--ext/com_dotnet/com_wrapper.c649
-rw-r--r--ext/com_dotnet/config.w3213
-rw-r--r--ext/com_dotnet/package.xml52
-rw-r--r--ext/com_dotnet/php_com_dotnet.h72
-rw-r--r--ext/com_dotnet/php_com_dotnet_internal.h182
-rw-r--r--ext/com_dotnet/tests/27974.phpt43
-rw-r--r--ext/com_dotnet/tests/bug33386.phpt40
-rw-r--r--ext/com_dotnet/tests/bug34272.phpt22
-rw-r--r--ext/com_dotnet/tests/bug39596.phpt23
-rw-r--r--ext/com_dotnet/tests/bug39606.phpt22
-rw-r--r--ext/com_dotnet/tests/bug45280.phpt19
-rw-r--r--ext/com_dotnet/tests/bug49192.phpt27
-rw-r--r--ext/com_dotnet/tests/variants.phpt637
26 files changed, 7783 insertions, 0 deletions
diff --git a/ext/com_dotnet/CREDITS b/ext/com_dotnet/CREDITS
new file mode 100644
index 0000000..8dd06fa
--- /dev/null
+++ b/ext/com_dotnet/CREDITS
@@ -0,0 +1,2 @@
+COM and .Net
+Wez Furlong
diff --git a/ext/com_dotnet/README b/ext/com_dotnet/README
new file mode 100644
index 0000000..0d9db40
--- /dev/null
+++ b/ext/com_dotnet/README
@@ -0,0 +1,71 @@
+This is the new php5 COM module.
+
+It is not 100% backwards compatible with PHP 4 ext/com, but you should not miss
+the "features" that have not been retained.
+
+This module exposes 3 classes: variant, com and dotnet(*).
+com and dotnet classes are descendants of the variant class; the only
+difference between the three are their constructors. Once instantiated, the
+module doesn't make a distinction between them.
+
+COM errrors are mapped to exceptions; you should protect your COM code using
+the try..catch construct if you want to be able to handle error conditions.
+
+Be warned that due to the way the ZE2 currently works, exceptions are only
+"armed" at the time they are detected, but do not "detonate" until the end of
+the statement. So, code like this:
+
+ $obj->foo[43]->bar();
+
+Where the foo[43] access triggers an exception will continue to call the bar()
+method on a null object and cause a fatal php error.
+
+Default properties and array access:
+
+$obj = new COM("...");
+$obj[1]->foo();
+
+The code above will use the type information for the object to determine its
+default property and then access it. In PHP 4, it was hard-coded to use the
+"Items" member, which was wrong.
+
+The default property will also be used by the casting support to determine the
+value for the object.
+
+Variants:
+
+This implementation of COM takes a simpler approach than the PHP 4 version;
+we only map a few native types to COM and vice-versa, leaving the more complex
+things as variants. This allows greater consistency of data when passing
+parameters to and from COM objects (no data will be lost). In addition, a
+large number of the variant API has been mapped to PHP space so that you can
+use it for working with the special variant decimal, currency and date time
+types. This could be used as a replacement for the bcmath extension, for
+example.
+
+You can use the new object casting hook to for a php-native representation of
+a variant object:
+
+$a = new variant(4);
+$b = new variant(6);
+$c = variant_add($a, $b);
+echo $c; // outputs 10 as a string, instead of Object
+
+Sample Script:
+
+<?php
+$word = new COM("word.application");
+print "Loaded Word, version {$word->Version}\n";
+$word->Visible = 1;
+$word->Documents->Add();
+$word->Selection->TypeText("This is a test...");
+$word->Documents[1]->SaveAs("Useless test.doc");
+$word->Quit();
+?>
+
+TODO:
+
+- documentation
+
+* dotnet support requires that you have the mscoree.h header from the .net sdk
+ when you build the module.
diff --git a/ext/com_dotnet/com_com.c b/ext/com_dotnet/com_com.c
new file mode 100644
index 0000000..fe48ee9
--- /dev/null
+++ b/ext/com_dotnet/com_com.c
@@ -0,0 +1,844 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 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_01.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$ */
+
+#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_exceptions.h"
+
+/* {{{ com_create_instance - ctor for COM class */
+PHP_FUNCTION(com_create_instance)
+{
+ zval *object = getThis();
+ zval *server_params = NULL;
+ php_com_dotnet_object *obj;
+ char *module_name, *typelib_name = NULL, *server_name = NULL;
+ char *user_name = NULL, *domain_name = NULL, *password = NULL;
+ int module_name_len, typelib_name_len, server_name_len,
+ user_name_len, domain_name_len, password_len;
+ OLECHAR *moniker;
+ CLSID clsid;
+ CLSCTX ctx = CLSCTX_SERVER;
+ HRESULT res = E_FAIL;
+ int mode = COMG(autoreg_case_sensitive) ? CONST_CS : 0;
+ ITypeLib *TL = NULL;
+ COSERVERINFO info;
+ COAUTHIDENTITY authid = {0};
+ COAUTHINFO authinfo = {
+ RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL,
+ RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE,
+ &authid, EOAC_NONE
+ };
+
+ php_com_initialize(TSRMLS_C);
+ obj = CDNO_FETCH(object);
+
+ if (FAILURE == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
+ ZEND_NUM_ARGS() TSRMLS_CC, "s|s!ls",
+ &module_name, &module_name_len, &server_name, &server_name_len,
+ &obj->code_page, &typelib_name, &typelib_name_len) &&
+ FAILURE == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
+ ZEND_NUM_ARGS() TSRMLS_CC, "sa|ls",
+ &module_name, &module_name_len, &server_params, &obj->code_page,
+ &typelib_name, &typelib_name_len)) {
+
+ php_com_throw_exception(E_INVALIDARG, "Could not create COM object - invalid arguments!" TSRMLS_CC);
+ ZVAL_NULL(object);
+ return;
+ }
+
+ if (server_name) {
+ ctx = CLSCTX_REMOTE_SERVER;
+ } else if (server_params) {
+ zval **tmp;
+
+ /* decode the data from the array */
+
+ if (SUCCESS == zend_hash_find(HASH_OF(server_params),
+ "Server", sizeof("Server"), (void**)&tmp)) {
+ convert_to_string_ex(tmp);
+ server_name = Z_STRVAL_PP(tmp);
+ server_name_len = Z_STRLEN_PP(tmp);
+ ctx = CLSCTX_REMOTE_SERVER;
+ }
+
+ if (SUCCESS == zend_hash_find(HASH_OF(server_params),
+ "Username", sizeof("Username"), (void**)&tmp)) {
+ convert_to_string_ex(tmp);
+ user_name = Z_STRVAL_PP(tmp);
+ user_name_len = Z_STRLEN_PP(tmp);
+ }
+
+ if (SUCCESS == zend_hash_find(HASH_OF(server_params),
+ "Password", sizeof("Password"), (void**)&tmp)) {
+ convert_to_string_ex(tmp);
+ password = Z_STRVAL_PP(tmp);
+ password_len = Z_STRLEN_PP(tmp);
+ }
+
+ if (SUCCESS == zend_hash_find(HASH_OF(server_params),
+ "Domain", sizeof("Domain"), (void**)&tmp)) {
+ convert_to_string_ex(tmp);
+ domain_name = Z_STRVAL_PP(tmp);
+ domain_name_len = Z_STRLEN_PP(tmp);
+ }
+
+ if (SUCCESS == zend_hash_find(HASH_OF(server_params),
+ "Flags", sizeof("Flags"), (void**)&tmp)) {
+ convert_to_long_ex(tmp);
+ ctx = (CLSCTX)Z_LVAL_PP(tmp);
+ }
+ }
+
+ if (server_name && !COMG(allow_dcom)) {
+ php_com_throw_exception(E_ERROR, "DCOM has been disabled by your administrator [com.allow_dcom=0]" TSRMLS_CC);
+ return;
+ }
+
+ moniker = php_com_string_to_olestring(module_name, module_name_len, obj->code_page TSRMLS_CC);
+
+ /* if instantiating a remote object, either directly, or via
+ * a moniker, fill in the relevant info */
+ if (server_name) {
+ info.dwReserved1 = 0;
+ info.dwReserved2 = 0;
+ info.pwszName = php_com_string_to_olestring(server_name, server_name_len, obj->code_page TSRMLS_CC);
+
+ if (user_name) {
+ authid.User = php_com_string_to_olestring(user_name, -1, obj->code_page TSRMLS_CC);
+ authid.UserLength = user_name_len;
+
+ if (password) {
+ authid.Password = (OLECHAR*)password;
+ authid.PasswordLength = password_len;
+ } else {
+ authid.Password = (OLECHAR*)"";
+ authid.PasswordLength = 0;
+ }
+
+ if (domain_name) {
+ authid.Domain = (OLECHAR*)domain_name;
+ authid.DomainLength = domain_name_len;
+ } else {
+ authid.Domain = (OLECHAR*)"";
+ authid.DomainLength = 0;
+ }
+ authid.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
+ info.pAuthInfo = &authinfo;
+ } else {
+ info.pAuthInfo = NULL;
+ }
+ }
+
+ if (FAILED(CLSIDFromString(moniker, &clsid))) {
+ /* try to use it as a moniker */
+ IBindCtx *pBindCtx = NULL;
+ IMoniker *pMoniker = NULL;
+ ULONG ulEaten;
+ BIND_OPTS2 bopt = {0};
+
+ if (SUCCEEDED(res = CreateBindCtx(0, &pBindCtx))) {
+ if (server_name) {
+ /* fill in the remote server info.
+ * MSDN docs indicate that this might be ignored in
+ * current win32 implementations, but at least we are
+ * doing the right thing in readiness for the day that
+ * it does work */
+ bopt.cbStruct = sizeof(bopt);
+ IBindCtx_GetBindOptions(pBindCtx, (BIND_OPTS*)&bopt);
+ bopt.pServerInfo = &info;
+ /* apparently, GetBindOptions will only ever return
+ * a regular BIND_OPTS structure. My gut feeling is
+ * that it will modify the size field to reflect that
+ * so lets be safe and set it to the BIND_OPTS2 size
+ * again */
+ bopt.cbStruct = sizeof(bopt);
+ IBindCtx_SetBindOptions(pBindCtx, (BIND_OPTS*)&bopt);
+ }
+
+ if (SUCCEEDED(res = MkParseDisplayName(pBindCtx, moniker, &ulEaten, &pMoniker))) {
+ res = IMoniker_BindToObject(pMoniker, pBindCtx,
+ NULL, &IID_IDispatch, (LPVOID*)&V_DISPATCH(&obj->v));
+
+ if (SUCCEEDED(res)) {
+ V_VT(&obj->v) = VT_DISPATCH;
+ }
+
+ IMoniker_Release(pMoniker);
+ }
+ }
+ if (pBindCtx) {
+ IBindCtx_Release(pBindCtx);
+ }
+ } else if (server_name) {
+ MULTI_QI qi;
+
+ qi.pIID = &IID_IDispatch;
+ qi.pItf = NULL;
+ qi.hr = S_OK;
+
+ res = CoCreateInstanceEx(&clsid, NULL, ctx, &info, 1, &qi);
+
+ if (SUCCEEDED(res)) {
+ res = qi.hr;
+ V_DISPATCH(&obj->v) = (IDispatch*)qi.pItf;
+ V_VT(&obj->v) = VT_DISPATCH;
+ }
+ } else {
+ res = CoCreateInstance(&clsid, NULL, CLSCTX_SERVER, &IID_IDispatch, (LPVOID*)&V_DISPATCH(&obj->v));
+ if (SUCCEEDED(res)) {
+ V_VT(&obj->v) = VT_DISPATCH;
+ }
+ }
+
+ if (server_name) {
+ STR_FREE((char*)info.pwszName);
+ STR_FREE((char*)authid.User);
+ }
+
+ efree(moniker);
+
+ if (FAILED(res)) {
+ char *werr, *msg;
+
+ werr = php_win32_error_to_msg(res);
+ spprintf(&msg, 0, "Failed to create COM object `%s': %s", module_name, werr);
+ LocalFree(werr);
+
+ php_com_throw_exception(res, msg TSRMLS_CC);
+ efree(msg);
+ ZVAL_NULL(object);
+ return;
+ }
+
+ /* we got the object and it lives ! */
+
+ /* see if it has TypeInfo available */
+ if (FAILED(IDispatch_GetTypeInfo(V_DISPATCH(&obj->v), 0, LANG_NEUTRAL, &obj->typeinfo)) && typelib_name) {
+ /* load up the library from the named file */
+ int cached;
+
+ TL = php_com_load_typelib_via_cache(typelib_name, obj->code_page, &cached TSRMLS_CC);
+
+ if (TL) {
+ if (COMG(autoreg_on) && !cached) {
+ php_com_import_typelib(TL, mode, obj->code_page TSRMLS_CC);
+ }
+
+ /* cross your fingers... there is no guarantee that this ITypeInfo
+ * instance has any relation to this IDispatch instance... */
+ ITypeLib_GetTypeInfo(TL, 0, &obj->typeinfo);
+ ITypeLib_Release(TL);
+ }
+ } else if (obj->typeinfo && COMG(autoreg_on)) {
+ int idx;
+
+ if (SUCCEEDED(ITypeInfo_GetContainingTypeLib(obj->typeinfo, &TL, &idx))) {
+ /* check if the library is already in the cache by getting its name */
+ BSTR name;
+
+ if (SUCCEEDED(ITypeLib_GetDocumentation(TL, -1, &name, NULL, NULL, NULL))) {
+ typelib_name = php_com_olestring_to_string(name, &typelib_name_len, obj->code_page TSRMLS_CC);
+
+ if (SUCCESS == zend_ts_hash_add(&php_com_typelibraries, typelib_name, typelib_name_len+1, (void*)&TL, sizeof(ITypeLib*), NULL)) {
+ php_com_import_typelib(TL, mode, obj->code_page TSRMLS_CC);
+
+ /* add a reference for the hash */
+ ITypeLib_AddRef(TL);
+ }
+
+ } else {
+ /* try it anyway */
+ php_com_import_typelib(TL, mode, obj->code_page TSRMLS_CC);
+ }
+
+ ITypeLib_Release(TL);
+ }
+ }
+
+}
+/* }}} */
+
+/* {{{ proto object com_get_active_object(string progid [, int code_page ])
+ Returns a handle to an already running instance of a COM object */
+PHP_FUNCTION(com_get_active_object)
+{
+ CLSID clsid;
+ char *module_name;
+ int module_name_len;
+ long code_page = COMG(code_page);
+ IUnknown *unk = NULL;
+ IDispatch *obj = NULL;
+ HRESULT res;
+ OLECHAR *module = NULL;
+
+ php_com_initialize(TSRMLS_C);
+ if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l",
+ &module_name, &module_name_len, &code_page)) {
+ php_com_throw_exception(E_INVALIDARG, "Invalid arguments!" TSRMLS_CC);
+ return;
+ }
+
+ module = php_com_string_to_olestring(module_name, module_name_len, code_page TSRMLS_CC);
+
+ res = CLSIDFromString(module, &clsid);
+
+ if (FAILED(res)) {
+ php_com_throw_exception(res, NULL TSRMLS_CC);
+ } else {
+ res = GetActiveObject(&clsid, NULL, &unk);
+
+ if (FAILED(res)) {
+ php_com_throw_exception(res, NULL TSRMLS_CC);
+ } else {
+ res = IUnknown_QueryInterface(unk, &IID_IDispatch, &obj);
+
+ if (FAILED(res)) {
+ php_com_throw_exception(res, NULL TSRMLS_CC);
+ } else if (obj) {
+ /* we got our dispatchable object */
+ php_com_wrap_dispatch(return_value, obj, code_page TSRMLS_CC);
+ }
+ }
+ }
+
+ if (obj) {
+ IDispatch_Release(obj);
+ }
+ if (unk) {
+ IUnknown_Release(obj);
+ }
+ efree(module);
+}
+/* }}} */
+
+/* Performs an Invoke on the given com object.
+ * returns a failure code and creates an exception if there was an error */
+HRESULT php_com_invoke_helper(php_com_dotnet_object *obj, DISPID id_member,
+ WORD flags, DISPPARAMS *disp_params, VARIANT *v, int silent, int allow_noarg TSRMLS_DC)
+{
+ HRESULT hr;
+ unsigned int arg_err;
+ EXCEPINFO e = {0};
+
+ hr = IDispatch_Invoke(V_DISPATCH(&obj->v), id_member,
+ &IID_NULL, LOCALE_SYSTEM_DEFAULT, flags, disp_params, v, &e, &arg_err);
+
+ if (silent == 0 && FAILED(hr)) {
+ char *source = NULL, *desc = NULL, *msg = NULL;
+ int source_len, desc_len;
+
+ switch (hr) {
+ case DISP_E_EXCEPTION:
+ if (e.bstrSource) {
+ source = php_com_olestring_to_string(e.bstrSource, &source_len, obj->code_page TSRMLS_CC);
+ SysFreeString(e.bstrSource);
+ }
+ if (e.bstrDescription) {
+ desc = php_com_olestring_to_string(e.bstrDescription, &desc_len, obj->code_page TSRMLS_CC);
+ SysFreeString(e.bstrDescription);
+ }
+ if (PG(html_errors)) {
+ spprintf(&msg, 0, "<b>Source:</b> %s<br/><b>Description:</b> %s",
+ source ? source : "Unknown",
+ desc ? desc : "Unknown");
+ } else {
+ spprintf(&msg, 0, "Source: %s\nDescription: %s",
+ source ? source : "Unknown",
+ desc ? desc : "Unknown");
+ }
+ if (desc) {
+ efree(desc);
+ }
+ if (source) {
+ efree(source);
+ }
+ if (e.bstrHelpFile) {
+ SysFreeString(e.bstrHelpFile);
+ }
+ break;
+
+ case DISP_E_PARAMNOTFOUND:
+ case DISP_E_TYPEMISMATCH:
+ desc = php_win32_error_to_msg(hr);
+ spprintf(&msg, 0, "Parameter %d: %s", arg_err, desc);
+ LocalFree(desc);
+ break;
+
+ case DISP_E_BADPARAMCOUNT:
+ if ((disp_params->cArgs + disp_params->cNamedArgs == 0) && (allow_noarg == 1)) {
+ /* if getting a property and they are missing all parameters,
+ * we want to create a proxy object for them; so lets not create an
+ * exception here */
+ msg = NULL;
+ break;
+ }
+ /* else fall through */
+
+ default:
+ desc = php_win32_error_to_msg(hr);
+ spprintf(&msg, 0, "Error [0x%08x] %s", hr, desc);
+ LocalFree(desc);
+ break;
+ }
+
+ if (msg) {
+ php_com_throw_exception(hr, msg TSRMLS_CC);
+ efree(msg);
+ }
+ }
+
+ return hr;
+}
+
+/* map an ID to a name */
+HRESULT php_com_get_id_of_name(php_com_dotnet_object *obj, char *name,
+ int namelen, DISPID *dispid TSRMLS_DC)
+{
+ OLECHAR *olename;
+ HRESULT hr;
+ DISPID *dispid_ptr;
+
+ if (namelen == -1) {
+ namelen = strlen(name);
+ }
+
+ if (obj->id_of_name_cache && SUCCESS == zend_hash_find(obj->id_of_name_cache, name, namelen, (void**)&dispid_ptr)) {
+ *dispid = *dispid_ptr;
+ return S_OK;
+ }
+
+ olename = php_com_string_to_olestring(name, namelen, obj->code_page TSRMLS_CC);
+
+ if (obj->typeinfo) {
+ hr = ITypeInfo_GetIDsOfNames(obj->typeinfo, &olename, 1, dispid);
+ if (FAILED(hr)) {
+ hr = IDispatch_GetIDsOfNames(V_DISPATCH(&obj->v), &IID_NULL, &olename, 1, LOCALE_SYSTEM_DEFAULT, dispid);
+ if (SUCCEEDED(hr)) {
+ /* fall back on IDispatch direct */
+ ITypeInfo_Release(obj->typeinfo);
+ obj->typeinfo = NULL;
+ }
+ }
+ } else {
+ hr = IDispatch_GetIDsOfNames(V_DISPATCH(&obj->v), &IID_NULL, &olename, 1, LOCALE_SYSTEM_DEFAULT, dispid);
+ }
+ efree(olename);
+
+ if (SUCCEEDED(hr)) {
+ /* cache the mapping */
+ if (!obj->id_of_name_cache) {
+ ALLOC_HASHTABLE(obj->id_of_name_cache);
+ zend_hash_init(obj->id_of_name_cache, 2, NULL, NULL, 0);
+ }
+ zend_hash_update(obj->id_of_name_cache, name, namelen, dispid, sizeof(*dispid), NULL);
+ }
+
+ return hr;
+}
+
+/* the core of COM */
+int php_com_do_invoke_byref(php_com_dotnet_object *obj, char *name, int namelen,
+ WORD flags, VARIANT *v, int nargs, zval ***args TSRMLS_DC)
+{
+ DISPID dispid, altdispid;
+ DISPPARAMS disp_params;
+ HRESULT hr;
+ VARIANT *vargs = NULL, *byref_vals = NULL;
+ int i, byref_count = 0, j;
+ zend_internal_function *f = (zend_internal_function*)EG(current_execute_data)->function_state.function;
+
+ /* assumption: that the active function (f) is the function we generated for the engine */
+ if (!f || f->arg_info == NULL) {
+ f = NULL;
+ }
+
+ hr = php_com_get_id_of_name(obj, name, namelen, &dispid TSRMLS_CC);
+
+ if (FAILED(hr)) {
+ char *winerr = NULL;
+ char *msg = NULL;
+ winerr = php_win32_error_to_msg(hr);
+ spprintf(&msg, 0, "Unable to lookup `%s': %s", name, winerr);
+ LocalFree(winerr);
+ php_com_throw_exception(hr, msg TSRMLS_CC);
+ efree(msg);
+ return FAILURE;
+ }
+
+
+ if (nargs) {
+ vargs = (VARIANT*)safe_emalloc(sizeof(VARIANT), nargs, 0);
+ }
+
+ if (f) {
+ for (i = 0; i < nargs; i++) {
+ if (f->arg_info[nargs - i - 1].pass_by_reference) {
+ byref_count++;
+ }
+ }
+ }
+
+ if (byref_count) {
+ byref_vals = (VARIANT*)safe_emalloc(sizeof(VARIANT), byref_count, 0);
+ for (j = 0, i = 0; i < nargs; i++) {
+ if (f->arg_info[nargs - i - 1].pass_by_reference) {
+ /* put the value into byref_vals instead */
+ php_com_variant_from_zval(&byref_vals[j], *args[nargs - i - 1], obj->code_page TSRMLS_CC);
+
+ /* if it is already byref, "move" it into the vargs array, otherwise
+ * make vargs a reference to this value */
+ if (V_VT(&byref_vals[j]) & VT_BYREF) {
+ memcpy(&vargs[i], &byref_vals[j], sizeof(vargs[i]));
+ VariantInit(&byref_vals[j]); /* leave the variant slot empty to simplify cleanup */
+ } else {
+ VariantInit(&vargs[i]);
+ V_VT(&vargs[i]) = V_VT(&byref_vals[j]) | VT_BYREF;
+ /* union magic ensures that this works out */
+ vargs[i].byref = &V_UINT(&byref_vals[j]);
+ }
+ j++;
+ } else {
+ php_com_variant_from_zval(&vargs[i], *args[nargs - i - 1], obj->code_page TSRMLS_CC);
+ }
+ }
+
+ } else {
+ /* Invoke'd args are in reverse order */
+ for (i = 0; i < nargs; i++) {
+ php_com_variant_from_zval(&vargs[i], *args[nargs - i - 1], obj->code_page TSRMLS_CC);
+ }
+ }
+
+ disp_params.cArgs = nargs;
+ disp_params.cNamedArgs = 0;
+ disp_params.rgvarg = vargs;
+ disp_params.rgdispidNamedArgs = NULL;
+
+ if (flags & DISPATCH_PROPERTYPUT) {
+ altdispid = DISPID_PROPERTYPUT;
+ disp_params.rgdispidNamedArgs = &altdispid;
+ disp_params.cNamedArgs = 1;
+ }
+
+ /* this will create an exception if needed */
+ hr = php_com_invoke_helper(obj, dispid, flags, &disp_params, v, 0, 0 TSRMLS_CC);
+
+ /* release variants */
+ if (vargs) {
+ for (i = 0, j = 0; i < nargs; i++) {
+ /* if this was byref, update the zval */
+ if (f && f->arg_info[nargs - i - 1].pass_by_reference) {
+ SEPARATE_ZVAL_IF_NOT_REF(args[nargs - i - 1]);
+
+ /* if the variant is pointing at the byref_vals, we need to map
+ * the pointee value as a zval; otherwise, the value is pointing
+ * into an existing PHP variant record */
+ if (V_VT(&vargs[i]) & VT_BYREF) {
+ if (vargs[i].byref == &V_UINT(&byref_vals[j])) {
+ /* copy that value */
+ php_com_zval_from_variant(*args[nargs - i - 1], &byref_vals[j],
+ obj->code_page TSRMLS_CC);
+ }
+ } else {
+ /* not sure if this can ever happen; the variant we marked as BYREF
+ * is no longer BYREF - copy its value */
+ php_com_zval_from_variant(*args[nargs - i - 1], &vargs[i],
+ obj->code_page TSRMLS_CC);
+ }
+ VariantClear(&byref_vals[j]);
+ j++;
+ }
+ VariantClear(&vargs[i]);
+ }
+ efree(vargs);
+ }
+
+ return SUCCEEDED(hr) ? SUCCESS : FAILURE;
+}
+
+
+
+int php_com_do_invoke_by_id(php_com_dotnet_object *obj, DISPID dispid,
+ WORD flags, VARIANT *v, int nargs, zval **args, int silent, int allow_noarg TSRMLS_DC)
+{
+ DISPID altdispid;
+ DISPPARAMS disp_params;
+ HRESULT hr;
+ VARIANT *vargs = NULL;
+ int i;
+
+ if (nargs) {
+ vargs = (VARIANT*)safe_emalloc(sizeof(VARIANT), nargs, 0);
+ }
+
+ /* Invoke'd args are in reverse order */
+ for (i = 0; i < nargs; i++) {
+ php_com_variant_from_zval(&vargs[i], args[nargs - i - 1], obj->code_page TSRMLS_CC);
+ }
+
+ disp_params.cArgs = nargs;
+ disp_params.cNamedArgs = 0;
+ disp_params.rgvarg = vargs;
+ disp_params.rgdispidNamedArgs = NULL;
+
+ if (flags & DISPATCH_PROPERTYPUT) {
+ altdispid = DISPID_PROPERTYPUT;
+ disp_params.rgdispidNamedArgs = &altdispid;
+ disp_params.cNamedArgs = 1;
+ }
+
+ /* this will create an exception if needed */
+ hr = php_com_invoke_helper(obj, dispid, flags, &disp_params, v, silent, allow_noarg TSRMLS_CC);
+
+ /* release variants */
+ if (vargs) {
+ for (i = 0; i < nargs; i++) {
+ VariantClear(&vargs[i]);
+ }
+ efree(vargs);
+ }
+
+ /* a bit of a hack this, but it's needed for COM array access. */
+ if (hr == DISP_E_BADPARAMCOUNT)
+ return hr;
+
+ return SUCCEEDED(hr) ? SUCCESS : FAILURE;
+}
+
+int php_com_do_invoke(php_com_dotnet_object *obj, char *name, int namelen,
+ WORD flags, VARIANT *v, int nargs, zval **args, int allow_noarg TSRMLS_DC)
+{
+ DISPID dispid;
+ HRESULT hr;
+ char *winerr = NULL;
+ char *msg = NULL;
+
+ hr = php_com_get_id_of_name(obj, name, namelen, &dispid TSRMLS_CC);
+
+ if (FAILED(hr)) {
+ winerr = php_win32_error_to_msg(hr);
+ spprintf(&msg, 0, "Unable to lookup `%s': %s", name, winerr);
+ LocalFree(winerr);
+ php_com_throw_exception(hr, msg TSRMLS_CC);
+ efree(msg);
+ return FAILURE;
+ }
+
+ return php_com_do_invoke_by_id(obj, dispid, flags, v, nargs, args, 0, allow_noarg TSRMLS_CC);
+}
+
+/* {{{ proto string com_create_guid()
+ Generate a globally unique identifier (GUID) */
+PHP_FUNCTION(com_create_guid)
+{
+ GUID retval;
+ OLECHAR *guid_string;
+
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+
+ php_com_initialize(TSRMLS_C);
+ if (CoCreateGuid(&retval) == S_OK && StringFromCLSID(&retval, &guid_string) == S_OK) {
+ Z_TYPE_P(return_value) = IS_STRING;
+ Z_STRVAL_P(return_value) = php_com_olestring_to_string(guid_string, &Z_STRLEN_P(return_value), CP_ACP TSRMLS_CC);
+
+ CoTaskMemFree(guid_string);
+ } else {
+ 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;
+ }
+
+ php_com_initialize(TSRMLS_C);
+ 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;
+ }
+
+ php_com_initialize(TSRMLS_C);
+ 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;
+
+ php_com_initialize(TSRMLS_C);
+ 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;
+ int 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;
+
+ php_com_initialize(TSRMLS_C);
+ 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
+ */
diff --git a/ext/com_dotnet/com_dotnet.c b/ext/com_dotnet/com_dotnet.c
new file mode 100644
index 0000000..7fbdae1
--- /dev/null
+++ b/ext/com_dotnet/com_dotnet.c
@@ -0,0 +1,322 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 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_01.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$ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php.h"
+
+#if HAVE_MSCOREE_H
+# include "php_ini.h"
+# include "ext/standard/info.h"
+# include "php_com_dotnet.h"
+# include "php_com_dotnet_internal.h"
+# include "Zend/zend_exceptions.h"
+# include <mscoree.h>
+
+/* Since there is no official public mscorlib.h header file, and since
+ * generating your own version from the elusive binary .tlb file takes a lot of
+ * hacking and results in a 3MB header file (!), we opt for this slightly
+ * voodoo approach. The following is just enough definition to be able to
+ * reach the _AppDomain::CreateInstance method that we need to use to be able
+ * to fire up .Net objects. We used to use IDispatch for this, but it would
+ * not always work.
+ *
+ * The following info was obtained using OleView to export the IDL from
+ * mscorlib.tlb. Note that OleView is unable to generate C headers for this
+ * particular tlb... hence this mess.
+ */
+
+const GUID IID_mscorlib_System_AppDomain = {
+0x05F696DC, 0x2B29, 0x3663, {0xAD, 0x8B, 0xC4, 0x38, 0x9C, 0xF2, 0xA7, 0x13 }};
+
+typedef struct _Imscorlib_System_AppDomain IAppDomain;
+
+struct _Imscorlib_System_AppDomainVtbl {
+ BEGIN_INTERFACE
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+ IAppDomain * This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */ void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
+ IAppDomain * This);
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
+ IAppDomain * This);
+
+ /* this is padding to get CreateInstance into the correct position */
+#define DUMMY_METHOD(x) HRESULT ( STDMETHODCALLTYPE *dummy_##x )(IAppDomain *This)
+
+ DUMMY_METHOD(GetTypeInfoCount);
+ DUMMY_METHOD(GetTypeInfo);
+ DUMMY_METHOD(GetIDsOfNames);
+ DUMMY_METHOD(Invoke);
+ DUMMY_METHOD(ToString);
+ DUMMY_METHOD(Equals);
+ DUMMY_METHOD(GetHashCode);
+ DUMMY_METHOD(GetType);
+ DUMMY_METHOD(InitializeLifetimeService);
+ DUMMY_METHOD(GetLifetimeService);
+ DUMMY_METHOD(Evidence);
+ DUMMY_METHOD(add_DomainUnload);
+ DUMMY_METHOD(remove_DomainUnload);
+ DUMMY_METHOD(add_AssemblyLoad);
+ DUMMY_METHOD(remove_AssemblyLoad);
+ DUMMY_METHOD(add_ProcessExit);
+ DUMMY_METHOD(remove_ProcessExit);
+ DUMMY_METHOD(add_TypeResolve);
+ DUMMY_METHOD(remove_TypeResolve);
+ DUMMY_METHOD(add_ResourceResolve);
+ DUMMY_METHOD(remove_ResourceResolve);
+ DUMMY_METHOD(add_AssemblyResolve);
+ DUMMY_METHOD(remove_AssemblyResolve);
+ DUMMY_METHOD(add_UnhandledException);
+ DUMMY_METHOD(remove_UnhandledException);
+ DUMMY_METHOD(DefineDynamicAssembly);
+ DUMMY_METHOD(DefineDynamicAssembly_2);
+ DUMMY_METHOD(DefineDynamicAssembly_3);
+ DUMMY_METHOD(DefineDynamicAssembly_4);
+ DUMMY_METHOD(DefineDynamicAssembly_5);
+ DUMMY_METHOD(DefineDynamicAssembly_6);
+ DUMMY_METHOD(DefineDynamicAssembly_7);
+ DUMMY_METHOD(DefineDynamicAssembly_8);
+ DUMMY_METHOD(DefineDynamicAssembly_9);
+
+ HRESULT ( STDMETHODCALLTYPE *CreateInstance )(IAppDomain * This, BSTR AssemblyName, BSTR typeName, IUnknown **pRetVal);
+ HRESULT ( STDMETHODCALLTYPE *CreateInstanceFrom )(IAppDomain * This, BSTR AssemblyFile, BSTR typeName, IUnknown **pRetVal);
+
+ /* more methods live here */
+
+ END_INTERFACE
+};
+
+struct _Imscorlib_System_AppDomain {
+ struct _Imscorlib_System_AppDomainVtbl *lpVtbl;
+};
+
+
+struct dotnet_runtime_stuff {
+ ICorRuntimeHost *dotnet_host;
+ IAppDomain *dotnet_domain;
+ DISPID create_instance;
+};
+
+static HRESULT dotnet_init(char **p_where TSRMLS_DC)
+{
+ HRESULT hr;
+ struct dotnet_runtime_stuff *stuff;
+ IUnknown *unk = NULL;
+ char *where = "";
+
+ stuff = malloc(sizeof(*stuff));
+ if (!stuff) {
+ return S_FALSE;
+ }
+ memset(stuff, 0, sizeof(*stuff));
+
+ where = "CoCreateInstance";
+ hr = CoCreateInstance(&CLSID_CorRuntimeHost, NULL, CLSCTX_ALL,
+ &IID_ICorRuntimeHost, (LPVOID*)&stuff->dotnet_host);
+
+ if (FAILED(hr))
+ goto out;
+
+ /* fire up the host and get the domain object */
+ where = "ICorRuntimeHost_Start\n";
+ hr = ICorRuntimeHost_Start(stuff->dotnet_host);
+ if (FAILED(hr))
+ goto out;
+
+ where = "ICorRuntimeHost_GetDefaultDomain";
+ hr = ICorRuntimeHost_GetDefaultDomain(stuff->dotnet_host, &unk);
+ if (FAILED(hr))
+ goto out;
+
+ where = "QI: System._AppDomain";
+ hr = IUnknown_QueryInterface(unk, &IID_mscorlib_System_AppDomain, (LPVOID*)&stuff->dotnet_domain);
+ if (FAILED(hr))
+ goto out;
+
+ COMG(dotnet_runtime_stuff) = stuff;
+
+out:
+ if (unk) {
+ IUnknown_Release(unk);
+ }
+ if (COMG(dotnet_runtime_stuff) == NULL) {
+ /* clean up */
+ if (stuff->dotnet_domain) {
+ IUnknown_Release(stuff->dotnet_domain);
+ }
+ if (stuff->dotnet_host) {
+ ICorRuntimeHost_Stop(stuff->dotnet_host);
+ ICorRuntimeHost_Release(stuff->dotnet_host);
+ }
+ free(stuff);
+
+ *p_where = where;
+
+ return hr;
+ }
+
+ return S_OK;
+}
+
+/* {{{ com_dotnet_create_instance - ctor for DOTNET class */
+PHP_FUNCTION(com_dotnet_create_instance)
+{
+ zval *object = getThis();
+ php_com_dotnet_object *obj;
+ char *assembly_name, *datatype_name;
+ int assembly_name_len, datatype_name_len;
+ struct dotnet_runtime_stuff *stuff;
+ OLECHAR *oleassembly, *oletype;
+ BSTR oleassembly_sys, oletype_sys;
+ HRESULT hr;
+ int ret = FAILURE;
+ char *where = "";
+ IUnknown *unk = NULL;
+
+ php_com_initialize(TSRMLS_C);
+ if (COMG(dotnet_runtime_stuff) == NULL) {
+ hr = dotnet_init(&where TSRMLS_CC);
+ if (FAILED(hr)) {
+ char buf[1024];
+ char *err = php_win32_error_to_msg(hr);
+ snprintf(buf, sizeof(buf), "Failed to init .Net runtime [%s] %s", where, err);
+ if (err)
+ LocalFree(err);
+ php_com_throw_exception(hr, buf TSRMLS_CC);
+ ZVAL_NULL(object);
+ return;
+ }
+ }
+
+ stuff = (struct dotnet_runtime_stuff*)COMG(dotnet_runtime_stuff);
+
+ obj = CDNO_FETCH(object);
+
+ if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|l",
+ &assembly_name, &assembly_name_len,
+ &datatype_name, &datatype_name_len,
+ &obj->code_page)) {
+ php_com_throw_exception(E_INVALIDARG, "Could not create .Net object - invalid arguments!" TSRMLS_CC);
+ ZVAL_NULL(object);
+ return;
+ }
+
+ oletype = php_com_string_to_olestring(datatype_name, datatype_name_len, obj->code_page TSRMLS_CC);
+ oleassembly = php_com_string_to_olestring(assembly_name, assembly_name_len, obj->code_page TSRMLS_CC);
+ oletype_sys = SysAllocString(oletype);
+ oleassembly_sys = SysAllocString(oleassembly);
+ where = "CreateInstance";
+ hr = stuff->dotnet_domain->lpVtbl->CreateInstance(stuff->dotnet_domain, oleassembly_sys, oletype_sys, &unk);
+ efree(oletype);
+ efree(oleassembly);
+ SysFreeString(oletype_sys);
+ SysFreeString(oleassembly_sys);
+
+ if (SUCCEEDED(hr)) {
+ VARIANT unwrapped;
+ IObjectHandle *handle = NULL;
+
+ where = "QI: IObjectHandle";
+ hr = IUnknown_QueryInterface(unk, &IID_IObjectHandle, &handle);
+
+ if (SUCCEEDED(hr)) {
+ where = "IObjectHandle_Unwrap";
+ hr = IObjectHandle_Unwrap(handle, &unwrapped);
+ if (SUCCEEDED(hr)) {
+
+ if (V_VT(&unwrapped) == VT_UNKNOWN) {
+ where = "Unwrapped, QI for IDispatch";
+ hr = IUnknown_QueryInterface(V_UNKNOWN(&unwrapped), &IID_IDispatch, &V_DISPATCH(&obj->v));
+
+ if (SUCCEEDED(hr)) {
+ V_VT(&obj->v) = VT_DISPATCH;
+
+ /* get its type-info */
+ IDispatch_GetTypeInfo(V_DISPATCH(&obj->v), 0, LANG_NEUTRAL, &obj->typeinfo);
+ ret = SUCCESS;
+ }
+ } else if (V_VT(&unwrapped) == VT_DISPATCH) {
+ /* unwrapped is now the dispatch pointer we want */
+ V_DISPATCH(&obj->v) = V_DISPATCH(&unwrapped);
+ V_VT(&obj->v) = VT_DISPATCH;
+
+ /* get its type-info */
+ IDispatch_GetTypeInfo(V_DISPATCH(&obj->v), 0, LANG_NEUTRAL, &obj->typeinfo);
+
+ ret = SUCCESS;
+ } else {
+ /* shouldn't happen, but let's be ready for it */
+ VariantClear(&unwrapped);
+ hr = E_INVALIDARG;
+ }
+ }
+ IObjectHandle_Release(handle);
+ }
+ IUnknown_Release(unk);
+ }
+
+ if (ret == FAILURE) {
+ char buf[1024];
+ char *err = php_win32_error_to_msg(hr);
+ snprintf(buf, sizeof(buf), "Failed to instantiate .Net object [%s] [0x%08x] %s", where, hr, err);
+ if (err && err[0]) {
+ LocalFree(err);
+ }
+ php_com_throw_exception(hr, buf TSRMLS_CC);
+ ZVAL_NULL(object);
+ return;
+ }
+}
+/* }}} */
+
+void php_com_dotnet_mshutdown(TSRMLS_D)
+{
+ struct dotnet_runtime_stuff *stuff = COMG(dotnet_runtime_stuff);
+
+ if (stuff->dotnet_domain) {
+ IDispatch_Release(stuff->dotnet_domain);
+ }
+ if (stuff->dotnet_host) {
+ ICorRuntimeHost_Stop(stuff->dotnet_host);
+ ICorRuntimeHost_Release(stuff->dotnet_host);
+ stuff->dotnet_host = NULL;
+ }
+ free(stuff);
+ COMG(dotnet_runtime_stuff) = NULL;
+}
+
+void php_com_dotnet_rshutdown(TSRMLS_D)
+{
+ struct dotnet_runtime_stuff *stuff = COMG(dotnet_runtime_stuff);
+
+ if (stuff->dotnet_domain) {
+ IDispatch_Release(stuff->dotnet_domain);
+ stuff->dotnet_domain = NULL;
+ }
+}
+
+#endif /* HAVE_MSCOREE_H */
diff --git a/ext/com_dotnet/com_extension.c b/ext/com_dotnet/com_extension.c
new file mode 100644
index 0000000..1257974
--- /dev/null
+++ b/ext/com_dotnet/com_extension.c
@@ -0,0 +1,520 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 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_01.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$ */
+
+#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_exceptions.h"
+
+ZEND_DECLARE_MODULE_GLOBALS(com_dotnet)
+static PHP_GINIT_FUNCTION(com_dotnet);
+
+TsHashTable php_com_typelibraries;
+
+zend_class_entry
+ *php_com_variant_class_entry,
+ *php_com_exception_class_entry,
+ *php_com_saproxy_class_entry;
+
+/* {{{ arginfo */
+ZEND_BEGIN_ARG_INFO_EX(arginfo_variant_set, 0, 0, 2)
+ ZEND_ARG_INFO(0, variant)
+ ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_variant_add, 0, 0, 2)
+ ZEND_ARG_INFO(0, left)
+ ZEND_ARG_INFO(0, right)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_variant_cat, 0, 0, 2)
+ ZEND_ARG_INFO(0, left)
+ ZEND_ARG_INFO(0, right)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_variant_sub, 0, 0, 2)
+ ZEND_ARG_INFO(0, left)
+ ZEND_ARG_INFO(0, right)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_variant_mul, 0, 0, 2)
+ ZEND_ARG_INFO(0, left)
+ ZEND_ARG_INFO(0, right)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_variant_and, 0, 0, 2)
+ ZEND_ARG_INFO(0, left)
+ ZEND_ARG_INFO(0, right)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_variant_div, 0, 0, 2)
+ ZEND_ARG_INFO(0, left)
+ ZEND_ARG_INFO(0, right)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_variant_eqv, 0, 0, 2)
+ ZEND_ARG_INFO(0, left)
+ ZEND_ARG_INFO(0, right)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_variant_idiv, 0, 0, 2)
+ ZEND_ARG_INFO(0, left)
+ ZEND_ARG_INFO(0, right)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_variant_imp, 0, 0, 2)
+ ZEND_ARG_INFO(0, left)
+ ZEND_ARG_INFO(0, right)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_variant_mod, 0, 0, 2)
+ ZEND_ARG_INFO(0, left)
+ ZEND_ARG_INFO(0, right)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_variant_or, 0, 0, 2)
+ ZEND_ARG_INFO(0, left)
+ ZEND_ARG_INFO(0, right)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_variant_pow, 0, 0, 2)
+ ZEND_ARG_INFO(0, left)
+ ZEND_ARG_INFO(0, right)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_variant_xor, 0, 0, 2)
+ ZEND_ARG_INFO(0, left)
+ ZEND_ARG_INFO(0, right)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_variant_abs, 0, 0, 1)
+ ZEND_ARG_INFO(0, left)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_variant_fix, 0, 0, 1)
+ ZEND_ARG_INFO(0, left)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_variant_int, 0, 0, 1)
+ ZEND_ARG_INFO(0, left)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_variant_neg, 0, 0, 1)
+ ZEND_ARG_INFO(0, left)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_variant_not, 0, 0, 1)
+ ZEND_ARG_INFO(0, left)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_variant_round, 0, 0, 2)
+ ZEND_ARG_INFO(0, left)
+ ZEND_ARG_INFO(0, decimals)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_variant_cmp, 0, 0, 2)
+ ZEND_ARG_INFO(0, left)
+ ZEND_ARG_INFO(0, right)
+ ZEND_ARG_INFO(0, lcid)
+ ZEND_ARG_INFO(0, flags)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_variant_date_to_timestamp, 0, 0, 1)
+ ZEND_ARG_INFO(0, variant)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_variant_date_from_timestamp, 0, 0, 1)
+ ZEND_ARG_INFO(0, timestamp)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_variant_get_type, 0, 0, 1)
+ ZEND_ARG_INFO(0, variant)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_variant_set_type, 0, 0, 2)
+ ZEND_ARG_INFO(0, variant)
+ ZEND_ARG_INFO(0, type)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_variant_cast, 0, 0, 2)
+ ZEND_ARG_INFO(0, variant)
+ ZEND_ARG_INFO(0, type)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_com_get_active_object, 0, 0, 1)
+ ZEND_ARG_INFO(0, progid)
+ ZEND_ARG_INFO(0, code_page)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_com_create_guid, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_com_event_sink, 0, 0, 2)
+ ZEND_ARG_INFO(0, comobject)
+ ZEND_ARG_INFO(0, sinkobject)
+ ZEND_ARG_INFO(0, sinkinterface)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_com_print_typeinfo, 0, 0, 1)
+ ZEND_ARG_INFO(0, comobject)
+ ZEND_ARG_INFO(0, dispinterface)
+ ZEND_ARG_INFO(0, wantsink)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_com_message_pump, 0, 0, 0)
+ ZEND_ARG_INFO(0, timeoutms)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_com_load_typelib, 0, 0, 1)
+ ZEND_ARG_INFO(0, typelib_name)
+ ZEND_ARG_INFO(0, case_insensitive)
+ZEND_END_ARG_INFO()
+/* }}} */
+
+const zend_function_entry com_dotnet_functions[] = {
+ PHP_FE(variant_set, arginfo_variant_set)
+ PHP_FE(variant_add, arginfo_variant_add)
+ PHP_FE(variant_cat, arginfo_variant_cat)
+ PHP_FE(variant_sub, arginfo_variant_sub)
+ PHP_FE(variant_mul, arginfo_variant_mul)
+ PHP_FE(variant_and, arginfo_variant_and)
+ PHP_FE(variant_div, arginfo_variant_div)
+ PHP_FE(variant_eqv, arginfo_variant_eqv)
+ PHP_FE(variant_idiv, arginfo_variant_idiv)
+ PHP_FE(variant_imp, arginfo_variant_imp)
+ PHP_FE(variant_mod, arginfo_variant_mod)
+ PHP_FE(variant_or, arginfo_variant_or)
+ PHP_FE(variant_pow, arginfo_variant_pow)
+ PHP_FE(variant_xor, arginfo_variant_xor)
+ PHP_FE(variant_abs, arginfo_variant_abs)
+ PHP_FE(variant_fix, arginfo_variant_fix)
+ PHP_FE(variant_int, arginfo_variant_int)
+ PHP_FE(variant_neg, arginfo_variant_neg)
+ PHP_FE(variant_not, arginfo_variant_not)
+ PHP_FE(variant_round, arginfo_variant_round)
+ PHP_FE(variant_cmp, arginfo_variant_cmp)
+ PHP_FE(variant_date_to_timestamp, arginfo_variant_date_to_timestamp)
+ PHP_FE(variant_date_from_timestamp, arginfo_variant_date_from_timestamp)
+ PHP_FE(variant_get_type, arginfo_variant_get_type)
+ PHP_FE(variant_set_type, arginfo_variant_set_type)
+ PHP_FE(variant_cast, arginfo_variant_cast)
+ /* com_com.c */
+ PHP_FE(com_create_guid, arginfo_com_create_guid)
+ PHP_FE(com_event_sink, arginfo_com_event_sink)
+ PHP_FE(com_print_typeinfo, arginfo_com_print_typeinfo)
+ PHP_FE(com_message_pump, arginfo_com_message_pump)
+ PHP_FE(com_load_typelib, arginfo_com_load_typelib)
+ PHP_FE(com_get_active_object, arginfo_com_get_active_object)
+ PHP_FE_END
+};
+
+/* {{{ com_dotnet_module_entry
+ */
+zend_module_entry com_dotnet_module_entry = {
+ STANDARD_MODULE_HEADER,
+ "com_dotnet",
+ com_dotnet_functions,
+ PHP_MINIT(com_dotnet),
+ PHP_MSHUTDOWN(com_dotnet),
+ PHP_RINIT(com_dotnet),
+ PHP_RSHUTDOWN(com_dotnet),
+ PHP_MINFO(com_dotnet),
+ "0.1",
+ PHP_MODULE_GLOBALS(com_dotnet),
+ PHP_GINIT(com_dotnet),
+ NULL,
+ NULL,
+ STANDARD_MODULE_PROPERTIES_EX
+};
+/* }}} */
+
+#ifdef COMPILE_DL_COM_DOTNET
+ZEND_GET_MODULE(com_dotnet)
+#endif
+
+/* {{{ 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 || !new_value[0] || (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", "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()
+/* }}} */
+
+/* {{{ PHP_GINIT_FUNCTION
+ */
+static PHP_GINIT_FUNCTION(com_dotnet)
+{
+ memset(com_dotnet_globals, 0, sizeof(*com_dotnet_globals));
+ com_dotnet_globals->code_page = CP_ACP;
+}
+/* }}} */
+
+/* {{{ PHP_MINIT_FUNCTION
+ */
+PHP_MINIT_FUNCTION(com_dotnet)
+{
+ zend_class_entry ce, *tmp;
+
+ php_com_wrapper_minit(INIT_FUNC_ARGS_PASSTHRU);
+ php_com_persist_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(TSRMLS_C), 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;
+ 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;
+ 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;
+ tmp = zend_register_internal_class_ex(&ce, php_com_variant_class_entry, "variant" TSRMLS_CC);
+ tmp->get_iterator = php_com_iter_get;
+#endif
+
+ REGISTER_INI_ENTRIES();
+
+#define COM_CONST(x) REGISTER_LONG_CONSTANT(#x, x, CONST_CS|CONST_PERSISTENT)
+
+ COM_CONST(CLSCTX_INPROC_SERVER);
+ COM_CONST(CLSCTX_INPROC_HANDLER);
+ COM_CONST(CLSCTX_LOCAL_SERVER);
+ COM_CONST(CLSCTX_REMOTE_SERVER);
+ COM_CONST(CLSCTX_SERVER);
+ COM_CONST(CLSCTX_ALL);
+
+#if 0
+ COM_CONST(DISPATCH_METHOD);
+ COM_CONST(DISPATCH_PROPERTYGET);
+ COM_CONST(DISPATCH_PROPERTYPUT);
+#endif
+
+ COM_CONST(VT_NULL);
+ COM_CONST(VT_EMPTY);
+ COM_CONST(VT_UI1);
+ COM_CONST(VT_I1);
+ COM_CONST(VT_UI2);
+ COM_CONST(VT_I2);
+ COM_CONST(VT_UI4);
+ COM_CONST(VT_I4);
+ COM_CONST(VT_R4);
+ COM_CONST(VT_R8);
+ COM_CONST(VT_BOOL);
+ COM_CONST(VT_ERROR);
+ COM_CONST(VT_CY);
+ COM_CONST(VT_DATE);
+ COM_CONST(VT_BSTR);
+ COM_CONST(VT_DECIMAL);
+ COM_CONST(VT_UNKNOWN);
+ COM_CONST(VT_DISPATCH);
+ COM_CONST(VT_VARIANT);
+ COM_CONST(VT_INT);
+ COM_CONST(VT_UINT);
+ COM_CONST(VT_ARRAY);
+ COM_CONST(VT_BYREF);
+
+ COM_CONST(CP_ACP);
+ COM_CONST(CP_MACCP);
+ COM_CONST(CP_OEMCP);
+ COM_CONST(CP_UTF7);
+ COM_CONST(CP_UTF8);
+ COM_CONST(CP_SYMBOL);
+ COM_CONST(CP_THREAD_ACP);
+
+ COM_CONST(VARCMP_LT);
+ COM_CONST(VARCMP_EQ);
+ COM_CONST(VARCMP_GT);
+ COM_CONST(VARCMP_NULL);
+
+ COM_CONST(NORM_IGNORECASE);
+ COM_CONST(NORM_IGNORENONSPACE);
+ COM_CONST(NORM_IGNORESYMBOLS);
+ COM_CONST(NORM_IGNOREWIDTH);
+ COM_CONST(NORM_IGNOREKANATYPE);
+#ifdef NORM_IGNOREKASHIDA
+ COM_CONST(NORM_IGNOREKASHIDA);
+#endif
+ COM_CONST(DISP_E_DIVBYZERO);
+ COM_CONST(DISP_E_OVERFLOW);
+ COM_CONST(DISP_E_BADINDEX);
+ COM_CONST(MK_E_UNAVAILABLE);
+
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ PHP_MSHUTDOWN_FUNCTION
+ */
+PHP_MSHUTDOWN_FUNCTION(com_dotnet)
+{
+ UNREGISTER_INI_ENTRIES();
+#if HAVE_MSCOREE_H
+ if (COMG(dotnet_runtime_stuff)) {
+ php_com_dotnet_mshutdown(TSRMLS_C);
+ }
+#endif
+
+ zend_ts_hash_destroy(&php_com_typelibraries);
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ PHP_RINIT_FUNCTION
+ */
+PHP_RINIT_FUNCTION(com_dotnet)
+{
+ COMG(rshutdown_started) = 0;
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ PHP_RSHUTDOWN_FUNCTION
+ */
+PHP_RSHUTDOWN_FUNCTION(com_dotnet)
+{
+#if HAVE_MSCOREE_H
+ if (COMG(dotnet_runtime_stuff)) {
+ php_com_dotnet_rshutdown(TSRMLS_C);
+ }
+#endif
+ COMG(rshutdown_started) = 1;
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ PHP_MINFO_FUNCTION
+ */
+PHP_MINFO_FUNCTION(com_dotnet)
+{
+ php_info_print_table_start();
+
+ php_info_print_table_header(2, "COM support", "enabled");
+ php_info_print_table_header(2, "DCOM support", COMG(allow_dcom) ? "enabled" : "disabled");
+
+#if HAVE_MSCOREE_H
+ php_info_print_table_header(2, ".Net support", "enabled");
+#else
+ php_info_print_table_header(2, ".Net support", "not present in this build");
+#endif
+
+ php_info_print_table_end();
+
+ DISPLAY_INI_ENTRIES();
+}
+/* }}} */
+
+/*
+ * 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
+ */
diff --git a/ext/com_dotnet/com_handlers.c b/ext/com_dotnet/com_handlers.c
new file mode 100644
index 0000000..4bdbf52
--- /dev/null
+++ b/ext/com_dotnet/com_handlers.c
@@ -0,0 +1,683 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 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_01.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$ */
+
+#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_exceptions.h"
+
+static zval *com_property_read(zval *object, zval *member, int type, const zend_literal *key TSRMLS_DC)
+{
+ zval *return_value;
+ php_com_dotnet_object *obj;
+ VARIANT v;
+ HRESULT res;
+
+ MAKE_STD_ZVAL(return_value);
+ ZVAL_NULL(return_value);
+ Z_SET_REFCOUNT_P(return_value, 0);
+ Z_UNSET_ISREF_P(return_value);
+
+ obj = CDNO_FETCH(object);
+
+ if (V_VT(&obj->v) == VT_DISPATCH) {
+ VariantInit(&v);
+
+ convert_to_string_ex(&member);
+
+ res = php_com_do_invoke(obj, Z_STRVAL_P(member), Z_STRLEN_P(member),
+ DISPATCH_METHOD|DISPATCH_PROPERTYGET, &v, 0, NULL, 1 TSRMLS_CC);
+
+ if (res == SUCCESS) {
+ php_com_zval_from_variant(return_value, &v, obj->code_page TSRMLS_CC);
+ VariantClear(&v);
+ } else if (res == DISP_E_BADPARAMCOUNT) {
+ php_com_saproxy_create(object, return_value, member TSRMLS_CC);
+ }
+ } else {
+ php_com_throw_exception(E_INVALIDARG, "this variant has no properties" TSRMLS_CC);
+ }
+
+ return return_value;
+}
+
+static void com_property_write(zval *object, zval *member, zval *value, const zend_literal *key TSRMLS_DC)
+{
+ php_com_dotnet_object *obj;
+ VARIANT v;
+
+ obj = CDNO_FETCH(object);
+
+ if (V_VT(&obj->v) == VT_DISPATCH) {
+ VariantInit(&v);
+
+ convert_to_string_ex(&member);
+ if (SUCCESS == php_com_do_invoke(obj, Z_STRVAL_P(member), Z_STRLEN_P(member),
+ DISPATCH_PROPERTYPUT|DISPATCH_PROPERTYPUTREF, &v, 1, &value, 0 TSRMLS_CC)) {
+ VariantClear(&v);
+ }
+ } else {
+ php_com_throw_exception(E_INVALIDARG, "this variant has no properties" TSRMLS_CC);
+ }
+}
+
+static zval *com_read_dimension(zval *object, zval *offset, int type TSRMLS_DC)
+{
+ zval *return_value;
+ php_com_dotnet_object *obj;
+ VARIANT v;
+
+ MAKE_STD_ZVAL(return_value);
+ ZVAL_NULL(return_value);
+ Z_SET_REFCOUNT_P(return_value, 0);
+ Z_UNSET_ISREF_P(return_value);
+
+ obj = CDNO_FETCH(object);
+
+ if (V_VT(&obj->v) == VT_DISPATCH) {
+ VariantInit(&v);
+
+ if (SUCCESS == php_com_do_invoke_by_id(obj, DISPID_VALUE,
+ DISPATCH_METHOD|DISPATCH_PROPERTYGET, &v, 1, &offset, 0, 0 TSRMLS_CC)) {
+ php_com_zval_from_variant(return_value, &v, obj->code_page TSRMLS_CC);
+ VariantClear(&v);
+ }
+ } else if (V_ISARRAY(&obj->v)) {
+ convert_to_long(offset);
+
+ 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, offset TSRMLS_CC);
+ }
+
+ } else {
+ php_com_throw_exception(E_INVALIDARG, "this variant is not an array type" TSRMLS_CC);
+ }
+
+ return return_value;
+}
+
+static void com_write_dimension(zval *object, zval *offset, zval *value TSRMLS_DC)
+{
+ php_com_dotnet_object *obj;
+ zval *args[2];
+ VARIANT v;
+ HRESULT res;
+
+ obj = CDNO_FETCH(object);
+
+ if (V_VT(&obj->v) == VT_DISPATCH) {
+ args[0] = offset;
+ args[1] = value;
+
+ VariantInit(&v);
+
+ if (SUCCESS == php_com_do_invoke_by_id(obj, DISPID_VALUE,
+ DISPATCH_METHOD|DISPATCH_PROPERTYPUT, &v, 2, args, 0, 0 TSRMLS_CC)) {
+ VariantClear(&v);
+ }
+ } else if (V_ISARRAY(&obj->v)) {
+ LONG indices = 0;
+ VARTYPE vt;
+
+ if (SafeArrayGetDim(V_ARRAY(&obj->v)) == 1) {
+ if (FAILED(SafeArrayGetVartype(V_ARRAY(&obj->v), &vt)) || vt == VT_EMPTY) {
+ vt = V_VT(&obj->v) & ~VT_ARRAY;
+ }
+
+ convert_to_long(offset);
+ indices = Z_LVAL_P(offset);
+
+ VariantInit(&v);
+ php_com_variant_from_zval(&v, value, obj->code_page TSRMLS_CC);
+
+ if (V_VT(&v) != vt) {
+ VariantChangeType(&v, &v, 0, vt);
+ }
+
+ if (vt == VT_VARIANT) {
+ res = SafeArrayPutElement(V_ARRAY(&obj->v), &indices, &v);
+ } else {
+ res = SafeArrayPutElement(V_ARRAY(&obj->v), &indices, &v.lVal);
+ }
+
+ VariantClear(&v);
+
+ if (FAILED(res)) {
+ php_com_throw_exception(res, NULL TSRMLS_CC);
+ }
+
+ } else {
+ 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);
+ }
+
+ } else {
+ php_com_throw_exception(E_INVALIDARG, "this variant is not an array type" TSRMLS_CC);
+ }
+}
+
+#if 0
+static void com_object_set(zval **property, zval *value TSRMLS_DC)
+{
+ /* Not yet implemented in the engine */
+}
+
+static zval *com_object_get(zval *property TSRMLS_DC)
+{
+ /* Not yet implemented in the engine */
+ return NULL;
+}
+#endif
+
+static int com_property_exists(zval *object, zval *member, int check_empty, const zend_literal *key TSRMLS_DC)
+{
+ DISPID dispid;
+ php_com_dotnet_object *obj;
+
+ obj = CDNO_FETCH(object);
+
+ if (V_VT(&obj->v) == VT_DISPATCH) {
+ convert_to_string_ex(&member);
+ if (SUCCEEDED(php_com_get_id_of_name(obj, Z_STRVAL_P(member), Z_STRLEN_P(member), &dispid TSRMLS_CC))) {
+ /* TODO: distinguish between property and method! */
+ return 1;
+ }
+ } else {
+ /* TODO: check for safearray */
+ }
+
+ return 0;
+}
+
+static int com_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 com_property_delete(zval *object, zval *member, const zend_literal *key TSRMLS_DC)
+{
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot delete properties from a COM object");
+}
+
+static void com_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 *com_properties_get(zval *object TSRMLS_DC)
+{
+ /* TODO: use type-info to get all the names and values ?
+ * DANGER: if we do that, there is a strong possibility for
+ * infinite recursion when the hash is displayed via var_dump().
+ * Perhaps it is best to leave it un-implemented.
+ */
+ return NULL;
+}
+
+static void function_dtor(void *pDest)
+{
+ zend_internal_function *f = (zend_internal_function*)pDest;
+
+ efree((char*)f->function_name);
+ if (f->arg_info) {
+ efree(f->arg_info);
+ }
+}
+
+static PHP_FUNCTION(com_method_handler)
+{
+ Z_OBJ_HANDLER_P(getThis(), call_method)(
+ ((zend_internal_function*)EG(current_execute_data)->function_state.function)->function_name,
+ INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+
+static union _zend_function *com_method_get(zval **object_ptr, char *name, int len, const zend_literal *key TSRMLS_DC)
+{
+ zend_internal_function f, *fptr = NULL;
+ php_com_dotnet_object *obj;
+ union _zend_function *func;
+ DISPID dummy;
+ zval *object = *object_ptr;
+
+ obj = CDNO_FETCH(object);
+
+ if (V_VT(&obj->v) != VT_DISPATCH) {
+ return NULL;
+ }
+
+ if (FAILED(php_com_get_id_of_name(obj, name, len, &dummy TSRMLS_CC))) {
+ return NULL;
+ }
+
+ /* check cache */
+ if (obj->method_cache == NULL || FAILURE == zend_hash_find(obj->method_cache, name, len, (void**)&fptr)) {
+ f.type = ZEND_OVERLOADED_FUNCTION;
+ f.num_args = 0;
+ f.arg_info = NULL;
+ f.scope = obj->ce;
+ f.fn_flags = ZEND_ACC_CALL_VIA_HANDLER;
+ f.function_name = estrndup(name, len);
+ f.handler = PHP_FN(com_method_handler);
+
+ fptr = &f;
+
+ if (obj->typeinfo) {
+ /* look for byref params */
+ ITypeComp *comp;
+ ITypeInfo *TI = NULL;
+ DESCKIND kind;
+ BINDPTR bindptr;
+ OLECHAR *olename;
+ ULONG lhash;
+ int i;
+
+ if (SUCCEEDED(ITypeInfo_GetTypeComp(obj->typeinfo, &comp))) {
+ olename = php_com_string_to_olestring(name, len, obj->code_page TSRMLS_CC);
+ lhash = LHashValOfNameSys(SYS_WIN32, LOCALE_SYSTEM_DEFAULT, olename);
+
+ if (SUCCEEDED(ITypeComp_Bind(comp, olename, lhash, INVOKE_FUNC, &TI, &kind, &bindptr))) {
+ switch (kind) {
+ case DESCKIND_FUNCDESC:
+ f.arg_info = ecalloc(bindptr.lpfuncdesc->cParams, sizeof(zend_arg_info));
+
+ for (i = 0; i < bindptr.lpfuncdesc->cParams; i++) {
+ f.arg_info[i].allow_null = 1;
+ if (bindptr.lpfuncdesc->lprgelemdescParam[i].paramdesc.wParamFlags & PARAMFLAG_FOUT) {
+ f.arg_info[i].pass_by_reference = 1;
+ }
+ }
+
+ f.num_args = bindptr.lpfuncdesc->cParams;
+
+ ITypeInfo_ReleaseFuncDesc(TI, bindptr.lpfuncdesc);
+ break;
+
+ /* these should not happen, but *might* happen if the user
+ * screws up; lets avoid a leak in that case */
+ case DESCKIND_VARDESC:
+ ITypeInfo_ReleaseVarDesc(TI, bindptr.lpvardesc);
+ break;
+ case DESCKIND_TYPECOMP:
+ ITypeComp_Release(bindptr.lptcomp);
+ break;
+
+ case DESCKIND_NONE:
+ break;
+ }
+ if (TI) {
+ ITypeInfo_Release(TI);
+ }
+ }
+ ITypeComp_Release(comp);
+ efree(olename);
+ }
+ }
+
+ if (fptr) {
+ /* save this method in the cache */
+ if (!obj->method_cache) {
+ ALLOC_HASHTABLE(obj->method_cache);
+ zend_hash_init(obj->method_cache, 2, NULL, function_dtor, 0);
+ }
+
+ zend_hash_update(obj->method_cache, name, len, &f, sizeof(f), (void**)&fptr);
+ }
+ }
+
+ if (fptr) {
+ /* duplicate this into a new chunk of emalloc'd memory,
+ * since the engine will efree it */
+ func = emalloc(sizeof(*fptr));
+ memcpy(func, fptr, sizeof(*fptr));
+
+ return func;
+ }
+
+ return NULL;
+}
+
+static int com_call_method(const char *method, INTERNAL_FUNCTION_PARAMETERS)
+{
+ zval ***args = NULL;
+ php_com_dotnet_object *obj;
+ int nargs;
+ VARIANT v;
+ int ret = FAILURE;
+
+ obj = CDNO_FETCH(getThis());
+
+ if (V_VT(&obj->v) != VT_DISPATCH) {
+ return FAILURE;
+ }
+
+ nargs = ZEND_NUM_ARGS();
+
+ if (nargs) {
+ args = (zval ***)safe_emalloc(sizeof(zval *), nargs, 0);
+ zend_get_parameters_array_ex(nargs, args);
+ }
+
+ VariantInit(&v);
+
+ if (SUCCESS == php_com_do_invoke_byref(obj, (char*)method, -1, DISPATCH_METHOD|DISPATCH_PROPERTYGET, &v, nargs, args TSRMLS_CC)) {
+ php_com_zval_from_variant(return_value, &v, obj->code_page TSRMLS_CC);
+ ret = SUCCESS;
+ VariantClear(&v);
+ }
+
+ if (args) {
+ efree(args);
+ }
+
+ return ret;
+}
+
+static union _zend_function *com_constructor_get(zval *object TSRMLS_DC)
+{
+ php_com_dotnet_object *obj;
+ static zend_internal_function c, d, v;
+
+ obj = CDNO_FETCH(object);
+
+#define POPULATE_CTOR(f, fn) \
+ f.type = ZEND_INTERNAL_FUNCTION; \
+ f.function_name = (char *) obj->ce->name; \
+ f.scope = obj->ce; \
+ f.arg_info = NULL; \
+ f.num_args = 0; \
+ f.fn_flags = 0; \
+ f.handler = ZEND_FN(fn); \
+ return (union _zend_function*)&f;
+
+ switch (obj->ce->name[0]) {
+#if HAVE_MSCOREE_H
+ case 'd':
+ POPULATE_CTOR(d, com_dotnet_create_instance);
+#endif
+
+ case 'c':
+ POPULATE_CTOR(c, com_create_instance);
+
+ case 'v':
+ POPULATE_CTOR(v, com_variant_create_instance);
+
+ default:
+ return NULL;
+ }
+}
+
+static zend_class_entry *com_class_entry_get(const zval *object TSRMLS_DC)
+{
+ php_com_dotnet_object *obj;
+ obj = CDNO_FETCH(object);
+
+ return obj->ce;
+}
+
+static int com_class_name_get(const zval *object, const char **class_name, zend_uint *class_name_len, int parent TSRMLS_DC)
+{
+ php_com_dotnet_object *obj;
+ obj = CDNO_FETCH(object);
+
+ *class_name = estrndup(obj->ce->name, obj->ce->name_length);
+ *class_name_len = obj->ce->name_length;
+
+ return 0;
+}
+
+/* This compares two variants for equality */
+static int com_objects_compare(zval *object1, zval *object2 TSRMLS_DC)
+{
+ php_com_dotnet_object *obja, *objb;
+ int ret;
+ /* strange header bug problem here... the headers define the proto without the
+ * flags parameter. However, the MSDN docs state that there is a flags parameter,
+ * and my VC6 won't link unless the code uses the version with 4 parameters.
+ * So, we have this declaration here to fix it */
+ STDAPI VarCmp(LPVARIANT pvarLeft, LPVARIANT pvarRight, LCID lcid, DWORD flags);
+
+ obja = CDNO_FETCH(object1);
+ objb = CDNO_FETCH(object2);
+
+ switch (VarCmp(&obja->v, &objb->v, LOCALE_SYSTEM_DEFAULT, 0)) {
+ case VARCMP_LT:
+ ret = -1;
+ break;
+ case VARCMP_GT:
+ ret = 1;
+ break;
+ case VARCMP_EQ:
+ ret = 0;
+ break;
+ default:
+ /* either or both operands are NULL...
+ * not 100% sure how to handle this */
+ ret = -2;
+ }
+
+ return ret;
+}
+
+static int com_object_cast(zval *readobj, zval *writeobj, int type TSRMLS_DC)
+{
+ php_com_dotnet_object *obj;
+ VARIANT v;
+ VARTYPE vt = VT_EMPTY;
+ HRESULT res = S_OK;
+
+ obj = CDNO_FETCH(readobj);
+ ZVAL_NULL(writeobj);
+ VariantInit(&v);
+
+ if (V_VT(&obj->v) == VT_DISPATCH) {
+ if (SUCCESS != php_com_do_invoke_by_id(obj, DISPID_VALUE,
+ DISPATCH_METHOD|DISPATCH_PROPERTYGET, &v, 0, NULL, 1, 0 TSRMLS_CC)) {
+ VariantCopy(&v, &obj->v);
+ }
+ } else {
+ VariantCopy(&v, &obj->v);
+ }
+
+ switch(type) {
+ case IS_LONG:
+ vt = VT_INT;
+ break;
+ case IS_DOUBLE:
+ vt = VT_R8;
+ break;
+ case IS_BOOL:
+ vt = VT_BOOL;
+ break;
+ case IS_STRING:
+ vt = VT_BSTR;
+ break;
+ default:
+ ;
+ }
+
+ if (vt != VT_EMPTY && vt != V_VT(&v)) {
+ res = VariantChangeType(&v, &v, 0, vt);
+ }
+
+ if (SUCCEEDED(res)) {
+ php_com_zval_from_variant(writeobj, &v, obj->code_page TSRMLS_CC);
+ }
+
+ VariantClear(&v);
+
+ if (SUCCEEDED(res)) {
+ return SUCCESS;
+ }
+
+ return zend_std_cast_object_tostring(readobj, writeobj, type TSRMLS_CC);
+}
+
+static int com_object_count(zval *object, long *count TSRMLS_DC)
+{
+ php_com_dotnet_object *obj;
+ LONG ubound = 0, lbound = 0;
+
+ obj = CDNO_FETCH(object);
+
+ if (!V_ISARRAY(&obj->v)) {
+ return FAILURE;
+ }
+
+ SafeArrayGetLBound(V_ARRAY(&obj->v), 1, &lbound);
+ SafeArrayGetUBound(V_ARRAY(&obj->v), 1, &ubound);
+
+ *count = ubound - lbound + 1;
+
+ return SUCCESS;
+}
+
+zend_object_handlers php_com_object_handlers = {
+ ZEND_OBJECTS_STORE_HANDLERS,
+ com_property_read,
+ com_property_write,
+ com_read_dimension,
+ com_write_dimension,
+ NULL,
+ NULL, /* com_object_get, */
+ NULL, /* com_object_set, */
+ com_property_exists,
+ com_property_delete,
+ com_dimension_exists,
+ com_dimension_delete,
+ com_properties_get,
+ com_method_get,
+ com_call_method,
+ com_constructor_get,
+ com_class_entry_get,
+ com_class_name_get,
+ com_objects_compare,
+ com_object_cast,
+ com_object_count,
+ NULL, /* get_debug_info */
+ NULL, /* get_closure */
+ NULL, /* get_gc */
+};
+
+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_free_storage(void *object TSRMLS_DC)
+{
+ php_com_dotnet_object *obj = (php_com_dotnet_object*)object;
+
+ if (obj->typeinfo) {
+ 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);
+
+ if (obj->method_cache) {
+ zend_hash_destroy(obj->method_cache);
+ FREE_HASHTABLE(obj->method_cache);
+ }
+ if (obj->id_of_name_cache) {
+ zend_hash_destroy(obj->id_of_name_cache);
+ FREE_HASHTABLE(obj->id_of_name_cache);
+ }
+ efree(obj);
+}
+
+void php_com_object_clone(void *object, void **clone_ptr TSRMLS_DC)
+{
+ php_com_dotnet_object *cloneobj, *origobject;
+
+ origobject = (php_com_dotnet_object*)object;
+ cloneobj = (php_com_dotnet_object*)emalloc(sizeof(php_com_dotnet_object));
+
+ memcpy(cloneobj, origobject, sizeof(*cloneobj));
+
+ /* VariantCopy will perform VariantClear; we don't want to clobber
+ * the IDispatch that we memcpy'd, so we init a new variant in the
+ * clone structure */
+ VariantInit(&cloneobj->v);
+ /* We use the Indirection-following version of the API since we
+ * want to clone as much as possible */
+ VariantCopyInd(&cloneobj->v, &origobject->v);
+
+ if (cloneobj->typeinfo) {
+ ITypeInfo_AddRef(cloneobj->typeinfo);
+ }
+
+ *clone_ptr = cloneobj;
+}
+
+zend_object_value php_com_object_new(zend_class_entry *ce TSRMLS_DC)
+{
+ php_com_dotnet_object *obj;
+ zend_object_value retval;
+
+ php_com_initialize(TSRMLS_C);
+ obj = emalloc(sizeof(*obj));
+ memset(obj, 0, sizeof(*obj));
+
+ VariantInit(&obj->v);
+ obj->code_page = CP_ACP;
+ obj->ce = ce;
+ obj->zo.ce = ce;
+
+ retval.handle = zend_objects_store_put(obj, NULL, php_com_object_free_storage, php_com_object_clone TSRMLS_CC);
+ retval.handlers = &php_com_object_handlers;
+
+ return retval;
+}
diff --git a/ext/com_dotnet/com_iterator.c b/ext/com_dotnet/com_iterator.c
new file mode 100644
index 0000000..ce4bdd6
--- /dev/null
+++ b/ext/com_dotnet/com_iterator.c
@@ -0,0 +1,250 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 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_01.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$ */
+
+#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_exceptions.h"
+
+struct php_com_iterator {
+ zend_object_iterator iter;
+ IEnumVARIANT *ev;
+ ulong key;
+ VARIANT v; /* cached element */
+ int code_page;
+ VARIANT safe_array;
+ VARTYPE sa_type;
+ LONG sa_max;
+ zval *zdata;
+};
+
+static void com_iter_dtor(zend_object_iterator *iter TSRMLS_DC)
+{
+ struct php_com_iterator *I = (struct php_com_iterator*)iter->data;
+
+ if (I->ev) {
+ IEnumVARIANT_Release(I->ev);
+ }
+ VariantClear(&I->v);
+ VariantClear(&I->safe_array);
+ if (I->zdata) {
+ zval_ptr_dtor((zval**)&I->zdata);
+ }
+ efree(I);
+}
+
+static int com_iter_valid(zend_object_iterator *iter TSRMLS_DC)
+{
+ struct php_com_iterator *I = (struct php_com_iterator*)iter->data;
+
+ if (I->zdata) {
+ return SUCCESS;
+ }
+
+ return FAILURE;
+}
+
+static void com_iter_get_data(zend_object_iterator *iter, zval ***data TSRMLS_DC)
+{
+ struct php_com_iterator *I = (struct php_com_iterator*)iter->data;
+
+ *data = &I->zdata;
+}
+
+static int com_iter_get_key(zend_object_iterator *iter, char **str_key, uint *str_key_len,
+ ulong *int_key TSRMLS_DC)
+{
+ struct php_com_iterator *I = (struct php_com_iterator*)iter->data;
+
+ if (I->key == (ulong)-1) {
+ return HASH_KEY_NON_EXISTANT;
+ }
+ *int_key = I->key;
+ return HASH_KEY_IS_LONG;
+}
+
+static int com_iter_move_forwards(zend_object_iterator *iter TSRMLS_DC)
+{
+ struct php_com_iterator *I = (struct php_com_iterator*)iter->data;
+ unsigned long n_fetched;
+ zval *ptr;
+
+ /* release current cached element */
+ VariantClear(&I->v);
+
+ if (I->zdata) {
+ zval_ptr_dtor((zval**)&I->zdata);
+ I->zdata = NULL;
+ }
+
+ if (I->ev) {
+ /* Get the next element */
+ if (SUCCEEDED(IEnumVARIANT_Next(I->ev, 1, &I->v, &n_fetched)) && n_fetched > 0) {
+ I->key++;
+ } else {
+ /* indicate that there are no more items */
+ I->key = (ulong)-1;
+ return FAILURE;
+ }
+ } else {
+ /* safe array */
+ if (I->key >= (ULONG) 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) == 0) {
+ I->key = (ulong)-1;
+ return FAILURE;
+ }
+ }
+
+ MAKE_STD_ZVAL(ptr);
+ 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); */
+ I->zdata = ptr;
+ return SUCCESS;
+}
+
+
+static zend_object_iterator_funcs com_iter_funcs = {
+ com_iter_dtor,
+ com_iter_valid,
+ com_iter_get_data,
+ com_iter_get_key,
+ com_iter_move_forwards,
+ NULL
+};
+
+zend_object_iterator *php_com_iter_get(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC)
+{
+ php_com_dotnet_object *obj;
+ struct php_com_iterator *I;
+ IEnumVARIANT *iev = NULL;
+ DISPPARAMS dp;
+ VARIANT v;
+ unsigned long n_fetched;
+ zval *ptr;
+
+ if (by_ref) {
+ zend_error(E_ERROR, "An iterator cannot be used with foreach by reference");
+ }
+
+ obj = CDNO_FETCH(object);
+
+ 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);
+
+ I = (struct php_com_iterator*)ecalloc(1, sizeof(*I));
+ I->iter.funcs = &com_iter_funcs;
+ I->iter.data = I;
+ I->code_page = obj->code_page;
+ I->zdata = NULL;
+ VariantInit(&I->safe_array);
+ VariantInit(&I->v);
+
+ 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;
+ MAKE_STD_ZVAL(ptr);
+ php_com_zval_from_variant(ptr, &I->v, I->code_page TSRMLS_CC);
+ I->zdata = ptr;
+ } else {
+ I->key = (ulong)-1;
+ }
+
+ } else {
+ /* 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;
+ MAKE_STD_ZVAL(ptr);
+ php_com_zval_from_variant(ptr, &I->v, I->code_page TSRMLS_CC);
+ I->zdata = ptr;
+ } 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);
+ efree(I);
+ }
+ return NULL;
+}
+
diff --git a/ext/com_dotnet/com_misc.c b/ext/com_dotnet/com_misc.c
new file mode 100644
index 0000000..023c5b8
--- /dev/null
+++ b/ext/com_dotnet/com_misc.c
@@ -0,0 +1,144 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 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_01.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$ */
+
+#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_exceptions.h"
+
+void php_com_throw_exception(HRESULT code, char *message TSRMLS_DC)
+{
+ int free_msg = 0;
+ if (message == NULL) {
+ message = php_win32_error_to_msg(code);
+ free_msg = 1;
+ }
+ zend_throw_exception(php_com_exception_class_entry, message, (long)code TSRMLS_CC);
+ if (free_msg) {
+ LocalFree(message);
+ }
+}
+
+PHP_COM_DOTNET_API void php_com_wrap_dispatch(zval *z, IDispatch *disp,
+ int codepage TSRMLS_DC)
+{
+ php_com_dotnet_object *obj;
+
+ obj = emalloc(sizeof(*obj));
+ memset(obj, 0, sizeof(*obj));
+ obj->code_page = codepage;
+ obj->ce = php_com_variant_class_entry;
+ obj->zo.ce = php_com_variant_class_entry;
+
+ VariantInit(&obj->v);
+ V_VT(&obj->v) = VT_DISPATCH;
+ V_DISPATCH(&obj->v) = disp;
+
+ IDispatch_AddRef(V_DISPATCH(&obj->v));
+ IDispatch_GetTypeInfo(V_DISPATCH(&obj->v), 0, LANG_NEUTRAL, &obj->typeinfo);
+
+ Z_TYPE_P(z) = IS_OBJECT;
+ z->value.obj.handle = zend_objects_store_put(obj, NULL, php_com_object_free_storage, php_com_object_clone TSRMLS_CC);
+ z->value.obj.handlers = &php_com_object_handlers;
+}
+
+PHP_COM_DOTNET_API void php_com_wrap_variant(zval *z, VARIANT *v,
+ int codepage TSRMLS_DC)
+{
+ php_com_dotnet_object *obj;
+
+ obj = emalloc(sizeof(*obj));
+ memset(obj, 0, sizeof(*obj));
+ obj->code_page = codepage;
+ obj->ce = php_com_variant_class_entry;
+ obj->zo.ce = php_com_variant_class_entry;
+
+ VariantInit(&obj->v);
+ VariantCopyInd(&obj->v, v);
+ obj->modified = 0;
+
+ if ((V_VT(&obj->v) == VT_DISPATCH) && (V_DISPATCH(&obj->v) != NULL)) {
+ IDispatch_GetTypeInfo(V_DISPATCH(&obj->v), 0, LANG_NEUTRAL, &obj->typeinfo);
+ }
+
+ Z_TYPE_P(z) = IS_OBJECT;
+
+ z->value.obj.handle = zend_objects_store_put(obj, NULL, php_com_object_free_storage, 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 */
+PHP_COM_DOTNET_API 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(DISP_E_BADINDEX, "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;
+}
diff --git a/ext/com_dotnet/com_olechar.c b/ext/com_dotnet/com_olechar.c
new file mode 100644
index 0000000..e456df2
--- /dev/null
+++ b/ext/com_dotnet/com_olechar.c
@@ -0,0 +1,101 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 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_01.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> |
+ | Harald Radi <h.radi@nme.at> |
+ +----------------------------------------------------------------------+
+ */
+
+/* $Id$ */
+
+#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"
+
+
+PHP_COM_DOTNET_API OLECHAR *php_com_string_to_olestring(char *string, uint string_len, int codepage TSRMLS_DC)
+{
+ OLECHAR *olestring = NULL;
+ DWORD flags = codepage == CP_UTF8 ? 0 : MB_PRECOMPOSED | MB_ERR_INVALID_CHARS;
+ BOOL ok;
+
+ if (string_len == -1) {
+ /* determine required length for the buffer (includes NUL terminator) */
+ string_len = MultiByteToWideChar(codepage, flags, string, -1, NULL, 0);
+ } else {
+ /* allow room for NUL terminator */
+ string_len++;
+ }
+
+ if (string_len > 0) {
+ olestring = (OLECHAR*)safe_emalloc(string_len, sizeof(OLECHAR), 0);
+ ok = MultiByteToWideChar(codepage, flags, string, string_len, olestring, string_len);
+ } else {
+ ok = FALSE;
+ olestring = (OLECHAR*)emalloc(sizeof(OLECHAR));
+ *olestring = 0;
+ }
+
+ if (!ok) {
+ char *msg = php_win32_error_to_msg(GetLastError());
+
+ php_error_docref(NULL TSRMLS_CC, E_WARNING,
+ "Could not convert string to unicode: `%s'", msg);
+
+ LocalFree(msg);
+ }
+
+ return olestring;
+}
+
+PHP_COM_DOTNET_API char *php_com_olestring_to_string(OLECHAR *olestring, uint *string_len, int codepage TSRMLS_DC)
+{
+ char *string;
+ uint length = 0;
+ BOOL ok;
+
+ length = WideCharToMultiByte(codepage, 0, olestring, -1, NULL, 0, NULL, NULL);
+
+ if (length) {
+ string = (char*)safe_emalloc(length, sizeof(char), 0);
+ length = WideCharToMultiByte(codepage, 0, olestring, -1, string, length, NULL, NULL);
+ ok = length > 0;
+ } else {
+ string = (char*)emalloc(sizeof(char));
+ *string = '\0';
+ ok = FALSE;
+ length = 0;
+ }
+
+ if (!ok) {
+ char *msg = php_win32_error_to_msg(GetLastError());
+
+ php_error_docref(NULL TSRMLS_CC, E_WARNING,
+ "Could not convert string from unicode: `%s'", msg);
+
+ LocalFree(msg);
+ }
+
+ if (string_len) {
+ *string_len = length-1;
+ }
+
+ return string;
+}
diff --git a/ext/com_dotnet/com_persist.c b/ext/com_dotnet/com_persist.c
new file mode 100644
index 0000000..8953a41
--- /dev/null
+++ b/ext/com_dotnet/com_persist.c
@@ -0,0 +1,777 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 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_01.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$ */
+
+/* Infrastructure for working with persistent COM objects.
+ * Implements: IStream* wrapper for PHP streams.
+ * TODO: Magic __wakeup and __sleep handlers for serialization
+ * (can wait till 5.1) */
+
+#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_exceptions.h"
+
+/* {{{ expose php_stream as a COM IStream */
+
+typedef struct {
+ CONST_VTBL struct IStreamVtbl *lpVtbl;
+ DWORD engine_thread;
+ LONG refcount;
+ php_stream *stream;
+ int id;
+} php_istream;
+
+static int le_istream;
+static void istream_destructor(php_istream *stm TSRMLS_DC);
+
+static void istream_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
+{
+ php_istream *stm = (php_istream *)rsrc->ptr;
+ istream_destructor(stm TSRMLS_CC);
+}
+
+#define FETCH_STM() \
+ php_istream *stm = (php_istream*)This; \
+ TSRMLS_FETCH(); \
+ if (GetCurrentThreadId() != stm->engine_thread) \
+ return RPC_E_WRONG_THREAD;
+
+#define FETCH_STM_EX() \
+ php_istream *stm = (php_istream*)This; \
+ if (GetCurrentThreadId() != stm->engine_thread) \
+ return RPC_E_WRONG_THREAD;
+
+static HRESULT STDMETHODCALLTYPE stm_queryinterface(
+ IStream *This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */ void **ppvObject)
+{
+ FETCH_STM_EX();
+
+ if (IsEqualGUID(&IID_IUnknown, riid) ||
+ IsEqualGUID(&IID_IStream, riid)) {
+ *ppvObject = This;
+ InterlockedIncrement(&stm->refcount);
+ return S_OK;
+ }
+
+ *ppvObject = NULL;
+ return E_NOINTERFACE;
+}
+
+static ULONG STDMETHODCALLTYPE stm_addref(IStream *This)
+{
+ FETCH_STM_EX();
+
+ return InterlockedIncrement(&stm->refcount);
+}
+
+static ULONG STDMETHODCALLTYPE stm_release(IStream *This)
+{
+ ULONG ret;
+ FETCH_STM();
+
+ ret = InterlockedDecrement(&stm->refcount);
+ if (ret == 0) {
+ /* destroy it */
+ if (stm->id)
+ zend_list_delete(stm->id);
+ }
+ return ret;
+}
+
+static HRESULT STDMETHODCALLTYPE stm_read(IStream *This, void *pv, ULONG cb, ULONG *pcbRead)
+{
+ int nread;
+ FETCH_STM();
+
+ nread = php_stream_read(stm->stream, pv, cb);
+
+ if (pcbRead) {
+ *pcbRead = nread > 0 ? nread : 0;
+ }
+ if (nread > 0) {
+ return S_OK;
+ }
+ return S_FALSE;
+}
+
+static HRESULT STDMETHODCALLTYPE stm_write(IStream *This, void const *pv, ULONG cb, ULONG *pcbWritten)
+{
+ int nwrote;
+ FETCH_STM();
+
+ nwrote = php_stream_write(stm->stream, pv, cb);
+
+ if (pcbWritten) {
+ *pcbWritten = nwrote > 0 ? nwrote : 0;
+ }
+ if (nwrote > 0) {
+ return S_OK;
+ }
+ return S_FALSE;
+}
+
+static HRESULT STDMETHODCALLTYPE stm_seek(IStream *This, LARGE_INTEGER dlibMove,
+ DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
+{
+ off_t offset;
+ int whence;
+ int ret;
+ FETCH_STM();
+
+ switch (dwOrigin) {
+ case STREAM_SEEK_SET: whence = SEEK_SET; break;
+ case STREAM_SEEK_CUR: whence = SEEK_CUR; break;
+ case STREAM_SEEK_END: whence = SEEK_END; break;
+ default:
+ return STG_E_INVALIDFUNCTION;
+ }
+
+ if (dlibMove.HighPart) {
+ /* we don't support 64-bit offsets */
+ return STG_E_INVALIDFUNCTION;
+ }
+
+ offset = (off_t) dlibMove.QuadPart;
+
+ ret = php_stream_seek(stm->stream, offset, whence);
+
+ if (plibNewPosition) {
+ plibNewPosition->QuadPart = (ULONGLONG)(ret >= 0 ? ret : 0);
+ }
+
+ return ret >= 0 ? S_OK : STG_E_INVALIDFUNCTION;
+}
+
+static HRESULT STDMETHODCALLTYPE stm_set_size(IStream *This, ULARGE_INTEGER libNewSize)
+{
+ FETCH_STM();
+
+ if (libNewSize.HighPart) {
+ return STG_E_INVALIDFUNCTION;
+ }
+
+ if (php_stream_truncate_supported(stm->stream)) {
+ int ret = php_stream_truncate_set_size(stm->stream, (size_t)libNewSize.QuadPart);
+
+ if (ret == 0) {
+ return S_OK;
+ }
+ }
+
+ return STG_E_INVALIDFUNCTION;
+}
+
+static HRESULT STDMETHODCALLTYPE stm_copy_to(IStream *This, IStream *pstm, ULARGE_INTEGER cb,
+ ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
+{
+ FETCH_STM_EX();
+
+ return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE stm_commit(IStream *This, DWORD grfCommitFlags)
+{
+ FETCH_STM();
+
+ php_stream_flush(stm->stream);
+
+ return S_OK;
+}
+
+static HRESULT STDMETHODCALLTYPE stm_revert(IStream *This)
+{
+ /* NOP */
+ return S_OK;
+}
+
+static HRESULT STDMETHODCALLTYPE stm_lock_region(IStream *This,
+ ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD lockType)
+{
+ return STG_E_INVALIDFUNCTION;
+}
+
+static HRESULT STDMETHODCALLTYPE stm_unlock_region(IStream *This,
+ ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD lockType)
+{
+ return STG_E_INVALIDFUNCTION;
+}
+
+static HRESULT STDMETHODCALLTYPE stm_stat(IStream *This,
+ STATSTG *pstatstg, DWORD grfStatFlag)
+{
+ return STG_E_INVALIDFUNCTION;
+}
+
+static HRESULT STDMETHODCALLTYPE stm_clone(IStream *This, IStream **ppstm)
+{
+ return STG_E_INVALIDFUNCTION;
+}
+
+static struct IStreamVtbl php_istream_vtbl = {
+ stm_queryinterface,
+ stm_addref,
+ stm_release,
+ stm_read,
+ stm_write,
+ stm_seek,
+ stm_set_size,
+ stm_copy_to,
+ stm_commit,
+ stm_revert,
+ stm_lock_region,
+ stm_unlock_region,
+ stm_stat,
+ stm_clone
+};
+
+static void istream_destructor(php_istream *stm TSRMLS_DC)
+{
+ if (stm->id) {
+ int id = stm->id;
+ stm->id = 0;
+ zend_list_delete(id);
+ return;
+ }
+
+ if (stm->refcount > 0) {
+ CoDisconnectObject((IUnknown*)stm, 0);
+ }
+
+ zend_list_delete(stm->stream->rsrc_id);
+
+ CoTaskMemFree(stm);
+}
+/* }}} */
+
+PHP_COM_DOTNET_API IStream *php_com_wrapper_export_stream(php_stream *stream TSRMLS_DC)
+{
+ php_istream *stm = (php_istream*)CoTaskMemAlloc(sizeof(*stm));
+
+ if (stm == NULL)
+ return NULL;
+
+ memset(stm, 0, sizeof(*stm));
+ stm->engine_thread = GetCurrentThreadId();
+ stm->lpVtbl = &php_istream_vtbl;
+ stm->refcount = 1;
+ stm->stream = stream;
+
+ zend_list_addref(stream->rsrc_id);
+ stm->id = zend_list_insert(stm, le_istream TSRMLS_CC);
+
+ return (IStream*)stm;
+}
+
+#define CPH_ME(fname, arginfo) PHP_ME(com_persist, fname, arginfo, ZEND_ACC_PUBLIC)
+#define CPH_SME(fname, arginfo) PHP_ME(com_persist, fname, arginfo, ZEND_ACC_ALLOW_STATIC|ZEND_ACC_PUBLIC)
+#define CPH_METHOD(fname) static PHP_METHOD(com_persist, fname)
+
+#define CPH_FETCH() php_com_persist_helper *helper = (php_com_persist_helper*)zend_object_store_get_object(getThis() TSRMLS_CC);
+
+#define CPH_NO_OBJ() if (helper->unk == NULL) { php_com_throw_exception(E_INVALIDARG, "No COM object is associated with this helper instance" TSRMLS_CC); return; }
+
+typedef struct {
+ zend_object std;
+ long codepage;
+ IUnknown *unk;
+ IPersistStream *ips;
+ IPersistStreamInit *ipsi;
+ IPersistFile *ipf;
+} php_com_persist_helper;
+
+static zend_object_handlers helper_handlers;
+static zend_class_entry *helper_ce;
+
+static inline HRESULT get_persist_stream(php_com_persist_helper *helper)
+{
+ if (!helper->ips && helper->unk) {
+ return IUnknown_QueryInterface(helper->unk, &IID_IPersistStream, &helper->ips);
+ }
+ return helper->ips ? S_OK : E_NOTIMPL;
+}
+
+static inline HRESULT get_persist_stream_init(php_com_persist_helper *helper)
+{
+ if (!helper->ipsi && helper->unk) {
+ return IUnknown_QueryInterface(helper->unk, &IID_IPersistStreamInit, &helper->ipsi);
+ }
+ return helper->ipsi ? S_OK : E_NOTIMPL;
+}
+
+static inline HRESULT get_persist_file(php_com_persist_helper *helper)
+{
+ if (!helper->ipf && helper->unk) {
+ return IUnknown_QueryInterface(helper->unk, &IID_IPersistFile, &helper->ipf);
+ }
+ return helper->ipf ? S_OK : E_NOTIMPL;
+}
+
+
+/* {{{ proto string COMPersistHelper::GetCurFile()
+ Determines the filename into which an object will be saved, or false if none is set, via IPersistFile::GetCurFile */
+CPH_METHOD(GetCurFileName)
+{
+ HRESULT res;
+ OLECHAR *olename = NULL;
+ CPH_FETCH();
+
+ CPH_NO_OBJ();
+
+ res = get_persist_file(helper);
+ if (helper->ipf) {
+ res = IPersistFile_GetCurFile(helper->ipf, &olename);
+
+ if (res == S_OK) {
+ Z_TYPE_P(return_value) = IS_STRING;
+ Z_STRVAL_P(return_value) = php_com_olestring_to_string(olename,
+ &Z_STRLEN_P(return_value), helper->codepage TSRMLS_CC);
+ CoTaskMemFree(olename);
+ return;
+ } else if (res == S_FALSE) {
+ CoTaskMemFree(olename);
+ RETURN_FALSE;
+ }
+ php_com_throw_exception(res, NULL TSRMLS_CC);
+ } else {
+ php_com_throw_exception(res, NULL TSRMLS_CC);
+ }
+}
+/* }}} */
+
+
+/* {{{ proto bool COMPersistHelper::SaveToFile(string filename [, bool remember])
+ Persist object data to file, via IPersistFile::Save */
+CPH_METHOD(SaveToFile)
+{
+ HRESULT res;
+ char *filename, *fullpath = NULL;
+ int filename_len;
+ zend_bool remember = TRUE;
+ OLECHAR *olefilename = NULL;
+ CPH_FETCH();
+
+ CPH_NO_OBJ();
+
+ res = get_persist_file(helper);
+ if (helper->ipf) {
+ if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "p!|b",
+ &filename, &filename_len, &remember)) {
+ php_com_throw_exception(E_INVALIDARG, "Invalid arguments" TSRMLS_CC);
+ return;
+ }
+
+ if (filename) {
+ fullpath = expand_filepath(filename, NULL TSRMLS_CC);
+ if (!fullpath) {
+ RETURN_FALSE;
+ }
+
+ if (php_check_open_basedir(fullpath TSRMLS_CC)) {
+ efree(fullpath);
+ RETURN_FALSE;
+ }
+
+ olefilename = php_com_string_to_olestring(filename, strlen(fullpath), helper->codepage TSRMLS_CC);
+ efree(fullpath);
+ }
+ res = IPersistFile_Save(helper->ipf, olefilename, remember);
+ if (SUCCEEDED(res)) {
+ if (!olefilename) {
+ res = IPersistFile_GetCurFile(helper->ipf, &olefilename);
+ if (S_OK == res) {
+ IPersistFile_SaveCompleted(helper->ipf, olefilename);
+ CoTaskMemFree(olefilename);
+ olefilename = NULL;
+ }
+ } else if (remember) {
+ IPersistFile_SaveCompleted(helper->ipf, olefilename);
+ }
+ }
+
+ if (olefilename) {
+ efree(olefilename);
+ }
+
+ if (FAILED(res)) {
+ php_com_throw_exception(res, NULL TSRMLS_CC);
+ }
+
+ } else {
+ php_com_throw_exception(res, NULL TSRMLS_CC);
+ }
+}
+/* }}} */
+
+/* {{{ proto bool COMPersistHelper::LoadFromFile(string filename [, int flags])
+ Load object data from file, via IPersistFile::Load */
+CPH_METHOD(LoadFromFile)
+{
+ HRESULT res;
+ char *filename, *fullpath;
+ int filename_len;
+ long flags = 0;
+ OLECHAR *olefilename;
+ CPH_FETCH();
+
+ CPH_NO_OBJ();
+
+ res = get_persist_file(helper);
+ if (helper->ipf) {
+
+ if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "p|l",
+ &filename, &filename_len, &flags)) {
+ php_com_throw_exception(E_INVALIDARG, "Invalid arguments" TSRMLS_CC);
+ return;
+ }
+
+ if (!(fullpath = expand_filepath(filename, NULL TSRMLS_CC))) {
+ RETURN_FALSE;
+ }
+
+ if (php_check_open_basedir(fullpath TSRMLS_CC)) {
+ efree(fullpath);
+ RETURN_FALSE;
+ }
+
+ olefilename = php_com_string_to_olestring(fullpath, strlen(fullpath), helper->codepage TSRMLS_CC);
+ efree(fullpath);
+
+ res = IPersistFile_Load(helper->ipf, olefilename, flags);
+ efree(olefilename);
+
+ if (FAILED(res)) {
+ php_com_throw_exception(res, NULL TSRMLS_CC);
+ }
+
+ } else {
+ php_com_throw_exception(res, NULL TSRMLS_CC);
+ }
+}
+/* }}} */
+
+/* {{{ proto int COMPersistHelper::GetMaxStreamSize()
+ Gets maximum stream size required to store the object data, via IPersistStream::GetSizeMax (or IPersistStreamInit::GetSizeMax) */
+CPH_METHOD(GetMaxStreamSize)
+{
+ HRESULT res;
+ ULARGE_INTEGER size;
+ CPH_FETCH();
+
+ CPH_NO_OBJ();
+
+ res = get_persist_stream_init(helper);
+ if (helper->ipsi) {
+ res = IPersistStreamInit_GetSizeMax(helper->ipsi, &size);
+ } else {
+ res = get_persist_stream(helper);
+ if (helper->ips) {
+ res = IPersistStream_GetSizeMax(helper->ips, &size);
+ } else {
+ php_com_throw_exception(res, NULL TSRMLS_CC);
+ return;
+ }
+ }
+
+ if (res != S_OK) {
+ php_com_throw_exception(res, NULL TSRMLS_CC);
+ } else {
+ /* TODO: handle 64 bit properly */
+ RETURN_LONG((LONG)size.QuadPart);
+ }
+}
+/* }}} */
+
+/* {{{ proto int COMPersistHelper::InitNew()
+ Initializes the object to a default state, via IPersistStreamInit::InitNew */
+CPH_METHOD(InitNew)
+{
+ HRESULT res;
+ CPH_FETCH();
+
+ CPH_NO_OBJ();
+
+ res = get_persist_stream_init(helper);
+ if (helper->ipsi) {
+ res = IPersistStreamInit_InitNew(helper->ipsi);
+
+ if (res != S_OK) {
+ php_com_throw_exception(res, NULL TSRMLS_CC);
+ } else {
+ RETURN_TRUE;
+ }
+ } else {
+ php_com_throw_exception(res, NULL TSRMLS_CC);
+ }
+}
+/* }}} */
+
+/* {{{ proto mixed COMPersistHelper::LoadFromStream(resource stream)
+ Initializes an object from the stream where it was previously saved, via IPersistStream::Load or OleLoadFromStream */
+CPH_METHOD(LoadFromStream)
+{
+ zval *zstm;
+ php_stream *stream;
+ IStream *stm = NULL;
+ HRESULT res;
+ CPH_FETCH();
+
+ if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zstm)) {
+ php_com_throw_exception(E_INVALIDARG, "invalid arguments" TSRMLS_CC);
+ return;
+ }
+
+ php_stream_from_zval_no_verify(stream, &zstm);
+
+ if (stream == NULL) {
+ php_com_throw_exception(E_INVALIDARG, "expected a stream" TSRMLS_CC);
+ return;
+ }
+
+ stm = php_com_wrapper_export_stream(stream TSRMLS_CC);
+ if (stm == NULL) {
+ php_com_throw_exception(E_UNEXPECTED, "failed to wrap stream" TSRMLS_CC);
+ return;
+ }
+
+ res = S_OK;
+ RETVAL_TRUE;
+
+ if (helper->unk == NULL) {
+ IDispatch *disp = NULL;
+
+ /* we need to create an object and load using OleLoadFromStream */
+ res = OleLoadFromStream(stm, &IID_IDispatch, &disp);
+
+ if (SUCCEEDED(res)) {
+ php_com_wrap_dispatch(return_value, disp, COMG(code_page) TSRMLS_CC);
+ }
+ } else {
+ res = get_persist_stream_init(helper);
+ if (helper->ipsi) {
+ res = IPersistStreamInit_Load(helper->ipsi, stm);
+ } else {
+ res = get_persist_stream(helper);
+ if (helper->ips) {
+ res = IPersistStreamInit_Load(helper->ipsi, stm);
+ }
+ }
+ }
+ IStream_Release(stm);
+
+ if (FAILED(res)) {
+ php_com_throw_exception(res, NULL TSRMLS_CC);
+ RETURN_NULL();
+ }
+}
+/* }}} */
+
+/* {{{ proto int COMPersistHelper::SaveToStream(resource stream)
+ Saves the object to a stream, via IPersistStream::Save */
+CPH_METHOD(SaveToStream)
+{
+ zval *zstm;
+ php_stream *stream;
+ IStream *stm = NULL;
+ HRESULT res;
+ CPH_FETCH();
+
+ CPH_NO_OBJ();
+
+ if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zstm)) {
+ php_com_throw_exception(E_INVALIDARG, "invalid arguments" TSRMLS_CC);
+ return;
+ }
+
+ php_stream_from_zval_no_verify(stream, &zstm);
+
+ if (stream == NULL) {
+ php_com_throw_exception(E_INVALIDARG, "expected a stream" TSRMLS_CC);
+ return;
+ }
+
+ stm = php_com_wrapper_export_stream(stream TSRMLS_CC);
+ if (stm == NULL) {
+ php_com_throw_exception(E_UNEXPECTED, "failed to wrap stream" TSRMLS_CC);
+ return;
+ }
+
+ res = get_persist_stream_init(helper);
+ if (helper->ipsi) {
+ res = IPersistStreamInit_Save(helper->ipsi, stm, TRUE);
+ } else {
+ res = get_persist_stream(helper);
+ if (helper->ips) {
+ res = IPersistStream_Save(helper->ips, stm, TRUE);
+ }
+ }
+
+ IStream_Release(stm);
+
+ if (FAILED(res)) {
+ php_com_throw_exception(res, NULL TSRMLS_CC);
+ return;
+ }
+
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto int COMPersistHelper::__construct([object com_object])
+ Creates a persistence helper object, usually associated with a com_object */
+CPH_METHOD(__construct)
+{
+ php_com_dotnet_object *obj = NULL;
+ zval *zobj = NULL;
+ CPH_FETCH();
+
+ if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|O!",
+ &zobj, php_com_variant_class_entry)) {
+ php_com_throw_exception(E_INVALIDARG, "invalid arguments" TSRMLS_CC);
+ return;
+ }
+
+ if (!zobj) {
+ return;
+ }
+
+ obj = CDNO_FETCH(zobj);
+
+ if (V_VT(&obj->v) != VT_DISPATCH || V_DISPATCH(&obj->v) == NULL) {
+ php_com_throw_exception(E_INVALIDARG, "parameter must represent an IDispatch COM object" TSRMLS_CC);
+ return;
+ }
+
+ /* it is always safe to cast an interface to IUnknown */
+ helper->unk = (IUnknown*)V_DISPATCH(&obj->v);
+ IUnknown_AddRef(helper->unk);
+ helper->codepage = obj->code_page;
+}
+/* }}} */
+
+
+
+
+static const zend_function_entry com_persist_helper_methods[] = {
+ CPH_ME(__construct, NULL)
+ CPH_ME(GetCurFileName, NULL)
+ CPH_ME(SaveToFile, NULL)
+ CPH_ME(LoadFromFile, NULL)
+ CPH_ME(GetMaxStreamSize, NULL)
+ CPH_ME(InitNew, NULL)
+ CPH_ME(LoadFromStream, NULL)
+ CPH_ME(SaveToStream, NULL)
+ PHP_FE_END
+};
+
+static void helper_free_storage(void *obj TSRMLS_DC)
+{
+ php_com_persist_helper *object = (php_com_persist_helper*)obj;
+
+ if (object->ipf) {
+ IPersistFile_Release(object->ipf);
+ }
+ if (object->ips) {
+ IPersistStream_Release(object->ips);
+ }
+ if (object->ipsi) {
+ IPersistStreamInit_Release(object->ipsi);
+ }
+ if (object->unk) {
+ IUnknown_Release(object->unk);
+ }
+ zend_object_std_dtor(&object->std TSRMLS_CC);
+ efree(object);
+}
+
+
+static void helper_clone(void *obj, void **clone_ptr TSRMLS_DC)
+{
+ php_com_persist_helper *clone, *object = (php_com_persist_helper*)obj;
+
+ clone = emalloc(sizeof(*object));
+ memcpy(clone, object, sizeof(*object));
+ *clone_ptr = clone;
+
+ zend_object_std_init(&clone->std, object->std.ce TSRMLS_CC);
+
+ if (clone->ipf) {
+ IPersistFile_AddRef(clone->ipf);
+ }
+ if (clone->ips) {
+ IPersistStream_AddRef(clone->ips);
+ }
+ if (clone->ipsi) {
+ IPersistStreamInit_AddRef(clone->ipsi);
+ }
+ if (clone->unk) {
+ IUnknown_AddRef(clone->unk);
+ }
+}
+
+static zend_object_value helper_new(zend_class_entry *ce TSRMLS_DC)
+{
+ php_com_persist_helper *helper;
+ zend_object_value retval;
+
+ helper = emalloc(sizeof(*helper));
+ memset(helper, 0, sizeof(*helper));
+
+ zend_object_std_init(&helper->std, helper_ce TSRMLS_CC);
+
+ retval.handle = zend_objects_store_put(helper, NULL, helper_free_storage, helper_clone TSRMLS_CC);
+ retval.handlers = &helper_handlers;
+
+ return retval;
+}
+
+int php_com_persist_minit(INIT_FUNC_ARGS)
+{
+ zend_class_entry ce;
+
+ memcpy(&helper_handlers, zend_get_std_object_handlers(), sizeof(helper_handlers));
+ helper_handlers.clone_obj = NULL;
+
+ INIT_CLASS_ENTRY(ce, "COMPersistHelper", com_persist_helper_methods);
+ ce.create_object = helper_new;
+ helper_ce = zend_register_internal_class(&ce TSRMLS_CC);
+ helper_ce->ce_flags |= ZEND_ACC_FINAL;
+
+ le_istream = zend_register_list_destructors_ex(istream_dtor,
+ NULL, "com_dotnet_istream_wrapper", module_number);
+
+ return SUCCESS;
+}
+
+/*
+ * 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
+ */
diff --git a/ext/com_dotnet/com_saproxy.c b/ext/com_dotnet/com_saproxy.c
new file mode 100644
index 0000000..ad92849
--- /dev/null
+++ b/ext/com_dotnet/com_saproxy.c
@@ -0,0 +1,586 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 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_01.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.
+ * In addition, the proxy is now able to handle properties of COM objects
+ * that smell like PHP arrays.
+ * */
+
+#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_exceptions.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) */
+ zval **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 inline void clone_indices(php_com_saproxy *dest, php_com_saproxy *src, int ndims)
+{
+ int i;
+
+ for (i = 0; i < ndims; i++) {
+ MAKE_STD_ZVAL(dest->indices[i]);
+ *dest->indices[i] = *src->indices[i];
+ zval_copy_ctor(dest->indices[i]);
+ }
+}
+
+static zval *saproxy_property_read(zval *object, zval *member, int type, const zend_literal *key TSRMLS_DC)
+{
+ zval *return_value;
+
+ MAKE_STD_ZVAL(return_value);
+ ZVAL_NULL(return_value);
+
+ 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, const zend_literal *key TSRMLS_DC)
+{
+ php_com_throw_exception(E_INVALIDARG, "safearray has no properties" TSRMLS_CC);
+}
+
+static zval *saproxy_read_dimension(zval *object, zval *offset, int type TSRMLS_DC)
+{
+ php_com_saproxy *proxy = SA_FETCH(object);
+ zval *return_value;
+ UINT dims, i;
+ SAFEARRAY *sa;
+ LONG ubound, lbound;
+ HRESULT res;
+
+ MAKE_STD_ZVAL(return_value);
+ ZVAL_NULL(return_value);
+
+ if (V_VT(&proxy->obj->v) == VT_DISPATCH) {
+ VARIANT v;
+ zval **args;
+
+ /* prop-get using first dimension as the property name,
+ * all subsequent dimensions and the offset as parameters */
+
+ args = safe_emalloc(proxy->dimensions + 1, sizeof(zval *), 0);
+
+ for (i = 1; i < (UINT) proxy->dimensions; i++) {
+ args[i-1] = proxy->indices[i];
+ }
+ args[i-1] = offset;
+
+ convert_to_string(proxy->indices[0]);
+ VariantInit(&v);
+
+ res = php_com_do_invoke(proxy->obj, Z_STRVAL_P(proxy->indices[0]),
+ Z_STRLEN_P(proxy->indices[0]), DISPATCH_METHOD|DISPATCH_PROPERTYGET, &v,
+ proxy->dimensions, args, 0 TSRMLS_CC);
+
+ if (res == SUCCESS) {
+ php_com_zval_from_variant(return_value, &v, proxy->obj->code_page TSRMLS_CC);
+ VariantClear(&v);
+ } else if (res == DISP_E_BADPARAMCOUNT) {
+ /* return another proxy */
+ php_com_saproxy_create(object, return_value, offset TSRMLS_CC);
+ }
+
+ return return_value;
+
+ } else if (!V_ISARRAY(&proxy->obj->v)) {
+ php_com_throw_exception(E_INVALIDARG, "invalid read from com proxy object" TSRMLS_CC);
+ return return_value;
+ }
+
+ /* the SafeArray case */
+
+ /* offset/index must be an integer */
+ convert_to_long(offset);
+
+ sa = V_ARRAY(&proxy->obj->v);
+ dims = SafeArrayGetDim(sa);
+
+ if ((UINT) 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(DISP_E_BADINDEX, "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 = safe_emalloc(dims, sizeof(LONG), 0);
+
+ /* copy indices from proxy */
+ for (i = 0; i < dims; i++) {
+ convert_to_long(proxy->indices[i]);
+ indices[i] = Z_LVAL_P(proxy->indices[i]);
+ }
+
+ /* 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) {
+ res = SafeArrayGetElement(sa, indices, &v);
+ } else {
+ V_VT(&v) = vt;
+ res = SafeArrayGetElement(sa, indices, &v.lVal);
+ }
+
+ efree(indices);
+
+ if (SUCCEEDED(res)) {
+ php_com_wrap_variant(return_value, &v, proxy->obj->code_page TSRMLS_CC);
+ } else {
+ php_com_throw_exception(res, NULL TSRMLS_CC);
+ }
+
+ VariantClear(&v);
+
+ } else {
+ /* return another proxy */
+ php_com_saproxy_create(object, return_value, offset TSRMLS_CC);
+ }
+
+ return return_value;
+}
+
+static void saproxy_write_dimension(zval *object, zval *offset, zval *value TSRMLS_DC)
+{
+ php_com_saproxy *proxy = SA_FETCH(object);
+ UINT dims, i;
+ HRESULT res;
+ VARIANT v;
+
+ if (V_VT(&proxy->obj->v) == VT_DISPATCH) {
+ /* We do a prop-set using the first dimension as the property name,
+ * all subsequent dimensions and offset as parameters, with value as
+ * the final value */
+ zval **args = safe_emalloc(proxy->dimensions + 2, sizeof(zval *), 0);
+
+ for (i = 1; i < (UINT) proxy->dimensions; i++) {
+ args[i-1] = proxy->indices[i];
+ }
+ args[i-1] = offset;
+ args[i] = value;
+
+ convert_to_string(proxy->indices[0]);
+ VariantInit(&v);
+ if (SUCCESS == php_com_do_invoke(proxy->obj, Z_STRVAL_P(proxy->indices[0]),
+ Z_STRLEN_P(proxy->indices[0]), DISPATCH_PROPERTYPUT, &v, proxy->dimensions + 1,
+ args, 0 TSRMLS_CC)) {
+ VariantClear(&v);
+ }
+
+ efree(args);
+
+ } else if (V_ISARRAY(&proxy->obj->v)) {
+ LONG *indices;
+ VARTYPE vt;
+
+ dims = SafeArrayGetDim(V_ARRAY(&proxy->obj->v));
+ indices = safe_emalloc(dims, sizeof(LONG), 0);
+ /* copy indices from proxy */
+ for (i = 0; i < dims; i++) {
+ convert_to_long(proxy->indices[i]);
+ indices[i] = Z_LVAL_P(proxy->indices[i]);
+ }
+
+ /* add user-supplied index */
+ convert_to_long(offset);
+ indices[dims-1] = Z_LVAL_P(offset);
+
+ if (FAILED(SafeArrayGetVartype(V_ARRAY(&proxy->obj->v), &vt)) || vt == VT_EMPTY) {
+ vt = V_VT(&proxy->obj->v) & ~VT_ARRAY;
+ }
+
+ VariantInit(&v);
+ php_com_variant_from_zval(&v, value, proxy->obj->code_page TSRMLS_CC);
+
+ if (V_VT(&v) != vt) {
+ VariantChangeType(&v, &v, 0, vt);
+ }
+
+ if (vt == VT_VARIANT) {
+ res = SafeArrayPutElement(V_ARRAY(&proxy->obj->v), indices, &v);
+ } else {
+ res = SafeArrayPutElement(V_ARRAY(&proxy->obj->v), indices, &v.lVal);
+ }
+
+ efree(indices);
+ VariantClear(&v);
+
+ if (FAILED(res)) {
+ php_com_throw_exception(res, NULL TSRMLS_CC);
+ }
+ } else {
+ php_com_throw_exception(E_NOTIMPL, "invalid write to com proxy object" TSRMLS_CC);
+ }
+}
+
+#if 0
+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;
+}
+#endif
+
+static int saproxy_property_exists(zval *object, zval *member, int check_empty, const zend_literal *key 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, const zend_literal *key 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, const char *name, int len, const zend_literal *key TSRMLS_DC)
+{
+ /* no methods */
+ return NULL;
+}
+
+static int saproxy_call_method(const char *method, INTERNAL_FUNCTION_PARAMETERS)
+{
+ return FAILURE;
+}
+
+static union _zend_function *saproxy_constructor_get(zval *object TSRMLS_DC)
+{
+ /* user cannot instantiate */
+ return NULL;
+}
+
+static zend_class_entry *saproxy_class_entry_get(const zval *object TSRMLS_DC)
+{
+ return php_com_saproxy_class_entry;
+}
+
+static int saproxy_class_name_get(const zval *object, const 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 TSRMLS_DC)
+{
+ return FAILURE;
+}
+
+static int saproxy_count_elements(zval *object, long *count TSRMLS_DC)
+{
+ php_com_saproxy *proxy = SA_FETCH(object);
+ LONG ubound, lbound;
+
+ if (!V_ISARRAY(&proxy->obj->v)) {
+ return FAILURE;
+ }
+
+ SafeArrayGetLBound(V_ARRAY(&proxy->obj->v), proxy->dimensions, &lbound);
+ SafeArrayGetUBound(V_ARRAY(&proxy->obj->v), proxy->dimensions, &ubound);
+
+ *count = ubound - lbound + 1;
+
+ return SUCCESS;
+}
+
+zend_object_handlers php_com_saproxy_handlers = {
+ ZEND_OBJECTS_STORE_HANDLERS,
+ saproxy_property_read,
+ saproxy_property_write,
+ saproxy_read_dimension,
+ saproxy_write_dimension,
+ NULL,
+ NULL, /* saproxy_object_get, */
+ NULL, /* 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,
+ saproxy_count_elements
+};
+
+static void saproxy_free_storage(void *object TSRMLS_DC)
+{
+ php_com_saproxy *proxy = (php_com_saproxy *)object;
+ int i;
+
+ for (i = 0; i < proxy->dimensions; i++) {
+ if (proxy->indices) {
+ FREE_ZVAL(proxy->indices[i]);
+ }
+ }
+
+ zval_ptr_dtor(&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));
+
+ Z_ADDREF_P(cloneproxy->zobj);
+ cloneproxy->indices = safe_emalloc(cloneproxy->dimensions, sizeof(zval *), 0);
+ clone_indices(cloneproxy, proxy, proxy->dimensions);
+
+ *clone_ptr = cloneproxy;
+}
+
+int php_com_saproxy_create(zval *com_object, zval *proxy_out, zval *index TSRMLS_DC)
+{
+ php_com_saproxy *proxy, *rel = NULL;
+
+ proxy = ecalloc(1, sizeof(*proxy));
+ proxy->dimensions = 1;
+
+ if (Z_OBJCE_P(com_object) == php_com_saproxy_class_entry) {
+ rel = SA_FETCH(com_object);
+ proxy->obj = rel->obj;
+ proxy->zobj = rel->zobj;
+ proxy->dimensions += rel->dimensions;
+ } else {
+ proxy->obj = CDNO_FETCH(com_object);
+ proxy->zobj = com_object;
+ }
+
+ Z_ADDREF_P(proxy->zobj);
+ proxy->indices = safe_emalloc(proxy->dimensions, sizeof(zval *), 0);
+
+ if (rel) {
+ clone_indices(proxy, rel, rel->dimensions);
+ }
+
+ MAKE_STD_ZVAL(proxy->indices[proxy->dimensions-1]);
+ *proxy->indices[proxy->dimensions-1] = *index;
+ zval_copy_ctor(proxy->indices[proxy->dimensions-1]);
+
+ Z_TYPE_P(proxy_out) = IS_OBJECT;
+ Z_OBJ_HANDLE_P(proxy_out) = zend_objects_store_put(proxy, NULL, saproxy_free_storage, 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_ptr_dtor(&I->proxy_obj);
+
+ efree(I->indices);
+ efree(I);
+}
+
+static int saproxy_iter_valid(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_valid,
+ 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, int by_ref TSRMLS_DC)
+{
+ php_com_saproxy *proxy = SA_FETCH(object);
+ php_com_saproxy_iter *I;
+ int i;
+
+ if (by_ref) {
+ zend_error(E_ERROR, "An iterator cannot be used with foreach by reference");
+ }
+
+ I = ecalloc(1, sizeof(*I));
+ I->iter.funcs = &saproxy_iter_funcs;
+ I->iter.data = I;
+
+ I->proxy = proxy;
+ I->proxy_obj = object;
+ Z_ADDREF_P(I->proxy_obj);
+
+ I->indices = safe_emalloc(proxy->dimensions + 1, sizeof(LONG), 0);
+ for (i = 0; i < proxy->dimensions; i++) {
+ convert_to_long(proxy->indices[i]);
+ I->indices[i] = Z_LVAL_P(proxy->indices[i]);
+ }
+
+ 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;
+}
+
diff --git a/ext/com_dotnet/com_typeinfo.c b/ext/com_dotnet/com_typeinfo.c
new file mode 100644
index 0000000..482b9b8
--- /dev/null
+++ b/ext/com_dotnet/com_typeinfo.c
@@ -0,0 +1,607 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 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_01.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> |
+ | Harald Radi <h.radi@nme.at> |
+ +----------------------------------------------------------------------+
+ */
+
+/* $Id$ */
+
+#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"
+
+
+/* The search string can be either:
+ * a) a file name
+ * 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"
+ */
+PHP_COM_DOTNET_API ITypeLib *php_com_load_typelib(char *search_string, int codepage TSRMLS_DC)
+{
+ ITypeLib *TL = NULL;
+ char *strtok_buf, *major, *minor;
+ CLSID clsid;
+ OLECHAR *p;
+ HRESULT hr;
+
+ search_string = php_strtok_r(search_string, ",", &strtok_buf);
+
+ if (search_string == NULL) {
+ return NULL;
+ }
+
+ major = php_strtok_r(NULL, ",", &strtok_buf);
+ minor = php_strtok_r(NULL, ",", &strtok_buf);
+
+ p = php_com_string_to_olestring(search_string, strlen(search_string), codepage TSRMLS_CC);
+
+ if (SUCCEEDED(CLSIDFromString(p, &clsid))) {
+ WORD major_i = 1, minor_i = 0;
+
+ /* pick up the major/minor numbers; if none specified, default to 1,0 */
+ if (major && minor) {
+ major_i = (WORD)atoi(major);
+ minor_i = (WORD)atoi(minor);
+ }
+
+ /* Load the TypeLib by GUID */
+ hr = LoadRegTypeLib((REFGUID)&clsid, major_i, minor_i, LANG_NEUTRAL, &TL);
+
+ /* if that failed, assumed that the GUID is actually a CLSID and
+ * attemp to get the library via an instance of that class */
+ if (FAILED(hr) && (major == NULL || minor == NULL)) {
+ IDispatch *disp = NULL;
+ ITypeInfo *info = NULL;
+ int idx;
+
+ if (SUCCEEDED(hr = CoCreateInstance(&clsid, NULL, CLSCTX_SERVER, &IID_IDispatch, (LPVOID*)&disp)) &&
+ SUCCEEDED(hr = IDispatch_GetTypeInfo(disp, 0, LANG_NEUTRAL, &info))) {
+ hr = ITypeInfo_GetContainingTypeLib(info, &TL, &idx);
+ }
+
+ if (info) {
+ ITypeInfo_Release(info);
+ }
+ if (disp) {
+ IDispatch_Release(disp);
+ }
+ }
+ } else {
+ /* Try to load it from a file; if it fails, do a really painful search of
+ * the registry */
+ if (FAILED(LoadTypeLib(p, &TL))) {
+ HKEY hkey, hsubkey;
+ DWORD SubKeys, MaxSubKeyLength;
+ char *keyname;
+ unsigned int i, j;
+ DWORD VersionCount;
+ char version[20];
+ char *libname;
+ DWORD libnamelen;
+
+ if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CLASSES_ROOT, "TypeLib", 0, KEY_READ, &hkey) &&
+ ERROR_SUCCESS == RegQueryInfoKey(hkey, NULL, NULL, NULL, &SubKeys,
+ &MaxSubKeyLength, NULL, NULL, NULL, NULL, NULL, NULL)) {
+
+ MaxSubKeyLength++; /* make room for NUL */
+ keyname = emalloc(MaxSubKeyLength);
+ libname = emalloc(strlen(search_string) + 1);
+
+ for (i = 0; i < SubKeys && TL == NULL; i++) {
+ if (ERROR_SUCCESS == RegEnumKey(hkey, i, keyname, MaxSubKeyLength) &&
+ ERROR_SUCCESS == RegOpenKeyEx(hkey, keyname, 0, KEY_READ, &hsubkey)) {
+ if (ERROR_SUCCESS == RegQueryInfoKey(hsubkey, NULL, NULL, NULL, &VersionCount,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL)) {
+ for (j = 0; j < VersionCount; j++) {
+ if (ERROR_SUCCESS != RegEnumKey(hsubkey, j, version, sizeof(version))) {
+ continue;
+ }
+ /* get the default value for this key and compare */
+ libnamelen = strlen(search_string)+1;
+ if (ERROR_SUCCESS == RegQueryValue(hsubkey, version, libname, &libnamelen)) {
+ if (0 == stricmp(libname, search_string)) {
+ char *str = NULL;
+ int major_tmp, minor_tmp;
+
+ /* fetch the GUID and add the version numbers */
+ if (2 != sscanf(version, "%d.%d", &major_tmp, &minor_tmp)) {
+ major_tmp = 1;
+ minor_tmp = 0;
+ }
+ spprintf(&str, 0, "%s,%d,%d", keyname, major_tmp, minor_tmp);
+ /* recurse */
+ TL = php_com_load_typelib(str, codepage TSRMLS_CC);
+
+ efree(str);
+ break;
+ }
+ }
+ }
+ }
+ RegCloseKey(hsubkey);
+ }
+ }
+ RegCloseKey(hkey);
+ efree(keyname);
+ efree(libname);
+ }
+ }
+ }
+
+ efree(p);
+
+ return TL;
+}
+
+/* Given a type-library, merge it into the current engine state */
+PHP_COM_DOTNET_API int php_com_import_typelib(ITypeLib *TL, int mode, int codepage TSRMLS_DC)
+{
+ int i, j, interfaces;
+ TYPEKIND pTKind;
+ ITypeInfo *TypeInfo;
+ VARDESC *pVarDesc;
+ UINT NameCount;
+ BSTR bstr_ids;
+ zend_constant c;
+ zval exists, results, value;
+ char *const_name;
+
+ if (TL == NULL) {
+ return FAILURE;
+ }
+
+ interfaces = ITypeLib_GetTypeInfoCount(TL);
+ for (i = 0; i < interfaces; i++) {
+ ITypeLib_GetTypeInfoType(TL, i, &pTKind);
+ if (pTKind == TKIND_ENUM) {
+ ITypeLib_GetTypeInfo(TL, i, &TypeInfo);
+ for (j = 0; ; j++) {
+ if (FAILED(ITypeInfo_GetVarDesc(TypeInfo, j, &pVarDesc))) {
+ break;
+ }
+ ITypeInfo_GetNames(TypeInfo, pVarDesc->memid, &bstr_ids, 1, &NameCount);
+ if (NameCount != 1) {
+ ITypeInfo_ReleaseVarDesc(TypeInfo, pVarDesc);
+ continue;
+ }
+
+ const_name = php_com_olestring_to_string(bstr_ids, &c.name_len, codepage TSRMLS_CC);
+ c.name = zend_strndup(const_name, c.name_len);
+ efree(const_name);
+ if(c.name == NULL) {
+ ITypeInfo_ReleaseVarDesc(TypeInfo, pVarDesc);
+ continue;
+ }
+ c.name_len++; /* include NUL */
+ SysFreeString(bstr_ids);
+
+ /* sanity check for the case where the constant is already defined */
+ if (zend_get_constant(c.name, c.name_len - 1, &exists TSRMLS_CC)) {
+ if (COMG(autoreg_verbose) && !compare_function(&results, &c.value, &exists TSRMLS_CC)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Type library constant %s is already defined", c.name);
+ }
+ free(c.name);
+ ITypeInfo_ReleaseVarDesc(TypeInfo, pVarDesc);
+ continue;
+ }
+
+ /* register the constant */
+ php_com_zval_from_variant(&value, pVarDesc->lpvarValue, codepage TSRMLS_CC);
+ if (Z_TYPE(value) == IS_LONG) {
+ c.flags = mode;
+ c.value.type = IS_LONG;
+ c.value.value.lval = Z_LVAL(value);
+ c.module_number = 0;
+ zend_register_constant(&c TSRMLS_CC);
+ }
+ ITypeInfo_ReleaseVarDesc(TypeInfo, pVarDesc);
+ }
+ ITypeInfo_Release(TypeInfo);
+ }
+ }
+ return SUCCESS;
+}
+
+/* Type-library stuff */
+void php_com_typelibrary_dtor(void *pDest)
+{
+ ITypeLib **Lib = (ITypeLib**)pDest;
+ ITypeLib_Release(*Lib);
+}
+
+PHP_COM_DOTNET_API ITypeLib *php_com_load_typelib_via_cache(char *search_string,
+ int codepage, int *cached TSRMLS_DC)
+{
+ ITypeLib **TLp;
+ ITypeLib *TL;
+ char *name_dup;
+ int l;
+
+ l = strlen(search_string);
+
+ if (zend_ts_hash_find(&php_com_typelibraries, search_string, l+1,
+ (void**)&TLp) == SUCCESS) {
+ *cached = 1;
+ /* add a reference for the caller */
+ ITypeLib_AddRef(*TLp);
+ return *TLp;
+ }
+
+ *cached = 0;
+ name_dup = estrndup(search_string, l);
+ TL = php_com_load_typelib(name_dup, codepage TSRMLS_CC);
+ efree(name_dup);
+
+ if (TL) {
+ if (SUCCESS == zend_ts_hash_update(&php_com_typelibraries,
+ search_string, l+1, (void*)&TL, sizeof(ITypeLib*), NULL)) {
+ /* add a reference for the hash table */
+ ITypeLib_AddRef(TL);
+ }
+ }
+
+ 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, CP_THREAD_ACP 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" },
+ { VT_SAFEARRAY, "VT_SAFEARRAY" },
+ { 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*)safe_emalloc((func->cParams + 1), sizeof(BSTR), 0);
+
+ 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;
+}
+
+
diff --git a/ext/com_dotnet/com_variant.c b/ext/com_dotnet/com_variant.c
new file mode 100644
index 0000000..853b7df
--- /dev/null
+++ b/ext/com_dotnet/com_variant.c
@@ -0,0 +1,1075 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 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_01.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$ */
+
+#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"
+
+/* create an automation SafeArray from a PHP array.
+ * Only creates a single-dimensional array of variants.
+ * The keys of the PHP hash MUST be numeric. If the array
+ * is sparse, then the gaps will be filled with NULL variants */
+static void safe_array_from_zval(VARIANT *v, zval *z, int codepage TSRMLS_DC)
+{
+ SAFEARRAY *sa = NULL;
+ SAFEARRAYBOUND bound;
+ HashPosition pos;
+ int keytype;
+ char *strindex;
+ int strindexlen;
+ long intindex = -1;
+ long max_index = 0;
+ VARIANT *va;
+ zval **item;
+
+ /* find the largest array index, and assert that all keys are integers */
+ zend_hash_internal_pointer_reset_ex(HASH_OF(z), &pos);
+ for (;; zend_hash_move_forward_ex(HASH_OF(z), &pos)) {
+
+ keytype = zend_hash_get_current_key_ex(HASH_OF(z), &strindex, &strindexlen, &intindex, 0, &pos);
+
+ if (HASH_KEY_IS_STRING == keytype) {
+ goto bogus;
+ } else if (HASH_KEY_NON_EXISTANT == keytype) {
+ break;
+ }
+ if (intindex > max_index) {
+ max_index = intindex;
+ }
+ }
+
+ /* allocate the structure */
+ bound.lLbound = 0;
+ bound.cElements = intindex + 1;
+ sa = SafeArrayCreate(VT_VARIANT, 1, &bound);
+
+ /* get a lock on the array itself */
+ SafeArrayAccessData(sa, &va);
+ va = (VARIANT*)sa->pvData;
+
+ /* now fill it in */
+ zend_hash_internal_pointer_reset_ex(HASH_OF(z), &pos);
+ for (;; zend_hash_move_forward_ex(HASH_OF(z), &pos)) {
+ if (FAILURE == zend_hash_get_current_data_ex(HASH_OF(z), (void**)&item, &pos)) {
+ break;
+ }
+ zend_hash_get_current_key_ex(HASH_OF(z), &strindex, &strindexlen, &intindex, 0, &pos);
+ php_com_variant_from_zval(&va[intindex], *item, codepage TSRMLS_CC);
+ }
+
+ /* Unlock it and stuff it into our variant */
+ SafeArrayUnaccessData(sa);
+ V_VT(v) = VT_ARRAY|VT_VARIANT;
+ V_ARRAY(v) = sa;
+
+ return;
+
+bogus:
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "COM: converting from PHP array to VARIANT array; only arrays with numeric keys are allowed");
+
+ V_VT(v) = VT_NULL;
+
+ if (sa) {
+ SafeArrayUnlock(sa);
+ SafeArrayDestroy(sa);
+ }
+}
+
+PHP_COM_DOTNET_API void php_com_variant_from_zval(VARIANT *v, zval *z, int codepage TSRMLS_DC)
+{
+ OLECHAR *olestring;
+ php_com_dotnet_object *obj;
+
+ switch (Z_TYPE_P(z)) {
+ case IS_NULL:
+ V_VT(v) = VT_NULL;
+ break;
+
+ case IS_BOOL:
+ V_VT(v) = VT_BOOL;
+ V_BOOL(v) = Z_BVAL_P(z) ? VARIANT_TRUE : VARIANT_FALSE;
+ break;
+
+ case IS_OBJECT:
+ if (php_com_is_valid_object(z TSRMLS_CC)) {
+ obj = CDNO_FETCH(z);
+ if (V_VT(&obj->v) == VT_DISPATCH) {
+ /* pass the underlying object */
+ V_VT(v) = VT_DISPATCH;
+ if (V_DISPATCH(&obj->v)) {
+ IDispatch_AddRef(V_DISPATCH(&obj->v));
+ }
+ V_DISPATCH(v) = V_DISPATCH(&obj->v);
+ } else {
+ /* pass the variant by reference */
+ V_VT(v) = VT_VARIANT | VT_BYREF;
+ V_VARIANTREF(v) = &obj->v;
+ }
+ } else {
+ /* export the PHP object using our COM wrapper */
+ V_VT(v) = VT_DISPATCH;
+ V_DISPATCH(v) = php_com_wrapper_export(z TSRMLS_CC);
+ }
+ break;
+
+ case IS_ARRAY:
+ /* map as safe array */
+ safe_array_from_zval(v, z, codepage TSRMLS_CC);
+ break;
+
+ case IS_LONG:
+ V_VT(v) = VT_I4;
+ V_I4(v) = Z_LVAL_P(z);
+ break;
+
+ case IS_DOUBLE:
+ V_VT(v) = VT_R8;
+ V_R8(v) = Z_DVAL_P(z);
+ break;
+
+ case IS_STRING:
+ V_VT(v) = VT_BSTR;
+ olestring = php_com_string_to_olestring(Z_STRVAL_P(z), Z_STRLEN_P(z), codepage TSRMLS_CC);
+ V_BSTR(v) = SysAllocStringByteLen((char*)olestring, Z_STRLEN_P(z) * sizeof(OLECHAR));
+ efree(olestring);
+ break;
+
+ case IS_RESOURCE:
+ case IS_CONSTANT:
+ case IS_CONSTANT_ARRAY:
+ default:
+ V_VT(v) = VT_NULL;
+ break;
+ }
+}
+
+PHP_COM_DOTNET_API int php_com_zval_from_variant(zval *z, VARIANT *v, int codepage TSRMLS_DC)
+{
+ OLECHAR *olestring = NULL;
+ int ret = SUCCESS;
+
+ switch (V_VT(v)) {
+ case VT_EMPTY:
+ case VT_NULL:
+ case VT_VOID:
+ ZVAL_NULL(z);
+ break;
+ case VT_UI1:
+ ZVAL_LONG(z, (long)V_UI1(v));
+ break;
+ case VT_I1:
+ ZVAL_LONG(z, (long)V_I1(v));
+ break;
+ case VT_UI2:
+ ZVAL_LONG(z, (long)V_UI2(v));
+ break;
+ case VT_I2:
+ ZVAL_LONG(z, (long)V_I2(v));
+ break;
+ case VT_UI4: /* TODO: promote to double if large? */
+ ZVAL_LONG(z, (long)V_UI4(v));
+ break;
+ case VT_I4:
+ ZVAL_LONG(z, (long)V_I4(v));
+ break;
+ case VT_INT:
+ ZVAL_LONG(z, V_INT(v));
+ break;
+ case VT_UINT: /* TODO: promote to double if large? */
+ ZVAL_LONG(z, (long)V_UINT(v));
+ break;
+ case VT_R4:
+ ZVAL_DOUBLE(z, (double)V_R4(v));
+ break;
+ case VT_R8:
+ ZVAL_DOUBLE(z, V_R8(v));
+ break;
+ case VT_BOOL:
+ ZVAL_BOOL(z, V_BOOL(v) ? 1 : 0);
+ break;
+ case VT_BSTR:
+ olestring = V_BSTR(v);
+ if (olestring) {
+ Z_TYPE_P(z) = IS_STRING;
+ Z_STRVAL_P(z) = php_com_olestring_to_string(olestring,
+ &Z_STRLEN_P(z), codepage TSRMLS_CC);
+ olestring = NULL;
+ }
+ break;
+ case VT_UNKNOWN:
+ if (V_UNKNOWN(v) != NULL) {
+ IDispatch *disp;
+
+ if (SUCCEEDED(IUnknown_QueryInterface(V_UNKNOWN(v), &IID_IDispatch, &disp))) {
+ php_com_wrap_dispatch(z, disp, codepage TSRMLS_CC);
+ IDispatch_Release(disp);
+ } else {
+ ret = FAILURE;
+ }
+ }
+ break;
+
+ case VT_DISPATCH:
+ if (V_DISPATCH(v) != NULL) {
+ php_com_wrap_dispatch(z, V_DISPATCH(v), codepage TSRMLS_CC);
+ }
+ 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);
+ }
+
+ if (olestring) {
+ efree(olestring);
+ }
+
+ if (ret == FAILURE) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "variant->zval: conversion from 0x%x ret=%d", V_VT(v), ret);
+ }
+
+ return ret;
+}
+
+
+PHP_COM_DOTNET_API int php_com_copy_variant(VARIANT *dstvar, VARIANT *srcvar TSRMLS_DC)
+{
+ int ret = SUCCESS;
+
+ switch (V_VT(dstvar) & ~VT_BYREF) {
+ case VT_EMPTY:
+ case VT_NULL:
+ case VT_VOID:
+ /* should not be possible */
+ break;
+
+ case VT_UI1:
+ if (V_VT(dstvar) & VT_BYREF) {
+ *V_UI1REF(dstvar) = V_UI1(srcvar);
+ } else {
+ V_UI1(dstvar) = V_UI1(srcvar);
+ }
+ break;
+
+ case VT_I1:
+ if (V_VT(dstvar) & VT_BYREF) {
+ *V_I1REF(dstvar) = V_I1(srcvar);
+ } else {
+ V_I1(dstvar) = V_I1(srcvar);
+ }
+ break;
+
+ case VT_UI2:
+ if (V_VT(dstvar) & VT_BYREF) {
+ *V_UI2REF(dstvar) = V_UI2(srcvar);
+ } else {
+ V_UI2(dstvar) = V_UI2(srcvar);
+ }
+ break;
+
+ case VT_I2:
+ if (V_VT(dstvar) & VT_BYREF) {
+ *V_I2REF(dstvar) = V_I2(srcvar);
+ } else {
+ V_I2(dstvar) = V_I2(srcvar);
+ }
+ break;
+
+ case VT_UI4:
+ if (V_VT(dstvar) & VT_BYREF) {
+ *V_UI4REF(dstvar) = V_UI4(srcvar);
+ } else {
+ V_UI4(dstvar) = V_UI4(srcvar);
+ }
+ break;
+
+ case VT_I4:
+ if (V_VT(dstvar) & VT_BYREF) {
+ *V_I4REF(dstvar) = V_I4(srcvar);
+ } else {
+ V_I4(dstvar) = V_I4(srcvar);
+ }
+ break;
+
+ case VT_INT:
+ if (V_VT(dstvar) & VT_BYREF) {
+ *V_INTREF(dstvar) = V_INT(srcvar);
+ } else {
+ V_INT(dstvar) = V_INT(srcvar);
+ }
+ break;
+
+ case VT_UINT:
+ if (V_VT(dstvar) & VT_BYREF) {
+ *V_UINTREF(dstvar) = V_UINT(srcvar);
+ } else {
+ V_UINT(dstvar) = V_UINT(srcvar);
+ }
+ break;
+
+ case VT_R4:
+ if (V_VT(dstvar) & VT_BYREF) {
+ *V_R4REF(dstvar) = V_R4(srcvar);
+ } else {
+ V_R4(dstvar) = V_R4(srcvar);
+ }
+ break;
+
+ case VT_R8:
+ if (V_VT(dstvar) & VT_BYREF) {
+ *V_R8REF(dstvar) = V_R8(srcvar);
+ } else {
+ V_R8(dstvar) = V_R8(srcvar);
+ }
+ break;
+
+ case VT_BOOL:
+ if (V_VT(dstvar) & VT_BYREF) {
+ *V_BOOLREF(dstvar) = V_BOOL(srcvar);
+ } else {
+ V_BOOL(dstvar) = V_BOOL(srcvar);
+ }
+ break;
+
+ case VT_BSTR:
+ if (V_VT(dstvar) & VT_BYREF) {
+ *V_BSTRREF(dstvar) = V_BSTR(srcvar);
+ } else {
+ V_BSTR(dstvar) = V_BSTR(srcvar);
+ }
+ break;
+
+ case VT_UNKNOWN:
+ if (V_VT(dstvar) & VT_BYREF) {
+ *V_UNKNOWNREF(dstvar) = V_UNKNOWN(srcvar);
+ } else {
+ V_UNKNOWN(dstvar) = V_UNKNOWN(srcvar);
+ }
+ break;
+
+ case VT_DISPATCH:
+ if (V_VT(dstvar) & VT_BYREF) {
+ *V_DISPATCHREF(dstvar) = V_DISPATCH(srcvar);
+ } else {
+ V_DISPATCH(dstvar) = V_DISPATCH(srcvar);
+ }
+ break;
+
+ case VT_VARIANT:
+ return php_com_copy_variant(V_VARIANTREF(dstvar), srcvar TSRMLS_CC);
+
+ default:
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "variant->variant: failed to copy from 0x%x to 0x%x", V_VT(dstvar), V_VT(srcvar));
+ ret = FAILURE;
+ }
+ return ret;
+}
+
+/* {{{ com_variant_create_instance - ctor for new VARIANT() */
+PHP_FUNCTION(com_variant_create_instance)
+{
+ /* VARTYPE == unsigned short */ long vt = VT_EMPTY;
+ long codepage = CP_ACP;
+ zval *object = getThis();
+ php_com_dotnet_object *obj;
+ zval *zvalue = NULL;
+ HRESULT res;
+
+ if (ZEND_NUM_ARGS() == 0) {
+ /* just leave things as-is - an empty variant */
+ return;
+ }
+
+ obj = CDNO_FETCH(object);
+
+ if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
+ "z!|ll", &zvalue, &vt, &codepage)) {
+ php_com_throw_exception(E_INVALIDARG, "Invalid arguments" TSRMLS_CC);
+ return;
+ }
+
+ php_com_initialize(TSRMLS_C);
+ if (ZEND_NUM_ARGS() == 3) {
+ obj->code_page = codepage;
+ }
+
+ if (zvalue) {
+ php_com_variant_from_zval(&obj->v, zvalue, obj->code_page TSRMLS_CC);
+ }
+
+ /* Only perform conversion if variant not already of type passed */
+ if ((ZEND_NUM_ARGS() >= 2) && (vt != V_VT(&obj->v))) {
+
+ /* If already an array and VT_ARRAY is passed then:
+ - if only VT_ARRAY passed then do not perform a conversion
+ - if VT_ARRAY plus other type passed then perform conversion
+ but will probably fail (origional behavior)
+ */
+ if ((vt & VT_ARRAY) && (V_VT(&obj->v) & VT_ARRAY)) {
+ long orig_vt = vt;
+
+ vt &= ~VT_ARRAY;
+ if (vt) {
+ vt = orig_vt;
+ }
+ }
+
+ if (vt) {
+ res = VariantChangeType(&obj->v, &obj->v, 0, (VARTYPE)vt);
+
+ if (FAILED(res)) {
+ char *werr, *msg;
+
+ werr = php_win32_error_to_msg(res);
+ spprintf(&msg, 0, "Variant type conversion failed: %s", werr);
+ LocalFree(werr);
+
+ php_com_throw_exception(res, msg TSRMLS_CC);
+ efree(msg);
+ }
+ }
+ }
+
+ if (V_VT(&obj->v) != VT_DISPATCH && obj->typeinfo) {
+ ITypeInfo_Release(obj->typeinfo);
+ obj->typeinfo = NULL;
+ }
+}
+/* }}} */
+
+/* {{{ proto void variant_set(object variant, mixed value)
+ Assigns a new value for a variant object */
+PHP_FUNCTION(variant_set)
+{
+ zval *zobj, *zvalue = NULL;
+ php_com_dotnet_object *obj;
+
+ if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
+ "Oz!", &zobj, php_com_variant_class_entry, &zvalue)) {
+ return;
+ }
+
+ obj = CDNO_FETCH(zobj);
+
+ /* dtor the old value */
+ if (obj->typeinfo) {
+ 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);
+
+ php_com_variant_from_zval(&obj->v, zvalue, obj->code_page TSRMLS_CC);
+ /* remember we modified this variant */
+ obj->modified = 1;
+}
+/* }}} */
+
+enum variant_binary_opcode {
+ VOP_ADD, VOP_CAT, VOP_SUB, VOP_MUL, VOP_AND, VOP_DIV,
+ VOP_EQV, VOP_IDIV, VOP_IMP, VOP_MOD, VOP_OR, VOP_POW,
+ VOP_XOR
+};
+
+enum variant_unary_opcode {
+ VOP_ABS, VOP_FIX, VOP_INT, VOP_NEG, VOP_NOT
+};
+
+static void variant_binary_operation(enum variant_binary_opcode op, INTERNAL_FUNCTION_PARAMETERS) /* {{{ */
+{
+ VARIANT vres;
+ VARIANT left_val, right_val;
+ VARIANT *vleft = NULL, *vright = NULL;
+ zval *zleft = NULL, *zright = NULL;
+ php_com_dotnet_object *obj;
+ HRESULT result;
+ int codepage = CP_ACP;
+
+ VariantInit(&left_val);
+ VariantInit(&right_val);
+ VariantInit(&vres);
+
+ if (SUCCESS == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
+ ZEND_NUM_ARGS() TSRMLS_CC, "OO", &zleft, php_com_variant_class_entry,
+ &zright, php_com_variant_class_entry)) {
+ obj = CDNO_FETCH(zleft);
+ vleft = &obj->v;
+ obj = CDNO_FETCH(zright);
+ vright = &obj->v;
+ } else if (SUCCESS == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
+ ZEND_NUM_ARGS() TSRMLS_CC, "Oz!", &zleft, php_com_variant_class_entry,
+ &zright)) {
+ obj = CDNO_FETCH(zleft);
+ vleft = &obj->v;
+ vright = &right_val;
+ php_com_variant_from_zval(vright, zright, codepage TSRMLS_CC);
+ } else if (SUCCESS == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
+ ZEND_NUM_ARGS() TSRMLS_CC, "z!O", &zleft, &zright, php_com_variant_class_entry)) {
+ obj = CDNO_FETCH(zright);
+ vright = &obj->v;
+ vleft = &left_val;
+ php_com_variant_from_zval(vleft, zleft, codepage TSRMLS_CC);
+ } else if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
+ "z!z!", &zleft, &zright)) {
+
+ vleft = &left_val;
+ php_com_variant_from_zval(vleft, zleft, codepage TSRMLS_CC);
+
+ vright = &right_val;
+ php_com_variant_from_zval(vright, zright, codepage TSRMLS_CC);
+
+ } else {
+ return;
+ }
+
+ switch (op) {
+ case VOP_ADD:
+ result = VarAdd(vleft, vright, &vres);
+ break;
+ case VOP_CAT:
+ result = VarCat(vleft, vright, &vres);
+ break;
+ case VOP_SUB:
+ result = VarSub(vleft, vright, &vres);
+ break;
+ case VOP_MUL:
+ result = VarMul(vleft, vright, &vres);
+ break;
+ case VOP_AND:
+ result = VarAnd(vleft, vright, &vres);
+ break;
+ case VOP_DIV:
+ result = VarDiv(vleft, vright, &vres);
+ break;
+ case VOP_EQV:
+ result = VarEqv(vleft, vright, &vres);
+ break;
+ case VOP_IDIV:
+ result = VarIdiv(vleft, vright, &vres);
+ break;
+ case VOP_IMP:
+ result = VarImp(vleft, vright, &vres);
+ break;
+ case VOP_MOD:
+ result = VarMod(vleft, vright, &vres);
+ break;
+ case VOP_OR:
+ result = VarOr(vleft, vright, &vres);
+ break;
+ case VOP_POW:
+ result = VarPow(vleft, vright, &vres);
+ break;
+ case VOP_XOR:
+ result = VarXor(vleft, vright, &vres);
+ break;
+ /*Let say it fails as no valid op has been given */
+ default:
+ result = E_INVALIDARG;
+ }
+
+ if (SUCCEEDED(result)) {
+ php_com_wrap_variant(return_value, &vres, codepage TSRMLS_CC);
+ } else {
+ php_com_throw_exception(result, NULL TSRMLS_CC);
+ }
+
+ VariantClear(&vres);
+ VariantClear(&left_val);
+ VariantClear(&right_val);
+}
+/* }}} */
+
+/* {{{ proto mixed variant_add(mixed left, mixed right)
+ "Adds" two variant values together and returns the result */
+PHP_FUNCTION(variant_add)
+{
+ variant_binary_operation(VOP_ADD, INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto mixed variant_cat(mixed left, mixed right)
+ concatenates two variant values together and returns the result */
+PHP_FUNCTION(variant_cat)
+{
+ variant_binary_operation(VOP_CAT, INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto mixed variant_sub(mixed left, mixed right)
+ subtracts the value of the right variant from the left variant value and returns the result */
+PHP_FUNCTION(variant_sub)
+{
+ variant_binary_operation(VOP_SUB, INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto mixed variant_mul(mixed left, mixed right)
+ multiplies the values of the two variants and returns the result */
+PHP_FUNCTION(variant_mul)
+{
+ variant_binary_operation(VOP_MUL, INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto mixed variant_and(mixed left, mixed right)
+ performs a bitwise AND operation between two variants and returns the result */
+PHP_FUNCTION(variant_and)
+{
+ variant_binary_operation(VOP_AND, INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto mixed variant_div(mixed left, mixed right)
+ Returns the result from dividing two variants */
+PHP_FUNCTION(variant_div)
+{
+ variant_binary_operation(VOP_DIV, INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto mixed variant_eqv(mixed left, mixed right)
+ Performs a bitwise equivalence on two variants */
+PHP_FUNCTION(variant_eqv)
+{
+ variant_binary_operation(VOP_EQV, INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto mixed variant_idiv(mixed left, mixed right)
+ Converts variants to integers and then returns the result from dividing them */
+PHP_FUNCTION(variant_idiv)
+{
+ variant_binary_operation(VOP_IDIV, INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto mixed variant_imp(mixed left, mixed right)
+ Performs a bitwise implication on two variants */
+PHP_FUNCTION(variant_imp)
+{
+ variant_binary_operation(VOP_IMP, INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto mixed variant_mod(mixed left, mixed right)
+ Divides two variants and returns only the remainder */
+PHP_FUNCTION(variant_mod)
+{
+ variant_binary_operation(VOP_MOD, INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto mixed variant_or(mixed left, mixed right)
+ Performs a logical disjunction on two variants */
+PHP_FUNCTION(variant_or)
+{
+ variant_binary_operation(VOP_OR, INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto mixed variant_pow(mixed left, mixed right)
+ Returns the result of performing the power function with two variants */
+PHP_FUNCTION(variant_pow)
+{
+ variant_binary_operation(VOP_POW, INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto mixed variant_xor(mixed left, mixed right)
+ Performs a logical exclusion on two variants */
+PHP_FUNCTION(variant_xor)
+{
+ variant_binary_operation(VOP_XOR, INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+static void variant_unary_operation(enum variant_unary_opcode op, INTERNAL_FUNCTION_PARAMETERS) /* {{{ */
+{
+ VARIANT vres;
+ VARIANT left_val;
+ VARIANT *vleft = NULL;
+ zval *zleft = NULL;
+ php_com_dotnet_object *obj;
+ HRESULT result;
+ int codepage = CP_ACP;
+
+ VariantInit(&left_val);
+ VariantInit(&vres);
+
+ if (SUCCESS == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
+ ZEND_NUM_ARGS() TSRMLS_CC, "O", &zleft, php_com_variant_class_entry)) {
+ obj = CDNO_FETCH(zleft);
+ vleft = &obj->v;
+ } else if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
+ "z!", &zleft)) {
+ vleft = &left_val;
+ php_com_variant_from_zval(vleft, zleft, codepage TSRMLS_CC);
+ } else {
+ return;
+ }
+
+ switch (op) {
+ case VOP_ABS:
+ result = VarAbs(vleft, &vres);
+ break;
+ case VOP_FIX:
+ result = VarFix(vleft, &vres);
+ break;
+ case VOP_INT:
+ result = VarInt(vleft, &vres);
+ break;
+ case VOP_NEG:
+ result = VarNeg(vleft, &vres);
+ break;
+ case VOP_NOT:
+ result = VarNot(vleft, &vres);
+ break;
+ default:
+ result = E_INVALIDARG;
+ }
+
+ if (SUCCEEDED(result)) {
+ php_com_wrap_variant(return_value, &vres, codepage TSRMLS_CC);
+ } else {
+ php_com_throw_exception(result, NULL TSRMLS_CC);
+ }
+
+ VariantClear(&vres);
+ VariantClear(&left_val);
+}
+/* }}} */
+
+/* {{{ proto mixed variant_abs(mixed left)
+ Returns the absolute value of a variant */
+PHP_FUNCTION(variant_abs)
+{
+ variant_unary_operation(VOP_ABS, INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto mixed variant_fix(mixed left)
+ Returns the integer part ? of a variant */
+PHP_FUNCTION(variant_fix)
+{
+ variant_unary_operation(VOP_FIX, INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto mixed variant_int(mixed left)
+ Returns the integer portion of a variant */
+PHP_FUNCTION(variant_int)
+{
+ variant_unary_operation(VOP_INT, INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto mixed variant_neg(mixed left)
+ Performs logical negation on a variant */
+PHP_FUNCTION(variant_neg)
+{
+ variant_unary_operation(VOP_NEG, INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto mixed variant_not(mixed left)
+ Performs bitwise not negation on a variant */
+PHP_FUNCTION(variant_not)
+{
+ variant_unary_operation(VOP_NOT, INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto mixed variant_round(mixed left, int decimals)
+ Rounds a variant to the specified number of decimal places */
+PHP_FUNCTION(variant_round)
+{
+ VARIANT vres;
+ VARIANT left_val;
+ VARIANT *vleft = NULL;
+ zval *zleft = NULL;
+ php_com_dotnet_object *obj;
+ int codepage = CP_ACP;
+ long decimals = 0;
+
+ VariantInit(&left_val);
+ VariantInit(&vres);
+
+ if (SUCCESS == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
+ ZEND_NUM_ARGS() TSRMLS_CC, "Ol", &zleft, php_com_variant_class_entry, &decimals)) {
+ obj = CDNO_FETCH(zleft);
+ vleft = &obj->v;
+ } else if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
+ "z!l", &zleft, &decimals)) {
+ vleft = &left_val;
+ php_com_variant_from_zval(vleft, zleft, codepage TSRMLS_CC);
+ } else {
+ return;
+ }
+
+ if (SUCCEEDED(VarRound(vleft, decimals, &vres))) {
+ php_com_wrap_variant(return_value, &vres, codepage TSRMLS_CC);
+ }
+
+ VariantClear(&vres);
+ VariantClear(&left_val);
+}
+/* }}} */
+
+/* {{{ proto int variant_cmp(mixed left, mixed right [, int lcid [, int flags]])
+ Compares two variants */
+PHP_FUNCTION(variant_cmp)
+{
+ VARIANT left_val, right_val;
+ VARIANT *vleft = NULL, *vright = NULL;
+ zval *zleft = NULL, *zright = NULL;
+ php_com_dotnet_object *obj;
+ int codepage = CP_ACP;
+ long lcid = LOCALE_SYSTEM_DEFAULT;
+ long flags = 0;
+ /* it is safe to ignore the warning for this line; see the comments in com_handlers.c */
+ STDAPI VarCmp(LPVARIANT pvarLeft, LPVARIANT pvarRight, LCID lcid, DWORD flags);
+
+ VariantInit(&left_val);
+ VariantInit(&right_val);
+
+ if (SUCCESS == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
+ ZEND_NUM_ARGS() TSRMLS_CC, "OO|ll", &zleft, php_com_variant_class_entry,
+ &zright, php_com_variant_class_entry, &lcid, &flags)) {
+ obj = CDNO_FETCH(zleft);
+ vleft = &obj->v;
+ obj = CDNO_FETCH(zright);
+ vright = &obj->v;
+ } else if (SUCCESS == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
+ ZEND_NUM_ARGS() TSRMLS_CC, "Oz!|ll", &zleft, php_com_variant_class_entry,
+ &zright, &lcid, &flags)) {
+ obj = CDNO_FETCH(zleft);
+ vleft = &obj->v;
+ vright = &right_val;
+ php_com_variant_from_zval(vright, zright, codepage TSRMLS_CC);
+ } else if (SUCCESS == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
+ ZEND_NUM_ARGS() TSRMLS_CC, "z!O|ll", &zleft, &zright, php_com_variant_class_entry,
+ &lcid, &flags)) {
+ obj = CDNO_FETCH(zright);
+ vright = &obj->v;
+ vleft = &left_val;
+ php_com_variant_from_zval(vleft, zleft, codepage TSRMLS_CC);
+ } else if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
+ "z!z!|ll", &zleft, &zright, &lcid, &flags)) {
+
+ vleft = &left_val;
+ php_com_variant_from_zval(vleft, zleft, codepage TSRMLS_CC);
+
+ vright = &right_val;
+ php_com_variant_from_zval(vright, zright, codepage TSRMLS_CC);
+
+ } else {
+ return;
+ }
+
+ ZVAL_LONG(return_value, VarCmp(vleft, vright, lcid, flags));
+
+ VariantClear(&left_val);
+ VariantClear(&right_val);
+}
+/* }}} */
+
+/* {{{ proto int variant_date_to_timestamp(object variant)
+ Converts a variant date/time value to unix timestamp */
+PHP_FUNCTION(variant_date_to_timestamp)
+{
+ VARIANT vres;
+ zval *zleft = NULL;
+ php_com_dotnet_object *obj;
+
+ VariantInit(&vres);
+
+ if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
+ "O", &zleft, php_com_variant_class_entry)) {
+ return;
+ }
+ obj = CDNO_FETCH(zleft);
+
+ if (SUCCEEDED(VariantChangeType(&vres, &obj->v, 0, VT_DATE))) {
+ SYSTEMTIME systime;
+ struct tm tmv;
+
+ VariantTimeToSystemTime(V_DATE(&vres), &systime);
+
+ memset(&tmv, 0, sizeof(tmv));
+ tmv.tm_year = systime.wYear - 1900;
+ tmv.tm_mon = systime.wMonth - 1;
+ tmv.tm_mday = systime.wDay;
+ tmv.tm_hour = systime.wHour;
+ tmv.tm_min = systime.wMinute;
+ tmv.tm_sec = systime.wSecond;
+ tmv.tm_isdst = -1;
+
+ tzset();
+ RETVAL_LONG(mktime(&tmv));
+ }
+
+ VariantClear(&vres);
+}
+/* }}} */
+
+/* {{{ proto object variant_date_from_timestamp(int timestamp)
+ Returns a variant date representation of a unix timestamp */
+PHP_FUNCTION(variant_date_from_timestamp)
+{
+ long timestamp;
+ time_t ttstamp;
+ SYSTEMTIME systime;
+ struct tm *tmv;
+ VARIANT res;
+
+ if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l",
+ &timestamp)) {
+ return;
+ }
+
+ if (timestamp < 0) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Timestamp value must be a positive value.");
+ RETURN_FALSE;
+ }
+
+ VariantInit(&res);
+ tzset();
+ ttstamp = timestamp;
+ tmv = localtime(&ttstamp);
+ memset(&systime, 0, sizeof(systime));
+
+ systime.wDay = tmv->tm_mday;
+ systime.wHour = tmv->tm_hour;
+ systime.wMinute = tmv->tm_min;
+ systime.wMonth = tmv->tm_mon + 1;
+ systime.wSecond = tmv->tm_sec;
+ systime.wYear = tmv->tm_year + 1900;
+
+ V_VT(&res) = VT_DATE;
+ SystemTimeToVariantTime(&systime, &V_DATE(&res));
+
+ php_com_wrap_variant(return_value, &res, CP_ACP TSRMLS_CC);
+
+ VariantClear(&res);
+}
+/* }}} */
+
+/* {{{ proto int variant_get_type(object variant)
+ Returns the VT_XXX type code for a variant */
+PHP_FUNCTION(variant_get_type)
+{
+ zval *zobj;
+ php_com_dotnet_object *obj;
+
+ if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
+ "O", &zobj, php_com_variant_class_entry)) {
+ return;
+ }
+ obj = CDNO_FETCH(zobj);
+
+ RETURN_LONG(V_VT(&obj->v));
+}
+/* }}} */
+
+/* {{{ proto void variant_set_type(object variant, int type)
+ Convert a variant into another type. Variant is modified "in-place" */
+PHP_FUNCTION(variant_set_type)
+{
+ zval *zobj;
+ php_com_dotnet_object *obj;
+ /* VARTYPE == unsigned short */ long vt;
+ HRESULT res;
+
+ if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
+ "Ol", &zobj, php_com_variant_class_entry, &vt)) {
+ return;
+ }
+ obj = CDNO_FETCH(zobj);
+
+ res = VariantChangeType(&obj->v, &obj->v, 0, (VARTYPE)vt);
+
+ if (SUCCEEDED(res)) {
+ if (vt != VT_DISPATCH && obj->typeinfo) {
+ ITypeInfo_Release(obj->typeinfo);
+ obj->typeinfo = NULL;
+ }
+ } else {
+ char *werr, *msg;
+
+ werr = php_win32_error_to_msg(res);
+ spprintf(&msg, 0, "Variant type conversion failed: %s", werr);
+ LocalFree(werr);
+
+ php_com_throw_exception(res, msg TSRMLS_CC);
+ efree(msg);
+ }
+}
+/* }}} */
+
+/* {{{ proto object variant_cast(object variant, int type)
+ Convert a variant into a new variant object of another type */
+PHP_FUNCTION(variant_cast)
+{
+ zval *zobj;
+ php_com_dotnet_object *obj;
+ /* VARTYPE == unsigned short */ long vt;
+ VARIANT vres;
+ HRESULT res;
+
+ if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
+ "Ol", &zobj, php_com_variant_class_entry, &vt)) {
+ return;
+ }
+ obj = CDNO_FETCH(zobj);
+
+ VariantInit(&vres);
+ res = VariantChangeType(&vres, &obj->v, 0, (VARTYPE)vt);
+
+ if (SUCCEEDED(res)) {
+ php_com_wrap_variant(return_value, &vres, obj->code_page TSRMLS_CC);
+ } else {
+ char *werr, *msg;
+
+ werr = php_win32_error_to_msg(res);
+ spprintf(&msg, 0, "Variant type conversion failed: %s", werr);
+ LocalFree(werr);
+
+ php_com_throw_exception(res, msg TSRMLS_CC);
+ efree(msg);
+ }
+
+ VariantClear(&vres);
+}
+/* }}} */
+
diff --git a/ext/com_dotnet/com_wrapper.c b/ext/com_dotnet/com_wrapper.c
new file mode 100644
index 0000000..597b059
--- /dev/null
+++ b/ext/com_dotnet/com_wrapper.c
@@ -0,0 +1,649 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 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_01.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 */
+
+ DWORD 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 TSRMLS_DC);
+
+static void dispatch_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
+{
+ php_dispatchex *disp = (php_dispatchex *)rsrc->ptr;
+ disp_destructor(disp TSRMLS_CC);
+}
+
+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];
+
+ snprintf(buf, sizeof(buf), "T=%08x ", GetCurrentThreadId());
+ 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; \
+ TSRMLS_FETCH(); \
+ if (COMG(rshutdown_started)) { \
+ trace(" PHP Object:%p (name:unknown) %s\n", disp->object, methname); \
+ } else { \
+ trace(" PHP Object:%p (name:%s) %s\n", disp->object, Z_OBJCE_P(disp->object)->name, methname); \
+ } \
+ if (GetCurrentThreadId() != disp->engine_thread) { \
+ return RPC_E_WRONG_THREAD; \
+ }
+
+static HRESULT STDMETHODCALLTYPE disp_queryinterface(
+ IDispatchEx *This,
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */ void **ppvObject)
+{
+ FETCH_DISP("QueryInterface");
+
+ if (IsEqualGUID(&IID_IUnknown, riid) ||
+ IsEqualGUID(&IID_IDispatch, riid) ||
+ IsEqualGUID(&IID_IDispatchEx, riid) ||
+ IsEqualGUID(&disp->sinkid, riid)) {
+ *ppvObject = This;
+ InterlockedIncrement(&disp->refcount);
+ return S_OK;
+ }
+
+ *ppvObject = NULL;
+ return E_NOINTERFACE;
+}
+
+static ULONG STDMETHODCALLTYPE disp_addref(IDispatchEx *This)
+{
+ FETCH_DISP("AddRef");
+
+ return InterlockedIncrement(&disp->refcount);
+}
+
+static ULONG STDMETHODCALLTYPE disp_release(IDispatchEx *This)
+{
+ ULONG ret;
+ FETCH_DISP("Release");
+
+ ret = InterlockedDecrement(&disp->refcount);
+ trace("-- refcount now %d\n", ret);
+ if (ret == 0) {
+ /* destroy it */
+ if (disp->id)
+ zend_list_delete(disp->id);
+ }
+ return ret;
+}
+
+static HRESULT STDMETHODCALLTYPE disp_gettypeinfocount(
+ IDispatchEx *This,
+ /* [out] */ UINT *pctinfo)
+{
+ FETCH_DISP("GetTypeInfoCount");
+
+ *pctinfo = 0;
+ return S_OK;
+}
+
+static HRESULT STDMETHODCALLTYPE disp_gettypeinfo(
+ IDispatchEx *This,
+ /* [in] */ UINT iTInfo,
+ /* [in] */ LCID lcid,
+ /* [out] */ ITypeInfo **ppTInfo)
+{
+ FETCH_DISP("GetTypeInfo");
+
+ *ppTInfo = NULL;
+ return DISP_E_BADINDEX;
+}
+
+static HRESULT STDMETHODCALLTYPE disp_getidsofnames(
+ IDispatchEx *This,
+ /* [in] */ REFIID riid,
+ /* [size_is][in] */ LPOLESTR *rgszNames,
+ /* [in] */ UINT cNames,
+ /* [in] */ LCID lcid,
+ /* [size_is][out] */ DISPID *rgDispId)
+{
+ UINT i;
+ HRESULT ret = S_OK;
+ 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;
+ FETCH_DISP("GetDispID");
+
+ name = php_com_olestring_to_string(bstrName, &namelen, COMG(code_page) TSRMLS_CC);
+
+ trace("Looking for %s, namelen=%d in %p\n", name, namelen, disp->name_to_dispid);
+
+ /* Lookup the name in the hash */
+ if (zend_hash_find(disp->name_to_dispid, name, namelen+1, (void**)&tmp) == SUCCESS) {
+ trace("found it\n");
+ *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;
+ 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 [%d] flags=%08x args=%d\n", id, Z_STRVAL_PP(name), Z_STRLEN_PP(name), wFlags, pdp->cArgs);
+
+ /* convert args into zvals.
+ * Args are in reverse order */
+ if (pdp->cArgs) {
+ params = (zval ***)safe_emalloc(sizeof(zval **), pdp->cArgs, 0);
+ 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] = (zval**)emalloc(sizeof(zval**));
+ *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) {
+ zend_try {
+ if (SUCCESS == call_user_function_ex(EG(function_table), &disp->object, *name,
+ &retval, pdp->cArgs, params, 1, NULL TSRMLS_CC)) {
+ ret = S_OK;
+ trace("function called ok\n");
+
+ /* Copy any modified values to callers copy of variant*/
+ for (i = 0; i < pdp->cArgs; i++) {
+ php_com_dotnet_object *obj = CDNO_FETCH(*params[i]);
+ VARIANT *srcvar = &obj->v;
+ VARIANT *dstvar = &pdp->rgvarg[ pdp->cArgs - 1 - i];
+ if ((V_VT(dstvar) & VT_BYREF) && obj->modified ) {
+ trace("percolate modified value for arg %d VT=%08x\n", i, V_VT(dstvar));
+ php_com_copy_variant(dstvar, srcvar TSRMLS_CC);
+ }
+ }
+ } else {
+ trace("failed to call func\n");
+ ret = DISP_E_EXCEPTION;
+ }
+ } zend_catch {
+ trace("something blew up\n");
+ ret = DISP_E_EXCEPTION;
+ } zend_end_try();
+ } else {
+ trace("Don't know how to handle this invocation %08x\n", wFlags);
+ }
+
+ /* release arguments */
+ if (params) {
+ for (i = 0; i < pdp->cArgs; i++) {
+ zval_ptr_dtor(params[i]);
+ efree(params[i]);
+ }
+ efree(params);
+ }
+
+ /* return value */
+ if (retval) {
+ if (pvarRes) {
+ VariantInit(pvarRes);
+ 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)
+{
+ FETCH_DISP("DeleteMemberByName");
+
+ /* TODO: unset */
+
+ return S_FALSE;
+}
+
+static HRESULT STDMETHODCALLTYPE disp_deletememberbydispid(
+ IDispatchEx *This,
+ /* [in] */ DISPID id)
+{
+ FETCH_DISP("DeleteMemberByDispID");
+
+ /* TODO: unset */
+
+ return S_FALSE;
+}
+
+static HRESULT STDMETHODCALLTYPE disp_getmemberproperties(
+ IDispatchEx *This,
+ /* [in] */ DISPID id,
+ /* [in] */ DWORD grfdexFetch,
+ /* [out] */ DWORD *pgrfdex)
+{
+ FETCH_DISP("GetMemberProperties");
+
+ return DISP_E_UNKNOWNNAME;
+}
+
+static HRESULT STDMETHODCALLTYPE disp_getmembername(
+ IDispatchEx *This,
+ /* [in] */ DISPID id,
+ /* [out] */ BSTR *pbstrName)
+{
+ zval *name;
+ 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;
+ FETCH_DISP("GetNextDispID");
+
+ while(!zend_hash_index_exists(disp->dispid_to_name, next))
+ next++;
+
+ if (zend_hash_index_exists(disp->dispid_to_name, next)) {
+ *pid = next;
+ return S_OK;
+ }
+ return S_FALSE;
+}
+
+static HRESULT STDMETHODCALLTYPE disp_getnamespaceparent(
+ IDispatchEx *This,
+ /* [out] */ IUnknown **ppunk)
+{
+ FETCH_DISP("GetNameSpaceParent");
+
+ *ppunk = NULL;
+ return E_NOTIMPL;
+}
+
+static struct IDispatchExVtbl php_dispatch_vtbl = {
+ disp_queryinterface,
+ disp_addref,
+ disp_release,
+ disp_gettypeinfocount,
+ disp_gettypeinfo,
+ disp_getidsofnames,
+ disp_invoke,
+ disp_getdispid,
+ disp_invokeex,
+ disp_deletememberbyname,
+ disp_deletememberbydispid,
+ disp_getmemberproperties,
+ disp_getmembername,
+ disp_getnextdispid,
+ disp_getnamespaceparent
+};
+
+
+/* enumerate functions and properties of the object and assign
+ * dispatch ids */
+static void 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) {
+ snprintf(namebuf, sizeof(namebuf), "%d", pid);
+ name = namebuf;
+ namelen = strlen(namebuf)+1;
+ }
+
+ 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, (void**)&tmp) == SUCCESS)
+ continue;
+
+ /* add the mappings */
+ MAKE_STD_ZVAL(tmp);
+ ZVAL_STRINGL(tmp, name, namelen-1, 1);
+ pid = zend_hash_next_free_element(disp->dispid_to_name);
+ 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, (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) {
+ snprintf(namebuf, sizeof(namebuf), "%d", pid);
+ name = namebuf;
+ namelen = strlen(namebuf) + 1;
+ }
+
+ 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, (void**)&tmp) == SUCCESS)
+ continue;
+
+ /* add the mappings */
+ MAKE_STD_ZVAL(tmp);
+ ZVAL_STRINGL(tmp, name, namelen-1, 1);
+ pid = zend_hash_next_free_element(disp->dispid_to_name);
+ 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, (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 wrapper for PHP object %p (%s)\n", object, Z_OBJCE_P(object)->name);
+
+ if (disp == NULL)
+ return NULL;
+
+ memset(disp, 0, sizeof(php_dispatchex));
+
+ disp->engine_thread = GetCurrentThreadId();
+ disp->lpVtbl = &php_dispatch_vtbl;
+ disp->refcount = 1;
+
+
+ if (object)
+ Z_ADDREF_P(object);
+ disp->object = object;
+
+ disp->id = zend_list_insert(disp, le_dispatch TSRMLS_CC);
+
+ return disp;
+}
+
+static void disp_destructor(php_dispatchex *disp TSRMLS_DC)
+{
+ /* Object store not available during request shutdown */
+ if (COMG(rshutdown_started)) {
+ trace("destroying COM wrapper for PHP object %p (name:unknown)\n", disp->object);
+ } else {
+ trace("destroying COM wrapper for PHP object %p (name:%s)\n", disp->object, 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);
+}
+
+PHP_COM_DOTNET_API 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;
+}
+
+PHP_COM_DOTNET_API 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;
+}
+
+
diff --git a/ext/com_dotnet/config.w32 b/ext/com_dotnet/config.w32
new file mode 100644
index 0000000..1526392
--- /dev/null
+++ b/ext/com_dotnet/config.w32
@@ -0,0 +1,13 @@
+// $Id$
+// vim:ft=javascript
+
+ARG_ENABLE("com-dotnet", "COM and .Net support", "yes");
+
+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_wrapper.c com_saproxy.c com_persist.c");
+ AC_DEFINE('HAVE_COM_DOTNET', 1, 'Have COM_DOTNET support');
+ CHECK_HEADER_ADD_INCLUDE('mscoree.h', 'CFLAGS_COM_DOTNET');
+}
diff --git a/ext/com_dotnet/package.xml b/ext/com_dotnet/package.xml
new file mode 100644
index 0000000..2839447
--- /dev/null
+++ b/ext/com_dotnet/package.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!DOCTYPE package SYSTEM "../pear/package.dtd">
+<package>
+ <name>com_dotnet</name>
+ <summary>Com and .NET support functions for Windows</summary>
+ <maintainers>
+ <maintainer>
+ <user>wez</user>
+ <name>Wez Furlong</name>
+ <email>wez@php.net</email>
+ <role>lead</role>
+ </maintainer>
+ </maintainers>
+ <description>
+...
+ </description>
+ <license>PHP</license>
+ <release>
+ <state>beta</state>
+ <version>5.0.0rc1</version>
+ <date>2004-03-19</date>
+ <notes>
+package.xml added to support installation using pear installer
+ </notes>
+ <filelist>
+ <file role="doc" name="CREDITS"/>
+ <file role="doc" name="README"/>
+ <file role="src" name="config.w32"/>
+ <file role="src" name="com_com.c"/>
+ <file role="src" name="com_dotnet.c"/>
+ <file role="src" name="com_extension.c"/>
+ <file role="src" name="com_handlers.c"/>
+ <file role="src" name="com_misc.c"/>
+ <file role="src" name="com_olechar.c"/>
+ <file role="src" name="com_typeinfo.c"/>
+ <file role="src" name="com_variant.c"/>
+ <file role="src" name="com_iterator.c"/>
+ <file role="src" name="com_saproxy.c"/>
+ <file role="src" name="com_wrapper.c"/>
+ <file role="src" name="php_com_dotnet.h"/>
+ <file role="src" name="php_com_dotnet_internal.h"/>
+ <file role="test" name="tests/variants.phpt"/>
+ </filelist>
+ <deps>
+ <dep type="php" rel="ge" version="5" />
+ <!-- doesn't work yet <dep type="os" rel="has" name="windows"/> -->
+ </deps>
+ </release>
+</package>
+<!--
+vim:et:ts=1:sw=1
+-->
diff --git a/ext/com_dotnet/php_com_dotnet.h b/ext/com_dotnet/php_com_dotnet.h
new file mode 100644
index 0000000..8771e27
--- /dev/null
+++ b/ext/com_dotnet/php_com_dotnet.h
@@ -0,0 +1,72 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 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_01.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$ */
+
+#ifndef PHP_COM_DOTNET_H
+#define PHP_COM_DOTNET_H
+
+extern zend_module_entry com_dotnet_module_entry;
+#define phpext_com_dotnet_ptr &com_dotnet_module_entry
+
+#ifdef ZTS
+#include "TSRM.h"
+#endif
+
+#ifdef PHP_WIN32
+# define PHP_COM_DOTNET_API __declspec(dllexport)
+#elif defined(__GNUC__) && __GNUC__ >= 4
+# define PHP_COM_DOTNET_API __attribute__ ((visibility("default")))
+#else
+# define PHP_COM_DOTNET_API
+#endif
+
+PHP_MINIT_FUNCTION(com_dotnet);
+PHP_MSHUTDOWN_FUNCTION(com_dotnet);
+PHP_RINIT_FUNCTION(com_dotnet);
+PHP_RSHUTDOWN_FUNCTION(com_dotnet);
+PHP_MINFO_FUNCTION(com_dotnet);
+
+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_bool rshutdown_started;
+ZEND_END_MODULE_GLOBALS(com_dotnet)
+
+#ifdef ZTS
+# define COMG(v) TSRMG(com_dotnet_globals_id, zend_com_dotnet_globals *, v)
+#else
+# define COMG(v) (com_dotnet_globals.v)
+#endif
+
+extern ZEND_DECLARE_MODULE_GLOBALS(com_dotnet);
+
+#endif /* PHP_COM_DOTNET_H */
+
+/*
+ * 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
+ */
diff --git a/ext/com_dotnet/php_com_dotnet_internal.h b/ext/com_dotnet/php_com_dotnet_internal.h
new file mode 100644
index 0000000..015de45
--- /dev/null
+++ b/ext/com_dotnet/php_com_dotnet_internal.h
@@ -0,0 +1,182 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 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_01.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$ */
+
+#ifndef PHP_COM_DOTNET_INTERNAL_H
+#define PHP_COM_DOTNET_INTERNAL_H
+
+#define _WIN32_DCOM
+#define COBJMACROS
+#include <ocidl.h>
+#include <oleauto.h>
+#include <unknwn.h>
+#include <dispex.h>
+#include "win32/winutil.h"
+
+typedef struct _php_com_dotnet_object {
+ zend_object zo;
+
+ VARIANT v;
+ int modified;
+
+ ITypeInfo *typeinfo;
+ long code_page;
+
+ zend_class_entry *ce;
+
+ /* associated event sink */
+ IDispatch *sink_dispatch;
+ GUID sink_id;
+ DWORD sink_cookie;
+
+ /* cache for method signatures */
+ HashTable *method_cache;
+ /* cache for name -> DISPID */
+ HashTable *id_of_name_cache;
+} php_com_dotnet_object;
+
+static inline int php_com_is_valid_object(zval *zv TSRMLS_DC)
+{
+ zend_class_entry *ce = Z_OBJCE_P(zv);
+ return strcmp("com", ce->name) == 0 ||
+ strcmp("dotnet", ce->name) == 0 ||
+ strcmp("variant", ce->name) == 0;
+}
+
+#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(E_UNEXPECTED, "expected a variant object" TSRMLS_CC); \
+ return; \
+ } \
+ obj = (php_com_dotnet_object*)zend_object_store_get_object(zv TSRMLS_CC); \
+} while(0)
+
+/* com_extension.c */
+TsHashTable php_com_typelibraries;
+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_free_storage(void *object 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, int by_ref TSRMLS_DC);
+int php_com_saproxy_create(zval *com_object, zval *proxy_out, zval *index TSRMLS_DC);
+
+/* com_olechar.c */
+PHP_COM_DOTNET_API char *php_com_olestring_to_string(OLECHAR *olestring,
+ uint *string_len, int codepage TSRMLS_DC);
+PHP_COM_DOTNET_API OLECHAR *php_com_string_to_olestring(char *string,
+ uint string_len, int codepage TSRMLS_DC);
+
+
+/* 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);
+PHP_FUNCTION(com_get_active_object);
+
+HRESULT php_com_invoke_helper(php_com_dotnet_object *obj, DISPID id_member,
+ WORD flags, DISPPARAMS *disp_params, VARIANT *v, int silent, int allow_noarg TSRMLS_DC);
+HRESULT php_com_get_id_of_name(php_com_dotnet_object *obj, char *name,
+ int namelen, DISPID *dispid TSRMLS_DC);
+int php_com_do_invoke_by_id(php_com_dotnet_object *obj, DISPID dispid,
+ WORD flags, VARIANT *v, int nargs, zval **args, int silent, int allow_noarg TSRMLS_DC);
+int php_com_do_invoke(php_com_dotnet_object *obj, char *name, int namelen,
+ WORD flags, VARIANT *v, int nargs, zval **args, int allow_noarg TSRMLS_DC);
+int php_com_do_invoke_byref(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);
+PHP_COM_DOTNET_API IDispatch *php_com_wrapper_export_as_sink(zval *val, GUID *sinkid, HashTable *id_to_name TSRMLS_DC);
+PHP_COM_DOTNET_API IDispatch *php_com_wrapper_export(zval *val TSRMLS_DC);
+
+/* com_persist.c */
+int php_com_persist_minit(INIT_FUNC_ARGS);
+
+/* com_variant.c */
+PHP_FUNCTION(com_variant_create_instance);
+PHP_FUNCTION(variant_set);
+PHP_FUNCTION(variant_add);
+PHP_FUNCTION(variant_cat);
+PHP_FUNCTION(variant_sub);
+PHP_FUNCTION(variant_mul);
+PHP_FUNCTION(variant_and);
+PHP_FUNCTION(variant_div);
+PHP_FUNCTION(variant_eqv);
+PHP_FUNCTION(variant_idiv);
+PHP_FUNCTION(variant_imp);
+PHP_FUNCTION(variant_mod);
+PHP_FUNCTION(variant_or);
+PHP_FUNCTION(variant_pow);
+PHP_FUNCTION(variant_xor);
+PHP_FUNCTION(variant_abs);
+PHP_FUNCTION(variant_fix);
+PHP_FUNCTION(variant_int);
+PHP_FUNCTION(variant_neg);
+PHP_FUNCTION(variant_not);
+PHP_FUNCTION(variant_round);
+PHP_FUNCTION(variant_cmp);
+PHP_FUNCTION(variant_date_to_timestamp);
+PHP_FUNCTION(variant_date_from_timestamp);
+PHP_FUNCTION(variant_get_type);
+PHP_FUNCTION(variant_set_type);
+PHP_FUNCTION(variant_cast);
+
+PHP_COM_DOTNET_API void php_com_variant_from_zval_with_type(VARIANT *v, zval *z, VARTYPE type, int codepage TSRMLS_DC);
+PHP_COM_DOTNET_API void php_com_variant_from_zval(VARIANT *v, zval *z, int codepage TSRMLS_DC);
+PHP_COM_DOTNET_API int php_com_zval_from_variant(zval *z, VARIANT *v, int codepage TSRMLS_DC);
+PHP_COM_DOTNET_API int php_com_copy_variant(VARIANT *dst, VARIANT *src TSRMLS_DC);
+
+/* com_dotnet.c */
+PHP_FUNCTION(com_dotnet_create_instance);
+void php_com_dotnet_rshutdown(TSRMLS_D);
+void php_com_dotnet_mshutdown(TSRMLS_D);
+
+/* com_misc.c */
+void php_com_throw_exception(HRESULT code, char *message TSRMLS_DC);
+PHP_COM_DOTNET_API void php_com_wrap_dispatch(zval *z, IDispatch *disp,
+ int codepage TSRMLS_DC);
+PHP_COM_DOTNET_API void php_com_wrap_variant(zval *z, VARIANT *v,
+ int codepage TSRMLS_DC);
+PHP_COM_DOTNET_API int php_com_safearray_get_elem(VARIANT *array, VARIANT *dest, LONG dim1 TSRMLS_DC);
+
+/* com_typeinfo.c */
+PHP_COM_DOTNET_API ITypeLib *php_com_load_typelib_via_cache(char *search_string,
+ int codepage, int *cached TSRMLS_DC);
+PHP_COM_DOTNET_API ITypeLib *php_com_load_typelib(char *search_string, int codepage TSRMLS_DC);
+PHP_COM_DOTNET_API 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, int by_ref TSRMLS_DC);
+
+
+#endif
diff --git a/ext/com_dotnet/tests/27974.phpt b/ext/com_dotnet/tests/27974.phpt
new file mode 100644
index 0000000..30c42b6
--- /dev/null
+++ b/ext/com_dotnet/tests/27974.phpt
@@ -0,0 +1,43 @@
+--TEST--
+COM: mapping a safearray
+--SKIPIF--
+<?php # vim:ft=php
+if (!extension_loaded("com_dotnet")) print "skip COM/.Net support not present"; ?>
+--FILE--
+<?php // $Id$
+error_reporting(E_ALL);
+
+try {
+ $v = new VARIANT(array("123", "456", "789"));
+ var_dump($v);
+ print $v[0] . "\n";
+ print $v[1] . "\n";
+ print $v[2] . "\n";
+ $v[1] = "hello";
+ foreach ($v as $item) {
+ var_dump($item);
+ }
+ try {
+ $v[3] = "shouldn't work";
+ } catch (com_exception $e) {
+ if ($e->getCode() != DISP_E_BADINDEX) {
+ throw $e;
+ }
+ echo "Got BADINDEX exception OK!\n";
+ }
+ echo "OK!";
+} catch (Exception $e) {
+ print $e;
+}
+?>
+--EXPECT--
+object(variant)#1 (0) {
+}
+123
+456
+789
+string(3) "123"
+string(5) "hello"
+string(3) "789"
+Got BADINDEX exception OK!
+OK!
diff --git a/ext/com_dotnet/tests/bug33386.phpt b/ext/com_dotnet/tests/bug33386.phpt
new file mode 100644
index 0000000..e57f127
--- /dev/null
+++ b/ext/com_dotnet/tests/bug33386.phpt
@@ -0,0 +1,40 @@
+--TEST--
+Bug #33386 (ScriptControl only sees last function of class)
+--SKIPIF--
+<?php
+if (!extension_loaded("com_dotnet")) print "skip COM/.Net support not present"; ?>
+--FILE--
+<?php
+error_reporting(E_ALL);
+
+class twoFuncs {
+ public function func1() { echo " func one\n"; }
+ public function func2() { echo " func two\n"; }
+}
+
+try {
+ $ciTF = new twoFuncs;
+
+ $oScript = new COM("MSScriptControl.ScriptControl");
+ $oScript->Language = "VBScript";
+
+ $oScript->AddObject ("tfA", $ciTF, true);
+ foreach (array(1,2) as $i) {
+ $oScript->ExecuteStatement ("tfA.func$i");
+ $oScript->ExecuteStatement ("func$i");
+ }
+ $oScript->AddObject ("tfB", $ciTF);
+ foreach (array(1,2) as $i) {
+ $oScript->ExecuteStatement ("tfB.func$i");
+ }
+} catch (Exception $e) {
+ print $e;
+}
+?>
+--EXPECT--
+ func one
+ func one
+ func two
+ func two
+ func one
+ func two
diff --git a/ext/com_dotnet/tests/bug34272.phpt b/ext/com_dotnet/tests/bug34272.phpt
new file mode 100644
index 0000000..3a65e2c
--- /dev/null
+++ b/ext/com_dotnet/tests/bug34272.phpt
@@ -0,0 +1,22 @@
+--TEST--
+Bug #34272 (empty array onto COM object blows up)
+--SKIPIF--
+<?php
+if (!extension_loaded("com_dotnet")) print "skip COM/.Net support not present"; ?>
+--FILE--
+<?php
+error_reporting(E_ALL);
+
+try {
+ $dict = new COM("Scripting.Dictionary");
+ $dict->add('foo', array());
+ print sizeof($dict['foo'])."\n";
+ $dict->add('bar', array(23));
+ print sizeof($dict['bar'])." \n";
+} catch (Exception $e) {
+ print $e;
+}
+?>
+--EXPECT--
+0
+1
diff --git a/ext/com_dotnet/tests/bug39596.phpt b/ext/com_dotnet/tests/bug39596.phpt
new file mode 100644
index 0000000..dc8d1ef
--- /dev/null
+++ b/ext/com_dotnet/tests/bug39596.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Bug #39596 (Creating Variant of type VT_ARRAY)
+--SKIPIF--
+<?php
+if (!extension_loaded("com_dotnet")) print "skip COM/.Net support not present"; ?>
+--FILE--
+<?php
+error_reporting(E_ALL);
+
+try {
+ $binding_string = array('aaa','bbb','ccc');
+ $v = new VARIANT( $binding_string, VT_ARRAY );
+ foreach ($v AS $element) {
+ print $element."\n";
+ }
+} catch (Exception $e) {
+ print $e;
+}
+?>
+--EXPECT--
+aaa
+bbb
+ccc
diff --git a/ext/com_dotnet/tests/bug39606.phpt b/ext/com_dotnet/tests/bug39606.phpt
new file mode 100644
index 0000000..4487c1d
--- /dev/null
+++ b/ext/com_dotnet/tests/bug39606.phpt
@@ -0,0 +1,22 @@
+--TEST--
+COM: Loading typelib corrupts memory
+--SKIPIF--
+<?php # vim:ft=php
+if (!extension_loaded("com_dotnet")) print "skip COM/.Net support not present"; ?>
+--FILE--
+<?php // $Id$
+error_reporting(E_ALL);
+
+$arEnv = array_change_key_case($_SERVER, CASE_UPPER);
+
+$root = dirname($arEnv['COMSPEC']);
+$typelib = $root.'\activeds.tlb';
+
+var_dump(com_load_typelib($typelib));
+var_dump(com_load_typelib($typelib));
+?>
+===DONE===
+--EXPECT--
+bool(true)
+bool(true)
+===DONE=== \ No newline at end of file
diff --git a/ext/com_dotnet/tests/bug45280.phpt b/ext/com_dotnet/tests/bug45280.phpt
new file mode 100644
index 0000000..a1f7e18
--- /dev/null
+++ b/ext/com_dotnet/tests/bug45280.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Bug #45280 (Reflection of instantiated COM classes causes PHP to crash)
+--SKIPIF--
+<?php
+if (!extension_loaded("reflection")){ echo "skip, no reflection support present"; }
+if (!extension_loaded("com_dotnet")){ echo "skip COM/.Net support not present"; }
+?>
+--FILE--
+<?php
+$dict = new COM("Scripting.Dictionary");
+
+ob_start();
+ReflectionObject::export($dict);
+ob_get_clean();
+
+echo 'done';
+?>
+--EXPECT--
+done
diff --git a/ext/com_dotnet/tests/bug49192.phpt b/ext/com_dotnet/tests/bug49192.phpt
new file mode 100644
index 0000000..ccbf237
--- /dev/null
+++ b/ext/com_dotnet/tests/bug49192.phpt
@@ -0,0 +1,27 @@
+--TEST--
+Bug #49192 (PHP crashes when GC invoked on COM object)
+--SKIPIF--
+<?php
+if (!extension_loaded("com_dotnet")) print "skip COM/.Net support not present"; ?>
+--XFAIL--
+1
+--FILE--
+<?php
+
+// this test fails to load ADO
+//
+// a change in windows longhorn x64(affecting vista, 7, 8, 2008, 2008r2) broke ADO.
+//
+// there is a fix available, but user has to install it.
+// given that ADO was deprecated a long time ago in favor of newer APIs,
+// I don't think its worth the trouble of making the user install the fix to
+// get an accurate test run. its better to just not run the test or expect it to fail.
+//
+// see: http://support.microsoft.com/kb/2517589
+// see: http://www.infoq.com/news/2011/10/ADO-Win7
+
+$dbConnection = new Com('ADODB.Connection');
+var_dump(gc_collect_cycles());
+?>
+--EXPECT--
+int(0)
diff --git a/ext/com_dotnet/tests/variants.phpt b/ext/com_dotnet/tests/variants.phpt
new file mode 100644
index 0000000..0fd27be
--- /dev/null
+++ b/ext/com_dotnet/tests/variants.phpt
@@ -0,0 +1,637 @@
+--TEST--
+COM: General variant tests
+--SKIPIF--
+<?php # vim:ft=php
+if (!extension_loaded("com_dotnet")) print "skip COM/.Net support not present"; ?>
+--FILE--
+<?php // $Id$
+error_reporting(E_ALL);
+
+$v = new VARIANT();
+if (VT_EMPTY != variant_get_type($v)) {
+ echo "VT_EMPTY: bork\n";
+}
+
+$values = array(VT_I4 => 42, VT_R8 => 3.5, VT_BSTR => "hello", VT_BOOL => false);
+$binary_ops = array('add', 'cat', 'sub', 'mul', 'and', 'div',
+ 'eqv', 'idiv', 'imp', 'mod', 'or', 'pow', 'xor');
+
+foreach ($values as $t => $val) {
+ $v = new VARIANT($val);
+ if ($t != variant_get_type($v)) {
+ printf("Bork: [%d] %d: %s\n", $t, variant_get_type($v), $val);
+ print $v . "\n";
+ }
+ $results = array();
+
+ foreach ($values as $op2) {
+ echo "--\n";
+ foreach ($binary_ops as $op) {
+ try {
+ echo "$op: " . call_user_func('variant_' . $op, $v, $op2) . "\n";
+ } catch (com_exception $e) {
+ echo "$op:\n";
+ echo "\tvariant_$op($v, $op2)\n";
+ echo "\texception " . $e->getMessage();
+ printf("\tcode %08x\n\n", $e->getCode());
+ }
+ }
+ }
+}
+
+echo "OK!";
+?>
+--EXPECT--
+--
+add: 84
+cat: 4242
+sub: 0
+mul: 1764
+and: 42
+div: 1
+eqv: -1
+idiv: 1
+imp: -1
+mod: 0
+or: 42
+pow: 1.50130937545297E+68
+xor: 0
+--
+add: 45.5
+cat: 423.5
+sub: 38.5
+mul: 147
+and: 0
+div: 12
+eqv: -47
+idiv: 10
+imp: -43
+mod: 2
+or: 46
+pow: 480145.116863642
+xor: 46
+--
+add:
+ variant_add(42, hello)
+ exception Type mismatch.
+ code 80020005
+
+cat: 42hello
+sub:
+ variant_sub(42, hello)
+ exception Type mismatch.
+ code 80020005
+
+mul:
+ variant_mul(42, hello)
+ exception Type mismatch.
+ code 80020005
+
+and:
+ variant_and(42, hello)
+ exception Type mismatch.
+ code 80020005
+
+div:
+ variant_div(42, hello)
+ exception Type mismatch.
+ code 80020005
+
+eqv:
+ variant_eqv(42, hello)
+ exception Type mismatch.
+ code 80020005
+
+idiv:
+ variant_idiv(42, hello)
+ exception Type mismatch.
+ code 80020005
+
+imp:
+ variant_imp(42, hello)
+ exception Type mismatch.
+ code 80020005
+
+mod:
+ variant_mod(42, hello)
+ exception Type mismatch.
+ code 80020005
+
+or:
+ variant_or(42, hello)
+ exception Type mismatch.
+ code 80020005
+
+pow:
+ variant_pow(42, hello)
+ exception Type mismatch.
+ code 80020005
+
+xor:
+ variant_xor(42, hello)
+ exception Type mismatch.
+ code 80020005
+
+--
+add: 42
+cat: 42False
+sub: 42
+mul: 0
+and: 0
+div:
+ variant_div(42, )
+ exception Division by zero.
+ code 80020012
+
+eqv: -43
+idiv:
+ variant_idiv(42, )
+ exception Division by zero.
+ code 80020012
+
+imp: -43
+mod:
+ variant_mod(42, )
+ exception Division by zero.
+ code 80020012
+
+or: 42
+pow: 1
+xor: 42
+--
+add: 45.5
+cat: 3.542
+sub: -38.5
+mul: 147
+and: 0
+div: 8.33333333333333E-02
+eqv: -47
+idiv: 0
+imp: -5
+mod: 4
+or: 46
+pow: 7.09345573078604E+22
+xor: 46
+--
+add: 7
+cat: 3.53.5
+sub: 0
+mul: 12.25
+and: 4
+div: 1
+eqv: -1
+idiv: 1
+imp: -1
+mod: 0
+or: 4
+pow: 80.2117802289664
+xor: 0
+--
+add:
+ variant_add(3.5, hello)
+ exception Type mismatch.
+ code 80020005
+
+cat: 3.5hello
+sub:
+ variant_sub(3.5, hello)
+ exception Type mismatch.
+ code 80020005
+
+mul:
+ variant_mul(3.5, hello)
+ exception Type mismatch.
+ code 80020005
+
+and:
+ variant_and(3.5, hello)
+ exception Type mismatch.
+ code 80020005
+
+div:
+ variant_div(3.5, hello)
+ exception Type mismatch.
+ code 80020005
+
+eqv:
+ variant_eqv(3.5, hello)
+ exception Type mismatch.
+ code 80020005
+
+idiv:
+ variant_idiv(3.5, hello)
+ exception Type mismatch.
+ code 80020005
+
+imp:
+ variant_imp(3.5, hello)
+ exception Type mismatch.
+ code 80020005
+
+mod:
+ variant_mod(3.5, hello)
+ exception Type mismatch.
+ code 80020005
+
+or:
+ variant_or(3.5, hello)
+ exception Type mismatch.
+ code 80020005
+
+pow:
+ variant_pow(3.5, hello)
+ exception Type mismatch.
+ code 80020005
+
+xor:
+ variant_xor(3.5, hello)
+ exception Type mismatch.
+ code 80020005
+
+--
+add: 3.5
+cat: 3.5False
+sub: 3.5
+mul: 0
+and: 0
+div:
+ variant_div(3.5, )
+ exception Division by zero.
+ code 80020012
+
+eqv: -5
+idiv:
+ variant_idiv(3.5, )
+ exception Division by zero.
+ code 80020012
+
+imp: -5
+mod:
+ variant_mod(3.5, )
+ exception Division by zero.
+ code 80020012
+
+or: 4
+pow: 1
+xor: 4
+--
+add:
+ variant_add(hello, 42)
+ exception Type mismatch.
+ code 80020005
+
+cat: hello42
+sub:
+ variant_sub(hello, 42)
+ exception Type mismatch.
+ code 80020005
+
+mul:
+ variant_mul(hello, 42)
+ exception Type mismatch.
+ code 80020005
+
+and:
+ variant_and(hello, 42)
+ exception Type mismatch.
+ code 80020005
+
+div:
+ variant_div(hello, 42)
+ exception Type mismatch.
+ code 80020005
+
+eqv:
+ variant_eqv(hello, 42)
+ exception Type mismatch.
+ code 80020005
+
+idiv:
+ variant_idiv(hello, 42)
+ exception Type mismatch.
+ code 80020005
+
+imp:
+ variant_imp(hello, 42)
+ exception Type mismatch.
+ code 80020005
+
+mod:
+ variant_mod(hello, 42)
+ exception Type mismatch.
+ code 80020005
+
+or:
+ variant_or(hello, 42)
+ exception Type mismatch.
+ code 80020005
+
+pow:
+ variant_pow(hello, 42)
+ exception Type mismatch.
+ code 80020005
+
+xor:
+ variant_xor(hello, 42)
+ exception Type mismatch.
+ code 80020005
+
+--
+add:
+ variant_add(hello, 3.5)
+ exception Type mismatch.
+ code 80020005
+
+cat: hello3.5
+sub:
+ variant_sub(hello, 3.5)
+ exception Type mismatch.
+ code 80020005
+
+mul:
+ variant_mul(hello, 3.5)
+ exception Type mismatch.
+ code 80020005
+
+and:
+ variant_and(hello, 3.5)
+ exception Type mismatch.
+ code 80020005
+
+div:
+ variant_div(hello, 3.5)
+ exception Type mismatch.
+ code 80020005
+
+eqv:
+ variant_eqv(hello, 3.5)
+ exception Type mismatch.
+ code 80020005
+
+idiv:
+ variant_idiv(hello, 3.5)
+ exception Type mismatch.
+ code 80020005
+
+imp:
+ variant_imp(hello, 3.5)
+ exception Type mismatch.
+ code 80020005
+
+mod:
+ variant_mod(hello, 3.5)
+ exception Type mismatch.
+ code 80020005
+
+or:
+ variant_or(hello, 3.5)
+ exception Type mismatch.
+ code 80020005
+
+pow:
+ variant_pow(hello, 3.5)
+ exception Type mismatch.
+ code 80020005
+
+xor:
+ variant_xor(hello, 3.5)
+ exception Type mismatch.
+ code 80020005
+
+--
+add: hellohello
+cat: hellohello
+sub:
+ variant_sub(hello, hello)
+ exception Type mismatch.
+ code 80020005
+
+mul:
+ variant_mul(hello, hello)
+ exception Type mismatch.
+ code 80020005
+
+and:
+ variant_and(hello, hello)
+ exception Type mismatch.
+ code 80020005
+
+div:
+ variant_div(hello, hello)
+ exception Type mismatch.
+ code 80020005
+
+eqv:
+ variant_eqv(hello, hello)
+ exception Type mismatch.
+ code 80020005
+
+idiv:
+ variant_idiv(hello, hello)
+ exception Type mismatch.
+ code 80020005
+
+imp:
+ variant_imp(hello, hello)
+ exception Type mismatch.
+ code 80020005
+
+mod:
+ variant_mod(hello, hello)
+ exception Type mismatch.
+ code 80020005
+
+or:
+ variant_or(hello, hello)
+ exception Type mismatch.
+ code 80020005
+
+pow:
+ variant_pow(hello, hello)
+ exception Type mismatch.
+ code 80020005
+
+xor:
+ variant_xor(hello, hello)
+ exception Type mismatch.
+ code 80020005
+
+--
+add:
+ variant_add(hello, )
+ exception Type mismatch.
+ code 80020005
+
+cat: helloFalse
+sub:
+ variant_sub(hello, )
+ exception Type mismatch.
+ code 80020005
+
+mul:
+ variant_mul(hello, )
+ exception Type mismatch.
+ code 80020005
+
+and:
+ variant_and(hello, )
+ exception Type mismatch.
+ code 80020005
+
+div:
+ variant_div(hello, )
+ exception Type mismatch.
+ code 80020005
+
+eqv:
+ variant_eqv(hello, )
+ exception Type mismatch.
+ code 80020005
+
+idiv:
+ variant_idiv(hello, )
+ exception Type mismatch.
+ code 80020005
+
+imp:
+ variant_imp(hello, )
+ exception Type mismatch.
+ code 80020005
+
+mod:
+ variant_mod(hello, )
+ exception Type mismatch.
+ code 80020005
+
+or:
+ variant_or(hello, )
+ exception Type mismatch.
+ code 80020005
+
+pow:
+ variant_pow(hello, )
+ exception Type mismatch.
+ code 80020005
+
+xor:
+ variant_xor(hello, )
+ exception Type mismatch.
+ code 80020005
+
+--
+add: 42
+cat: False42
+sub: -42
+mul: 0
+and: 0
+div: 0
+eqv: -43
+idiv: 0
+imp: -1
+mod: 0
+or: 42
+pow: 0
+xor: 42
+--
+add: 3.5
+cat: False3.5
+sub: -3.5
+mul: 0
+and: 0
+div: 0
+eqv: -5
+idiv: 0
+imp: -1
+mod: 0
+or: 4
+pow: 0
+xor: 4
+--
+add:
+ variant_add(0, hello)
+ exception Type mismatch.
+ code 80020005
+
+cat: Falsehello
+sub:
+ variant_sub(0, hello)
+ exception Type mismatch.
+ code 80020005
+
+mul:
+ variant_mul(0, hello)
+ exception Type mismatch.
+ code 80020005
+
+and:
+ variant_and(0, hello)
+ exception Type mismatch.
+ code 80020005
+
+div:
+ variant_div(0, hello)
+ exception Type mismatch.
+ code 80020005
+
+eqv:
+ variant_eqv(0, hello)
+ exception Type mismatch.
+ code 80020005
+
+idiv:
+ variant_idiv(0, hello)
+ exception Type mismatch.
+ code 80020005
+
+imp:
+ variant_imp(0, hello)
+ exception Type mismatch.
+ code 80020005
+
+mod:
+ variant_mod(0, hello)
+ exception Type mismatch.
+ code 80020005
+
+or:
+ variant_or(0, hello)
+ exception Type mismatch.
+ code 80020005
+
+pow:
+ variant_pow(0, hello)
+ exception Type mismatch.
+ code 80020005
+
+xor:
+ variant_xor(0, hello)
+ exception Type mismatch.
+ code 80020005
+
+--
+add: 0
+cat: FalseFalse
+sub: 0
+mul: 0
+and: 0
+div:
+ variant_div(0, )
+ exception Out of present range.
+ code 8002000a
+
+eqv: -1
+idiv:
+ variant_idiv(0, )
+ exception Division by zero.
+ code 80020012
+
+imp: -1
+mod:
+ variant_mod(0, )
+ exception Division by zero.
+ code 80020012
+
+or: 0
+pow: 1
+xor: 0
+OK!