1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Author: Wez Furlong <wez@thebrainroom.com> |
16 +----------------------------------------------------------------------+
17 */
18
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22
23 #include "php.h"
24
25 #if HAVE_MSCOREE_H
26 # include "php_ini.h"
27 # include "ext/standard/info.h"
28 # include "php_com_dotnet.h"
29 # include "php_com_dotnet_internal.h"
30 # include "Zend/zend_exceptions.h"
31 # include <mscoree.h>
32
33 /* Since there is no official public mscorlib.h header file, and since
34 * generating your own version from the elusive binary .tlb file takes a lot of
35 * hacking and results in a 3MB header file (!), we opt for this slightly
36 * voodoo approach. The following is just enough definition to be able to
37 * reach the _AppDomain::CreateInstance method that we need to use to be able
38 * to fire up .Net objects. We used to use IDispatch for this, but it would
39 * not always work.
40 *
41 * The following info was obtained using OleView to export the IDL from
42 * mscorlib.tlb. Note that OleView is unable to generate C headers for this
43 * particular tlb... hence this mess.
44 */
45
46 const GUID IID_mscorlib_System_AppDomain = {
47 0x05F696DC, 0x2B29, 0x3663, {0xAD, 0x8B, 0xC4, 0x38, 0x9C, 0xF2, 0xA7, 0x13 }};
48
49 typedef struct _Imscorlib_System_AppDomain IAppDomain;
50
51 struct _Imscorlib_System_AppDomainVtbl {
52 BEGIN_INTERFACE
53
54 HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
55 IAppDomain * This,
56 /* [in] */ REFIID riid,
57 /* [iid_is][out] */ void **ppvObject);
58
59 ULONG ( STDMETHODCALLTYPE *AddRef )(
60 IAppDomain * This);
61
62 ULONG ( STDMETHODCALLTYPE *Release )(
63 IAppDomain * This);
64
65 /* this is padding to get CreateInstance into the correct position */
66 #define DUMMY_METHOD(x) HRESULT ( STDMETHODCALLTYPE *dummy_##x )(IAppDomain *This)
67
68 DUMMY_METHOD(GetTypeInfoCount);
69 DUMMY_METHOD(GetTypeInfo);
70 DUMMY_METHOD(GetIDsOfNames);
71 DUMMY_METHOD(Invoke);
72 DUMMY_METHOD(ToString);
73 DUMMY_METHOD(Equals);
74 DUMMY_METHOD(GetHashCode);
75 DUMMY_METHOD(GetType);
76 DUMMY_METHOD(InitializeLifetimeService);
77 DUMMY_METHOD(GetLifetimeService);
78 DUMMY_METHOD(Evidence);
79 DUMMY_METHOD(add_DomainUnload);
80 DUMMY_METHOD(remove_DomainUnload);
81 DUMMY_METHOD(add_AssemblyLoad);
82 DUMMY_METHOD(remove_AssemblyLoad);
83 DUMMY_METHOD(add_ProcessExit);
84 DUMMY_METHOD(remove_ProcessExit);
85 DUMMY_METHOD(add_TypeResolve);
86 DUMMY_METHOD(remove_TypeResolve);
87 DUMMY_METHOD(add_ResourceResolve);
88 DUMMY_METHOD(remove_ResourceResolve);
89 DUMMY_METHOD(add_AssemblyResolve);
90 DUMMY_METHOD(remove_AssemblyResolve);
91 DUMMY_METHOD(add_UnhandledException);
92 DUMMY_METHOD(remove_UnhandledException);
93 DUMMY_METHOD(DefineDynamicAssembly);
94 DUMMY_METHOD(DefineDynamicAssembly_2);
95 DUMMY_METHOD(DefineDynamicAssembly_3);
96 DUMMY_METHOD(DefineDynamicAssembly_4);
97 DUMMY_METHOD(DefineDynamicAssembly_5);
98 DUMMY_METHOD(DefineDynamicAssembly_6);
99 DUMMY_METHOD(DefineDynamicAssembly_7);
100 DUMMY_METHOD(DefineDynamicAssembly_8);
101 DUMMY_METHOD(DefineDynamicAssembly_9);
102
103 HRESULT ( STDMETHODCALLTYPE *CreateInstance )(IAppDomain * This, BSTR AssemblyName, BSTR typeName, IUnknown **pRetVal);
104 HRESULT ( STDMETHODCALLTYPE *CreateInstanceFrom )(IAppDomain * This, BSTR AssemblyFile, BSTR typeName, IUnknown **pRetVal);
105
106 /* more methods live here */
107
108 END_INTERFACE
109 };
110
111 struct _Imscorlib_System_AppDomain {
112 struct _Imscorlib_System_AppDomainVtbl *lpVtbl;
113 };
114
115
116 struct dotnet_runtime_stuff {
117 ICorRuntimeHost *dotnet_host;
118 IAppDomain *dotnet_domain;
119 DISPID create_instance;
120 };
121
dotnet_init(char ** p_where)122 static HRESULT dotnet_init(char **p_where)
123 {
124 HRESULT hr;
125 struct dotnet_runtime_stuff *stuff;
126 IUnknown *unk = NULL;
127 char *where = "";
128
129 stuff = malloc(sizeof(*stuff));
130 if (!stuff) {
131 return S_FALSE;
132 }
133 memset(stuff, 0, sizeof(*stuff));
134
135 where = "CoCreateInstance";
136 hr = CoCreateInstance(&CLSID_CorRuntimeHost, NULL, CLSCTX_ALL,
137 &IID_ICorRuntimeHost, (LPVOID*)&stuff->dotnet_host);
138
139 if (FAILED(hr))
140 goto out;
141
142 /* fire up the host and get the domain object */
143 where = "ICorRuntimeHost_Start\n";
144 hr = ICorRuntimeHost_Start(stuff->dotnet_host);
145 if (FAILED(hr))
146 goto out;
147
148 where = "ICorRuntimeHost_GetDefaultDomain";
149 hr = ICorRuntimeHost_GetDefaultDomain(stuff->dotnet_host, &unk);
150 if (FAILED(hr))
151 goto out;
152
153 where = "QI: System._AppDomain";
154 hr = IUnknown_QueryInterface(unk, &IID_mscorlib_System_AppDomain, (LPVOID*)&stuff->dotnet_domain);
155 if (FAILED(hr))
156 goto out;
157
158 COMG(dotnet_runtime_stuff) = stuff;
159
160 out:
161 if (unk) {
162 IUnknown_Release(unk);
163 }
164 if (COMG(dotnet_runtime_stuff) == NULL) {
165 /* clean up */
166 if (stuff->dotnet_domain) {
167 IUnknown_Release(stuff->dotnet_domain);
168 }
169 if (stuff->dotnet_host) {
170 ICorRuntimeHost_Stop(stuff->dotnet_host);
171 ICorRuntimeHost_Release(stuff->dotnet_host);
172 }
173 free(stuff);
174
175 *p_where = where;
176
177 return hr;
178 }
179
180 return S_OK;
181 }
182
183 /* {{{ com_dotnet_create_instance - ctor for DOTNET class */
PHP_FUNCTION(com_dotnet_create_instance)184 PHP_FUNCTION(com_dotnet_create_instance)
185 {
186 zval *object = getThis();
187 php_com_dotnet_object *obj;
188 char *assembly_name, *datatype_name;
189 size_t assembly_name_len, datatype_name_len;
190 struct dotnet_runtime_stuff *stuff;
191 OLECHAR *oleassembly, *oletype;
192 BSTR oleassembly_sys, oletype_sys;
193 HRESULT hr;
194 int ret = FAILURE;
195 char *where = "";
196 IUnknown *unk = NULL;
197 zend_long cp = GetACP();
198 const struct php_win32_cp *cp_it;
199
200 php_com_initialize();
201 stuff = (struct dotnet_runtime_stuff*)COMG(dotnet_runtime_stuff);
202 if (stuff == NULL) {
203 hr = dotnet_init(&where);
204 if (FAILED(hr)) {
205 char buf[1024];
206 char *err = php_win32_error_to_msg(hr);
207 snprintf(buf, sizeof(buf), "Failed to init .Net runtime [%s] %s", where, err);
208 php_win32_error_msg_free(err);
209 php_com_throw_exception(hr, buf);
210 return;
211 }
212 stuff = (struct dotnet_runtime_stuff*)COMG(dotnet_runtime_stuff);
213
214 } else if (stuff->dotnet_domain == NULL) {
215 where = "ICorRuntimeHost_GetDefaultDomain";
216 hr = ICorRuntimeHost_GetDefaultDomain(stuff->dotnet_host, &unk);
217 if (FAILED(hr)) {
218 char buf[1024];
219 char *err = php_win32_error_to_msg(hr);
220 snprintf(buf, sizeof(buf), "Failed to re-init .Net domain [%s] %s", where, err);
221 php_win32_error_msg_free(err);
222 php_com_throw_exception(hr, buf);
223 ZVAL_NULL(object);
224 return;
225 }
226
227 where = "QI: System._AppDomain";
228 hr = IUnknown_QueryInterface(unk, &IID_mscorlib_System_AppDomain, (LPVOID*)&stuff->dotnet_domain);
229 if (FAILED(hr)) {
230 char buf[1024];
231 char *err = php_win32_error_to_msg(hr);
232 snprintf(buf, sizeof(buf), "Failed to re-init .Net domain [%s] %s", where, err);
233 php_win32_error_msg_free(err);
234 php_com_throw_exception(hr, buf);
235 ZVAL_NULL(object);
236 return;
237 }
238 }
239
240 obj = CDNO_FETCH(object);
241
242 if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "ss|l",
243 &assembly_name, &assembly_name_len,
244 &datatype_name, &datatype_name_len,
245 &cp)) {
246 php_com_throw_exception(E_INVALIDARG, "Could not create .Net object - invalid arguments!");
247 return;
248 }
249
250 cp_it = php_win32_cp_get_by_id((DWORD)cp);
251 if (!cp_it) {
252 php_com_throw_exception(E_INVALIDARG, "Could not create .Net object - invalid codepage!");
253 return;
254 }
255 obj->code_page = (int)cp_it->id;
256
257 oletype = php_com_string_to_olestring(datatype_name, datatype_name_len, obj->code_page);
258 oleassembly = php_com_string_to_olestring(assembly_name, assembly_name_len, obj->code_page);
259 oletype_sys = SysAllocString(oletype);
260 oleassembly_sys = SysAllocString(oleassembly);
261 where = "CreateInstance";
262 hr = stuff->dotnet_domain->lpVtbl->CreateInstance(stuff->dotnet_domain, oleassembly_sys, oletype_sys, &unk);
263 efree(oletype);
264 efree(oleassembly);
265 SysFreeString(oletype_sys);
266 SysFreeString(oleassembly_sys);
267
268 if (SUCCEEDED(hr)) {
269 VARIANT unwrapped;
270 IObjectHandle *handle = NULL;
271
272 where = "QI: IObjectHandle";
273 hr = IUnknown_QueryInterface(unk, &IID_IObjectHandle, &handle);
274
275 if (SUCCEEDED(hr)) {
276 where = "IObjectHandle_Unwrap";
277 hr = IObjectHandle_Unwrap(handle, &unwrapped);
278 if (SUCCEEDED(hr)) {
279
280 if (V_VT(&unwrapped) == VT_UNKNOWN) {
281 where = "Unwrapped, QI for IDispatch";
282 hr = IUnknown_QueryInterface(V_UNKNOWN(&unwrapped), &IID_IDispatch, &V_DISPATCH(&obj->v));
283
284 if (SUCCEEDED(hr)) {
285 V_VT(&obj->v) = VT_DISPATCH;
286
287 /* get its type-info */
288 IDispatch_GetTypeInfo(V_DISPATCH(&obj->v), 0, LANG_NEUTRAL, &obj->typeinfo);
289 ret = SUCCESS;
290 }
291 } else if (V_VT(&unwrapped) == VT_DISPATCH) {
292 /* unwrapped is now the dispatch pointer we want */
293 V_DISPATCH(&obj->v) = V_DISPATCH(&unwrapped);
294 V_VT(&obj->v) = VT_DISPATCH;
295
296 /* get its type-info */
297 IDispatch_GetTypeInfo(V_DISPATCH(&obj->v), 0, LANG_NEUTRAL, &obj->typeinfo);
298
299 ret = SUCCESS;
300 } else {
301 /* shouldn't happen, but let's be ready for it */
302 VariantClear(&unwrapped);
303 hr = E_INVALIDARG;
304 }
305 }
306 IObjectHandle_Release(handle);
307 }
308 IUnknown_Release(unk);
309 }
310
311 if (ret == FAILURE) {
312 char buf[1024];
313 char *err = php_win32_error_to_msg(hr);
314 snprintf(buf, sizeof(buf), "Failed to instantiate .Net object [%s] [0x%08x] %s", where, hr, err);
315 php_win32_error_msg_free(err);
316 php_com_throw_exception(hr, buf);
317 return;
318 }
319 }
320 /* }}} */
321
php_com_dotnet_mshutdown(void)322 void php_com_dotnet_mshutdown(void)
323 {
324 struct dotnet_runtime_stuff *stuff = COMG(dotnet_runtime_stuff);
325
326 if (stuff->dotnet_domain) {
327 IDispatch_Release(stuff->dotnet_domain);
328 }
329 if (stuff->dotnet_host) {
330 ICorRuntimeHost_Stop(stuff->dotnet_host);
331 ICorRuntimeHost_Release(stuff->dotnet_host);
332 stuff->dotnet_host = NULL;
333 }
334 free(stuff);
335 COMG(dotnet_runtime_stuff) = NULL;
336 }
337
php_com_dotnet_rshutdown(void)338 void php_com_dotnet_rshutdown(void)
339 {
340 struct dotnet_runtime_stuff *stuff = COMG(dotnet_runtime_stuff);
341
342 if (stuff->dotnet_domain) {
343 IDispatch_Release(stuff->dotnet_domain);
344 stuff->dotnet_domain = NULL;
345 }
346 }
347
348 #endif /* HAVE_MSCOREE_H */
349