Leaked source code of windows server 2003
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.

526 lines
15 KiB

  1. /****************************** Module Header ******************************\
  2. * Module Name: DMGDDE.C
  3. *
  4. * This module contains functions used for interfacing with DDE structures
  5. * and such.
  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. UINT EmptyQueueTimerId = 0;
  14. /***************************** Private Function ****************************\
  15. * timeout()
  16. *
  17. * This routine creates a timer for hwndTimeout. It then runs a modal loop
  18. * which will exit once the pai->wTimeoutStatus word indicates things are
  19. * either done (TOS_DONE), aborted (TOS_ABORT), or the system is shutting
  20. * down (TOS_SHUTDOWN). A values of TOS_TICK is used to support timouts
  21. * >64K in length.
  22. *
  23. * Returns fSuccess, ie TRUE if TOS_DONE was received. before TOS_ABORT.
  24. *
  25. * PUBDOC START
  26. * Synchronous client transaction modal loops:
  27. *
  28. * During Synchronous transactions, a client application will enter a modal
  29. * loop while waiting for the server to respond to the request. If an
  30. * application wishes to filter messages to the modal loop, it may do so
  31. * by setting a message filter tied to MSGF_DDEMGR. Applications should
  32. * be aware however that the DDEMGR modal loop processes private messages
  33. * in the WM_USER range, WM_DDE messages, and WM_TIMER messages with timer IDs
  34. * using the TID_ constants defined in ddeml.h.
  35. * These messages must not be filtered by an application!!!
  36. *
  37. * PUBDOC END
  38. *
  39. * History:
  40. * Created sanfords 12/19/88
  41. \***************************************************************************/
  42. BOOL timeout(
  43. PAPPINFO pai,
  44. DWORD ulTimeout,
  45. HWND hwndTimeout)
  46. {
  47. MSG msg;
  48. PAPPINFO paiT;
  49. SEMENTER();
  50. /*
  51. * We check all instances in this task (thread) since we cannot let
  52. * one thread enter a modal loop two levels deep.
  53. */
  54. paiT = NULL;
  55. while (paiT = GetCurrentAppInfo(paiT)) {
  56. if (paiT->hwndTimer) {
  57. SETLASTERROR(pai, DMLERR_REENTRANCY);
  58. AssertF(FALSE, "Recursive timeout call");
  59. SEMLEAVE();
  60. return(FALSE);
  61. }
  62. }
  63. pai->hwndTimer = hwndTimeout;
  64. SEMLEAVE();
  65. if (!SetTimer(hwndTimeout, TID_TIMEOUT,
  66. ulTimeout > 0xffffL ? 0xffff : (WORD)ulTimeout, NULL)) {
  67. SETLASTERROR(pai, DMLERR_SYS_ERROR);
  68. return(FALSE);
  69. }
  70. if (ulTimeout < 0xffff0000) {
  71. ulTimeout += 0x00010000;
  72. }
  73. //
  74. // We use this instance-wide global variable to note timeouts so that
  75. // we don't need to rely on PostMessage() to work when faking timeouts.
  76. //
  77. do {
  78. ulTimeout -= 0x00010000;
  79. if (ulTimeout <= 0xffffL) {
  80. // the last timeout should be shorter than 0xffff
  81. SetTimer(hwndTimeout, TID_TIMEOUT, (WORD)ulTimeout, NULL);
  82. }
  83. pai->wTimeoutStatus = TOS_CLEAR;
  84. /*
  85. * stay in modal loop until a timeout happens.
  86. */
  87. while (pai->wTimeoutStatus == TOS_CLEAR) {
  88. if (!GetMessage(&msg, (HWND)NULL, 0, 0)) {
  89. /*
  90. * Somebody posted a WM_QUIT message - get out of this
  91. * timer loop and repost so main loop gets it. This
  92. * fixes a bug where some apps (petzolds ShowPop) use
  93. * rapid synchronous transactions which interfere with
  94. * their proper closing.
  95. */
  96. pai->wTimeoutStatus = TOS_ABORT;
  97. PostMessage(msg.hwnd, WM_QUIT, 0, 0);
  98. } else {
  99. if (!CallMsgFilter(&msg, MSGF_DDEMGR))
  100. DispatchMessage(&msg);
  101. }
  102. }
  103. } while (pai->wTimeoutStatus == TOS_TICK && HIWORD(ulTimeout));
  104. KillTimer(hwndTimeout, TID_TIMEOUT);
  105. //
  106. // remove any remaining timeout message in the queue.
  107. //
  108. while (PeekMessage(&msg, hwndTimeout, WM_TIMER, WM_TIMER,
  109. PM_NOYIELD | PM_REMOVE)) {
  110. if (msg.message == WM_QUIT) {
  111. /*
  112. * Windows BUG: This call will succeed on WM_QUIT messages!
  113. */
  114. PostQuitMessage(0);
  115. break;
  116. }
  117. }
  118. SEMENTER();
  119. pai->hwndTimer = 0;
  120. SEMLEAVE();
  121. /*
  122. * post a callback check incase we blocked callbacks due to being
  123. * in a timeout.
  124. */
  125. if (!PostMessage(pai->hwndDmg, UM_CHECKCBQ, 0, (DWORD)(LPSTR)pai)) {
  126. SETLASTERROR(pai, DMLERR_SYS_ERROR);
  127. }
  128. return(TRUE);
  129. }
  130. /***************************** Private Function ****************************\
  131. * Allocates global DDE memory and fills in first two words with fsStatus
  132. * and wFmt.
  133. *
  134. * History: created 6/15/90 rich gartland
  135. \***************************************************************************/
  136. HANDLE AllocDDESel(fsStatus, wFmt, cbData)
  137. WORD fsStatus;
  138. WORD wFmt;
  139. DWORD cbData;
  140. {
  141. HANDLE hMem = NULL;
  142. DDEDATA FAR * pMem;
  143. SEMENTER();
  144. if (!cbData)
  145. cbData++; // fixes GLOBALALLOC bug where 0 size object allocation fails
  146. if ((hMem = GLOBALALLOC(GMEM_DDESHARE, cbData))) {
  147. pMem = (DDEDATA FAR * )GLOBALPTR(hMem);
  148. *(WORD FAR * )pMem = fsStatus;
  149. pMem->cfFormat = wFmt;
  150. }
  151. SEMLEAVE();
  152. return(hMem);
  153. }
  154. /***************************** Private Function ****************************\
  155. * This routine institutes a callback directly if psi->fEnableCB is set
  156. * and calls QReply to complete the transaction,
  157. * otherwise it places the data into the queue for processing.
  158. *
  159. * Since hData may be freed by the app at any time once the callback is
  160. * issued, we cannot depend on it being there for QReply. Therefore we
  161. * save all the pertinant data in the queue along with it.
  162. *
  163. * Returns fSuccess.
  164. *
  165. * History:
  166. * Created 9/12/89 Sanfords
  167. \***************************************************************************/
  168. BOOL MakeCallback(
  169. PCOMMONINFO pcoi,
  170. HCONV hConv,
  171. HSZ hszTopic,
  172. HSZ hszItem,
  173. WORD wFmt,
  174. WORD wType,
  175. HDDEDATA hData,
  176. DWORD dwData1,
  177. DWORD dwData2,
  178. WORD msg,
  179. WORD fsStatus,
  180. HWND hwndPartner,
  181. HANDLE hMemFree,
  182. BOOL fQueueOnly)
  183. {
  184. PCBLI pcbli;
  185. SEMENTER();
  186. pcbli = (PCBLI)NewLstItem(pcoi->pai->plstCB, ILST_LAST);
  187. if (pcbli == NULL) {
  188. SETLASTERROR(pcoi->pai, DMLERR_MEMORY_ERROR);
  189. SEMLEAVE();
  190. return(FALSE);
  191. }
  192. pcbli->hConv = hConv;
  193. pcbli->hszTopic = hszTopic;
  194. pcbli->hszItem = hszItem;
  195. pcbli->wFmt = wFmt;
  196. pcbli->wType = wType;
  197. pcbli->hData = hData;
  198. pcbli->dwData1 = dwData1;
  199. pcbli->dwData2 = dwData2;
  200. pcbli->msg = msg;
  201. pcbli->fsStatus = fsStatus;
  202. pcbli->hwndPartner = hwndPartner;
  203. pcbli->hMemFree = hMemFree;
  204. pcbli->pai = pcoi->pai;
  205. pcbli->fQueueOnly = fQueueOnly;
  206. SEMLEAVE();
  207. if (!(pcoi->fs & ST_BLOCKED))
  208. if (!PostMessage(pcoi->pai->hwndDmg, UM_CHECKCBQ,
  209. 0, (DWORD)(LPSTR)pcoi->pai)) {
  210. SETLASTERROR(pcoi->pai, DMLERR_SYS_ERROR);
  211. }
  212. #ifdef DEBUG
  213. if (hMemFree) {
  214. LogDdeObject(0xB000, hMemFree);
  215. }
  216. #endif
  217. return(TRUE);
  218. }
  219. #define MAX_PMRETRIES 3
  220. //
  221. // This routine extends the size of the windows message queue by queueing
  222. // up failed posts on the sender side. This avoids the problems of full
  223. // client queues and of windows behavior of giving DDE messages priority.
  224. //
  225. BOOL PostDdeMessage(
  226. PCOMMONINFO pcoi, // senders COMMONINFO
  227. WORD msg,
  228. HWND hwndFrom, // == wParam
  229. LONG lParam,
  230. WORD msgAssoc,
  231. HGLOBAL hAssoc)
  232. {
  233. LPMQL pmql;
  234. PPMQI ppmqi;
  235. int cTries;
  236. HANDLE hTaskFrom, hTaskTo;
  237. HWND hwndTo;
  238. PQST pMQ;
  239. hwndTo = (HWND)pcoi->hConvPartner;
  240. if (!IsWindow(hwndTo)) {
  241. return(FALSE);
  242. }
  243. hTaskTo = GetWindowTask(hwndTo);
  244. /*
  245. * locate message overflow queue for our target task (pMQ)
  246. */
  247. for (pmql = gMessageQueueList; pmql; pmql = pmql->next) {
  248. if (pmql->hTaskTo == hTaskTo) {
  249. break;
  250. }
  251. }
  252. if (pmql != NULL) {
  253. pMQ = pmql->pMQ;
  254. } else {
  255. pMQ = NULL;
  256. }
  257. /*
  258. * See if any messages are already queued up
  259. */
  260. if (pMQ && pMQ->cItems) {
  261. if (msg == WM_DDE_TERMINATE) {
  262. /*
  263. * remove any non-terminate queued messages from us to them.
  264. */
  265. ppmqi = (PPMQI)FindNextQi(pMQ, NULL, FALSE);
  266. while (ppmqi) {
  267. FreeDdeMsgData(ppmqi->msg, ppmqi->lParam);
  268. FreeDdeMsgData(ppmqi->msgAssoc,
  269. MAKELPARAM(ppmqi->hAssoc, ppmqi->hAssoc));
  270. ppmqi = (PPMQI)FindNextQi(pMQ, (PQUEUEITEM)ppmqi,
  271. ppmqi->hwndTo == hwndTo &&
  272. ppmqi->wParam == hwndFrom);
  273. }
  274. pMQ = NULL; // so we just post it
  275. } else {
  276. // add the latest post attempt
  277. ppmqi = (PPMQI)Addqi(pMQ);
  278. if (ppmqi == NULL) {
  279. SETLASTERROR(pcoi->pai, DMLERR_MEMORY_ERROR);
  280. return(FALSE); // out of memory
  281. }
  282. ppmqi->hwndTo = hwndTo;
  283. ppmqi->msg = msg;
  284. ppmqi->wParam = hwndFrom;
  285. ppmqi->lParam = lParam;
  286. ppmqi->hAssoc = hAssoc;
  287. ppmqi->msgAssoc = msgAssoc;
  288. }
  289. }
  290. if (pMQ == NULL || pMQ->cItems == 0) {
  291. // just post the given message - no queue involved.
  292. cTries = 0;
  293. hTaskFrom = GetWindowTask(hwndFrom);
  294. while (!PostMessage(hwndTo, msg, hwndFrom, lParam)) {
  295. /*
  296. * we yielded so recheck target window
  297. */
  298. if (!IsWindow(hwndTo)) {
  299. return(FALSE);
  300. }
  301. /*
  302. * Give reciever a chance to clean out his queue
  303. */
  304. if (hTaskTo != hTaskFrom) {
  305. Yield();
  306. } else if (!(pcoi->pai->wFlags & AWF_INPOSTDDEMSG)) {
  307. MSG msgs;
  308. PAPPINFO pai;
  309. pcoi->pai->wFlags |= AWF_INPOSTDDEMSG;
  310. /*
  311. * Reciever is US!
  312. *
  313. * We need to empty our queue of stuff so we can post more
  314. * to ourselves.
  315. */
  316. while (PeekMessage((MSG FAR *)&msgs, NULL,
  317. WM_DDE_FIRST, WM_DDE_LAST, PM_REMOVE)) {
  318. DispatchMessage((MSG FAR *)&msgs);
  319. }
  320. /*
  321. * tell all instances in this task to process their
  322. * callbacks so we can clear our queue.
  323. */
  324. for (pai = pAppInfoList; pai != NULL; pai = pai->next) {
  325. if (pai->hTask == hTaskFrom) {
  326. CheckCBQ(pai);
  327. }
  328. }
  329. pcoi->pai->wFlags &= ~AWF_INPOSTDDEMSG;
  330. }
  331. if (cTries++ > MAX_PMRETRIES) {
  332. /*
  333. * relocate message overflow queue for our target task (pMQ)
  334. * We need to do this again because we gave up control
  335. * with the dispatch message and CheckCBQ calls.
  336. */
  337. for (pmql = gMessageQueueList; pmql; pmql = pmql->next) {
  338. if (pmql->hTaskTo == hTaskTo) {
  339. break;
  340. }
  341. }
  342. if (pmql == NULL) {
  343. /*
  344. * create and link in a new queue for the target task
  345. */
  346. pmql = (LPMQL)FarAllocMem(hheapDmg, sizeof(MQL));
  347. if (pmql == NULL) {
  348. SETLASTERROR(pcoi->pai, DMLERR_MEMORY_ERROR);
  349. return(FALSE);
  350. }
  351. pmql->pMQ = CreateQ(sizeof(PMQI));
  352. if (pmql->pMQ == NULL) {
  353. FarFreeMem(pmql);
  354. SETLASTERROR(pcoi->pai, DMLERR_MEMORY_ERROR);
  355. return(FALSE);
  356. }
  357. pmql->hTaskTo = hTaskTo;
  358. pmql->next = gMessageQueueList;
  359. gMessageQueueList = pmql;
  360. }
  361. pMQ = pmql->pMQ;
  362. ppmqi = (PPMQI)Addqi(pMQ);
  363. if (ppmqi == NULL) {
  364. SETLASTERROR(pcoi->pai, DMLERR_MEMORY_ERROR);
  365. return(FALSE); // out of memory
  366. }
  367. ppmqi->hwndTo = hwndTo;
  368. ppmqi->msg = msg;
  369. ppmqi->wParam = hwndFrom;
  370. ppmqi->lParam = lParam;
  371. ppmqi->hAssoc = hAssoc;
  372. ppmqi->msgAssoc = msgAssoc;
  373. return(TRUE);
  374. }
  375. }
  376. #ifdef DEBUG
  377. LogDdeObject(msg | 0x1000, lParam);
  378. if (msgAssoc) {
  379. LogDdeObject(msgAssoc | 0x9000, MAKELPARAM(hAssoc, hAssoc));
  380. }
  381. #endif
  382. return(TRUE);
  383. }
  384. // come here if the queue exists - empty it as far as we can.
  385. EmptyDDEPostQ();
  386. return(TRUE);
  387. }
  388. //
  389. // EmptyDDEPost
  390. //
  391. // This function checks the DDE post queue list and emptys it as far as
  392. // possible.
  393. //
  394. BOOL EmptyDDEPostQ()
  395. {
  396. PPMQI ppmqi;
  397. LPMQL pPMQL, pPMQLPrev;
  398. PQST pMQ;
  399. BOOL fMoreToDo = FALSE;
  400. pPMQLPrev = NULL;
  401. pPMQL = gMessageQueueList;
  402. while (pPMQL) {
  403. pMQ = pPMQL->pMQ;
  404. while (pMQ->cItems) {
  405. ppmqi = (PPMQI)Findqi(pMQ, QID_OLDEST);
  406. if (!PostMessage(ppmqi->hwndTo, ppmqi->msg, ppmqi->wParam, ppmqi->lParam)) {
  407. if (IsWindow(ppmqi->hwndTo)) {
  408. fMoreToDo = TRUE;
  409. break; // skip to next target queue
  410. } else {
  411. FreeDdeMsgData(ppmqi->msg, ppmqi->lParam);
  412. FreeDdeMsgData(ppmqi->msgAssoc,
  413. MAKELPARAM(ppmqi->hAssoc, ppmqi->hAssoc));
  414. }
  415. } else {
  416. #ifdef DEBUG
  417. LogDdeObject(ppmqi->msg | 0x2000, ppmqi->lParam);
  418. if (ppmqi->msgAssoc) {
  419. LogDdeObject(ppmqi->msgAssoc | 0xA000,
  420. MAKELPARAM(ppmqi->hAssoc, ppmqi->hAssoc));
  421. }
  422. #endif
  423. }
  424. Deleteqi(pMQ, QID_OLDEST);
  425. }
  426. if (pMQ->cItems == 0) {
  427. /*
  428. * Delete needless queue (selector)
  429. */
  430. DestroyQ(pMQ);
  431. if (pPMQLPrev) {
  432. pPMQLPrev->next = pPMQL->next;
  433. FarFreeMem(pPMQL);
  434. pPMQL = pPMQLPrev;
  435. } else {
  436. gMessageQueueList = gMessageQueueList->next;
  437. FarFreeMem(pPMQL);
  438. pPMQL = gMessageQueueList;
  439. continue;
  440. }
  441. }
  442. pPMQLPrev = pPMQL;
  443. pPMQL = pPMQL->next;
  444. }
  445. if (fMoreToDo & !EmptyQueueTimerId) {
  446. EmptyQueueTimerId = SetTimer(NULL, TID_EMPTYPOSTQ,
  447. TIMEOUT_QUEUECHECK, (TIMERPROC)EmptyQTimerProc);
  448. }
  449. return(fMoreToDo);
  450. }
  451. /*
  452. * Used to asynchronously check overflow message queues w/o using PostMessage()
  453. */
  454. void CALLBACK EmptyQTimerProc(
  455. HWND hwnd,
  456. UINT msg,
  457. UINT tid,
  458. DWORD dwTime)
  459. {
  460. KillTimer(NULL, EmptyQueueTimerId);
  461. EmptyQueueTimerId = 0;
  462. EmptyDDEPostQ();
  463. }