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