1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2018 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 if (err)
209 LocalFree(err);
210 php_com_throw_exception(hr, buf);
211 return;
212 }
213 stuff = (struct dotnet_runtime_stuff*)COMG(dotnet_runtime_stuff);
214
215 } else if (stuff->dotnet_domain == NULL) {
216 where = "ICorRuntimeHost_GetDefaultDomain";
217 hr = ICorRuntimeHost_GetDefaultDomain(stuff->dotnet_host, &unk);
218 if (FAILED(hr)) {
219 char buf[1024];
220 char *err = php_win32_error_to_msg(hr);
221 snprintf(buf, sizeof(buf), "Failed to re-init .Net domain [%s] %s", where, err);
222 if (err)
223 LocalFree(err);
224 php_com_throw_exception(hr, buf);
225 ZVAL_NULL(object);
226 return;
227 }
228
229 where = "QI: System._AppDomain";
230 hr = IUnknown_QueryInterface(unk, &IID_mscorlib_System_AppDomain, (LPVOID*)&stuff->dotnet_domain);
231 if (FAILED(hr)) {
232 char buf[1024];
233 char *err = php_win32_error_to_msg(hr);
234 snprintf(buf, sizeof(buf), "Failed to re-init .Net domain [%s] %s", where, err);
235 if (err)
236 LocalFree(err);
237 php_com_throw_exception(hr, buf);
238 ZVAL_NULL(object);
239 return;
240 }
241 }
242
243 obj = CDNO_FETCH(object);
244
245 if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "ss|l",
246 &assembly_name, &assembly_name_len,
247 &datatype_name, &datatype_name_len,
248 &cp)) {
249 php_com_throw_exception(E_INVALIDARG, "Could not create .Net object - invalid arguments!");
250 return;
251 }
252
253 cp_it = php_win32_cp_get_by_id((DWORD)cp);
254 if (!cp_it) {
255 php_com_throw_exception(E_INVALIDARG, "Could not create .Net object - invalid codepage!");
256 return;
257 }
258 obj->code_page = (int)cp_it->id;
259
260 oletype = php_com_string_to_olestring(datatype_name, datatype_name_len, obj->code_page);
261 oleassembly = php_com_string_to_olestring(assembly_name, assembly_name_len, obj->code_page);
262 oletype_sys = SysAllocString(oletype);
263 oleassembly_sys = SysAllocString(oleassembly);
264 where = "CreateInstance";
265 hr = stuff->dotnet_domain->lpVtbl->CreateInstance(stuff->dotnet_domain, oleassembly_sys, oletype_sys, &unk);
266 efree(oletype);
267 efree(oleassembly);
268 SysFreeString(oletype_sys);
269 SysFreeString(oleassembly_sys);
270
271 if (SUCCEEDED(hr)) {
272 VARIANT unwrapped;
273 IObjectHandle *handle = NULL;
274
275 where = "QI: IObjectHandle";
276 hr = IUnknown_QueryInterface(unk, &IID_IObjectHandle, &handle);
277
278 if (SUCCEEDED(hr)) {
279 where = "IObjectHandle_Unwrap";
280 hr = IObjectHandle_Unwrap(handle, &unwrapped);
281 if (SUCCEEDED(hr)) {
282
283 if (V_VT(&unwrapped) == VT_UNKNOWN) {
284 where = "Unwrapped, QI for IDispatch";
285 hr = IUnknown_QueryInterface(V_UNKNOWN(&unwrapped), &IID_IDispatch, &V_DISPATCH(&obj->v));
286
287 if (SUCCEEDED(hr)) {
288 V_VT(&obj->v) = VT_DISPATCH;
289
290 /* get its type-info */
291 IDispatch_GetTypeInfo(V_DISPATCH(&obj->v), 0, LANG_NEUTRAL, &obj->typeinfo);
292 ret = SUCCESS;
293 }
294 } else if (V_VT(&unwrapped) == VT_DISPATCH) {
295 /* unwrapped is now the dispatch pointer we want */
296 V_DISPATCH(&obj->v) = V_DISPATCH(&unwrapped);
297 V_VT(&obj->v) = VT_DISPATCH;
298
299 /* get its type-info */
300 IDispatch_GetTypeInfo(V_DISPATCH(&obj->v), 0, LANG_NEUTRAL, &obj->typeinfo);
301
302 ret = SUCCESS;
303 } else {
304 /* shouldn't happen, but let's be ready for it */
305 VariantClear(&unwrapped);
306 hr = E_INVALIDARG;
307 }
308 }
309 IObjectHandle_Release(handle);
310 }
311 IUnknown_Release(unk);
312 }
313
314 if (ret == FAILURE) {
315 char buf[1024];
316 char *err = php_win32_error_to_msg(hr);
317 snprintf(buf, sizeof(buf), "Failed to instantiate .Net object [%s] [0x%08x] %s", where, hr, err);
318 if (err && err[0]) {
319 LocalFree(err);
320 }
321 php_com_throw_exception(hr, buf);
322 return;
323 }
324 }
325 /* }}} */
326
php_com_dotnet_mshutdown(void)327 void php_com_dotnet_mshutdown(void)
328 {
329 struct dotnet_runtime_stuff *stuff = COMG(dotnet_runtime_stuff);
330
331 if (stuff->dotnet_domain) {
332 IDispatch_Release(stuff->dotnet_domain);
333 }
334 if (stuff->dotnet_host) {
335 ICorRuntimeHost_Stop(stuff->dotnet_host);
336 ICorRuntimeHost_Release(stuff->dotnet_host);
337 stuff->dotnet_host = NULL;
338 }
339 free(stuff);
340 COMG(dotnet_runtime_stuff) = NULL;
341 }
342
php_com_dotnet_rshutdown(void)343 void php_com_dotnet_rshutdown(void)
344 {
345 struct dotnet_runtime_stuff *stuff = COMG(dotnet_runtime_stuff);
346
347 if (stuff->dotnet_domain) {
348 IDispatch_Release(stuff->dotnet_domain);
349 stuff->dotnet_domain = NULL;
350 }
351 }
352
353 #endif /* HAVE_MSCOREE_H */
354