com_com.c 23 KB

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