Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1358 lines
40 KiB

  1. /****************************** Module Header ******************************\
  2. *
  3. * Module Name: DMGWNDP.C
  4. *
  5. * This module contains all the window procs for the DDE manager.
  6. *
  7. * Created: 12/23/88 sanfords
  8. *
  9. * Copyright (c) 1988, 1989 Microsoft Corporation
  10. \***************************************************************************/
  11. #include "ddemlp.h"
  12. VOID FreeDdeMsgData(WORD msg, LPARAM lParam);
  13. /*
  14. * ----------------CLIENT SECTION------------------
  15. *
  16. * Each client conversation has associated with it a window and a queue.
  17. * A conversation has one synchronous transaction and may have many
  18. * asynchronous transactions. A transaction is differientiated by its
  19. * state and other pertinant data. A transaction may be synchronous,
  20. * asynchronous, (initiated by DdeMgrClientTransaction()), or it may be external,
  21. * (initiated by an advise loop.)
  22. *
  23. * A transaction is active if it is in the middle of tranfer, otherwise
  24. * it is shutdown. A shutdown transaction is either successful or
  25. * failed. When an asynchronous transaction shuts down, the client
  26. * is notified via the callback function. (XTYP_XACT_COMPLETE)
  27. *
  28. * The synchronous transaction, when active, is in a timeout loop which
  29. * can shut-down the transaction at the end of a predefined time period.
  30. * Shutdown synchronous transactions imediately transfer their information
  31. * to the client application by returning to DdeClientTransaction().
  32. *
  33. * active asynchronous transactions remain in the client queue until removed
  34. * by the client application via DdeAbandonTransaction() or by transaction
  35. * completion.
  36. *
  37. * external transactions take place when the client is in an advise
  38. * data loop. These transactions pass through the callback function to
  39. * the client to be accepted.(XTYP_ADVDATA)
  40. */
  41. /***************************** Private Function ****************************\
  42. * long EXPENTRY ClientWndProc(hwnd, msg, mp1, mp2);
  43. *
  44. * This window controls a single DDE conversation from the CLIENT side.
  45. * If closed, it will automaticly abort any conversationn in progress.
  46. * It maintains an internal list of any extra WM_DDEINITIATEACK messages
  47. * it receives so that it can be queried later about this information.
  48. * Any extra WM_DDEINITIATEACK messages comming in will be immediately
  49. * terminated.
  50. * It also maintains an internal list of all items which currently are
  51. * in active ADVISE loops.
  52. *
  53. * History:
  54. * Created 12/16/88 SANFORDS
  55. \***************************************************************************/
  56. LONG EXPENTRY ClientWndProc(hwnd, msg, wParam, lParam)
  57. HWND hwnd;
  58. WORD msg;
  59. WORD wParam;
  60. DWORD lParam;
  61. {
  62. register PCLIENTINFO pci;
  63. long mrData;
  64. #ifdef DEBUG
  65. LogDdeObject(msg | 0x4000, lParam);
  66. #endif
  67. pci = (PCLIENTINFO)GetWindowLong(hwnd, GWL_PCI);
  68. switch (msg) {
  69. case WM_CREATE:
  70. return(ClientCreate(hwnd, LPCREATESTRUCT_GETPAI(lParam)));
  71. break;
  72. case UM_SETBLOCK:
  73. pci->ci.fs = (pci->ci.fs & ~(ST_BLOCKED | ST_BLOCKNEXT)) | wParam;
  74. if (!wParam || wParam & ST_BLOCKNEXT) {
  75. EmptyDDEPostQ();
  76. }
  77. break;
  78. case WM_DDE_ACK:
  79. if (pci->ci.xad.state == XST_INIT1 || pci->ci.xad.state == XST_INIT2) {
  80. ClientInitAck(hwnd, pci, wParam, (ATOM)LOWORD(lParam),(ATOM)HIWORD(lParam));
  81. //
  82. // This always returns TRUE -NOT BECAUSE THAT'S WHAT THE PROTOCOL
  83. // CALLS FOR but because some bad sample code got out and so
  84. // a lot of apps out there will delete the WM_DDE_ACK atoms
  85. // if a FALSE is returned.
  86. //
  87. return(TRUE);
  88. } else {
  89. DoClientDDEmsg(pci, hwnd, msg, (HWND)wParam, lParam);
  90. return(0);
  91. }
  92. break;
  93. case WM_DDE_DATA:
  94. DoClientDDEmsg(pci, hwnd, msg, (HWND)wParam, lParam);
  95. break;
  96. case UM_QUERY:
  97. /*
  98. * wParam = info index.
  99. * lParam = pData. If pData==0, return data else copy into pData.
  100. */
  101. switch (wParam) {
  102. case Q_CLIENT:
  103. mrData = TRUE;
  104. break;
  105. case Q_APPINFO:
  106. mrData = (long)(LPSTR)pci->ci.pai;
  107. break;
  108. }
  109. if (lParam == 0)
  110. return(mrData);
  111. else
  112. *(long FAR *)lParam = mrData;
  113. return(1);
  114. break;
  115. case WM_DDE_TERMINATE:
  116. case UM_TERMINATE:
  117. Terminate(hwnd, wParam, pci);
  118. break;
  119. case WM_TIMER:
  120. if (wParam == TID_TIMEOUT) {
  121. pci->ci.pai->wTimeoutStatus |= TOS_TICK;
  122. }
  123. break;
  124. case UM_DISCONNECT:
  125. Disconnect(hwnd, wParam, pci);
  126. break;
  127. case WM_DESTROY:
  128. SEMCHECKOUT();
  129. if (pci->ci.fs & ST_CONNECTED) {
  130. pci->ci.fs &= ~ST_PERM2DIE; // stops infinite loop
  131. Disconnect(hwnd, 0, pci);
  132. }
  133. if (pci->ci.fs & ST_NOTIFYONDEATH) {
  134. HWND hwndOwner;
  135. hwndOwner = GetWindow(hwnd, GW_OWNER);
  136. if (hwndOwner)
  137. PostMessage(hwndOwner, UM_DISCONNECT, ST_IM_DEAD, 0L);
  138. }
  139. SEMENTER();
  140. DestroyQ(pci->pQ);
  141. pci->pQ = NULL;
  142. DestroyQ(pci->ci.pPMQ);
  143. pci->ci.pPMQ = NULL;
  144. CleanupAdvList(hwnd, pci);
  145. DestroyLst(pci->pClientAdvList);
  146. if (pci->ci.xad.state != XST_INIT1) {
  147. FreeHsz(LOWORD(pci->ci.hszSvcReq));
  148. FreeHsz(pci->ci.aServerApp);
  149. FreeHsz(pci->ci.aTopic);
  150. }
  151. /*
  152. * remove all plstCB entries that reference this window.
  153. */
  154. {
  155. PCBLI pli, pliNext;
  156. for (pli = (PCBLI)pci->ci.pai->plstCB->pItemFirst;
  157. pli != NULL;
  158. pli = (PCBLI)pliNext) {
  159. pliNext = (PCBLI)pli->next;
  160. if ((HWND)pli->hConv == hwnd) {
  161. if (((PCBLI)pli)->hMemFree) {
  162. GLOBALFREE(((PCBLI)pli)->hMemFree);
  163. }
  164. RemoveLstItem(pci->ci.pai->plstCB, (PLITEM)pli);
  165. }
  166. }
  167. }
  168. FarFreeMem((LPBYTE)pci);
  169. SEMLEAVE();
  170. // fall through
  171. default:
  172. return(DefWindowProc(hwnd, msg, wParam, lParam));
  173. break;
  174. }
  175. return(0);
  176. }
  177. /***************************** Private Function ****************************\
  178. * This handles client window processing of WM_DDE_ACK and WM_DDE_DATA msgs.
  179. * (Note that Acks to INITIATE messages are handled in ClientInitAck.)
  180. * On exit pddes is freed.
  181. *
  182. * History:
  183. * Created 9/1/89 Sanfords
  184. \***************************************************************************/
  185. BOOL DoClientDDEmsg(
  186. PCLIENTINFO pci,
  187. HWND hwndClient,
  188. WORD msg,
  189. HWND hwndServer,
  190. DWORD lParam)
  191. {
  192. PCQDATA pqd;
  193. int i;
  194. ATOM aItem;
  195. LAP far *lpLostAck;
  196. if (!(pci->ci.fs & ST_CONNECTED)) {
  197. FreeDdeMsgData(msg, lParam);
  198. return(FALSE);
  199. }
  200. /*
  201. * Check if it fits the synchronous transaction data
  202. */
  203. if (fExpectedMsg(&pci->ci.xad, lParam, msg)) {
  204. if (AdvanceXaction(hwndClient, pci, &pci->ci.xad, lParam, msg,
  205. &pci->ci.pai->LastError)) {
  206. if (pci->ci.pai->hwndTimer) {
  207. pci->ci.pai->wTimeoutStatus |= TOS_DONE;
  208. }
  209. }
  210. return TRUE;
  211. }
  212. /*
  213. * See if it fits any asynchronous transaction data - if any exist
  214. */
  215. if (pci->pQ != NULL && pci->pQ->pqiHead != NULL) {
  216. SEMENTER();
  217. pqd = (PCQDATA)pci->pQ->pqiHead;
  218. /*
  219. * cycle from oldest to newest.
  220. */
  221. for (i = pci->pQ->cItems; i; i--) {
  222. pqd = (PCQDATA)pqd->next;
  223. if (!fExpectedMsg(&pqd->xad, lParam, msg))
  224. continue;
  225. if (AdvanceXaction(hwndClient, pci, &pqd->xad, lParam, msg,
  226. &pqd->xad.LastError)) {
  227. ClientXferRespond(hwndClient, &pqd->xad, &pqd->xad.LastError);
  228. SEMLEAVE();
  229. pci->ci.pai->LastError = pqd->xad.LastError;
  230. if (!pqd->xad.fAbandoned) {
  231. MakeCallback(&pci->ci, MAKEHCONV(hwndClient), (HSZ)pci->ci.aTopic,
  232. pqd->xad.pXferInfo->hszItem, pqd->xad.pXferInfo->wFmt,
  233. XTYP_XACT_COMPLETE, pqd->xad.pdata,
  234. MAKEID(pqd), (DWORD)pqd->xad.DDEflags, 0, 0, hwndServer,
  235. 0, FALSE);
  236. }
  237. return TRUE;
  238. }
  239. SEMLEAVE();
  240. return FALSE;
  241. }
  242. SEMLEAVE();
  243. }
  244. /*
  245. * It doesn't fit anything, assume its an advise data message.
  246. */
  247. if (msg == WM_DDE_DATA) {
  248. DDE_DATA FAR *pMem;
  249. PADVLI padvli;
  250. WORD wStatus;
  251. WORD wFmt;
  252. aItem = HIWORD(lParam);
  253. if (LOWORD(lParam)) {
  254. pMem = (DDE_DATA FAR*)GLOBALLOCK((HANDLE)LOWORD(lParam));
  255. if (pMem == NULL) {
  256. SETLASTERROR(pci->ci.pai, DMLERR_MEMORY_ERROR);
  257. return(FALSE);
  258. }
  259. wFmt = pMem->wFmt;
  260. wStatus = pMem->wStatus;
  261. GLOBALUNLOCK((HANDLE)LOWORD(lParam));
  262. } else {
  263. padvli = FindAdvList(pci->pClientAdvList, 0, 0, aItem, 0);
  264. if (padvli != NULL) {
  265. wFmt = padvli->wFmt;
  266. } else {
  267. wFmt = 0;
  268. }
  269. wStatus = DDE_FACK;
  270. }
  271. if (wStatus & DDE_FREQUESTED) {
  272. // Its out of line - drop it.
  273. if (wStatus & DDE_FACKREQ) {
  274. // ACK it
  275. PostDdeMessage(&pci->ci, WM_DDE_ACK, hwndClient,
  276. MAKELONG(DDE_FACK, aItem), 0, 0);
  277. }
  278. FreeDDEData((HANDLE)LOWORD(lParam), wFmt);
  279. if (aItem)
  280. GlobalDeleteAtom(aItem);
  281. return FALSE;
  282. }
  283. MakeCallback(&pci->ci, MAKEHCONV(hwndClient), (HSZ)pci->ci.aTopic,
  284. (HSZ)aItem,
  285. wFmt,
  286. XTYP_ADVDATA,
  287. RecvPrep(pci->ci.pai, LOWORD(lParam), HDATA_NOAPPFREE),
  288. 0, 0, msg, pMem ? wStatus : 0, (HWND)pci->ci.hConvPartner, 0, FALSE);
  289. return TRUE;
  290. }
  291. AssertF(pci->ci.xad.state != XST_INIT1 && pci->ci.xad.state != XST_INIT2,
  292. "Init logic problem");
  293. AssertF(msg == WM_DDE_ACK, "DoClientDDEMsg() logic problem");
  294. /*
  295. * throw it away ... first find the lost ack in in the lost ack pile
  296. */
  297. if (lpLostAck = (LAP far *)FindPileItem(pLostAckPile, CmpWORD,
  298. PHMEM(lParam), FPI_DELETE)) {
  299. if (lpLostAck->type == XTYP_EXECUTE) {
  300. GLOBALFREE((HANDLE)HIWORD(lParam));
  301. } else {
  302. if (HIWORD(lParam)) {
  303. GlobalDeleteAtom(HIWORD(lParam)); // message copy
  304. }
  305. }
  306. } else {
  307. AssertF(FALSE, "DoClientDDEmsg: could not find lost ack");
  308. // its a fairly safe assumption we didn't get a random execute ACK
  309. // back so free the atom.
  310. if (HIWORD(lParam)) {
  311. GlobalDeleteAtom(HIWORD(lParam)); // message copy
  312. }
  313. }
  314. return FALSE;
  315. }
  316. /***************************** Private Function ****************************\
  317. * This routine matches a conversation transaction with a DDE message. If
  318. * the state, wType, format, itemname dde structure data and the message
  319. * received all agree, TRUE is returned. It only handles DATA or ACK messages.
  320. *
  321. * History:
  322. * Created 9/1/89 Sanfords
  323. \***************************************************************************/
  324. BOOL fExpectedMsg(
  325. PXADATA pXad,
  326. DWORD lParam,
  327. WORD msg)
  328. {
  329. DDEDATA FAR *pMem;
  330. if (msg == WM_DDE_DATA) {
  331. BOOL fRet;
  332. if (pXad->state != XST_REQSENT)
  333. return(FALSE);
  334. if (!(pMem = (DDEDATA FAR*)GLOBALLOCK(LOWORD(lParam))))
  335. return(FALSE);
  336. /* make sure the format and item name match */
  337. fRet = pMem->fResponse &&
  338. ((WORD)pMem->cfFormat == pXad->pXferInfo->wFmt) &&
  339. (HIWORD(lParam) == LOWORD(pXad->pXferInfo->hszItem));
  340. GLOBALUNLOCK(LOWORD(lParam));
  341. return(fRet);
  342. }
  343. switch (pXad->state) {
  344. case XST_REQSENT:
  345. case XST_POKESENT:
  346. case XST_ADVSENT:
  347. case XST_UNADVSENT:
  348. return((msg == WM_DDE_ACK) &&
  349. HIWORD(lParam) == LOWORD(pXad->pXferInfo->hszItem));
  350. break;
  351. case XST_EXECSENT:
  352. /* we expect an ACK with a data handle matching that sent */
  353. return((msg == WM_DDE_ACK) &&
  354. (HIWORD(lParam) == HIWORD(pXad->pXferInfo->hDataClient)));
  355. break;
  356. }
  357. return(FALSE);
  358. }
  359. /***************************** Private Function ****************************\
  360. * This function assumes that msg is an apropriate message for the transaction
  361. * referenced by pXad. It acts on msg as apropriate. pddes is the DDESTRUCT
  362. * associated with msg.
  363. *
  364. * Returns fSuccess ie: transaction is ready to close up.
  365. *
  366. * History:
  367. * Created 9/1/89 Sanfords
  368. \***************************************************************************/
  369. BOOL AdvanceXaction(hwnd, pci, pXad, lParam, msg, pErr)
  370. HWND hwnd;
  371. PCLIENTINFO pci;
  372. PXADATA pXad;
  373. DWORD lParam;
  374. WORD msg;
  375. LPWORD pErr;
  376. {
  377. HANDLE hData;
  378. LPSTR pMem;
  379. WORD lo,hi;
  380. pXad->DDEflags = 0;
  381. lo = LOWORD(lParam);
  382. hi = HIWORD(lParam);
  383. switch (msg) {
  384. case WM_DDE_ACK:
  385. if (pXad->state == XST_EXECSENT || !(lo & DDE_FACK))
  386. FreeDataHandle(pci->ci.pai, pXad->pXferInfo->hDataClient, TRUE);
  387. if (pXad->pXferInfo->pulResult != NULL)
  388. *(LPWORD)pXad->pXferInfo->pulResult = lo;
  389. switch (pXad->state) {
  390. case XST_ADVSENT:
  391. case XST_EXECSENT:
  392. case XST_POKESENT:
  393. case XST_REQSENT:
  394. case XST_UNADVSENT:
  395. if (lo & DDE_FACK) {
  396. /*
  397. * handle successes
  398. */
  399. switch (pXad->state) {
  400. case XST_POKESENT:
  401. pXad->state = XST_POKEACKRCVD;
  402. break;
  403. case XST_EXECSENT:
  404. pXad->state = XST_EXECACKRCVD;
  405. break;
  406. case XST_ADVSENT:
  407. pXad->state = XST_ADVACKRCVD;
  408. break;
  409. case XST_UNADVSENT:
  410. pXad->state = XST_UNADVACKRCVD;
  411. break;
  412. case XST_REQSENT:
  413. /*
  414. * requests are not expected to send a +ACK. only
  415. * -ACK or data. We ignore a +ACK to a request.
  416. */
  417. return(FALSE);
  418. }
  419. } else { // NACK
  420. /*
  421. * handle the expected ACK failures.
  422. */
  423. hData = (HANDLE)HIWORD(pXad->pXferInfo->hDataClient);
  424. *pErr = DMLERR_NOTPROCESSED;
  425. if (lo & DDE_FBUSY)
  426. *pErr = DMLERR_BUSY;
  427. switch (pXad->state) {
  428. case XST_POKESENT:
  429. /* free the hData sent with original message */
  430. /* but only if fRelease was set */
  431. pMem = GLOBALLOCK(hData);
  432. /* we stowed the handle in lo word */
  433. if (pMem && ((DDEPOKE FAR*)pMem)->fRelease)
  434. FreeDDEData(hData, ((DDEPOKE FAR*)pMem)->cfFormat);
  435. break;
  436. case XST_ADVSENT:
  437. /* free the hOptions sent with original message */
  438. /* we stowed the handle in hDataClient */
  439. GLOBALFREE(hData);
  440. break;
  441. }
  442. pXad->state = XST_INCOMPLETE;
  443. }
  444. }
  445. return(TRUE);
  446. break;
  447. case WM_DDE_DATA:
  448. switch (pXad->state) {
  449. case XST_REQSENT:
  450. case XST_ADVSENT:
  451. pXad->state = XST_DATARCVD;
  452. /*
  453. * send an ack if requested - we dare not return the given
  454. * lParam because it may be a data item sent to several
  455. * clients and we would mess up the fsStatus word for
  456. * all processes involved.
  457. */
  458. pMem = GLOBALLOCK((HANDLE)lo);
  459. if (pMem != NULL) {
  460. if (!((DDEDATA FAR*)pMem)->fRelease) {
  461. //
  462. // Since this is potentially a synchronous request which
  463. // the app has to free, we must give the app a copy
  464. // so the origonal one can safely be freed by the server.
  465. //
  466. pXad->pdata = CopyHDDEDATA(pci->ci.pai, MAKELONG(0, lo));
  467. } else {
  468. pXad->pdata = RecvPrep(pci->ci.pai, lo, 0);
  469. }
  470. if (((DDEDATA FAR*)pMem)->fAckReq) {
  471. // reuse atom from data message
  472. PostDdeMessage(&pci->ci, WM_DDE_ACK, hwnd, MAKELONG(DDE_FACK, hi), 0, 0);
  473. } else {
  474. if (hi) {
  475. GlobalDeleteAtom(hi); // free message copy
  476. }
  477. }
  478. }
  479. return(TRUE);
  480. break;
  481. }
  482. }
  483. return(FALSE);
  484. }
  485. VOID CheckCBQ(
  486. PAPPINFO pai)
  487. {
  488. PCBLI pli, pliNext;
  489. PCLIENTINFO pci;
  490. BOOL fBreak;
  491. DWORD dwRet;
  492. /*
  493. * This is where we actually do callbacks. We do them via this
  494. * window proc so that we can asynchronously institute callbacks
  495. * via a PostMsg().
  496. */
  497. SEMCHECKOUT();
  498. SEMENTER();
  499. /*
  500. * process all enabled conversation callbacks.
  501. */
  502. fBreak = FALSE;
  503. for (pli = (PCBLI)pai->plstCB->pItemFirst;
  504. pai->lpMemReserve && !fBreak && pli; pli = (PCBLI)pliNext) {
  505. pliNext = (PCBLI)pli->next;
  506. if (pai->cInProcess) // covers us on recursion
  507. break;
  508. // auto-flush for dead conversations.
  509. if (!IsWindow((HWND)pli->hConv) ||
  510. ((pci = (PCLIENTINFO)GetWindowLong((HWND)pli->hConv, GWL_PCI)) == NULL) ||
  511. !(pci->ci.fs & ST_CONNECTED)) {
  512. /*
  513. * auto-flush for disconnected conversations.
  514. */
  515. if (((PCBLI)pli)->hMemFree) {
  516. GLOBALFREE(((PCBLI)pli)->hMemFree);
  517. }
  518. RemoveLstItem(pai->plstCB, (PLITEM)pli);
  519. continue;
  520. }
  521. if (pci->ci.fs & ST_BLOCKED)
  522. continue;
  523. if (pci->ci.fs & ST_BLOCKNEXT) {
  524. pci->ci.fs |= ST_BLOCKED;
  525. pci->ci.fs &= ~ST_BLOCKNEXT;
  526. }
  527. if (pli->fQueueOnly) {
  528. dwRet = 0;
  529. #ifdef DEBUG
  530. if (pli->hMemFree) {
  531. LogDdeObject(0xE000, pli->hMemFree);
  532. }
  533. #endif
  534. } else {
  535. /*
  536. * make the actual callback here.
  537. */
  538. #ifdef DEBUG
  539. if (pli->hMemFree) {
  540. LogDdeObject(0xD000, pli->hMemFree);
  541. }
  542. #endif
  543. dwRet = DoCallback(pai, pli->hConv, pli->hszTopic,
  544. pli->hszItem, pli->wFmt, pli->wType, pli->hData,
  545. pli->dwData1, pli->dwData2);
  546. }
  547. /*
  548. * If the callback resulted in a BLOCK, disable this conversation.
  549. */
  550. if (dwRet == CBR_BLOCK && !(pli->wType & XTYPF_NOBLOCK)) {
  551. pci->ci.fs |= ST_BLOCKED;
  552. continue;
  553. } else {
  554. /*
  555. * otherwise finish processing the callback.
  556. */
  557. QReply(pli, dwRet);
  558. RemoveLstItem(pai->plstCB, (PLITEM)pli);
  559. }
  560. }
  561. SEMLEAVE();
  562. }
  563. /*
  564. * This function handles disconnecting a conversation window. afCmd contains
  565. * ST_ flags that describe what actions to take.
  566. */
  567. void Disconnect(
  568. HWND hwnd,
  569. WORD afCmd,
  570. PCLIENTINFO pci)
  571. {
  572. if (afCmd & ST_CHECKPARTNER) {
  573. if (!IsWindow((HWND)pci->ci.hConvPartner)) {
  574. if (pci->ci.fs & ST_TERM_WAITING) {
  575. pci->ci.fs &= ~ST_TERM_WAITING;
  576. pci->ci.pai->cZombies--;
  577. TRACETERM((szT, "Disconnect: Checked partner is dead. Zombies decremented.\n"));
  578. pci->ci.fs |= ST_TERMINATED;
  579. }
  580. }
  581. afCmd &= ~ST_CHECKPARTNER;
  582. }
  583. // Do NOT do disconnects within timeout loops!
  584. if (pci->ci.pai->hwndTimer == hwnd) {
  585. pci->ci.pai->wTimeoutStatus |= TOS_ABORT;
  586. pci->ci.fs |= ST_DISC_ATTEMPTED;
  587. TRACETERM((szT, "Disconnect: defering disconnect of %x. Aborting timeout loop.\n",
  588. hwnd));
  589. return;
  590. }
  591. /*
  592. * note disconnect call for ddespy apps
  593. */
  594. MONCONN(pci->ci.pai, pci->ci.aServerApp, pci->ci.aTopic,
  595. ((pci->ci.fs & ST_CLIENT) ? hwnd : (HWND)pci->ci.hConvPartner),
  596. ((pci->ci.fs & ST_CLIENT) ? (HWND)pci->ci.hConvPartner : hwnd),
  597. FALSE);
  598. /*
  599. * or in optional ST_PERM2DIE bit from caller
  600. */
  601. pci->ci.fs |= afCmd;
  602. /*
  603. * Terminate states fall through the following stages:
  604. *
  605. * 1) connected, not_waiting(for ack term)
  606. * 2) disconnected, waiting
  607. * 3) disconnected, not_waiting, terminated
  608. * 4) disconnected, not_waiting, terminated, perm to die
  609. * 5) self destruct window
  610. *
  611. * If the disconnect operation was originated by the other side:
  612. *
  613. * 1) connected, not_waiting
  614. * 2) disconected, not_waiting, terminated
  615. * 3) disconnected, not_waiting, teriminated perm to die
  616. * 4) self desstruct window
  617. *
  618. * Note that a postmessage may fail for 2 reasons:
  619. * 1) the partner window is dead - in which case we just dispense
  620. * with terminates altogether and pretend we are terminated.
  621. * 2) the target queue is full. This won't happen on NT but
  622. * could on win31. PostDdeMessage handles this case by
  623. * queueing the outgoing message on our side and continuing
  624. * to try to get it posted and hangs around for the ACK.
  625. * This function can only fail if the target window died or
  626. * we ran out of memory. In either case, we have to punt on
  627. * terminates and consider ourselves disconnected and terminated.
  628. *
  629. * When we do get into a state where we are waiting for a
  630. * terminate, we increment our zombie count. This gets
  631. * decremented when either we get the expected terminate or
  632. * our partner window dies/postmessage fails.
  633. */
  634. if (pci->ci.fs & ST_CONNECTED) {
  635. if (pci->ci.fs & ST_CLIENT) {
  636. AbandonTransaction(hwnd, pci->ci.pai, NULL, FALSE);
  637. }
  638. CleanupAdvList(hwnd, pci);
  639. pci->ci.fs &= ~ST_CONNECTED;
  640. if (PostDdeMessage(&pci->ci, WM_DDE_TERMINATE, hwnd, 0L, 0, 0)) {
  641. if (!(pci->ci.fs & ST_TERM_WAITING)) {
  642. pci->ci.fs |= ST_TERM_WAITING;
  643. pci->ci.pai->cZombies++;
  644. TRACETERM((szT, "cZombies incremented..."));
  645. }
  646. TRACETERM((szT,
  647. "Disconnect: Posted Terminate(%x->%x)\n",
  648. hwnd, (HWND)pci->ci.hConvPartner,
  649. ((LPAPPINFO)pci->ci.pai)->cZombies));
  650. } else {
  651. pci->ci.fs |= ST_TERMINATED;
  652. if (pci->ci.fs & ST_TERM_WAITING) {
  653. pci->ci.fs &= ~ST_TERM_WAITING;
  654. pci->ci.pai->cZombies--;
  655. TRACETERM((szT, "cZombies decremented..."));
  656. }
  657. TRACETERM((szT,
  658. "Disconnect: Terminate post(%x->%x) failed.\n",
  659. hwnd,
  660. (HWND)pci->ci.hConvPartner));
  661. }
  662. pci->ci.xad.state = XST_NULL;
  663. }
  664. TRACETERM((szT,
  665. "Disconnect: cZombies=%d[%x:%x].\n",
  666. pci->ci.pai->cZombies,
  667. HIWORD(&((LPAPPINFO)pci->ci.pai)->cZombies),
  668. LOWORD(&((LPAPPINFO)pci->ci.pai)->cZombies)));
  669. /*
  670. * Self destruction is only allowed when we have permission to die,
  671. * are disconnected, are not waiting for a terminate, and are
  672. * terminated.
  673. */
  674. if ((pci->ci.fs & (ST_CONNECTED | ST_PERM2DIE | ST_TERMINATED | ST_TERM_WAITING)) ==
  675. (ST_PERM2DIE | ST_TERMINATED)) {
  676. DestroyWindow(hwnd);
  677. TRACETERM((szT, "Disconnect: Destroying %x.\n", hwnd));
  678. }
  679. }
  680. /*
  681. * This function handles WM_DDE_TERMINATE processing for a conversation window.
  682. */
  683. void Terminate(
  684. HWND hwnd,
  685. HWND hwndFrom,
  686. PCLIENTINFO pci)
  687. {
  688. SEMCHECKOUT();
  689. /*
  690. * Only accept terminates from whom we are talking to. Anything else
  691. * is noise.
  692. */
  693. if (hwndFrom != (HWND)pci->ci.hConvPartner) {
  694. // bogus extra-ack terminate - ignore
  695. TRACETERM((szT, "Terminate: %x is ignoring terminate from %x. Partner should be %x!\n",
  696. hwnd, hwndFrom, (HWND)pci->ci.hConvPartner));
  697. return;
  698. }
  699. /*
  700. * If we are in a timeout loop, cancel it first. We will come back
  701. * here when we recieve our self-posted terminate message.
  702. */
  703. if (pci->ci.pai->hwndTimer == hwnd) {
  704. pci->ci.pai->wTimeoutStatus |= TOS_ABORT;
  705. PostMessage(hwnd, UM_TERMINATE, hwndFrom, 0);
  706. TRACETERM((szT, "Terminate: Canceling timeout loop for %x.\n",
  707. pci->ci.pai));
  708. return;
  709. }
  710. if (pci->ci.fs & ST_CONNECTED) {
  711. /*
  712. * unexpected/initial external terminate case
  713. */
  714. if (pci->ci.fs & ST_CLIENT) {
  715. /*
  716. * Abandon any async transactions that may be in progress
  717. * on this conversation.
  718. */
  719. AbandonTransaction(hwnd, pci->ci.pai, NULL, FALSE);
  720. }
  721. /*
  722. * Make any remaining queued up callbacks first.
  723. */
  724. CheckCBQ(pci->ci.pai);
  725. pci->ci.fs &= ~ST_CONNECTED;
  726. pci->ci.fs |= ST_TERMINATED | ST_PERM2DIE;
  727. TRACETERM((szT, "Terminate: received in connected state.(%x<-%x), fs=%x\n",
  728. hwnd, (HWND)pci->ci.hConvPartner, pci->ci.fs));
  729. MONCONN(pci->ci.pai, pci->ci.aServerApp, pci->ci.aTopic,
  730. (pci->ci.fs & ST_CLIENT) ? hwnd : (HWND)pci->ci.hConvPartner,
  731. (pci->ci.fs & ST_CLIENT) ? (HWND)pci->ci.hConvPartner : hwnd, FALSE);
  732. if (PostDdeMessage(&pci->ci, WM_DDE_TERMINATE, hwnd, 0L, 0, 0)) {
  733. TRACETERM((szT, "Terminate: Posting ack terminate(%x->%x).\n",
  734. hwnd, (HWND)pci->ci.hConvPartner));
  735. } else {
  736. TRACETERM((szT, "Terminate: Posting ack terminate(%x->%x) failed.\n",
  737. hwnd, (HWND)pci->ci.hConvPartner));
  738. }
  739. DoCallback(pci->ci.pai, MAKEHCONV(hwnd), 0, 0, 0, XTYP_DISCONNECT, 0L, 0L,
  740. pci->ci.fs & ST_ISSELF ? 1 : 0);
  741. pci->ci.xad.state = XST_NULL;
  742. CleanupAdvList(hwnd, pci);
  743. }
  744. if (pci->ci.fs & ST_TERM_WAITING) {
  745. pci->ci.fs &= ~ST_TERM_WAITING;
  746. pci->ci.pai->cZombies--;
  747. TRACETERM((szT, "cZombies decremented..."));
  748. /*
  749. * expected external terminate case
  750. */
  751. TRACETERM((szT, "Terminate: Received ack terminate(%x<-%x), cZombies=%d[%x:%x].\n",
  752. hwnd, (HWND)pci->ci.hConvPartner,
  753. ((LPAPPINFO)pci->ci.pai)->cZombies,
  754. HIWORD(&((LPAPPINFO)pci->ci.pai)->cZombies),
  755. LOWORD(&((LPAPPINFO)pci->ci.pai)->cZombies)));
  756. }
  757. pci->ci.fs |= ST_TERMINATED;
  758. if (pci->ci.fs & ST_PERM2DIE) {
  759. DestroyWindow(hwnd);
  760. TRACETERM((szT, "Terminate: Destroying %x.\n", hwnd));
  761. }
  762. }
  763. /*
  764. * ----------------------------SERVER SECTION--------------------------------
  765. */
  766. /***************************** Public Function ****************************\
  767. * long EXPENTRY ServerWndProc(hwnd, msg, mp1, mp2)
  768. * HWND hwnd;
  769. * WORD msg;
  770. * MPARAM mp1;
  771. * MPARAM mp2;
  772. *
  773. * DESCRIPTION:
  774. * This processes DDE conversations from the server end.
  775. * It stores internal information and acts much like a state machine.
  776. * If closed, it will automaticly abort any conversation in progress.
  777. * It also maintains an internal list of all items which currently are
  778. * in active ADVISE loops.
  779. * PUBDOC START
  780. * These server windows have the feature that a conversation can be
  781. * re-initiated with them by a client. The Client merely terminates
  782. * the conversation and then re-initiates by using a SendMsg to this
  783. * window. This allows a client to change the topic of the conversation
  784. * or to pass the conversation on to another client window without
  785. * loosing the server it initiated with. This is quite useful for
  786. * wild initiates.
  787. * PUBDOC END
  788. *
  789. * History:
  790. * 10/18/89 sanfords Added hack to make hszItem==0L when offszItem==offabData.
  791. * 1/4/89 sanfords created
  792. \***************************************************************************/
  793. long EXPENTRY ServerWndProc(hwnd, msg, wParam, lParam)
  794. HWND hwnd;
  795. WORD msg;
  796. WORD wParam;
  797. DWORD lParam;
  798. {
  799. register PSERVERINFO psi;
  800. long mrData;
  801. HDDEDATA hData = 0L;
  802. WORD wFmt = 0;
  803. WORD wStat = 0;
  804. #ifdef DEBUG
  805. LogDdeObject(msg | 0x8000, lParam);
  806. #endif
  807. psi = (PSERVERINFO)GetWindowLong(hwnd, GWL_PCI);
  808. switch (msg) {
  809. case WM_DDE_REQUEST:
  810. case WM_DDE_ACK:
  811. case WM_DDE_ADVISE:
  812. case WM_DDE_UNADVISE:
  813. case WM_DDE_POKE:
  814. case WM_DDE_EXECUTE:
  815. ServerProcessDDEMsg(psi, msg, hwnd, (HWND)wParam, LOWORD(lParam), HIWORD(lParam));
  816. return(0);
  817. }
  818. switch (msg) {
  819. case WM_CREATE:
  820. return(ServerCreate(hwnd, LPCREATESTRUCT_GETPAI(lParam)));
  821. break;
  822. case UM_SETBLOCK:
  823. psi->ci.fs = (psi->ci.fs & ~(ST_BLOCKED | ST_BLOCKNEXT)) | wParam;
  824. if (!wParam || wParam & ST_BLOCKNEXT) {
  825. EmptyDDEPostQ();
  826. }
  827. break;
  828. case UMSR_CHGPARTNER:
  829. psi->ci.hConvPartner = MAKEHCONV(wParam);
  830. break;
  831. case UM_QUERY:
  832. /*
  833. * wParam = info index.
  834. * lParam = pData. If pData==0, return data else copy into pData.
  835. */
  836. switch (wParam) {
  837. case Q_CLIENT:
  838. mrData = FALSE;
  839. break;
  840. case Q_APPINFO:
  841. mrData = (long)(LPSTR)psi->ci.pai;
  842. break;
  843. }
  844. if (lParam == 0)
  845. return(mrData);
  846. else
  847. *(long FAR *)lParam = mrData;
  848. return(1);
  849. break;
  850. case WM_DDE_TERMINATE:
  851. case UM_TERMINATE:
  852. Terminate(hwnd, (HWND)wParam, (PCLIENTINFO)psi);
  853. break;
  854. case UM_DISCONNECT:
  855. Disconnect(hwnd, wParam, (PCLIENTINFO)psi);
  856. break;
  857. case WM_TIMER:
  858. if (wParam == TID_TIMEOUT) {
  859. psi->ci.pai->wTimeoutStatus |= TOS_TICK;
  860. }
  861. break;
  862. case WM_DESTROY:
  863. SEMCHECKOUT();
  864. /*
  865. * Send ourselves a terminate and free local data.
  866. */
  867. if (psi->ci.fs & ST_CONNECTED) {
  868. psi->ci.fs &= ~ST_PERM2DIE; // stops infinite loop
  869. Disconnect(hwnd, 0, (PCLIENTINFO)psi);
  870. }
  871. if (psi->ci.fs & ST_NOTIFYONDEATH) {
  872. PostMessage(psi->ci.pai->hwndSvrRoot, UM_DISCONNECT, ST_IM_DEAD, 0L);
  873. }
  874. SEMENTER();
  875. CleanupAdvList(hwnd, (PCLIENTINFO)psi);
  876. FreeHsz(psi->ci.aServerApp);
  877. FreeHsz(LOWORD(psi->ci.hszSvcReq));
  878. FreeHsz(psi->ci.aTopic);
  879. DestroyQ(psi->ci.pPMQ);
  880. psi->ci.pPMQ = NULL;
  881. /*
  882. * remove all plstCB entries that reference this window.
  883. */
  884. {
  885. PCBLI pli, pliNext;
  886. for (pli = (PCBLI)psi->ci.pai->plstCB->pItemFirst;
  887. pli != NULL;
  888. pli = (PCBLI)pliNext) {
  889. pliNext = (PCBLI)pli->next;
  890. if ((HWND)pli->hConv == hwnd) {
  891. if (((PCBLI)pli)->hMemFree) {
  892. GLOBALFREE(((PCBLI)pli)->hMemFree);
  893. }
  894. RemoveLstItem(psi->ci.pai->plstCB, (PLITEM)pli);
  895. }
  896. }
  897. }
  898. FarFreeMem((LPBYTE)psi);
  899. SEMLEAVE();
  900. // fall through
  901. default:
  902. return(DefWindowProc(hwnd, msg, wParam, lParam));
  903. break;
  904. }
  905. return(0);
  906. }
  907. /*
  908. * ----------------FRAME SECTION------------------
  909. *
  910. * A frame window exists on behalf of every registered thread. It
  911. * handles conversation initiation and therefore issues callbacks
  912. * to the server app as needed to notify or query the server app.
  913. * The callback queue is always bypassed for these synchronous
  914. * events.
  915. */
  916. /***************************** Private Function ****************************\
  917. * long EXPENTRY subframeWndProc(hwnd, msg, mp1, mp2)
  918. * HWND hwnd;
  919. * WORD msg;
  920. * MPARAM mp1;
  921. * MPARAM mp2;
  922. *
  923. * This routine takes care of setting up server windows as needed to respond
  924. * to incomming WM_DDE_INTIIATE messages. It is subclassed from the top
  925. * level frame of the server application.
  926. *
  927. * History: created 12/20/88 sanfords
  928. \***************************************************************************/
  929. long EXPENTRY subframeWndProc(hwnd, msg, wParam, lParam)
  930. HWND hwnd;
  931. WORD msg;
  932. WORD wParam;
  933. DWORD lParam;
  934. {
  935. switch (msg) {
  936. case WM_CREATE:
  937. SetWindowWord(hwnd, GWW_PAI, (WORD)LPCREATESTRUCT_GETPAI(lParam));
  938. break;
  939. case WM_DDE_INITIATE:
  940. ServerFrameInitConv((PAPPINFO)GetWindowWord(hwnd, GWW_PAI), hwnd, (HWND)wParam, LOWORD(lParam), HIWORD(lParam));
  941. break;
  942. default:
  943. return(DefWindowProc(hwnd, msg, wParam, lParam));
  944. break;
  945. }
  946. }
  947. /*
  948. * This version only goes one level deep
  949. */
  950. VOID ChildMsg(
  951. HWND hwndParent,
  952. WORD msg,
  953. WORD wParam,
  954. DWORD lParam,
  955. BOOL fPost)
  956. {
  957. register HWND hwnd;
  958. register HWND hwndNext;
  959. if (!IsWindow(hwndParent)) {
  960. return;
  961. }
  962. if (!(hwnd = GetWindow(hwndParent, GW_CHILD)))
  963. return;
  964. do {
  965. // incase hwnd goes away during send or post
  966. hwndNext = GetWindow(hwnd, GW_HWNDNEXT);
  967. if (fPost) {
  968. PostMessage(hwnd, msg, wParam, lParam);
  969. } else {
  970. SendMessage(hwnd, msg, wParam, lParam);
  971. }
  972. hwnd = hwndNext;
  973. } while (hwnd);
  974. }
  975. /*
  976. * main application window - parent of all others in app.
  977. */
  978. long EXPENTRY DmgWndProc(hwnd, msg, wParam, lParam)
  979. HWND hwnd;
  980. WORD msg;
  981. WORD wParam;
  982. DWORD lParam;
  983. {
  984. #define pai ((PAPPINFO)lParam)
  985. hwnd;
  986. wParam;
  987. switch (msg) {
  988. case WM_CREATE:
  989. SetWindowWord(hwnd, GWW_PAI, (WORD)LPCREATESTRUCT_GETPAI(lParam));
  990. break;
  991. case UM_REGISTER:
  992. case UM_UNREGISTER:
  993. return(ProcessRegistrationMessage(hwnd, msg, wParam, lParam));
  994. break;
  995. case UM_FIXHEAP:
  996. {
  997. // lParam = pai;
  998. PCBLI pcbli; // current point of search
  999. PCBLI pcbliStart; // search start point
  1000. PCBLI pcbliNextStart; // next search start point
  1001. if (pai->cInProcess) {
  1002. // repost and wait till this is clear.
  1003. PostMessage(hwnd, UM_FIXHEAP, 0, lParam);
  1004. return(0);
  1005. }
  1006. /*
  1007. * We are probably in an impossible situation here - the callback queue
  1008. * is likely stuffed full of advise data callbacks because the
  1009. * server data changes are outrunning the client's ability to
  1010. * process them.
  1011. *
  1012. * Here we attempt to remove all duplicated advise data callbacks from
  1013. * the queue leaving only the most recent entries. We assume we can
  1014. * do this now because we are not InProcess and this is in response
  1015. * to a post message.
  1016. */
  1017. SEMENTER();
  1018. pcbliStart = (PCBLI)pai->plstCB->pItemFirst;
  1019. do {
  1020. while (pcbliStart && pcbliStart->wType != XTYP_ADVDATA) {
  1021. pcbliStart = (PCBLI)pcbliStart->next;
  1022. }
  1023. if (!pcbliStart) {
  1024. break;
  1025. }
  1026. pcbli = (PCBLI)pcbliStart->next;
  1027. if (!pcbli) {
  1028. break;
  1029. }
  1030. while (pcbli) {
  1031. if (pcbli->wType == XTYP_ADVDATA &&
  1032. pcbli->hConv == pcbliStart->hConv &&
  1033. pcbli->hszItem == pcbliStart->hszItem &&
  1034. pcbli->wFmt == pcbliStart->wFmt) {
  1035. // Match, remove older copy
  1036. QReply(pcbliStart, DDE_FBUSY);
  1037. pcbliNextStart = (PCBLI)pcbliStart->next;
  1038. RemoveLstItem(pai->plstCB, (PLITEM)pcbliStart);
  1039. pcbliStart = pcbliNextStart;
  1040. break;
  1041. } else {
  1042. pcbli = (PCBLI)pcbli->next;
  1043. }
  1044. }
  1045. if (!pcbli) {
  1046. pcbliStart = (PCBLI)pcbliStart->next;
  1047. }
  1048. } while (TRUE);
  1049. if (pai->lpMemReserve == NULL) {
  1050. pai->lpMemReserve = FarAllocMem(pai->hheapApp, CB_RESERVE);
  1051. }
  1052. SEMLEAVE();
  1053. }
  1054. // Fall Through...
  1055. case UM_CHECKCBQ:
  1056. CheckCBQ(pai);
  1057. return(0);
  1058. break;
  1059. default:
  1060. DefWindowProc(hwnd, msg, wParam, lParam);
  1061. break;
  1062. }
  1063. #undef pai
  1064. }
  1065. // this proc creates a window that only hangs around till its children are
  1066. // all gone and its been given the ok to go.
  1067. long EXPENTRY ConvListWndProc(hwnd, msg, wParam, lParam)
  1068. HWND hwnd;
  1069. WORD msg;
  1070. WORD wParam;
  1071. DWORD lParam;
  1072. {
  1073. switch (msg) {
  1074. case WM_CREATE:
  1075. SetWindowWord(hwnd, GWW_STATE, 0);
  1076. SetWindowWord(hwnd, GWW_CHECKVAL, ++hwInst);
  1077. if (((LPCREATESTRUCT)lParam)->lpCreateParams) {
  1078. SetWindowWord(hwnd, GWW_PAI, (WORD)LPCREATESTRUCT_GETPAI(lParam));
  1079. }
  1080. else {
  1081. SetWindowWord(hwnd, GWW_PAI, 0);
  1082. }
  1083. break;
  1084. case UM_SETBLOCK:
  1085. ChildMsg(hwnd, UM_SETBLOCK, wParam, lParam, FALSE);
  1086. break;
  1087. case UM_DISCONNECT:
  1088. switch (wParam) {
  1089. case ST_PERM2DIE:
  1090. SetWindowWord(hwnd, GWW_STATE, ST_PERM2DIE);
  1091. ChildMsg(hwnd, UM_DISCONNECT, ST_PERM2DIE | ST_NOTIFYONDEATH, 0L, FALSE);
  1092. case ST_IM_DEAD:
  1093. if (GetWindowWord(hwnd, GWW_STATE) == ST_PERM2DIE &&
  1094. !GetWindow(hwnd, GW_CHILD)) {
  1095. DestroyWindow(hwnd);
  1096. }
  1097. break;
  1098. }
  1099. break;
  1100. default:
  1101. return(DefWindowProc(hwnd, msg, wParam, lParam));
  1102. break;
  1103. }
  1104. }
  1105. HDDEDATA DoCallback(
  1106. PAPPINFO pai,
  1107. HCONV hConv,
  1108. HSZ hszTopic,
  1109. HSZ hszItem,
  1110. WORD wFmt,
  1111. WORD wType,
  1112. HDDEDATA hData,
  1113. DWORD dwData1,
  1114. DWORD dwData2)
  1115. {
  1116. HDDEDATA dwRet, dwT;
  1117. EXTDATAINFO edi;
  1118. SEMCHECKIN();
  1119. /*
  1120. * in case we somehow call this before initialization is complete.
  1121. */
  1122. if (pai == NULL || pai->pfnCallback == NULL)
  1123. return(0);
  1124. /*
  1125. * skip callbacks filtered out.
  1126. */
  1127. if (aulmapType[(wType & XTYP_MASK) >> XTYP_SHIFT] & pai->afCmd) {
  1128. /*
  1129. * filtered.
  1130. */
  1131. return(0);
  1132. }
  1133. /*
  1134. * neuter callbacks once in DdeUninitiate() mode. (No data in or out)
  1135. */
  1136. if (pai->wFlags & AWF_UNINITCALLED &&
  1137. wType & (XCLASS_DATA | XCLASS_FLAGS)) {
  1138. return(0);
  1139. }
  1140. if (hData) { // map ingoing data handle
  1141. edi.pai = pai;
  1142. edi.hData = hData | HDATA_NOAPPFREE;
  1143. hData = (HDDEDATA)(LPSTR)&edi;
  1144. }
  1145. pai->cInProcess++;
  1146. TRACEAPIIN((szT, "DDEMLCallback(%hx, %hx, %x, %x, %x, %x, %x, %x)\n",
  1147. wType, wFmt, hConv, hszTopic, hszItem, hData, dwData1, dwData2));
  1148. SEMLEAVE();
  1149. dwRet = (*pai->pfnCallback)
  1150. (wType, wFmt, hConv, hszTopic, hszItem, hData, dwData1, dwData2);
  1151. TRACEAPIOUT((szT, "DDEMLCallback:%x\n", dwRet));
  1152. if (cMonitor && wType != XTYP_MONITOR) {
  1153. // Copy the hData otherwise we SendMsg a pointer to stuff on the stack
  1154. // which doesn't work too well if we're running from a 32bit app!
  1155. if (hData) {
  1156. LPBYTE pDataNew = GLOBALPTR(GLOBALALLOC(GPTR, sizeof(edi)));
  1157. if (pDataNew) {
  1158. hmemcpy(pDataNew, &edi, sizeof(edi));
  1159. MonBrdcastCB(pai, wType, wFmt, hConv, hszTopic, hszItem, (HDDEDATA)pDataNew,
  1160. dwData1, dwData2, dwRet);
  1161. GLOBALFREE((HGLOBAL)HIWORD(pDataNew));
  1162. }
  1163. }
  1164. else {
  1165. MonBrdcastCB(pai, wType, wFmt, hConv, hszTopic, hszItem, hData,
  1166. dwData1, dwData2, dwRet);
  1167. }
  1168. }
  1169. SEMENTER();
  1170. pai->cInProcess--;
  1171. // unmap outcomming data handle.
  1172. if (dwRet && wType & XCLASS_DATA && dwRet != CBR_BLOCK) {
  1173. dwT = (HDDEDATA)((LPEXTDATAINFO)dwRet)->hData;
  1174. if (!(LOWORD(dwT) & HDATA_APPOWNED)) {
  1175. FarFreeMem((LPSTR)dwRet);
  1176. }
  1177. dwRet = dwT;
  1178. }
  1179. return(dwRet);
  1180. }