PageRenderTime 24ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/brlcad/tags/rel-7-10-2/src/other/tk/win/tkWinSend.c

https://bitbucket.org/vrrm/brl-cad-copy-for-fast-history-browsing-in-git
C | 1035 lines | 541 code | 129 blank | 365 comment | 84 complexity | 5c6c9fd99911a04dea1662bb8379daa1 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, LGPL-2.1, Apache-2.0, AGPL-3.0, LGPL-3.0, GPL-3.0, MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, 0BSD, BSD-3-Clause
  1. /*
  2. * tkWinSend.c --
  3. *
  4. * This file provides functions that implement the "send" command,
  5. * allowing commands to be passed from interpreter to interpreter.
  6. *
  7. * Copyright (c) 1997 by Sun Microsystems, Inc.
  8. * Copyright (c) 2003 Pat Thoyts <patthoyts@users.sourceforge.net>
  9. *
  10. * See the file "license.terms" for information on usage and redistribution of
  11. * this file, and for a DISCLAIMER OF ALL WARRANTIES.
  12. *
  13. * RCS: @(#) $Id$
  14. */
  15. #include "tkWinSendCom.h"
  16. /*
  17. * Should be defined in WTypes.h but mingw 1.0 is missing them.
  18. */
  19. #ifndef _ROTFLAGS_DEFINED
  20. #define _ROTFLAGS_DEFINED
  21. #define ROTFLAGS_REGISTRATIONKEEPSALIVE 0x01
  22. #define ROTFLAGS_ALLOWANYCLIENT 0x02
  23. #endif /* ! _ROTFLAGS_DEFINED */
  24. #define TKWINSEND_CLASS_NAME "TclEval"
  25. #define TKWINSEND_REGISTRATION_BASE L"TclEval"
  26. #define MK_E_MONIKERALREADYREGISTERED \
  27. MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x02A1)
  28. /*
  29. * Package information structure. This is used to keep interpreter specific
  30. * details for use when releasing the package resources upon interpreter
  31. * deletion or package removal.
  32. */
  33. typedef struct {
  34. char *name; /* The registered application name */
  35. DWORD cookie; /* ROT cookie returned on registration */
  36. LPUNKNOWN obj; /* Interface for the registration object */
  37. Tcl_Interp *interp;
  38. Tcl_Command token; /* Winsend command token */
  39. } RegisteredInterp;
  40. typedef struct SendEvent {
  41. Tcl_Event header;
  42. Tcl_Interp *interp;
  43. Tcl_Obj *cmdPtr;
  44. } SendEvent;
  45. #ifdef TK_SEND_ENABLED_ON_WINDOWS
  46. typedef struct {
  47. int initialized;
  48. } ThreadSpecificData;
  49. static Tcl_ThreadDataKey dataKey;
  50. #endif
  51. /*
  52. * Functions internal to this file.
  53. */
  54. static void CmdDeleteProc(ClientData clientData);
  55. static void InterpDeleteProc(ClientData clientData,
  56. Tcl_Interp *interp);
  57. static void RevokeObjectRegistration(RegisteredInterp *riPtr);
  58. static HRESULT BuildMoniker(CONST char *name, LPMONIKER *pmk);
  59. static HRESULT RegisterInterp(CONST char *name,
  60. RegisteredInterp *riPtr);
  61. static int FindInterpreterObject(Tcl_Interp *interp,
  62. CONST char *name, LPDISPATCH *ppdisp);
  63. static int Send(LPDISPATCH pdispInterp, Tcl_Interp *interp,
  64. int async, ClientData clientData, int objc,
  65. Tcl_Obj *CONST objv[]);
  66. static Tcl_Obj * Win32ErrorObj(HRESULT hrError);
  67. static void SendTrace(const char *format, ...);
  68. static Tcl_EventProc SendEventProc;
  69. #if defined(DEBUG) || defined(_DEBUG)
  70. #define TRACE SendTrace
  71. #else
  72. #define TRACE 1 ? ((void)0) : SendTrace
  73. #endif
  74. /*
  75. *--------------------------------------------------------------
  76. *
  77. * Tk_SetAppName --
  78. *
  79. * This function is called to associate an ASCII name with a Tk
  80. * application. If the application has already been named, the name
  81. * replaces the old one.
  82. *
  83. * Results:
  84. * The return value is the name actually given to the application. This
  85. * will normally be the same as name, but if name was already in use for
  86. * an application then a name of the form "name #2" will be chosen, with
  87. * a high enough number to make the name unique.
  88. *
  89. * Side effects:
  90. * Registration info is saved, thereby allowing the "send" command to be
  91. * used later to invoke commands in the application. In addition, the
  92. * "send" command is created in the application's interpreter. The
  93. * registration will be removed automatically if the interpreter is
  94. * deleted or the "send" command is removed.
  95. *
  96. *--------------------------------------------------------------
  97. */
  98. CONST char *
  99. Tk_SetAppName(
  100. Tk_Window tkwin, /* Token for any window in the application to
  101. * be named: it is just used to identify the
  102. * application and the display. */
  103. CONST char *name) /* The name that will be used to refer to the
  104. * interpreter in later "send" commands. Must
  105. * be globally unique. */
  106. {
  107. #ifndef TK_SEND_ENABLED_ON_WINDOWS
  108. /*
  109. * Temporarily disabled for bug #858822
  110. */
  111. return name;
  112. #else /* TK_SEND_ENABLED_ON_WINDOWS */
  113. ThreadSpecificData *tsdPtr = NULL;
  114. TkWindow *winPtr = (TkWindow *)tkwin;
  115. RegisteredInterp *riPtr = NULL;
  116. Tcl_Interp *interp;
  117. HRESULT hr = S_OK;
  118. interp = winPtr->mainPtr->interp;
  119. tsdPtr = (ThreadSpecificData *)
  120. Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
  121. /*
  122. * Initialise the COM library for this interpreter just once.
  123. */
  124. if (tsdPtr->initialized == 0) {
  125. hr = CoInitialize(0);
  126. if (FAILED(hr)) {
  127. Tcl_SetResult(interp,
  128. "failed to initialize the COM library", TCL_STATIC);
  129. return "";
  130. }
  131. tsdPtr->initialized = 1;
  132. TRACE("Initialized COM library for interp 0x%08X\n", (long)interp);
  133. }
  134. /*
  135. * If the interp hasn't been registered before then we need to create the
  136. * registration structure and the COM object. If it has been registered
  137. * already then we can reuse all and just register the new name.
  138. */
  139. riPtr = Tcl_GetAssocData(interp, "tkWinSend::ri", NULL);
  140. if (riPtr == NULL) {
  141. riPtr = (RegisteredInterp *)ckalloc(sizeof(RegisteredInterp));
  142. memset(riPtr, 0, sizeof(RegisteredInterp));
  143. riPtr->interp = interp;
  144. hr = TkWinSendCom_CreateInstance(interp, &IID_IUnknown,
  145. (void **)&riPtr->obj);
  146. Tcl_CreateObjCommand(interp, "send", Tk_SendObjCmd,
  147. (ClientData)riPtr, CmdDeleteProc);
  148. if (Tcl_IsSafe(interp)) {
  149. Tcl_HideCommand(interp, "send", "send");
  150. }
  151. Tcl_SetAssocData(interp, "tkWinSend::ri", NULL, (ClientData)riPtr);
  152. } else {
  153. RevokeObjectRegistration(riPtr);
  154. }
  155. RegisterInterp(name, riPtr);
  156. return (CONST char *) riPtr->name;
  157. #endif /* TK_SEND_ENABLED_ON_WINDOWS */
  158. }
  159. /*
  160. *----------------------------------------------------------------------
  161. *
  162. * TkGetInterpNames --
  163. *
  164. * This function is invoked to fetch a list of all the interpreter names
  165. * currently registered for the display of a particular window.
  166. *
  167. * Results:
  168. * A standard Tcl return value. Interp->result will be set to hold a list
  169. * of all the interpreter names defined for tkwin's display. If an error
  170. * occurs, then TCL_ERROR is returned and interp->result will hold an
  171. * error message.
  172. *
  173. * Side effects:
  174. * None.
  175. *
  176. *----------------------------------------------------------------------
  177. */
  178. int
  179. TkGetInterpNames(
  180. Tcl_Interp *interp, /* Interpreter for returning a result. */
  181. Tk_Window tkwin) /* Window whose display is to be used for the
  182. * lookup. */
  183. {
  184. #ifndef TK_SEND_ENABLED_ON_WINDOWS
  185. /*
  186. * Temporarily disabled for bug #858822
  187. */
  188. return TCL_OK;
  189. #else /* TK_SEND_ENABLED_ON_WINDOWS */
  190. LPRUNNINGOBJECTTABLE pROT = NULL;
  191. LPCOLESTR oleszStub = TKWINSEND_REGISTRATION_BASE;
  192. HRESULT hr = S_OK;
  193. Tcl_Obj *objList = NULL;
  194. int result = TCL_OK;
  195. hr = GetRunningObjectTable(0, &pROT);
  196. if (SUCCEEDED(hr)) {
  197. IBindCtx* pBindCtx = NULL;
  198. objList = Tcl_NewListObj(0, NULL);
  199. hr = CreateBindCtx(0, &pBindCtx);
  200. if (SUCCEEDED(hr)) {
  201. IEnumMoniker* pEnum;
  202. hr = pROT->lpVtbl->EnumRunning(pROT, &pEnum);
  203. if (SUCCEEDED(hr)) {
  204. IMoniker* pmk = NULL;
  205. while (pEnum->lpVtbl->Next(pEnum, 1, &pmk, NULL) == S_OK) {
  206. LPOLESTR olestr;
  207. hr = pmk->lpVtbl->GetDisplayName(pmk, pBindCtx, NULL,
  208. &olestr);
  209. if (SUCCEEDED(hr)) {
  210. IMalloc *pMalloc = NULL;
  211. if (wcsncmp(olestr, oleszStub,
  212. wcslen(oleszStub)) == 0) {
  213. LPOLESTR p = olestr + wcslen(oleszStub);
  214. if (*p) {
  215. result = Tcl_ListObjAppendElement(interp,
  216. objList, Tcl_NewUnicodeObj(p + 1, -1));
  217. }
  218. }
  219. hr = CoGetMalloc(1, &pMalloc);
  220. if (SUCCEEDED(hr)) {
  221. pMalloc->lpVtbl->Free(pMalloc, (void*)olestr);
  222. pMalloc->lpVtbl->Release(pMalloc);
  223. }
  224. }
  225. pmk->lpVtbl->Release(pmk);
  226. }
  227. pEnum->lpVtbl->Release(pEnum);
  228. }
  229. pBindCtx->lpVtbl->Release(pBindCtx);
  230. }
  231. pROT->lpVtbl->Release(pROT);
  232. }
  233. if (FAILED(hr)) {
  234. /*
  235. * Expire the list if set.
  236. */
  237. if (objList != NULL) {
  238. Tcl_DecrRefCount(objList);
  239. }
  240. Tcl_SetObjResult(interp, Win32ErrorObj(hr));
  241. result = TCL_ERROR;
  242. }
  243. if (result == TCL_OK) {
  244. Tcl_SetObjResult(interp, objList);
  245. }
  246. return result;
  247. #endif /* TK_SEND_ENABLED_ON_WINDOWS */
  248. }
  249. /*
  250. *--------------------------------------------------------------
  251. *
  252. * Tk_SendCmd --
  253. *
  254. * This function is invoked to process the "send" Tcl command. See the
  255. * user documentation for details on what it does.
  256. *
  257. * Results:
  258. * A standard Tcl result.
  259. *
  260. * Side effects:
  261. * See the user documentation.
  262. *
  263. *--------------------------------------------------------------
  264. */
  265. int
  266. Tk_SendObjCmd(
  267. ClientData clientData, /* Information about sender (only dispPtr
  268. * field is used). */
  269. Tcl_Interp *interp, /* Current interpreter. */
  270. int objc, /* Number of arguments. */
  271. Tcl_Obj *CONST objv[]) /* Argument strings. */
  272. {
  273. enum {
  274. SEND_ASYNC, SEND_DISPLAYOF, SEND_LAST
  275. };
  276. static CONST char *sendOptions[] = {
  277. "-async", "-displayof", "--", NULL
  278. };
  279. int result = TCL_OK;
  280. int i, optind, async = 0;
  281. Tcl_Obj *displayPtr = NULL;
  282. /*
  283. * Process the command options.
  284. */
  285. for (i = 1; i < objc; i++) {
  286. if (Tcl_GetIndexFromObj(interp, objv[i], sendOptions,
  287. "option", 0, &optind) != TCL_OK) {
  288. break;
  289. }
  290. if (optind == SEND_ASYNC) {
  291. ++async;
  292. } else if (optind == SEND_DISPLAYOF) {
  293. displayPtr = objv[++i];
  294. } else if (optind == SEND_LAST) {
  295. i++;
  296. break;
  297. }
  298. }
  299. /*
  300. * Ensure we still have a valid command.
  301. */
  302. if ((objc - i) < 2) {
  303. Tcl_WrongNumArgs(interp, 1, objv,
  304. "?-async? ?-displayof? ?--? interpName arg ?arg ...?");
  305. result = TCL_ERROR;
  306. }
  307. /*
  308. * We don't support displayPtr. See TIP #150.
  309. */
  310. if (displayPtr) {
  311. Tcl_SetStringObj(Tcl_GetObjResult(interp),
  312. "option not implemented: \"displayof\" is not available "
  313. "for this platform.", -1);
  314. result = TCL_ERROR;
  315. }
  316. /*
  317. * Send the arguments to the foreign interp.
  318. */
  319. /* FIX ME: we need to check for local interp */
  320. if (result == TCL_OK) {
  321. LPDISPATCH pdisp;
  322. result = FindInterpreterObject(interp, Tcl_GetString(objv[i]), &pdisp);
  323. if (result == TCL_OK) {
  324. i++;
  325. result = Send(pdisp, interp, async, clientData, objc-i, objv+i);
  326. pdisp->lpVtbl->Release(pdisp);
  327. }
  328. }
  329. return result;
  330. }
  331. /*
  332. *--------------------------------------------------------------
  333. *
  334. * FindInterpreterObject --
  335. *
  336. * Search the set of objects currently registered with the Running Object
  337. * Table for one which matches the registered name. Tk objects are named
  338. * using BuildMoniker by always prefixing with TclEval.
  339. *
  340. * Results:
  341. * If a matching object registration is found, then the registered
  342. * IDispatch interface pointer is returned. If not, then an error message
  343. * is placed in the interpreter and TCL_ERROR is returned.
  344. *
  345. * Side effects:
  346. * None.
  347. *
  348. *--------------------------------------------------------------
  349. */
  350. static int
  351. FindInterpreterObject(
  352. Tcl_Interp *interp,
  353. CONST char *name,
  354. LPDISPATCH *ppdisp)
  355. {
  356. LPRUNNINGOBJECTTABLE pROT = NULL;
  357. int result = TCL_OK;
  358. HRESULT hr = GetRunningObjectTable(0, &pROT);
  359. if (SUCCEEDED(hr)) {
  360. IBindCtx* pBindCtx = NULL;
  361. hr = CreateBindCtx(0, &pBindCtx);
  362. if (SUCCEEDED(hr)) {
  363. LPMONIKER pmk = NULL;
  364. hr = BuildMoniker(name, &pmk);
  365. if (SUCCEEDED(hr)) {
  366. IUnknown* punkInterp = NULL;
  367. hr = pROT->lpVtbl->IsRunning(pROT, pmk);
  368. hr = pmk->lpVtbl->BindToObject(pmk, pBindCtx, NULL,
  369. &IID_IUnknown, (void**)&punkInterp);
  370. if (SUCCEEDED(hr)) {
  371. hr = punkInterp->lpVtbl->QueryInterface(punkInterp,
  372. &IID_IDispatch, (void**)ppdisp);
  373. punkInterp->lpVtbl->Release(punkInterp);
  374. } else {
  375. Tcl_ResetResult(interp);
  376. Tcl_AppendResult(interp,
  377. "no application named \"", name, "\"", NULL);
  378. result = TCL_ERROR;
  379. }
  380. pmk->lpVtbl->Release(pmk);
  381. }
  382. pBindCtx->lpVtbl->Release(pBindCtx);
  383. }
  384. pROT->lpVtbl->Release(pROT);
  385. }
  386. if (FAILED(hr) && result == TCL_OK) {
  387. Tcl_SetObjResult(interp, Win32ErrorObj(hr));
  388. result = TCL_ERROR;
  389. }
  390. return result;
  391. }
  392. /*
  393. *--------------------------------------------------------------
  394. *
  395. * CmdDeleteProc --
  396. *
  397. * This function is invoked by Tcl when the "send" command is deleted in
  398. * an interpreter. It unregisters the interpreter.
  399. *
  400. * Results:
  401. * None.
  402. *
  403. * Side effects:
  404. * The interpreter given by riPtr is unregistered, the registration
  405. * structure is free'd and the COM object unregistered and released.
  406. *
  407. *--------------------------------------------------------------
  408. */
  409. static void
  410. CmdDeleteProc(
  411. ClientData clientData)
  412. {
  413. RegisteredInterp *riPtr = (RegisteredInterp *)clientData;
  414. /*
  415. * Lock the package structure in memory.
  416. */
  417. Tcl_Preserve(clientData);
  418. /*
  419. * Revoke the ROT registration.
  420. */
  421. RevokeObjectRegistration(riPtr);
  422. /*
  423. * Release the registration object.
  424. */
  425. riPtr->obj->lpVtbl->Release(riPtr->obj);
  426. riPtr->obj = NULL;
  427. Tcl_DeleteAssocData(riPtr->interp, "tkWinSend::ri");
  428. /*
  429. * Unlock the package data structure.
  430. */
  431. Tcl_Release(clientData);
  432. ckfree(clientData);
  433. }
  434. /*
  435. *--------------------------------------------------------------
  436. *
  437. * RevokeObjectRegistration --
  438. *
  439. * Releases the interpreters registration object from the Running Object
  440. * Table.
  441. *
  442. * Results:
  443. * None.
  444. *
  445. * Side effects:
  446. * The stored cookie value is zeroed and the name is free'd and the
  447. * pointer set to NULL.
  448. *
  449. *--------------------------------------------------------------
  450. */
  451. static void
  452. RevokeObjectRegistration(
  453. RegisteredInterp *riPtr)
  454. {
  455. LPRUNNINGOBJECTTABLE pROT = NULL;
  456. HRESULT hr = S_OK;
  457. if (riPtr->cookie != 0) {
  458. hr = GetRunningObjectTable(0, &pROT);
  459. if (SUCCEEDED(hr)) {
  460. hr = pROT->lpVtbl->Revoke(pROT, riPtr->cookie);
  461. pROT->lpVtbl->Release(pROT);
  462. riPtr->cookie = 0;
  463. }
  464. }
  465. /*
  466. * Release the name storage.
  467. */
  468. if (riPtr->name != NULL) {
  469. free(riPtr->name);
  470. riPtr->name = NULL;
  471. }
  472. }
  473. /*
  474. * ----------------------------------------------------------------------
  475. *
  476. * InterpDeleteProc --
  477. *
  478. * This is called when the interpreter is deleted and used to unregister
  479. * the COM libraries.
  480. *
  481. * Results:
  482. * None.
  483. *
  484. * Side effects:
  485. * None.
  486. *
  487. * ----------------------------------------------------------------------
  488. */
  489. static void
  490. InterpDeleteProc(
  491. ClientData clientData,
  492. Tcl_Interp *interp)
  493. {
  494. CoUninitialize();
  495. }
  496. /*
  497. * ----------------------------------------------------------------------
  498. *
  499. * BuildMoniker --
  500. *
  501. * Construct a moniker from the given name. This ensures that all our
  502. * monikers have the same prefix.
  503. *
  504. * Results:
  505. * S_OK. If the name cannot be turned into a moniker then a COM error
  506. * code is returned.
  507. *
  508. * Side effects:
  509. * The moniker created is stored at the address given by ppmk.
  510. *
  511. * ----------------------------------------------------------------------
  512. */
  513. static HRESULT
  514. BuildMoniker(
  515. CONST char *name,
  516. LPMONIKER *ppmk)
  517. {
  518. LPMONIKER pmkClass = NULL;
  519. HRESULT hr = CreateFileMoniker(TKWINSEND_REGISTRATION_BASE, &pmkClass);
  520. if (SUCCEEDED(hr)) {
  521. LPMONIKER pmkItem = NULL;
  522. Tcl_DString dString;
  523. Tcl_DStringInit(&dString);
  524. Tcl_UtfToUniCharDString(name, -1, &dString);
  525. hr = CreateFileMoniker((LPOLESTR)Tcl_DStringValue(&dString), &pmkItem);
  526. Tcl_DStringFree(&dString);
  527. if (SUCCEEDED(hr)) {
  528. hr = pmkClass->lpVtbl->ComposeWith(pmkClass, pmkItem, FALSE, ppmk);
  529. pmkItem->lpVtbl->Release(pmkItem);
  530. }
  531. pmkClass->lpVtbl->Release(pmkClass);
  532. }
  533. return hr;
  534. }
  535. /*
  536. * ----------------------------------------------------------------------
  537. *
  538. * RegisterInterp --
  539. *
  540. * Attempts to register the provided name for this interpreter. If the
  541. * given name is already in use, then a numeric suffix is appended as
  542. * " #n" until we identify a unique name.
  543. *
  544. * Results:
  545. * Returns S_OK if successful, else a COM error code.
  546. *
  547. * Side effects:
  548. * Registration returns a cookie value which is stored. We also store a
  549. * copy of the name.
  550. *
  551. * ----------------------------------------------------------------------
  552. */
  553. static HRESULT
  554. RegisterInterp(
  555. CONST char *name,
  556. RegisteredInterp *riPtr)
  557. {
  558. HRESULT hr = S_OK;
  559. LPRUNNINGOBJECTTABLE pROT = NULL;
  560. LPMONIKER pmk = NULL;
  561. int i, offset;
  562. CONST char *actualName = name;
  563. Tcl_DString dString;
  564. Tcl_DStringInit(&dString);
  565. hr = GetRunningObjectTable(0, &pROT);
  566. if (SUCCEEDED(hr)) {
  567. offset = 0;
  568. for (i = 1; SUCCEEDED(hr); i++) {
  569. if (i > 1) {
  570. if (i == 2) {
  571. Tcl_DStringInit(&dString);
  572. Tcl_DStringAppend(&dString, name, -1);
  573. Tcl_DStringAppend(&dString, " #", 2);
  574. offset = Tcl_DStringLength(&dString);
  575. Tcl_DStringSetLength(&dString, offset+TCL_INTEGER_SPACE);
  576. actualName = Tcl_DStringValue(&dString);
  577. }
  578. sprintf(Tcl_DStringValue(&dString) + offset, "%d", i);
  579. }
  580. hr = BuildMoniker(actualName, &pmk);
  581. if (SUCCEEDED(hr)) {
  582. hr = pROT->lpVtbl->Register(pROT,
  583. ROTFLAGS_REGISTRATIONKEEPSALIVE,
  584. riPtr->obj, pmk, &riPtr->cookie);
  585. pmk->lpVtbl->Release(pmk);
  586. }
  587. if (hr == MK_S_MONIKERALREADYREGISTERED) {
  588. pROT->lpVtbl->Revoke(pROT, riPtr->cookie);
  589. } else if (hr == S_OK) {
  590. break;
  591. }
  592. }
  593. pROT->lpVtbl->Release(pROT);
  594. }
  595. if (SUCCEEDED(hr)) {
  596. riPtr->name = strdup(actualName);
  597. }
  598. Tcl_DStringFree(&dString);
  599. return hr;
  600. }
  601. /*
  602. * ----------------------------------------------------------------------
  603. *
  604. * Send --
  605. *
  606. * Perform an interface call to the server object. We convert the Tcl
  607. * arguments into a BSTR using 'concat'. The result should be a BSTR that
  608. * we can set as the interp's result string.
  609. *
  610. * Results:
  611. * None.
  612. *
  613. * Side effects:
  614. * None.
  615. *
  616. * ----------------------------------------------------------------------
  617. */
  618. static int
  619. Send(
  620. LPDISPATCH pdispInterp, /* Pointer to the remote interp's COM
  621. * object. */
  622. Tcl_Interp *interp, /* The local interpreter. */
  623. int async, /* Flag for the calling style. */
  624. ClientData clientData, /* The RegisteredInterp structure for this
  625. * interp. */
  626. int objc, /* Number of arguments to be sent. */
  627. Tcl_Obj *CONST objv[]) /* The arguments to be sent. */
  628. {
  629. VARIANT vCmd, vResult;
  630. DISPPARAMS dp;
  631. EXCEPINFO ei;
  632. UINT uiErr = 0;
  633. HRESULT hr = S_OK, ehr = S_OK;
  634. Tcl_Obj *cmd = NULL;
  635. DISPID dispid;
  636. cmd = Tcl_ConcatObj(objc, objv);
  637. /*
  638. * Setup the arguments for the COM method call.
  639. */
  640. VariantInit(&vCmd);
  641. VariantInit(&vResult);
  642. memset(&dp, 0, sizeof(dp));
  643. memset(&ei, 0, sizeof(ei));
  644. vCmd.vt = VT_BSTR;
  645. vCmd.bstrVal = SysAllocString(Tcl_GetUnicode(cmd));
  646. dp.cArgs = 1;
  647. dp.rgvarg = &vCmd;
  648. /*
  649. * Select the method to use based upon the async flag and call the method.
  650. */
  651. dispid = async ? TKWINSENDCOM_DISPID_ASYNC : TKWINSENDCOM_DISPID_SEND;
  652. hr = pdispInterp->lpVtbl->Invoke(pdispInterp, dispid,
  653. &IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD,
  654. &dp, &vResult, &ei, &uiErr);
  655. /*
  656. * Convert the result into a string and place in the interps result.
  657. */
  658. ehr = VariantChangeType(&vResult, &vResult, 0, VT_BSTR);
  659. if (SUCCEEDED(ehr)) {
  660. Tcl_SetObjResult(interp, Tcl_NewUnicodeObj(vResult.bstrVal, -1));
  661. }
  662. /*
  663. * Errors are returned as dispatch exceptions. If an error code was
  664. * returned then we decode the exception and setup the Tcl error
  665. * variables.
  666. */
  667. if (hr == DISP_E_EXCEPTION) {
  668. Tcl_Obj *opError, *opErrorCode, *opErrorInfo;
  669. if (ei.bstrSource != NULL) {
  670. int len;
  671. char *szErrorInfo;
  672. opError = Tcl_NewUnicodeObj(ei.bstrSource, -1);
  673. Tcl_ListObjIndex(interp, opError, 0, &opErrorCode);
  674. Tcl_SetObjErrorCode(interp, opErrorCode);
  675. Tcl_ListObjIndex(interp, opError, 1, &opErrorInfo);
  676. szErrorInfo = Tcl_GetStringFromObj(opErrorInfo, &len);
  677. Tcl_AddObjErrorInfo(interp, szErrorInfo, len);
  678. }
  679. }
  680. /*
  681. * Clean up any COM allocated resources.
  682. */
  683. SysFreeString(ei.bstrDescription);
  684. SysFreeString(ei.bstrSource);
  685. SysFreeString(ei.bstrHelpFile);
  686. VariantClear(&vCmd);
  687. return (SUCCEEDED(hr) ? TCL_OK : TCL_ERROR);
  688. }
  689. /*
  690. * ----------------------------------------------------------------------
  691. *
  692. * Win32ErrorObj --
  693. *
  694. * Returns a string object containing text from a COM or Win32 error code
  695. *
  696. * Results:
  697. * A Tcl_Obj containing the Win32 error message.
  698. *
  699. * Side effects:
  700. * Removed the error message from the COM threads error object.
  701. *
  702. * ----------------------------------------------------------------------
  703. */
  704. static Tcl_Obj*
  705. Win32ErrorObj(
  706. HRESULT hrError)
  707. {
  708. LPTSTR lpBuffer = NULL, p = NULL;
  709. TCHAR sBuffer[30];
  710. Tcl_Obj* errPtr = NULL;
  711. FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
  712. NULL, (DWORD)hrError, LANG_NEUTRAL,
  713. (LPTSTR)&lpBuffer, 0, NULL);
  714. if (lpBuffer == NULL) {
  715. lpBuffer = sBuffer;
  716. wsprintf(sBuffer, _T("Error Code: %08lX"), hrError);
  717. }
  718. if ((p = _tcsrchr(lpBuffer, _T('\r'))) != NULL) {
  719. *p = _T('\0');
  720. }
  721. #ifdef _UNICODE
  722. errPtr = Tcl_NewUnicodeObj(lpBuffer, (int)wcslen(lpBuffer));
  723. #else
  724. errPtr = Tcl_NewStringObj(lpBuffer, (int)strlen(lpBuffer));
  725. #endif
  726. if (lpBuffer != sBuffer) {
  727. LocalFree((HLOCAL)lpBuffer);
  728. }
  729. return errPtr;
  730. }
  731. /*
  732. * ----------------------------------------------------------------------
  733. *
  734. * SetErrorInfo --
  735. *
  736. * Convert the error information from a Tcl interpreter into a COM
  737. * exception structure. This information is then registered with the COM
  738. * thread exception object so that it can be used for rich error
  739. * reporting by COM clients.
  740. *
  741. * Results:
  742. * None.
  743. *
  744. * Side effects:
  745. * The current COM thread has its error object modified.
  746. *
  747. * ----------------------------------------------------------------------
  748. */
  749. void
  750. SetExcepInfo(
  751. Tcl_Interp* interp,
  752. EXCEPINFO *pExcepInfo)
  753. {
  754. if (pExcepInfo) {
  755. Tcl_Obj *opError, *opErrorInfo, *opErrorCode;
  756. ICreateErrorInfo *pCEI;
  757. IErrorInfo *pEI;
  758. HRESULT hr;
  759. opError = Tcl_GetObjResult(interp);
  760. opErrorInfo = Tcl_GetVar2Ex(interp, "errorInfo",NULL, TCL_GLOBAL_ONLY);
  761. opErrorCode = Tcl_GetVar2Ex(interp, "errorCode",NULL, TCL_GLOBAL_ONLY);
  762. if (Tcl_IsShared(opErrorCode)) {
  763. Tcl_Obj *ec = Tcl_DuplicateObj(opErrorCode);
  764. Tcl_IncrRefCount(ec);
  765. Tcl_DecrRefCount(opErrorCode);
  766. opErrorCode = ec;
  767. }
  768. Tcl_ListObjAppendElement(interp, opErrorCode, opErrorInfo);
  769. pExcepInfo->bstrDescription = SysAllocString(Tcl_GetUnicode(opError));
  770. pExcepInfo->bstrSource = SysAllocString(Tcl_GetUnicode(opErrorCode));
  771. pExcepInfo->scode = E_FAIL;
  772. hr = CreateErrorInfo(&pCEI);
  773. if (SUCCEEDED(hr)) {
  774. hr = pCEI->lpVtbl->SetGUID(pCEI, &IID_IDispatch);
  775. hr = pCEI->lpVtbl->SetDescription(pCEI,
  776. pExcepInfo->bstrDescription);
  777. hr = pCEI->lpVtbl->SetSource(pCEI, pExcepInfo->bstrSource);
  778. hr = pCEI->lpVtbl->QueryInterface(pCEI, &IID_IErrorInfo,
  779. (void**) &pEI);
  780. if (SUCCEEDED(hr)) {
  781. SetErrorInfo(0, pEI);
  782. pEI->lpVtbl->Release(pEI);
  783. }
  784. pCEI->lpVtbl->Release(pCEI);
  785. }
  786. }
  787. }
  788. /*
  789. * ----------------------------------------------------------------------
  790. *
  791. * TkWinSend_QueueCommand --
  792. *
  793. * Queue a script for asynchronous evaluation. This is called from the
  794. * COM objects Async method.
  795. *
  796. * Results:
  797. * None.
  798. *
  799. * Side effects:
  800. * None.
  801. *
  802. * ----------------------------------------------------------------------
  803. */
  804. int
  805. TkWinSend_QueueCommand(
  806. Tcl_Interp *interp,
  807. Tcl_Obj *cmdPtr)
  808. {
  809. SendEvent *evPtr;
  810. TRACE("SendQueueCommand()\n");
  811. evPtr = (SendEvent *)ckalloc(sizeof(SendEvent));
  812. evPtr->header.proc = SendEventProc;
  813. evPtr->header.nextPtr = NULL;
  814. evPtr->interp = interp;
  815. Tcl_Preserve(evPtr->interp);
  816. if (Tcl_IsShared(cmdPtr)) {
  817. evPtr->cmdPtr = Tcl_DuplicateObj(cmdPtr);
  818. } else {
  819. evPtr->cmdPtr = cmdPtr;
  820. Tcl_IncrRefCount(evPtr->cmdPtr);
  821. }
  822. Tcl_QueueEvent((Tcl_Event *)evPtr, TCL_QUEUE_TAIL);
  823. return 0;
  824. }
  825. /*
  826. * ----------------------------------------------------------------------
  827. *
  828. * SendEventProc --
  829. *
  830. * Handle a request for an asynchronous send. Nothing is returned to the
  831. * caller so the result is discarded.
  832. *
  833. * Results:
  834. * Returns 1 if the event was handled or 0 to indicate it has been
  835. * deferred.
  836. *
  837. * Side effects:
  838. * The target interpreter's result will be modified.
  839. *
  840. * ----------------------------------------------------------------------
  841. */
  842. static int
  843. SendEventProc(
  844. Tcl_Event *eventPtr,
  845. int flags)
  846. {
  847. int result = TCL_OK;
  848. SendEvent *evPtr = (SendEvent *)eventPtr;
  849. TRACE("SendEventProc\n");
  850. result = Tcl_EvalObjEx(evPtr->interp, evPtr->cmdPtr,
  851. TCL_EVAL_DIRECT | TCL_EVAL_GLOBAL);
  852. Tcl_DecrRefCount(evPtr->cmdPtr);
  853. Tcl_Release(evPtr->interp);
  854. return 1; /* 1 to indicate the event has been handled */
  855. }
  856. /*
  857. * ----------------------------------------------------------------------
  858. *
  859. * SendTrace --
  860. *
  861. * Provide trace information to the Windows debug stream. To use this -
  862. * use the TRACE macro, which compiles to nothing when DEBUG is not
  863. * defined.
  864. *
  865. * Results:
  866. * None.
  867. *
  868. * Side effects:
  869. * None.
  870. *
  871. * ----------------------------------------------------------------------
  872. */
  873. static void
  874. SendTrace(
  875. const char *format, ...)
  876. {
  877. va_list args;
  878. static char buffer[1024];
  879. va_start(args, format);
  880. _vsnprintf(buffer, 1023, format, args);
  881. OutputDebugString(buffer);
  882. va_end(args);
  883. }
  884. /*
  885. * Local Variables:
  886. * mode: c
  887. * c-basic-offset: 4
  888. * fill-column: 78
  889. * End:
  890. */