PageRenderTime 25ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 0ms

/brlcad/tags/rel-7-14-0/src/other/tk/win/tkWinSend.c

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