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