Back to index

php5  5.3.10
com_dotnet.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_dotnet.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 
00027 #if HAVE_MSCOREE_H
00028 # include "php_ini.h"
00029 # include "ext/standard/info.h"
00030 # include "php_com_dotnet.h"
00031 # include "php_com_dotnet_internal.h"
00032 # include "Zend/zend_exceptions.h"
00033 # include <mscoree.h>
00034 
00035 /* Since there is no official public mscorlib.h header file, and since
00036  * generating your own version from the elusive binary .tlb file takes a lot of
00037  * hacking and results in a 3MB header file (!), we opt for this slightly
00038  * voodoo approach.  The following is just enough definition to be able to
00039  * reach the _AppDomain::CreateInstance method that we need to use to be able
00040  * to fire up .Net objects.  We used to use IDispatch for this, but it would
00041  * not always work.
00042  *
00043  * The following info was obtained using OleView to export the IDL from
00044  * mscorlib.tlb.  Note that OleView is unable to generate C headers for this
00045  * particular tlb... hence this mess.
00046  */
00047 
00048 const GUID IID_mscorlib_System_AppDomain = {
00049 0x05F696DC, 0x2B29, 0x3663, {0xAD, 0x8B, 0xC4, 0x38, 0x9C, 0xF2, 0xA7, 0x13 }};
00050 
00051 typedef struct _Imscorlib_System_AppDomain IAppDomain;
00052 
00053 struct _Imscorlib_System_AppDomainVtbl {
00054        BEGIN_INTERFACE
00055 
00056        HRESULT ( STDMETHODCALLTYPE *QueryInterface )( 
00057               IAppDomain * This,
00058               /* [in] */ REFIID riid,
00059               /* [iid_is][out] */ void **ppvObject);
00060         
00061        ULONG ( STDMETHODCALLTYPE *AddRef )( 
00062               IAppDomain * This);
00063         
00064        ULONG ( STDMETHODCALLTYPE *Release )( 
00065               IAppDomain * This);
00066         
00067        /* this is padding to get CreateInstance into the correct position */
00068 #define DUMMY_METHOD(x)            HRESULT ( STDMETHODCALLTYPE *dummy_##x )(IAppDomain *This)
00069 
00070        DUMMY_METHOD(GetTypeInfoCount);
00071        DUMMY_METHOD(GetTypeInfo);
00072        DUMMY_METHOD(GetIDsOfNames);
00073        DUMMY_METHOD(Invoke);
00074        DUMMY_METHOD(ToString);
00075        DUMMY_METHOD(Equals);
00076        DUMMY_METHOD(GetHashCode);
00077        DUMMY_METHOD(GetType);
00078        DUMMY_METHOD(InitializeLifetimeService);
00079        DUMMY_METHOD(GetLifetimeService);
00080        DUMMY_METHOD(Evidence);
00081        DUMMY_METHOD(add_DomainUnload);
00082        DUMMY_METHOD(remove_DomainUnload);
00083        DUMMY_METHOD(add_AssemblyLoad);
00084        DUMMY_METHOD(remove_AssemblyLoad);
00085        DUMMY_METHOD(add_ProcessExit);
00086        DUMMY_METHOD(remove_ProcessExit);
00087        DUMMY_METHOD(add_TypeResolve);
00088        DUMMY_METHOD(remove_TypeResolve);
00089        DUMMY_METHOD(add_ResourceResolve);
00090        DUMMY_METHOD(remove_ResourceResolve);
00091        DUMMY_METHOD(add_AssemblyResolve);
00092        DUMMY_METHOD(remove_AssemblyResolve);
00093        DUMMY_METHOD(add_UnhandledException);
00094        DUMMY_METHOD(remove_UnhandledException);
00095        DUMMY_METHOD(DefineDynamicAssembly);
00096        DUMMY_METHOD(DefineDynamicAssembly_2);
00097        DUMMY_METHOD(DefineDynamicAssembly_3);
00098        DUMMY_METHOD(DefineDynamicAssembly_4);
00099        DUMMY_METHOD(DefineDynamicAssembly_5);
00100        DUMMY_METHOD(DefineDynamicAssembly_6);
00101        DUMMY_METHOD(DefineDynamicAssembly_7);
00102        DUMMY_METHOD(DefineDynamicAssembly_8);
00103        DUMMY_METHOD(DefineDynamicAssembly_9);
00104 
00105        HRESULT ( STDMETHODCALLTYPE *CreateInstance )(IAppDomain * This, BSTR AssemblyName, BSTR typeName, IUnknown **pRetVal);
00106        HRESULT ( STDMETHODCALLTYPE *CreateInstanceFrom )(IAppDomain * This, BSTR AssemblyFile, BSTR typeName, IUnknown **pRetVal);
00107 
00108        /* more methods live here */
00109 
00110        END_INTERFACE
00111 };
00112 
00113 struct _Imscorlib_System_AppDomain {
00114        struct _Imscorlib_System_AppDomainVtbl *lpVtbl;
00115 };
00116 
00117 
00118 struct dotnet_runtime_stuff {
00119        ICorRuntimeHost *dotnet_host;
00120        IAppDomain           *dotnet_domain;
00121        DISPID create_instance;
00122 };
00123 
00124 static HRESULT dotnet_init(char **p_where TSRMLS_DC)
00125 {
00126        HRESULT hr;
00127        struct dotnet_runtime_stuff *stuff;
00128        IUnknown *unk = NULL;
00129        char *where = "";
00130 
00131        stuff = malloc(sizeof(*stuff));
00132        if (!stuff) {
00133               return S_FALSE;
00134        }
00135        memset(stuff, 0, sizeof(*stuff));
00136 
00137        where = "CoCreateInstance";
00138        hr = CoCreateInstance(&CLSID_CorRuntimeHost, NULL, CLSCTX_ALL,
00139                      &IID_ICorRuntimeHost, (LPVOID*)&stuff->dotnet_host);
00140 
00141        if (FAILED(hr))
00142               goto out;
00143 
00144        /* fire up the host and get the domain object */
00145        where = "ICorRuntimeHost_Start\n";
00146        hr = ICorRuntimeHost_Start(stuff->dotnet_host);
00147        if (FAILED(hr))
00148               goto out;
00149        
00150        where = "ICorRuntimeHost_GetDefaultDomain";
00151        hr = ICorRuntimeHost_GetDefaultDomain(stuff->dotnet_host, &unk);
00152        if (FAILED(hr))
00153               goto out;
00154 
00155        where = "QI: System._AppDomain";
00156        hr = IUnknown_QueryInterface(unk, &IID_mscorlib_System_AppDomain, (LPVOID*)&stuff->dotnet_domain);
00157        if (FAILED(hr))
00158               goto out;
00159               
00160        COMG(dotnet_runtime_stuff) = stuff;
00161 
00162 out:
00163        if (unk) {
00164               IUnknown_Release(unk);
00165        }
00166        if (COMG(dotnet_runtime_stuff) == NULL) {
00167               /* clean up */
00168               if (stuff->dotnet_domain) {
00169                      IUnknown_Release(stuff->dotnet_domain);
00170               }
00171               if (stuff->dotnet_host) {
00172                      ICorRuntimeHost_Stop(stuff->dotnet_host);
00173                      ICorRuntimeHost_Release(stuff->dotnet_host);
00174               }
00175               free(stuff);
00176 
00177               *p_where = where;
00178 
00179               return hr;
00180        }
00181 
00182        return S_OK;
00183 }
00184 
00185 /* {{{ com_dotnet_create_instance - ctor for DOTNET class */
00186 PHP_FUNCTION(com_dotnet_create_instance)
00187 {
00188        zval *object = getThis();
00189        php_com_dotnet_object *obj;
00190        char *assembly_name, *datatype_name;
00191        int assembly_name_len, datatype_name_len;
00192        struct dotnet_runtime_stuff *stuff;
00193        OLECHAR *oleassembly, *oletype;
00194        BSTR oleassembly_sys, oletype_sys;
00195        HRESULT hr;
00196        int ret = FAILURE;
00197        char *where = "";
00198        IUnknown *unk = NULL;
00199 
00200        php_com_initialize(TSRMLS_C);
00201        if (COMG(dotnet_runtime_stuff) == NULL) {
00202               hr = dotnet_init(&where TSRMLS_CC);
00203               if (FAILED(hr)) {
00204                      char buf[1024];
00205                      char *err = php_win_err(hr);
00206                      snprintf(buf, sizeof(buf), "Failed to init .Net runtime [%s] %s", where, err);
00207                      if (err)
00208                             LocalFree(err);
00209                      php_com_throw_exception(hr, buf TSRMLS_CC);
00210                      ZVAL_NULL(object);
00211                      return;
00212               }
00213        }
00214 
00215        stuff = (struct dotnet_runtime_stuff*)COMG(dotnet_runtime_stuff);
00216 
00217        obj = CDNO_FETCH(object);
00218 
00219        if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|l",
00220                      &assembly_name, &assembly_name_len,
00221                      &datatype_name, &datatype_name_len,
00222                      &obj->code_page)) {
00223               php_com_throw_exception(E_INVALIDARG, "Could not create .Net object - invalid arguments!" TSRMLS_CC);
00224               ZVAL_NULL(object);
00225               return;
00226        }
00227 
00228        oletype = php_com_string_to_olestring(datatype_name, datatype_name_len, obj->code_page TSRMLS_CC);
00229        oleassembly = php_com_string_to_olestring(assembly_name, assembly_name_len, obj->code_page TSRMLS_CC);
00230        oletype_sys = SysAllocString(oletype);
00231        oleassembly_sys = SysAllocString(oleassembly);
00232        where = "CreateInstance";
00233        hr = stuff->dotnet_domain->lpVtbl->CreateInstance(stuff->dotnet_domain, oleassembly_sys, oletype_sys, &unk);
00234        efree(oletype);
00235        efree(oleassembly);
00236        SysFreeString(oletype_sys);
00237        SysFreeString(oleassembly_sys);
00238 
00239        if (SUCCEEDED(hr)) {
00240               VARIANT unwrapped;
00241               IObjectHandle *handle = NULL;
00242 
00243               where = "QI: IObjectHandle";
00244               hr = IUnknown_QueryInterface(unk, &IID_IObjectHandle, &handle);
00245 
00246               if (SUCCEEDED(hr)) {
00247                      where = "IObjectHandle_Unwrap";
00248                      hr = IObjectHandle_Unwrap(handle, &unwrapped);
00249                      if (SUCCEEDED(hr)) {
00250 
00251                             if (V_VT(&unwrapped) == VT_UNKNOWN) {
00252                                    where = "Unwrapped, QI for IDispatch";
00253                                    hr = IUnknown_QueryInterface(V_UNKNOWN(&unwrapped), &IID_IDispatch, &V_DISPATCH(&obj->v));
00254 
00255                                    if (SUCCEEDED(hr)) {
00256                                           V_VT(&obj->v) = VT_DISPATCH;
00257 
00258                                           /* get its type-info */
00259                                           IDispatch_GetTypeInfo(V_DISPATCH(&obj->v), 0, LANG_NEUTRAL, &obj->typeinfo);
00260                                           ret = SUCCESS;
00261                                    }
00262                             } else if (V_VT(&unwrapped) == VT_DISPATCH) {
00263                                    /* unwrapped is now the dispatch pointer we want */
00264                                    V_DISPATCH(&obj->v) = V_DISPATCH(&unwrapped);
00265                                    V_VT(&obj->v) = VT_DISPATCH;
00266 
00267                                    /* get its type-info */
00268                                    IDispatch_GetTypeInfo(V_DISPATCH(&obj->v), 0, LANG_NEUTRAL, &obj->typeinfo);
00269 
00270                                    ret = SUCCESS;
00271                             } else {
00272                                    /* shouldn't happen, but let's be ready for it */
00273                                    VariantClear(&unwrapped);
00274                                    hr = E_INVALIDARG;
00275                             }
00276                      }
00277                      IObjectHandle_Release(handle);
00278               }
00279               IUnknown_Release(unk);
00280        }
00281 
00282        if (ret == FAILURE) {
00283               char buf[1024];
00284               char *err = php_win_err(hr);
00285               snprintf(buf, sizeof(buf), "Failed to instantiate .Net object [%s] [0x%08x] %s", where, hr, err);
00286               if (err && err[0]) {
00287                      LocalFree(err);
00288               }
00289               php_com_throw_exception(hr, buf TSRMLS_CC);
00290               ZVAL_NULL(object);
00291               return;
00292        }
00293 }
00294 /* }}} */
00295 
00296 void php_com_dotnet_mshutdown(TSRMLS_D)
00297 {
00298        struct dotnet_runtime_stuff *stuff = COMG(dotnet_runtime_stuff);
00299        
00300        if (stuff->dotnet_domain) {
00301               IDispatch_Release(stuff->dotnet_domain);
00302        }
00303        if (stuff->dotnet_host) {
00304               ICorRuntimeHost_Stop(stuff->dotnet_host);
00305               ICorRuntimeHost_Release(stuff->dotnet_host);
00306               stuff->dotnet_host = NULL;
00307        }
00308        free(stuff);
00309        COMG(dotnet_runtime_stuff) = NULL;
00310 }
00311 
00312 void php_com_dotnet_rshutdown(TSRMLS_D)
00313 {
00314        struct dotnet_runtime_stuff *stuff = COMG(dotnet_runtime_stuff);
00315        
00316        if (stuff->dotnet_domain) {
00317               IDispatch_Release(stuff->dotnet_domain);
00318               stuff->dotnet_domain = NULL;
00319        }
00320 }
00321 
00322 #endif /* HAVE_MSCOREE_H */