1 /*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | http://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Author: Wez Furlong <wez@thebrainroom.com> |
14 +----------------------------------------------------------------------+
15 */
16
17 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
20
21 #include "php.h"
22 #include "php_ini.h"
23 #include "ext/standard/info.h"
24 #include "php_com_dotnet.h"
25 #include "php_com_dotnet_internal.h"
26 #include "Zend/zend_exceptions.h"
27
28 /* {{{ com_create_instance - ctor for COM class */
PHP_METHOD(com,__construct)29 PHP_METHOD(com, __construct)
30 {
31 zval *object = getThis();
32 zend_string *server_name = NULL;
33 HashTable *server_params = NULL;
34 php_com_dotnet_object *obj;
35 char *module_name, *typelib_name = NULL;
36 size_t module_name_len = 0, typelib_name_len = 0;
37 zend_string *user_name = NULL, *password = NULL, *domain_name = NULL;
38 OLECHAR *moniker;
39 CLSID clsid;
40 CLSCTX ctx = CLSCTX_SERVER;
41 HRESULT res = E_FAIL;
42 int mode = COMG(autoreg_case_sensitive) ? CONST_CS : 0;
43 ITypeLib *TL = NULL;
44 COSERVERINFO info;
45 COAUTHIDENTITY authid = {0};
46 COAUTHINFO authinfo = {
47 RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL,
48 RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE,
49 &authid, EOAC_NONE
50 };
51 zend_long cp = GetACP();
52 const struct php_win32_cp *cp_it;
53
54 ZEND_PARSE_PARAMETERS_START(1, 4)
55 Z_PARAM_STRING(module_name, module_name_len)
56 Z_PARAM_OPTIONAL
57 Z_PARAM_ARRAY_HT_OR_STR_OR_NULL(server_params, server_name)
58 Z_PARAM_LONG(cp)
59 Z_PARAM_STRING(typelib_name, typelib_name_len)
60 ZEND_PARSE_PARAMETERS_END();
61
62 php_com_initialize();
63 obj = CDNO_FETCH(object);
64
65 cp_it = php_win32_cp_get_by_id((DWORD)cp);
66 if (!cp_it) {
67 php_com_throw_exception(E_INVALIDARG, "Could not create COM object - invalid codepage!");
68 RETURN_THROWS();
69 }
70 obj->code_page = (int)cp;
71
72 if (server_name) {
73 ctx = CLSCTX_REMOTE_SERVER;
74 } else if (server_params) {
75 zval *tmp;
76
77 /* decode the data from the array */
78
79 if (NULL != (tmp = zend_hash_str_find(server_params,
80 "Server", sizeof("Server")-1))) {
81 server_name = zval_get_string(tmp);
82 ctx = CLSCTX_REMOTE_SERVER;
83 }
84
85 if (NULL != (tmp = zend_hash_str_find(server_params,
86 "Username", sizeof("Username")-1))) {
87 user_name = zval_get_string(tmp);
88 }
89
90 if (NULL != (tmp = zend_hash_str_find(server_params,
91 "Password", sizeof("Password")-1))) {
92 password = zval_get_string(tmp);
93 }
94
95 if (NULL != (tmp = zend_hash_str_find(server_params,
96 "Domain", sizeof("Domain")-1))) {
97 domain_name = zval_get_string(tmp);
98 }
99
100 if (NULL != (tmp = zend_hash_str_find(server_params,
101 "Flags", sizeof("Flags")-1))) {
102 ctx = (CLSCTX)zval_get_long(tmp);
103 }
104 }
105
106 if (server_name && !COMG(allow_dcom)) {
107 php_com_throw_exception(E_ERROR, "DCOM has been disabled by your administrator [com.allow_dcom=0]");
108 RETURN_THROWS();
109 }
110
111 moniker = php_com_string_to_olestring(module_name, module_name_len, obj->code_page);
112
113 /* if instantiating a remote object, either directly, or via
114 * a moniker, fill in the relevant info */
115 if (server_name) {
116 info.dwReserved1 = 0;
117 info.dwReserved2 = 0;
118 info.pwszName = php_com_string_to_olestring(ZSTR_VAL(server_name), ZSTR_LEN(server_name), obj->code_page);
119
120 if (user_name) {
121 authid.User = (OLECHAR*) ZSTR_VAL(user_name);
122 authid.UserLength = (ULONG) ZSTR_LEN(user_name);
123
124 if (password) {
125 authid.Password = (OLECHAR*) ZSTR_VAL(password);
126 authid.PasswordLength = (ULONG) ZSTR_LEN(password);
127 } else {
128 authid.Password = (OLECHAR*) "";
129 authid.PasswordLength = 0;
130 }
131
132 if (domain_name) {
133 authid.Domain = (OLECHAR*) ZSTR_VAL(domain_name);
134 authid.DomainLength = (ULONG) ZSTR_LEN(domain_name);
135 } else {
136 authid.Domain = (OLECHAR*) "";
137 authid.DomainLength = 0;
138 }
139 authid.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
140 info.pAuthInfo = &authinfo;
141 } else {
142 info.pAuthInfo = NULL;
143 }
144 }
145
146 if (FAILED(CLSIDFromString(moniker, &clsid))) {
147 /* try to use it as a moniker */
148 IBindCtx *pBindCtx = NULL;
149 IMoniker *pMoniker = NULL;
150 ULONG ulEaten;
151 BIND_OPTS2 bopt = {0};
152
153 if (SUCCEEDED(res = CreateBindCtx(0, &pBindCtx))) {
154 if (server_name) {
155 /* fill in the remote server info.
156 * MSDN docs indicate that this might be ignored in
157 * current win32 implementations, but at least we are
158 * doing the right thing in readiness for the day that
159 * it does work */
160 bopt.cbStruct = sizeof(bopt);
161 IBindCtx_GetBindOptions(pBindCtx, (BIND_OPTS*)&bopt);
162 bopt.pServerInfo = &info;
163 /* apparently, GetBindOptions will only ever return
164 * a regular BIND_OPTS structure. My gut feeling is
165 * that it will modify the size field to reflect that
166 * so lets be safe and set it to the BIND_OPTS2 size
167 * again */
168 bopt.cbStruct = sizeof(bopt);
169 IBindCtx_SetBindOptions(pBindCtx, (BIND_OPTS*)&bopt);
170 }
171
172 if (SUCCEEDED(res = MkParseDisplayName(pBindCtx, moniker, &ulEaten, &pMoniker))) {
173 res = IMoniker_BindToObject(pMoniker, pBindCtx,
174 NULL, &IID_IDispatch, (LPVOID*)&V_DISPATCH(&obj->v));
175
176 if (SUCCEEDED(res)) {
177 V_VT(&obj->v) = VT_DISPATCH;
178 }
179
180 IMoniker_Release(pMoniker);
181 }
182 }
183 if (pBindCtx) {
184 IBindCtx_Release(pBindCtx);
185 }
186 } else if (server_name) {
187 MULTI_QI qi;
188
189 qi.pIID = &IID_IDispatch;
190 qi.pItf = NULL;
191 qi.hr = S_OK;
192
193 res = CoCreateInstanceEx(&clsid, NULL, ctx, &info, 1, &qi);
194
195 if (SUCCEEDED(res)) {
196 res = qi.hr;
197 V_DISPATCH(&obj->v) = (IDispatch*)qi.pItf;
198 V_VT(&obj->v) = VT_DISPATCH;
199 }
200 } else {
201 res = CoCreateInstance(&clsid, NULL, CLSCTX_SERVER, &IID_IDispatch, (LPVOID*)&V_DISPATCH(&obj->v));
202 if (SUCCEEDED(res)) {
203 V_VT(&obj->v) = VT_DISPATCH;
204 }
205 }
206
207 if (server_name) {
208 if (info.pwszName) efree(info.pwszName);
209 if (server_params) zend_string_release(server_name);
210 }
211 if (user_name) zend_string_release(user_name);
212 if (password) zend_string_release(password);
213 if (domain_name) zend_string_release(domain_name);
214
215 efree(moniker);
216
217 if (FAILED(res)) {
218 char *werr, *msg;
219
220 werr = php_win32_error_to_msg(res);
221 spprintf(&msg, 0, "Failed to create COM object `%s': %s", module_name, werr);
222 php_win32_error_msg_free(werr);
223
224 php_com_throw_exception(res, msg);
225 efree(msg);
226 RETURN_THROWS();
227 }
228
229 /* we got the object and it lives ! */
230
231 /* see if it has TypeInfo available */
232 if (FAILED(IDispatch_GetTypeInfo(V_DISPATCH(&obj->v), 0, LANG_NEUTRAL, &obj->typeinfo)) && typelib_name) {
233 /* load up the library from the named file */
234 TL = php_com_load_typelib_via_cache(typelib_name, obj->code_page);
235
236 if (TL) {
237 if (COMG(autoreg_on)) {
238 php_com_import_typelib(TL, mode, obj->code_page);
239 }
240
241 /* cross your fingers... there is no guarantee that this ITypeInfo
242 * instance has any relation to this IDispatch instance... */
243 ITypeLib_GetTypeInfo(TL, 0, &obj->typeinfo);
244 ITypeLib_Release(TL);
245 }
246 } else if (obj->typeinfo && COMG(autoreg_on)) {
247 UINT idx;
248
249 if (SUCCEEDED(ITypeInfo_GetContainingTypeLib(obj->typeinfo, &TL, &idx))) {
250 /* check if the library is already in the cache by getting its name */
251 BSTR name;
252
253 if (SUCCEEDED(ITypeLib_GetDocumentation(TL, -1, &name, NULL, NULL, NULL))) {
254 typelib_name = php_com_olestring_to_string(name, &typelib_name_len, obj->code_page);
255
256 if (NULL != php_com_cache_typelib(TL, typelib_name, typelib_name_len)) {
257 php_com_import_typelib(TL, mode, obj->code_page);
258
259 /* add a reference for the hash */
260 ITypeLib_AddRef(TL);
261 }
262 efree(typelib_name);
263 } else {
264 /* try it anyway */
265 php_com_import_typelib(TL, mode, obj->code_page);
266 }
267
268 ITypeLib_Release(TL);
269 }
270 }
271
272 }
273 /* }}} */
274
275 /* {{{ Returns a handle to an already running instance of a COM object */
PHP_FUNCTION(com_get_active_object)276 PHP_FUNCTION(com_get_active_object)
277 {
278 CLSID clsid;
279 char *module_name;
280 size_t module_name_len;
281 zend_long code_page;
282 zend_bool code_page_is_null = 1;
283 IUnknown *unk = NULL;
284 IDispatch *obj = NULL;
285 HRESULT res;
286 OLECHAR *module = NULL;
287
288 php_com_initialize();
289 if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "s|l!",
290 &module_name, &module_name_len, &code_page, &code_page_is_null)) {
291 RETURN_THROWS();
292 }
293
294 if (code_page_is_null) {
295 code_page = COMG(code_page);
296 }
297
298 module = php_com_string_to_olestring(module_name, module_name_len, (int)code_page);
299
300 res = CLSIDFromString(module, &clsid);
301
302 if (FAILED(res)) {
303 php_com_throw_exception(res, NULL);
304 } else {
305 res = GetActiveObject(&clsid, NULL, &unk);
306
307 if (FAILED(res)) {
308 php_com_throw_exception(res, NULL);
309 } else {
310 res = IUnknown_QueryInterface(unk, &IID_IDispatch, &obj);
311
312 if (FAILED(res)) {
313 php_com_throw_exception(res, NULL);
314 } else if (obj) {
315 /* we got our dispatchable object */
316 php_com_wrap_dispatch(return_value, obj, (int)code_page);
317 }
318 }
319 }
320
321 if (obj) {
322 IDispatch_Release(obj);
323 }
324 if (unk) {
325 IUnknown_Release(obj);
326 }
327 efree(module);
328 }
329 /* }}} */
330
331 /* Performs an Invoke on the given com object.
332 * returns a failure code and creates an exception if there was an error */
php_com_invoke_helper(php_com_dotnet_object * obj,DISPID id_member,WORD flags,DISPPARAMS * disp_params,VARIANT * v,int silent,int allow_noarg)333 HRESULT php_com_invoke_helper(php_com_dotnet_object *obj, DISPID id_member,
334 WORD flags, DISPPARAMS *disp_params, VARIANT *v, int silent, int allow_noarg)
335 {
336 HRESULT hr;
337 unsigned int arg_err;
338 EXCEPINFO e = {0};
339
340 hr = IDispatch_Invoke(V_DISPATCH(&obj->v), id_member,
341 &IID_NULL, LOCALE_SYSTEM_DEFAULT, flags, disp_params, v, &e, &arg_err);
342
343 if (silent == 0 && FAILED(hr)) {
344 char *source = NULL, *desc = NULL, *msg = NULL;
345 size_t source_len, desc_len;
346
347 switch (hr) {
348 case DISP_E_EXCEPTION:
349 if (e.bstrSource) {
350 source = php_com_olestring_to_string(e.bstrSource, &source_len, obj->code_page);
351 SysFreeString(e.bstrSource);
352 }
353 if (e.bstrDescription) {
354 desc = php_com_olestring_to_string(e.bstrDescription, &desc_len, obj->code_page);
355 SysFreeString(e.bstrDescription);
356 }
357 if (PG(html_errors)) {
358 spprintf(&msg, 0, "<b>Source:</b> %s<br/><b>Description:</b> %s",
359 source ? source : "Unknown",
360 desc ? desc : "Unknown");
361 } else {
362 spprintf(&msg, 0, "Source: %s\nDescription: %s",
363 source ? source : "Unknown",
364 desc ? desc : "Unknown");
365 }
366 if (desc) {
367 efree(desc);
368 }
369 if (source) {
370 efree(source);
371 }
372 if (e.bstrHelpFile) {
373 SysFreeString(e.bstrHelpFile);
374 }
375 break;
376
377 case DISP_E_PARAMNOTFOUND:
378 case DISP_E_TYPEMISMATCH:
379 desc = php_win32_error_to_msg(hr);
380 spprintf(&msg, 0, "Parameter %d: %s", arg_err, desc);
381 php_win32_error_msg_free(desc);
382 break;
383
384 case DISP_E_BADPARAMCOUNT:
385 if ((disp_params->cArgs + disp_params->cNamedArgs == 0) && (allow_noarg == 1)) {
386 /* if getting a property and they are missing all parameters,
387 * we want to create a proxy object for them; so lets not create an
388 * exception here */
389 msg = NULL;
390 break;
391 }
392 /* else fall through */
393
394 default:
395 desc = php_win32_error_to_msg(hr);
396 spprintf(&msg, 0, "Error [0x%08x] %s", hr, desc);
397 php_win32_error_msg_free(desc);
398 break;
399 }
400
401 if (msg) {
402 php_com_throw_exception(hr, msg);
403 efree(msg);
404 }
405 }
406
407 return hr;
408 }
409
410 /* map an ID to a name */
php_com_get_id_of_name(php_com_dotnet_object * obj,char * name,size_t namelen,DISPID * dispid)411 HRESULT php_com_get_id_of_name(php_com_dotnet_object *obj, char *name,
412 size_t namelen, DISPID *dispid)
413 {
414 OLECHAR *olename;
415 HRESULT hr;
416 zval *tmp;
417
418 if (namelen == -1) {
419 namelen = strlen(name);
420 }
421
422 if (obj->id_of_name_cache && NULL != (tmp = zend_hash_str_find(obj->id_of_name_cache, name, namelen))) {
423 *dispid = (DISPID)Z_LVAL_P(tmp);
424 return S_OK;
425 }
426
427 olename = php_com_string_to_olestring(name, namelen, obj->code_page);
428
429 if (obj->typeinfo) {
430 hr = ITypeInfo_GetIDsOfNames(obj->typeinfo, &olename, 1, dispid);
431 if (FAILED(hr)) {
432 HRESULT hr1 = hr;
433 hr = IDispatch_GetIDsOfNames(V_DISPATCH(&obj->v), &IID_NULL, &olename, 1, LOCALE_SYSTEM_DEFAULT, dispid);
434 if (SUCCEEDED(hr) && hr1 != E_NOTIMPL) {
435 /* fall back on IDispatch direct */
436 ITypeInfo_Release(obj->typeinfo);
437 obj->typeinfo = NULL;
438 }
439 }
440 } else {
441 hr = IDispatch_GetIDsOfNames(V_DISPATCH(&obj->v), &IID_NULL, &olename, 1, LOCALE_SYSTEM_DEFAULT, dispid);
442 }
443 efree(olename);
444
445 if (SUCCEEDED(hr)) {
446 zval tmp;
447
448 /* cache the mapping */
449 if (!obj->id_of_name_cache) {
450 ALLOC_HASHTABLE(obj->id_of_name_cache);
451 zend_hash_init(obj->id_of_name_cache, 2, NULL, NULL, 0);
452 }
453 ZVAL_LONG(&tmp, *dispid);
454 zend_hash_str_update(obj->id_of_name_cache, name, namelen, &tmp);
455 }
456
457 return hr;
458 }
459
460 /* the core of COM */
php_com_do_invoke_byref(php_com_dotnet_object * obj,zend_internal_function * f,WORD flags,VARIANT * v,int nargs,zval * args)461 int php_com_do_invoke_byref(php_com_dotnet_object *obj, zend_internal_function *f,
462 WORD flags, VARIANT *v, int nargs, zval *args)
463 {
464 DISPID dispid, altdispid;
465 DISPPARAMS disp_params;
466 HRESULT hr;
467 VARIANT *vargs = NULL, *byref_vals = NULL;
468 int i, byref_count = 0, j;
469
470 /* assumption: that the active function (f) is the function we generated for the engine */
471 if (!f) {
472 return FAILURE;
473 }
474
475 hr = php_com_get_id_of_name(obj, f->function_name->val, f->function_name->len, &dispid);
476
477 if (FAILED(hr)) {
478 char *msg = NULL;
479 char *winerr = php_win32_error_to_msg(hr);
480 spprintf(&msg, 0, "Unable to lookup `%s': %s", f->function_name->val, winerr);
481 php_win32_error_msg_free(winerr);
482 php_com_throw_exception(hr, msg);
483 efree(msg);
484 return FAILURE;
485 }
486
487
488 if (nargs) {
489 vargs = (VARIANT*)safe_emalloc(sizeof(VARIANT), nargs, 0);
490 }
491
492 if (f->arg_info) {
493 for (i = 0; i < nargs; i++) {
494 if (ZEND_ARG_SEND_MODE(&f->arg_info[nargs - i - 1])) {
495 byref_count++;
496 }
497 }
498 }
499
500 if (byref_count) {
501 byref_vals = (VARIANT*)safe_emalloc(sizeof(VARIANT), byref_count, 0);
502 for (j = 0, i = 0; i < nargs; i++) {
503 if (ZEND_ARG_SEND_MODE(&f->arg_info[nargs - i - 1])) {
504 /* put the value into byref_vals instead */
505 php_com_variant_from_zval(&byref_vals[j], &args[nargs - i - 1], obj->code_page);
506
507 /* if it is already byref, "move" it into the vargs array, otherwise
508 * make vargs a reference to this value */
509 if (V_VT(&byref_vals[j]) & VT_BYREF) {
510 memcpy(&vargs[i], &byref_vals[j], sizeof(vargs[i]));
511 VariantInit(&byref_vals[j]); /* leave the variant slot empty to simplify cleanup */
512 } else {
513 VariantInit(&vargs[i]);
514 V_VT(&vargs[i]) = V_VT(&byref_vals[j]) | VT_BYREF;
515 /* union magic ensures that this works out */
516 vargs[i].byref = &V_UINT(&byref_vals[j]);
517 }
518 j++;
519 } else {
520 php_com_variant_from_zval(&vargs[i], &args[nargs - i - 1], obj->code_page);
521 }
522 }
523
524 } else {
525 /* Invoke'd args are in reverse order */
526 for (i = 0; i < nargs; i++) {
527 php_com_variant_from_zval(&vargs[i], &args[nargs - i - 1], obj->code_page);
528 }
529 }
530
531 disp_params.cArgs = nargs;
532 disp_params.cNamedArgs = 0;
533 disp_params.rgvarg = vargs;
534 disp_params.rgdispidNamedArgs = NULL;
535
536 if (flags & DISPATCH_PROPERTYPUT) {
537 altdispid = DISPID_PROPERTYPUT;
538 disp_params.rgdispidNamedArgs = &altdispid;
539 disp_params.cNamedArgs = 1;
540 }
541
542 /* this will create an exception if needed */
543 hr = php_com_invoke_helper(obj, dispid, flags, &disp_params, v, 0, 0);
544
545 /* release variants */
546 if (vargs) {
547 if (f && f->arg_info) {
548 for (i = 0, j = 0; i < nargs; i++) {
549 /* if this was byref, update the zval */
550 if (ZEND_ARG_SEND_MODE(&f->arg_info[nargs - i - 1])) {
551 zval *arg = &args[nargs - i - 1];
552
553 ZVAL_DEREF(arg);
554 zval_ptr_dtor(arg);
555 ZVAL_NULL(arg);
556
557 /* if the variant is pointing at the byref_vals, we need to map
558 * the pointee value as a zval; otherwise, the value is pointing
559 * into an existing PHP variant record */
560 if (V_VT(&vargs[i]) & VT_BYREF) {
561 if (vargs[i].byref == &V_UINT(&byref_vals[j])) {
562 /* copy that value */
563 php_com_zval_from_variant(arg, &byref_vals[j], obj->code_page);
564 }
565 } else {
566 /* not sure if this can ever happen; the variant we marked as BYREF
567 * is no longer BYREF - copy its value */
568 php_com_zval_from_variant(arg, &vargs[i], obj->code_page);
569 }
570 VariantClear(&byref_vals[j]);
571 j++;
572 }
573 VariantClear(&vargs[i]);
574 }
575 } else {
576 for (i = 0, j = 0; i < nargs; i++) {
577 VariantClear(&vargs[i]);
578 }
579 }
580 efree(vargs);
581 if (byref_vals) efree(byref_vals);
582 }
583
584 return SUCCEEDED(hr) ? SUCCESS : FAILURE;
585 }
586
587
588
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)589 int php_com_do_invoke_by_id(php_com_dotnet_object *obj, DISPID dispid,
590 WORD flags, VARIANT *v, int nargs, zval *args, int silent, int allow_noarg)
591 {
592 DISPID altdispid;
593 DISPPARAMS disp_params;
594 HRESULT hr;
595 VARIANT *vargs = NULL;
596 int i;
597
598 if (nargs) {
599 vargs = (VARIANT*)safe_emalloc(sizeof(VARIANT), nargs, 0);
600 }
601
602 /* Invoke'd args are in reverse order */
603 for (i = 0; i < nargs; i++) {
604 php_com_variant_from_zval(&vargs[i], &args[nargs - i - 1], obj->code_page);
605 }
606
607 disp_params.cArgs = nargs;
608 disp_params.cNamedArgs = 0;
609 disp_params.rgvarg = vargs;
610 disp_params.rgdispidNamedArgs = NULL;
611
612 if (flags & DISPATCH_PROPERTYPUT) {
613 altdispid = DISPID_PROPERTYPUT;
614 disp_params.rgdispidNamedArgs = &altdispid;
615 disp_params.cNamedArgs = 1;
616 }
617
618 /* this will create an exception if needed */
619 hr = php_com_invoke_helper(obj, dispid, flags, &disp_params, v, silent, allow_noarg);
620
621 /* release variants */
622 if (vargs) {
623 for (i = 0; i < nargs; i++) {
624 VariantClear(&vargs[i]);
625 }
626 efree(vargs);
627 }
628
629 /* a bit of a hack this, but it's needed for COM array access. */
630 if (hr == DISP_E_BADPARAMCOUNT)
631 return hr;
632
633 return SUCCEEDED(hr) ? SUCCESS : FAILURE;
634 }
635
php_com_do_invoke(php_com_dotnet_object * obj,char * name,size_t namelen,WORD flags,VARIANT * v,int nargs,zval * args,int allow_noarg)636 int php_com_do_invoke(php_com_dotnet_object *obj, char *name, size_t namelen,
637 WORD flags, VARIANT *v, int nargs, zval *args, int allow_noarg)
638 {
639 DISPID dispid;
640 HRESULT hr;
641 char *msg = NULL;
642
643 hr = php_com_get_id_of_name(obj, name, namelen, &dispid);
644
645 if (FAILED(hr)) {
646 char *winerr = php_win32_error_to_msg(hr);
647 spprintf(&msg, 0, "Unable to lookup `%s': %s", name, winerr);
648 php_win32_error_msg_free(winerr);
649 php_com_throw_exception(hr, msg);
650 efree(msg);
651 return FAILURE;
652 }
653
654 return php_com_do_invoke_by_id(obj, dispid, flags, v, nargs, args, 0, allow_noarg);
655 }
656
657 /* {{{ Generate a globally unique identifier (GUID) */
PHP_FUNCTION(com_create_guid)658 PHP_FUNCTION(com_create_guid)
659 {
660 GUID retval;
661 OLECHAR *guid_string;
662
663 if (zend_parse_parameters_none() == FAILURE) {
664 RETURN_THROWS();
665 }
666
667 php_com_initialize();
668 if (CoCreateGuid(&retval) == S_OK && StringFromCLSID(&retval, &guid_string) == S_OK) {
669 size_t len;
670 char *str;
671
672 str = php_com_olestring_to_string(guid_string, &len, CP_ACP);
673 RETVAL_STRINGL(str, len);
674 // TODO: avoid reallocation ???
675 efree(str);
676
677 CoTaskMemFree(guid_string);
678 } else {
679 RETURN_FALSE;
680 }
681 }
682 /* }}} */
683
684 /* {{{ Connect events from a COM object to a PHP object */
PHP_FUNCTION(com_event_sink)685 PHP_FUNCTION(com_event_sink)
686 {
687 zval *object, *sinkobject;
688 zend_string *sink_str = NULL;
689 HashTable *sink_ht = NULL;
690 char *dispname = NULL, *typelibname = NULL;
691 php_com_dotnet_object *obj;
692 ITypeInfo *typeinfo = NULL;
693
694 ZEND_PARSE_PARAMETERS_START(2, 3)
695 Z_PARAM_OBJECT_OF_CLASS(object, php_com_variant_class_entry)
696 Z_PARAM_OBJECT(sinkobject)
697 Z_PARAM_OPTIONAL
698 Z_PARAM_ARRAY_HT_OR_STR_OR_NULL(sink_ht, sink_str)
699 ZEND_PARSE_PARAMETERS_END();
700
701 RETVAL_FALSE;
702
703 php_com_initialize();
704 obj = CDNO_FETCH(object);
705
706 if (sink_ht) {
707 /* 0 => typelibname, 1 => dispname */
708 zval *tmp;
709
710 if ((tmp = zend_hash_index_find(sink_ht, 0)) != NULL && Z_TYPE_P(tmp) == IS_STRING)
711 typelibname = Z_STRVAL_P(tmp);
712 if ((tmp = zend_hash_index_find(sink_ht, 1)) != NULL && Z_TYPE_P(tmp) == IS_STRING)
713 dispname = Z_STRVAL_P(tmp);
714 } else if (sink_str) {
715 dispname = ZSTR_VAL(sink_str);
716 }
717
718 typeinfo = php_com_locate_typeinfo(typelibname, obj, dispname, 1);
719
720 if (typeinfo) {
721 HashTable *id_to_name;
722
723 ALLOC_HASHTABLE(id_to_name);
724
725 if (php_com_process_typeinfo(typeinfo, id_to_name, 0, &obj->sink_id, obj->code_page)) {
726
727 /* Create the COM wrapper for this sink */
728 obj->sink_dispatch = php_com_wrapper_export_as_sink(sinkobject, &obj->sink_id, id_to_name);
729
730 /* Now hook it up to the source */
731 php_com_object_enable_event_sink(obj, TRUE);
732 RETVAL_TRUE;
733
734 } else {
735 FREE_HASHTABLE(id_to_name);
736 }
737 }
738
739 if (typeinfo) {
740 ITypeInfo_Release(typeinfo);
741 }
742
743 }
744 /* }}} */
745
746 /* {{{ Print out a PHP class definition for a dispatchable interface */
PHP_FUNCTION(com_print_typeinfo)747 PHP_FUNCTION(com_print_typeinfo)
748 {
749 zend_object *object_zpp;
750 zend_string *typelib_name_zpp = NULL;
751 char *ifacename = NULL;
752 char *typelibname = NULL;
753 size_t ifacelen;
754 zend_bool wantsink = 0;
755 php_com_dotnet_object *obj = NULL;
756 ITypeInfo *typeinfo;
757
758 ZEND_PARSE_PARAMETERS_START(1, 3)
759 Z_PARAM_OBJ_OF_CLASS_OR_STR(object_zpp, php_com_variant_class_entry, typelib_name_zpp)
760 Z_PARAM_OPTIONAL
761 Z_PARAM_STRING_OR_NULL(ifacename, ifacelen)
762 Z_PARAM_BOOL(wantsink)
763 ZEND_PARSE_PARAMETERS_END();
764
765 php_com_initialize();
766 if (object_zpp) {
767 obj = (php_com_dotnet_object*)object_zpp;
768 } else {
769 typelibname = ZSTR_VAL(typelib_name_zpp);
770 }
771
772 typeinfo = php_com_locate_typeinfo(typelibname, obj, ifacename, wantsink ? 1 : 0);
773 if (typeinfo) {
774 php_com_process_typeinfo(typeinfo, NULL, 1, NULL, obj ? obj->code_page : COMG(code_page));
775 ITypeInfo_Release(typeinfo);
776 RETURN_TRUE;
777 }
778
779 php_error_docref(NULL, E_WARNING, "Unable to find typeinfo using the parameters supplied");
780 RETURN_FALSE;
781 }
782 /* }}} */
783
784 /* {{{ Process COM messages, sleeping for up to timeoutms milliseconds */
PHP_FUNCTION(com_message_pump)785 PHP_FUNCTION(com_message_pump)
786 {
787 zend_long timeoutms = 0;
788 MSG msg;
789 DWORD result;
790
791 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &timeoutms) == FAILURE)
792 RETURN_THROWS();
793
794 php_com_initialize();
795 result = MsgWaitForMultipleObjects(0, NULL, FALSE, (DWORD)timeoutms, QS_ALLINPUT);
796
797 if (result == WAIT_OBJECT_0) {
798 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
799 TranslateMessage(&msg);
800 DispatchMessage(&msg);
801 }
802 /* we processed messages */
803 RETVAL_TRUE;
804 } else {
805 /* we did not process messages (timed out) */
806 RETVAL_FALSE;
807 }
808 }
809 /* }}} */
810
811 /* {{{ Loads a Typelibrary and registers its constants */
PHP_FUNCTION(com_load_typelib)812 PHP_FUNCTION(com_load_typelib)
813 {
814 char *name;
815 size_t namelen;
816 ITypeLib *pTL = NULL;
817 zend_bool cs = TRUE;
818 int codepage = COMG(code_page);
819
820 if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "s|b", &name, &namelen, &cs)) {
821 RETURN_THROWS();
822 }
823
824 if (!cs) {
825 php_error_docref(NULL, E_WARNING, "com_load_typelib(): Argument #2 ($case_insensitive) is ignored since declaration of case-insensitive constants is no longer supported");
826 }
827
828 RETVAL_FALSE;
829
830 php_com_initialize();
831 pTL = php_com_load_typelib_via_cache(name, codepage);
832 if (pTL) {
833 if (php_com_import_typelib(pTL, cs ? CONST_CS : 0, codepage) == SUCCESS) {
834 RETVAL_TRUE;
835 }
836
837 ITypeLib_Release(pTL);
838 pTL = NULL;
839 }
840 }
841 /* }}} */
842