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