Back to index

php5  5.3.10
com_com.c
Go to the documentation of this file.
00001 /*
00002    +----------------------------------------------------------------------+
00003    | PHP Version 5                                                        |
00004    +----------------------------------------------------------------------+
00005    | Copyright (c) 1997-2012 The PHP Group                                |
00006    +----------------------------------------------------------------------+
00007    | This source file is subject to version 3.01 of the PHP license,      |
00008    | that is bundled with this package in the file LICENSE, and is        |
00009    | available through the world-wide-web at the following url:           |
00010    | http://www.php.net/license/3_01.txt                                  |
00011    | If you did not receive a copy of the PHP license and are unable to   |
00012    | obtain it through the world-wide-web, please send a note to          |
00013    | license@php.net so we can mail you a copy immediately.               |
00014    +----------------------------------------------------------------------+
00015    | Author: Wez Furlong  <wez@thebrainroom.com>                          |
00016    +----------------------------------------------------------------------+
00017  */
00018 
00019 /* $Id: com_com.c 321634 2012-01-01 13:15:04Z felipe $ */
00020 
00021 #ifdef HAVE_CONFIG_H
00022 #include "config.h"
00023 #endif
00024 
00025 #include "php.h"
00026 #include "php_ini.h"
00027 #include "ext/standard/info.h"
00028 #include "php_com_dotnet.h"
00029 #include "php_com_dotnet_internal.h"
00030 #include "Zend/zend_exceptions.h"
00031 
00032 /* {{{ com_create_instance - ctor for COM class */
00033 PHP_FUNCTION(com_create_instance)
00034 {
00035        zval *object = getThis();
00036        zval *server_params = NULL;
00037        php_com_dotnet_object *obj;
00038        char *module_name, *typelib_name = NULL, *server_name = NULL;
00039        char *user_name = NULL, *domain_name = NULL, *password = NULL;
00040        int module_name_len, typelib_name_len, server_name_len,
00041               user_name_len, domain_name_len, password_len;
00042        OLECHAR *moniker;
00043        CLSID clsid;
00044        CLSCTX ctx = CLSCTX_SERVER;
00045        HRESULT res = E_FAIL;
00046        int mode = COMG(autoreg_case_sensitive) ? CONST_CS : 0;
00047        ITypeLib *TL = NULL;
00048        COSERVERINFO  info;
00049        COAUTHIDENTITY       authid = {0};
00050        COAUTHINFO           authinfo = {
00051               RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL,
00052               RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE,
00053               &authid, EOAC_NONE
00054        };
00055 
00056        php_com_initialize(TSRMLS_C);
00057        obj = CDNO_FETCH(object);
00058 
00059        if (FAILURE == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
00060                      ZEND_NUM_ARGS() TSRMLS_CC, "s|s!ls",
00061                      &module_name, &module_name_len, &server_name, &server_name_len,
00062                      &obj->code_page, &typelib_name, &typelib_name_len) &&
00063               FAILURE == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
00064                      ZEND_NUM_ARGS() TSRMLS_CC, "sa|ls",
00065                      &module_name, &module_name_len, &server_params, &obj->code_page,
00066                      &typelib_name, &typelib_name_len)) {
00067 
00068               php_com_throw_exception(E_INVALIDARG, "Could not create COM object - invalid arguments!" TSRMLS_CC);
00069               ZVAL_NULL(object);
00070               return;
00071        }
00072 
00073        if (server_name) {
00074               ctx = CLSCTX_REMOTE_SERVER;
00075        } else if (server_params) {
00076               zval **tmp;
00077 
00078               /* decode the data from the array */
00079 
00080               if (SUCCESS == zend_hash_find(HASH_OF(server_params),
00081                             "Server", sizeof("Server"), (void**)&tmp)) {
00082                      convert_to_string_ex(tmp);
00083                      server_name = Z_STRVAL_PP(tmp);
00084                      server_name_len = Z_STRLEN_PP(tmp);
00085                      ctx = CLSCTX_REMOTE_SERVER;
00086               }
00087 
00088               if (SUCCESS == zend_hash_find(HASH_OF(server_params),
00089                             "Username", sizeof("Username"), (void**)&tmp)) {
00090                      convert_to_string_ex(tmp);
00091                      user_name = Z_STRVAL_PP(tmp);
00092                      user_name_len = Z_STRLEN_PP(tmp);
00093               }
00094 
00095               if (SUCCESS == zend_hash_find(HASH_OF(server_params),
00096                             "Password", sizeof("Password"), (void**)&tmp)) {
00097                      convert_to_string_ex(tmp);
00098                      password = Z_STRVAL_PP(tmp);
00099                      password_len = Z_STRLEN_PP(tmp);
00100               }
00101 
00102               if (SUCCESS == zend_hash_find(HASH_OF(server_params),
00103                             "Domain", sizeof("Domain"), (void**)&tmp)) {
00104                      convert_to_string_ex(tmp);
00105                      domain_name = Z_STRVAL_PP(tmp);
00106                      domain_name_len = Z_STRLEN_PP(tmp);
00107               }
00108 
00109               if (SUCCESS == zend_hash_find(HASH_OF(server_params),
00110                             "Flags", sizeof("Flags"), (void**)&tmp)) {
00111                      convert_to_long_ex(tmp);
00112                      ctx = (CLSCTX)Z_LVAL_PP(tmp);
00113               }
00114        }
00115 
00116        if (server_name && !COMG(allow_dcom)) {
00117               php_com_throw_exception(E_ERROR, "DCOM has been disabled by your administrator [com.allow_dcom=0]" TSRMLS_CC);
00118               return;
00119        }
00120 
00121        moniker = php_com_string_to_olestring(module_name, module_name_len, obj->code_page TSRMLS_CC);
00122 
00123        /* if instantiating a remote object, either directly, or via
00124         * a moniker, fill in the relevant info */
00125        if (server_name) {
00126               info.dwReserved1 = 0;
00127               info.dwReserved2 = 0;
00128               info.pwszName = php_com_string_to_olestring(server_name, server_name_len, obj->code_page TSRMLS_CC);
00129 
00130               if (user_name) {
00131                      authid.User = php_com_string_to_olestring(user_name, -1, obj->code_page TSRMLS_CC);
00132                      authid.UserLength = user_name_len;
00133 
00134                      if (password) {
00135                             authid.Password = (OLECHAR*)password;
00136                             authid.PasswordLength = password_len;
00137                      } else {
00138                             authid.Password = (OLECHAR*)"";
00139                             authid.PasswordLength = 0;
00140                      }
00141 
00142                      if (domain_name) {
00143                             authid.Domain = (OLECHAR*)domain_name;
00144                             authid.DomainLength = domain_name_len;
00145                      } else {
00146                             authid.Domain = (OLECHAR*)"";
00147                             authid.DomainLength = 0;
00148                      }
00149                      authid.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
00150                      info.pAuthInfo = &authinfo;
00151               } else {
00152                      info.pAuthInfo = NULL;
00153               }
00154        }
00155 
00156        if (FAILED(CLSIDFromString(moniker, &clsid))) {
00157               /* try to use it as a moniker */
00158               IBindCtx *pBindCtx = NULL;
00159               IMoniker *pMoniker = NULL;
00160               ULONG ulEaten;
00161               BIND_OPTS2 bopt = {0};
00162 
00163               if (SUCCEEDED(res = CreateBindCtx(0, &pBindCtx))) {
00164                      if (server_name) {
00165                             /* fill in the remote server info.
00166                              * MSDN docs indicate that this might be ignored in
00167                              * current win32 implementations, but at least we are
00168                              * doing the right thing in readiness for the day that
00169                              * it does work */
00170                             bopt.cbStruct = sizeof(bopt);
00171                             IBindCtx_GetBindOptions(pBindCtx, (BIND_OPTS*)&bopt);
00172                             bopt.pServerInfo = &info;
00173                             /* apparently, GetBindOptions will only ever return
00174                              * a regular BIND_OPTS structure.  My gut feeling is
00175                              * that it will modify the size field to reflect that
00176                              * so lets be safe and set it to the BIND_OPTS2 size
00177                              * again */
00178                             bopt.cbStruct = sizeof(bopt);
00179                             IBindCtx_SetBindOptions(pBindCtx, (BIND_OPTS*)&bopt);
00180                      }
00181                      
00182                      if (SUCCEEDED(res = MkParseDisplayName(pBindCtx, moniker, &ulEaten, &pMoniker))) {
00183                             res = IMoniker_BindToObject(pMoniker, pBindCtx,
00184                                    NULL, &IID_IDispatch, (LPVOID*)&V_DISPATCH(&obj->v));
00185                      
00186                             if (SUCCEEDED(res)) {
00187                                    V_VT(&obj->v) = VT_DISPATCH;
00188                             }
00189 
00190                             IMoniker_Release(pMoniker);
00191                      }
00192               }
00193               if (pBindCtx) {
00194                      IBindCtx_Release(pBindCtx);
00195               }
00196        } else if (server_name) {
00197               MULTI_QI             qi;
00198 
00199               qi.pIID = &IID_IDispatch;
00200               qi.pItf = NULL;
00201               qi.hr = S_OK;
00202 
00203               res = CoCreateInstanceEx(&clsid, NULL, ctx, &info, 1, &qi);
00204 
00205               if (SUCCEEDED(res)) {
00206                      res = qi.hr;
00207                      V_DISPATCH(&obj->v) = (IDispatch*)qi.pItf;
00208                      V_VT(&obj->v) = VT_DISPATCH;
00209               }
00210        } else {
00211               res = CoCreateInstance(&clsid, NULL, CLSCTX_SERVER, &IID_IDispatch, (LPVOID*)&V_DISPATCH(&obj->v));
00212               if (SUCCEEDED(res)) {
00213                      V_VT(&obj->v) = VT_DISPATCH;
00214               }
00215        }
00216 
00217        if (server_name) {
00218               STR_FREE((char*)info.pwszName);
00219               STR_FREE((char*)authid.User);
00220        }
00221 
00222        efree(moniker);
00223 
00224        if (FAILED(res)) {
00225               char *werr, *msg;
00226 
00227               werr = php_win_err(res);
00228               spprintf(&msg, 0, "Failed to create COM object `%s': %s", module_name, werr);
00229               LocalFree(werr);
00230 
00231               php_com_throw_exception(res, msg TSRMLS_CC);
00232               efree(msg);
00233               ZVAL_NULL(object);
00234               return;
00235        }
00236 
00237        /* we got the object and it lives ! */
00238 
00239        /* see if it has TypeInfo available */
00240        if (FAILED(IDispatch_GetTypeInfo(V_DISPATCH(&obj->v), 0, LANG_NEUTRAL, &obj->typeinfo)) && typelib_name) {
00241               /* load up the library from the named file */
00242               int cached;
00243 
00244               TL = php_com_load_typelib_via_cache(typelib_name, obj->code_page, &cached TSRMLS_CC);
00245 
00246               if (TL) {
00247                      if (COMG(autoreg_on) && !cached) {
00248                             php_com_import_typelib(TL, mode, obj->code_page TSRMLS_CC);
00249                      }
00250 
00251                      /* cross your fingers... there is no guarantee that this ITypeInfo
00252                       * instance has any relation to this IDispatch instance... */
00253                      ITypeLib_GetTypeInfo(TL, 0, &obj->typeinfo);
00254                      ITypeLib_Release(TL);
00255               }
00256        } else if (obj->typeinfo && COMG(autoreg_on)) {
00257               int idx;
00258 
00259               if (SUCCEEDED(ITypeInfo_GetContainingTypeLib(obj->typeinfo, &TL, &idx))) {
00260                      /* check if the library is already in the cache by getting its name */
00261                      BSTR name;
00262 
00263                      if (SUCCEEDED(ITypeLib_GetDocumentation(TL, -1, &name, NULL, NULL, NULL))) {
00264                             typelib_name = php_com_olestring_to_string(name, &typelib_name_len, obj->code_page TSRMLS_CC);
00265 
00266                             if (SUCCESS == zend_ts_hash_add(&php_com_typelibraries, typelib_name, typelib_name_len+1, (void*)&TL, sizeof(ITypeLib*), NULL)) {
00267                                    php_com_import_typelib(TL, mode, obj->code_page TSRMLS_CC);
00268 
00269                                    /* add a reference for the hash */
00270                                    ITypeLib_AddRef(TL);
00271                             }
00272 
00273                      } else {
00274                             /* try it anyway */
00275                             php_com_import_typelib(TL, mode, obj->code_page TSRMLS_CC);
00276                      }
00277 
00278                      ITypeLib_Release(TL);
00279               }
00280        }
00281 
00282 }
00283 /* }}} */
00284 
00285 /* {{{ proto object com_get_active_object(string progid [, int code_page ])
00286    Returns a handle to an already running instance of a COM object */
00287 PHP_FUNCTION(com_get_active_object)
00288 {
00289        CLSID clsid;
00290        char *module_name;
00291        int module_name_len;
00292        long code_page = COMG(code_page);
00293        IUnknown *unk = NULL;
00294        IDispatch *obj = NULL;
00295        HRESULT res;
00296        OLECHAR *module = NULL;
00297 
00298        php_com_initialize(TSRMLS_C);
00299        if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l",
00300                             &module_name, &module_name_len, &code_page)) {
00301               php_com_throw_exception(E_INVALIDARG, "Invalid arguments!" TSRMLS_CC);
00302               return;
00303        }
00304 
00305        module = php_com_string_to_olestring(module_name, module_name_len, code_page TSRMLS_CC);
00306 
00307        res = CLSIDFromString(module, &clsid);
00308 
00309        if (FAILED(res)) {
00310               php_com_throw_exception(res, NULL TSRMLS_CC);
00311        } else {
00312               res = GetActiveObject(&clsid, NULL, &unk);
00313 
00314               if (FAILED(res)) {
00315                      php_com_throw_exception(res, NULL TSRMLS_CC);
00316               } else {
00317                      res = IUnknown_QueryInterface(unk, &IID_IDispatch, &obj);
00318 
00319                      if (FAILED(res)) {
00320                             php_com_throw_exception(res, NULL TSRMLS_CC);
00321                      } else if (obj) {
00322                             /* we got our dispatchable object */
00323                             php_com_wrap_dispatch(return_value, obj, code_page TSRMLS_CC);
00324                      }
00325               }
00326        }
00327 
00328        if (obj) {
00329               IDispatch_Release(obj);
00330        }
00331        if (unk) {
00332               IUnknown_Release(obj);
00333        }
00334        efree(module);
00335 }
00336 /* }}} */
00337 
00338 /* Performs an Invoke on the given com object.
00339  * returns a failure code and creates an exception if there was an error */
00340 HRESULT php_com_invoke_helper(php_com_dotnet_object *obj, DISPID id_member,
00341               WORD flags, DISPPARAMS *disp_params, VARIANT *v, int silent, int allow_noarg TSRMLS_DC)
00342 {
00343        HRESULT hr;
00344        unsigned int arg_err;
00345        EXCEPINFO e = {0};
00346 
00347        hr = IDispatch_Invoke(V_DISPATCH(&obj->v), id_member,
00348               &IID_NULL, LOCALE_SYSTEM_DEFAULT, flags, disp_params, v, &e, &arg_err);
00349 
00350        if (silent == 0 && FAILED(hr)) {
00351               char *source = NULL, *desc = NULL, *msg = NULL;
00352               int source_len, desc_len;
00353 
00354               switch (hr) {
00355                      case DISP_E_EXCEPTION:
00356                             if (e.bstrSource) {
00357                                    source = php_com_olestring_to_string(e.bstrSource, &source_len, obj->code_page TSRMLS_CC);
00358                                    SysFreeString(e.bstrSource);
00359                             }
00360                             if (e.bstrDescription) {
00361                                    desc = php_com_olestring_to_string(e.bstrDescription, &desc_len, obj->code_page TSRMLS_CC);
00362                                    SysFreeString(e.bstrDescription);
00363                             }
00364                             if (PG(html_errors)) {
00365                                    spprintf(&msg, 0, "<b>Source:</b> %s<br/><b>Description:</b> %s",
00366                                           source ? source : "Unknown",
00367                                           desc ? desc : "Unknown");
00368                             } else {
00369                                    spprintf(&msg, 0, "Source: %s\nDescription: %s",
00370                                           source ? source : "Unknown",
00371                                           desc ? desc : "Unknown");
00372                             }
00373                             if (desc) {
00374                                    efree(desc);
00375                             }
00376                             if (source) {
00377                                    efree(source);
00378                             }
00379                             if (e.bstrHelpFile) {
00380                                    SysFreeString(e.bstrHelpFile);
00381                             }
00382                             break;
00383 
00384                      case DISP_E_PARAMNOTFOUND:
00385                      case DISP_E_TYPEMISMATCH:
00386                             desc = php_win_err(hr);
00387                             spprintf(&msg, 0, "Parameter %d: %s", arg_err, desc);
00388                             LocalFree(desc);
00389                             break;
00390 
00391                      case DISP_E_BADPARAMCOUNT:
00392                             if ((disp_params->cArgs + disp_params->cNamedArgs == 0) && (allow_noarg == 1)) {
00393                                    /* if getting a property and they are missing all parameters,
00394                                     * we want to create a proxy object for them; so lets not create an
00395                                     * exception here */
00396                                    msg = NULL;
00397                                    break;
00398                             }
00399                             /* else fall through */
00400                             
00401                      default:
00402                             desc = php_win_err(hr);
00403                             spprintf(&msg, 0, "Error [0x%08x] %s", hr, desc);
00404                             LocalFree(desc);
00405                             break;
00406               }
00407 
00408               if (msg) {
00409                      php_com_throw_exception(hr, msg TSRMLS_CC);
00410                      efree(msg);
00411               }
00412        }
00413 
00414        return hr;
00415 }
00416 
00417 /* map an ID to a name */
00418 HRESULT php_com_get_id_of_name(php_com_dotnet_object *obj, char *name,
00419               int namelen, DISPID *dispid TSRMLS_DC)
00420 {
00421        OLECHAR *olename;
00422        HRESULT hr;
00423        DISPID *dispid_ptr;
00424 
00425        if (namelen == -1) {
00426               namelen = strlen(name);
00427        }
00428 
00429        if (obj->id_of_name_cache && SUCCESS == zend_hash_find(obj->id_of_name_cache, name, namelen, (void**)&dispid_ptr)) {
00430               *dispid = *dispid_ptr;
00431               return S_OK;
00432        }
00433        
00434        olename = php_com_string_to_olestring(name, namelen, obj->code_page TSRMLS_CC);
00435 
00436        if (obj->typeinfo) {
00437               hr = ITypeInfo_GetIDsOfNames(obj->typeinfo, &olename, 1, dispid);
00438               if (FAILED(hr)) {
00439                      hr = IDispatch_GetIDsOfNames(V_DISPATCH(&obj->v), &IID_NULL, &olename, 1, LOCALE_SYSTEM_DEFAULT, dispid);
00440                      if (SUCCEEDED(hr)) {
00441                             /* fall back on IDispatch direct */
00442                             ITypeInfo_Release(obj->typeinfo);
00443                             obj->typeinfo = NULL;
00444                      }
00445               }
00446        } else {
00447               hr = IDispatch_GetIDsOfNames(V_DISPATCH(&obj->v), &IID_NULL, &olename, 1, LOCALE_SYSTEM_DEFAULT, dispid);
00448        }
00449        efree(olename);
00450 
00451        if (SUCCEEDED(hr)) {
00452               /* cache the mapping */
00453               if (!obj->id_of_name_cache) {
00454                      ALLOC_HASHTABLE(obj->id_of_name_cache);
00455                      zend_hash_init(obj->id_of_name_cache, 2, NULL, NULL, 0);
00456               }
00457               zend_hash_update(obj->id_of_name_cache, name, namelen, dispid, sizeof(*dispid), NULL);
00458        }
00459        
00460        return hr;
00461 }
00462 
00463 /* the core of COM */
00464 int php_com_do_invoke_byref(php_com_dotnet_object *obj, char *name, int namelen,
00465               WORD flags,   VARIANT *v, int nargs, zval ***args TSRMLS_DC)
00466 {
00467        DISPID dispid, altdispid;
00468        DISPPARAMS disp_params;
00469        HRESULT hr;
00470        VARIANT *vargs = NULL, *byref_vals = NULL;
00471        int i, byref_count = 0, j;
00472        zend_internal_function *f = (zend_internal_function*)EG(current_execute_data)->function_state.function;
00473 
00474        /* assumption: that the active function (f) is the function we generated for the engine */
00475        if (!f || f->arg_info == NULL) {
00476           f = NULL;
00477        }
00478        
00479        hr = php_com_get_id_of_name(obj, name, namelen, &dispid TSRMLS_CC);
00480 
00481        if (FAILED(hr)) {
00482               char *winerr = NULL;
00483               char *msg = NULL;
00484               winerr = php_win_err(hr);
00485               spprintf(&msg, 0, "Unable to lookup `%s': %s", name, winerr);
00486               LocalFree(winerr);
00487               php_com_throw_exception(hr, msg TSRMLS_CC);
00488               efree(msg);
00489               return FAILURE;
00490        }
00491 
00492 
00493        if (nargs) {
00494               vargs = (VARIANT*)safe_emalloc(sizeof(VARIANT), nargs, 0);
00495        }
00496 
00497        if (f) {
00498               for (i = 0; i < nargs; i++) {
00499                      if (f->arg_info[nargs - i - 1].pass_by_reference) {
00500                             byref_count++;
00501                      }
00502               }
00503        }
00504 
00505        if (byref_count) {
00506               byref_vals = (VARIANT*)safe_emalloc(sizeof(VARIANT), byref_count, 0);
00507               for (j = 0, i = 0; i < nargs; i++) {
00508                      if (f->arg_info[nargs - i - 1].pass_by_reference) {
00509                             /* put the value into byref_vals instead */
00510                             php_com_variant_from_zval(&byref_vals[j], *args[nargs - i - 1], obj->code_page TSRMLS_CC);
00511 
00512                             /* if it is already byref, "move" it into the vargs array, otherwise
00513                              * make vargs a reference to this value */
00514                             if (V_VT(&byref_vals[j]) & VT_BYREF) {
00515                                    memcpy(&vargs[i], &byref_vals[j], sizeof(vargs[i]));
00516                                    VariantInit(&byref_vals[j]); /* leave the variant slot empty to simplify cleanup */
00517                             } else {
00518                                    VariantInit(&vargs[i]);
00519                                    V_VT(&vargs[i]) = V_VT(&byref_vals[j]) | VT_BYREF;
00520                                    /* union magic ensures that this works out */
00521                                    vargs[i].byref = &V_UINT(&byref_vals[j]);
00522                             }
00523                             j++;
00524                      } else {
00525                             php_com_variant_from_zval(&vargs[i], *args[nargs - i - 1], obj->code_page TSRMLS_CC);
00526                      }
00527               }
00528               
00529        } else {
00530               /* Invoke'd args are in reverse order */
00531               for (i = 0; i < nargs; i++) {
00532                      php_com_variant_from_zval(&vargs[i], *args[nargs - i - 1], obj->code_page TSRMLS_CC);
00533               }
00534        }
00535 
00536        disp_params.cArgs = nargs;
00537        disp_params.cNamedArgs = 0;
00538        disp_params.rgvarg = vargs;
00539        disp_params.rgdispidNamedArgs = NULL;
00540 
00541        if (flags & DISPATCH_PROPERTYPUT) {
00542               altdispid = DISPID_PROPERTYPUT;
00543               disp_params.rgdispidNamedArgs = &altdispid;
00544               disp_params.cNamedArgs = 1;
00545        }
00546 
00547        /* this will create an exception if needed */
00548        hr = php_com_invoke_helper(obj, dispid, flags, &disp_params, v, 0, 0 TSRMLS_CC);    
00549 
00550        /* release variants */
00551        if (vargs) {
00552               for (i = 0, j = 0; i < nargs; i++) {
00553                      /* if this was byref, update the zval */
00554                      if (f && f->arg_info[nargs - i - 1].pass_by_reference) {
00555                             SEPARATE_ZVAL_IF_NOT_REF(args[nargs - i - 1]);
00556 
00557                             /* if the variant is pointing at the byref_vals, we need to map
00558                              * the pointee value as a zval; otherwise, the value is pointing
00559                              * into an existing PHP variant record */
00560                             if (V_VT(&vargs[i]) & VT_BYREF) {
00561                                    if (vargs[i].byref == &V_UINT(&byref_vals[j])) {
00562                                           /* copy that value */
00563                                           php_com_zval_from_variant(*args[nargs - i - 1], &byref_vals[j],
00564                                                  obj->code_page TSRMLS_CC);
00565                                    }
00566                             } else {
00567                                    /* not sure if this can ever happen; the variant we marked as BYREF
00568                                     * is no longer BYREF - copy its value */
00569                                    php_com_zval_from_variant(*args[nargs - i - 1], &vargs[i],
00570                                           obj->code_page TSRMLS_CC);
00571                             }
00572                             VariantClear(&byref_vals[j]);
00573                             j++;
00574                      }      
00575                      VariantClear(&vargs[i]);
00576               }
00577               efree(vargs);
00578        }
00579 
00580        return SUCCEEDED(hr) ? SUCCESS : FAILURE;
00581 }
00582 
00583 
00584 
00585 int php_com_do_invoke_by_id(php_com_dotnet_object *obj, DISPID dispid,
00586               WORD flags,   VARIANT *v, int nargs, zval **args, int silent, int allow_noarg TSRMLS_DC)
00587 {
00588        DISPID altdispid;
00589        DISPPARAMS disp_params;
00590        HRESULT hr;
00591        VARIANT *vargs = NULL;
00592        int i;
00593 
00594        if (nargs) {
00595               vargs = (VARIANT*)safe_emalloc(sizeof(VARIANT), nargs, 0);
00596        }
00597 
00598        /* Invoke'd args are in reverse order */
00599        for (i = 0; i < nargs; i++) {
00600               php_com_variant_from_zval(&vargs[i], args[nargs - i - 1], obj->code_page TSRMLS_CC);
00601        }
00602 
00603        disp_params.cArgs = nargs;
00604        disp_params.cNamedArgs = 0;
00605        disp_params.rgvarg = vargs;
00606        disp_params.rgdispidNamedArgs = NULL;
00607 
00608        if (flags & DISPATCH_PROPERTYPUT) {
00609               altdispid = DISPID_PROPERTYPUT;
00610               disp_params.rgdispidNamedArgs = &altdispid;
00611               disp_params.cNamedArgs = 1;
00612        }
00613 
00614        /* this will create an exception if needed */
00615        hr = php_com_invoke_helper(obj, dispid, flags, &disp_params, v, silent, allow_noarg TSRMLS_CC);   
00616 
00617        /* release variants */
00618        if (vargs) {
00619               for (i = 0; i < nargs; i++) {
00620                      VariantClear(&vargs[i]);
00621               }
00622               efree(vargs);
00623        }
00624 
00625        /* a bit of a hack this, but it's needed for COM array access. */
00626        if (hr == DISP_E_BADPARAMCOUNT)
00627               return hr;
00628        
00629        return SUCCEEDED(hr) ? SUCCESS : FAILURE;
00630 }
00631 
00632 int php_com_do_invoke(php_com_dotnet_object *obj, char *name, int namelen,
00633               WORD flags,   VARIANT *v, int nargs, zval **args, int allow_noarg TSRMLS_DC)
00634 {
00635        DISPID dispid;
00636        HRESULT hr;
00637        char *winerr = NULL;
00638        char *msg = NULL;
00639 
00640        hr = php_com_get_id_of_name(obj, name, namelen, &dispid TSRMLS_CC);
00641 
00642        if (FAILED(hr)) {
00643               winerr = php_win_err(hr);
00644               spprintf(&msg, 0, "Unable to lookup `%s': %s", name, winerr);
00645               LocalFree(winerr);
00646               php_com_throw_exception(hr, msg TSRMLS_CC);
00647               efree(msg);
00648               return FAILURE;
00649        }
00650 
00651        return php_com_do_invoke_by_id(obj, dispid, flags, v, nargs, args, 0, allow_noarg TSRMLS_CC);
00652 }
00653 
00654 /* {{{ proto string com_create_guid()
00655    Generate a globally unique identifier (GUID) */
00656 PHP_FUNCTION(com_create_guid)
00657 {
00658        GUID retval;
00659        OLECHAR *guid_string;
00660 
00661        if (zend_parse_parameters_none() == FAILURE) {
00662               return;
00663        }
00664 
00665        php_com_initialize(TSRMLS_C);
00666        if (CoCreateGuid(&retval) == S_OK && StringFromCLSID(&retval, &guid_string) == S_OK) {
00667               Z_TYPE_P(return_value) = IS_STRING;
00668               Z_STRVAL_P(return_value) = php_com_olestring_to_string(guid_string, &Z_STRLEN_P(return_value), CP_ACP TSRMLS_CC);
00669 
00670               CoTaskMemFree(guid_string);
00671        } else {
00672               RETURN_FALSE;
00673        }
00674 }
00675 /* }}} */
00676 
00677 /* {{{ proto bool com_event_sink(object comobject, object sinkobject [, mixed sinkinterface])
00678    Connect events from a COM object to a PHP object */
00679 PHP_FUNCTION(com_event_sink)
00680 {
00681        zval *object, *sinkobject, *sink=NULL;
00682        char *dispname = NULL, *typelibname = NULL;
00683        zend_bool gotguid = 0;
00684        php_com_dotnet_object *obj;
00685        ITypeInfo *typeinfo = NULL;
00686 
00687        RETVAL_FALSE;
00688        
00689        if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Oo|z/",
00690                      &object, php_com_variant_class_entry, &sinkobject, &sink)) {
00691               RETURN_FALSE;
00692        }
00693 
00694        php_com_initialize(TSRMLS_C);
00695        obj = CDNO_FETCH(object);
00696        
00697        if (sink && Z_TYPE_P(sink) == IS_ARRAY) {
00698               /* 0 => typelibname, 1 => dispname */
00699               zval **tmp;
00700 
00701               if (zend_hash_index_find(Z_ARRVAL_P(sink), 0, (void**)&tmp) == SUCCESS)
00702                      typelibname = Z_STRVAL_PP(tmp);
00703               if (zend_hash_index_find(Z_ARRVAL_P(sink), 1, (void**)&tmp) == SUCCESS)
00704                      dispname = Z_STRVAL_PP(tmp);
00705        } else if (sink != NULL) {
00706               convert_to_string(sink);
00707               dispname = Z_STRVAL_P(sink);
00708        }
00709        
00710        typeinfo = php_com_locate_typeinfo(typelibname, obj, dispname, 1 TSRMLS_CC);
00711 
00712        if (typeinfo) {
00713               HashTable *id_to_name;
00714               
00715               ALLOC_HASHTABLE(id_to_name);
00716               
00717               if (php_com_process_typeinfo(typeinfo, id_to_name, 0, &obj->sink_id, obj->code_page TSRMLS_CC)) {
00718 
00719                      /* Create the COM wrapper for this sink */
00720                      obj->sink_dispatch = php_com_wrapper_export_as_sink(sinkobject, &obj->sink_id, id_to_name TSRMLS_CC);
00721 
00722                      /* Now hook it up to the source */
00723                      php_com_object_enable_event_sink(obj, TRUE TSRMLS_CC);
00724                      RETVAL_TRUE;
00725 
00726               } else {
00727                      FREE_HASHTABLE(id_to_name);
00728               }
00729        }
00730        
00731        if (typeinfo) {
00732               ITypeInfo_Release(typeinfo);
00733        }
00734 
00735 }
00736 /* }}} */
00737 
00738 /* {{{ proto bool com_print_typeinfo(object comobject | string typelib, string dispinterface, bool wantsink)
00739    Print out a PHP class definition for a dispatchable interface */
00740 PHP_FUNCTION(com_print_typeinfo)
00741 {
00742        zval *arg1;
00743        char *ifacename = NULL;
00744        char *typelibname = NULL;
00745        int ifacelen;
00746        zend_bool wantsink = 0;
00747        php_com_dotnet_object *obj = NULL;
00748        ITypeInfo *typeinfo;
00749        
00750        if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/|s!b", &arg1, &ifacename,
00751                             &ifacelen, &wantsink)) {
00752               RETURN_FALSE;
00753        }
00754 
00755        php_com_initialize(TSRMLS_C);
00756        if (Z_TYPE_P(arg1) == IS_OBJECT) {
00757               CDNO_FETCH_VERIFY(obj, arg1);
00758        } else {
00759               convert_to_string(arg1);
00760               typelibname = Z_STRVAL_P(arg1);
00761        }
00762 
00763        typeinfo = php_com_locate_typeinfo(typelibname, obj, ifacename, wantsink ? 1 : 0 TSRMLS_CC);
00764        if (typeinfo) {
00765               php_com_process_typeinfo(typeinfo, NULL, 1, NULL, obj ? obj->code_page : COMG(code_page) TSRMLS_CC);
00766               ITypeInfo_Release(typeinfo);
00767               RETURN_TRUE;
00768        } else {
00769               zend_error(E_WARNING, "Unable to find typeinfo using the parameters supplied");
00770        }
00771        RETURN_FALSE;
00772 }
00773 /* }}} */
00774 
00775 /* {{{ proto bool com_message_pump([int timeoutms])
00776    Process COM messages, sleeping for up to timeoutms milliseconds */
00777 PHP_FUNCTION(com_message_pump)
00778 {
00779        long timeoutms = 0;
00780        MSG msg;
00781        DWORD result;
00782        
00783        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &timeoutms) == FAILURE)
00784               RETURN_FALSE;
00785        
00786        php_com_initialize(TSRMLS_C);
00787        result = MsgWaitForMultipleObjects(0, NULL, FALSE, timeoutms, QS_ALLINPUT);
00788 
00789        if (result == WAIT_OBJECT_0) {
00790               while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
00791                      TranslateMessage(&msg);
00792                      DispatchMessage(&msg);
00793               }
00794               /* we processed messages */
00795               RETVAL_TRUE;
00796        } else {
00797               /* we did not process messages (timed out) */
00798               RETVAL_FALSE;
00799        }
00800 }
00801 /* }}} */
00802 
00803 /* {{{ proto bool com_load_typelib(string typelib_name [, int case_insensitive]) 
00804    Loads a Typelibrary and registers its constants */
00805 PHP_FUNCTION(com_load_typelib)
00806 {
00807        char *name;
00808        int namelen;
00809        ITypeLib *pTL = NULL;
00810        zend_bool cs = TRUE;
00811        int codepage = COMG(code_page);
00812        int cached = 0;
00813 
00814        if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", &name, &namelen, &cs)) {
00815               return;
00816        }
00817 
00818        RETVAL_FALSE;
00819        
00820        php_com_initialize(TSRMLS_C);
00821        pTL = php_com_load_typelib_via_cache(name, codepage, &cached TSRMLS_CC);
00822        if (pTL) {
00823               if (cached) {
00824                      RETVAL_TRUE;
00825               } else if (php_com_import_typelib(pTL, cs ? CONST_CS : 0, codepage TSRMLS_CC) == SUCCESS) {
00826                      RETVAL_TRUE;
00827               }
00828 
00829               ITypeLib_Release(pTL);
00830               pTL = NULL;
00831        }
00832 }
00833 /* }}} */
00834 
00835 
00836 
00837 /*
00838  * Local variables:
00839  * tab-width: 4
00840  * c-basic-offset: 4
00841  * End:
00842  * vim600: noet sw=4 ts=4 fdm=marker
00843  * vim<600: noet sw=4 ts=4
00844  */