com_com.c 23 KB

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