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.

767 lines
20 KiB

  1. //
  2. // COM.C
  3. // Utility functions
  4. //
  5. // Copyright(c) Microsoft 1997-
  6. //
  7. #include <as16.h>
  8. //
  9. // PostMessageNoFail()
  10. // This makes sure posted messages don't get lost on Win95 due to the fixed
  11. // interrupt queue. Conveniently, USER exports PostPostedMessages for
  12. // KERNEL to flush the queue, so we call that before calling PostMessage().
  13. //
  14. void PostMessageNoFail(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  15. {
  16. PostPostedMessages();
  17. PostMessage(hwnd, uMsg, wParam, lParam);
  18. }
  19. //
  20. // AnsiToUni()
  21. //
  22. // UniToAnsi() is conveniently exported by krnl386. However, we need
  23. // AnsiToUni() conversions also so we can make sure we end up where we
  24. // started. This one we have to roll our own thunk for
  25. //
  26. int AnsiToUni
  27. (
  28. LPSTR lpAnsi,
  29. int cchAnsi,
  30. LPWSTR lpUni,
  31. int cchUni
  32. )
  33. {
  34. DWORD dwMask;
  35. LONG lReturn;
  36. DebugEntry(AnsiToUni);
  37. ASSERT(g_lpfnAnsiToUni);
  38. ASSERT(SELECTOROF(lpAnsi));
  39. ASSERT(SELECTOROF(lpUni));
  40. //
  41. // Set up the mask. These are the params:
  42. // 0 -- CodePage (CP_ACP, which is 0L)
  43. // 1 -- Flags (0L)
  44. // 2 -- lpAnsi POINTER
  45. // 3 -- cchAnsi
  46. // 4 -- lpUni POINTER
  47. // 5 -- cchUni
  48. //
  49. //
  50. dwMask = (1L << 2) | (1L << 4);
  51. //
  52. // Take the win16lock an extra time; this API will release it, and we
  53. // can't yield in the middle of a GDI call.
  54. //
  55. _EnterWin16Lock();
  56. lReturn = CallProcEx32W(6, dwMask, (DWORD)g_lpfnAnsiToUni, 0L, 0L, lpAnsi,
  57. (DWORD)(UINT)cchAnsi, lpUni, (DWORD)(UINT)cchUni);
  58. _LeaveWin16Lock();
  59. DebugExitDWORD(AnsiToUni, (DWORD)lReturn);
  60. return((int)lReturn);
  61. }
  62. //
  63. // PATCHING CODE
  64. //
  65. //
  66. // Get32BitOnlyExport16()
  67. //
  68. // This function gets hold of a 16:16 function address that isn't exported
  69. // but is called via a flat thunk from the exported 32-bit version. We use
  70. // this for GDI and USER functions that are handy.
  71. //
  72. // This code assumes that the 32-bit routine looks like the following:
  73. // <DEBUG>
  74. // 68 dwStr32 Push string
  75. // E8 dwOffsetOut Call trace out
  76. // <DEBUG AND RETAIL>
  77. // B1 bIndex Put offset in thunk table into cl
  78. // OR
  79. // 66 B9 wIndex Put offset in thunk table into cx
  80. //
  81. // EB bOffset Jmp to common flat thunk routine
  82. // OR
  83. // 66 ED wOffset Jmp word to common flat thunk routine
  84. // OR
  85. // prolog of common flat thunk routine
  86. //
  87. BOOL GetGdi32OnlyExport
  88. (
  89. LPSTR lpszExport32,
  90. UINT cbJmpOffset,
  91. FARPROC FAR* lplpfn16
  92. )
  93. {
  94. BOOL rc;
  95. DebugEntry(GetGdi32OnlyExport);
  96. rc = Get32BitOnlyExport(GetProcAddress32(g_hInstGdi32, lpszExport32),
  97. cbJmpOffset, FT_GdiFThkThkConnectionData, lplpfn16);
  98. DebugExitBOOL(GetGdi32OnlyExport, rc);
  99. return(rc);
  100. }
  101. BOOL GetUser32OnlyExport
  102. (
  103. LPSTR lpszExport32,
  104. FARPROC FAR* lplpfn16
  105. )
  106. {
  107. BOOL rc;
  108. DebugEntry(GetUser32OnlyExport);
  109. rc = Get32BitOnlyExport(GetProcAddress32(g_hInstUser32, lpszExport32),
  110. 0, FT_UsrFThkThkConnectionData, lplpfn16);
  111. DebugExitBOOL(GetUser32OnlyExport, rc);
  112. return(rc);
  113. }
  114. BOOL Get32BitOnlyExport
  115. (
  116. DWORD dwfn32,
  117. UINT cbJmpOffset,
  118. LPDWORD lpThunkTable,
  119. FARPROC FAR* lplpfn16
  120. )
  121. {
  122. LPBYTE lpfn32;
  123. UINT offsetThunk;
  124. DebugEntry(Get32BitOnlyExport);
  125. ASSERT(lplpfn16);
  126. *lplpfn16 = NULL;
  127. //
  128. // The thunk table pointer points to two DWORDs. The first is a
  129. // checksum signature. The second is the pointer to the target
  130. // function array.
  131. //
  132. ASSERT(!IsBadReadPtr(lpThunkTable, 2*sizeof(DWORD)));
  133. lpThunkTable = (LPDWORD)lpThunkTable[1];
  134. ASSERT(!IsBadReadPtr(lpThunkTable, sizeof(DWORD)));
  135. //
  136. // Get 16:16 pointer to export
  137. //
  138. lpfn32 = NULL;
  139. if (!dwfn32)
  140. {
  141. ERROR_OUT(("Missing 32-bit export"));
  142. DC_QUIT;
  143. }
  144. lpfn32 = MapLS((LPVOID)dwfn32);
  145. if (!SELECTOROF(lpfn32))
  146. {
  147. ERROR_OUT(("Out of selectors"));
  148. DC_QUIT;
  149. }
  150. //
  151. // Was a jmp offset passed in? If so, decode the instruction there.
  152. // It should be a jmp <dword offset>. Figure out what EIP would be
  153. // if we jumped there. That's the place the flat thunk occurs.
  154. // Currently, only PolyPolyline needs this.
  155. //
  156. if (cbJmpOffset)
  157. {
  158. if (IsBadReadPtr(lpfn32, cbJmpOffset+5) ||
  159. (lpfn32[cbJmpOffset] != OPCODE32_JUMP4))
  160. {
  161. ERROR_OUT(("Can't read 32-bit export"));
  162. DC_QUIT;
  163. }
  164. //
  165. // Add dword at cbJmpOffset+1, add this number to (lpfn32+cbJmpOffset+
  166. // 5), which is the EIP of the next instruction after the jump. This
  167. // produces the EIP of the real thunk stub.
  168. //
  169. dwfn32 += cbJmpOffset + 5 + *(LPDWORD)(lpfn32+cbJmpOffset+1);
  170. UnMapLS(lpfn32);
  171. lpfn32 = MapLS((LPVOID)dwfn32);
  172. if (!SELECTOROF(lpfn32))
  173. {
  174. ERROR_OUT(("Out of selectors"));
  175. DC_QUIT;
  176. }
  177. }
  178. //
  179. // Verify that we can read 13 bytes. The reason this will won't go
  180. // past the end in a legitimate case is that this thunklet is either
  181. // followed by the large # of bytes in the common flat thunk routine,
  182. // or by another thunklet
  183. //
  184. if (IsBadReadPtr(lpfn32, 13))
  185. {
  186. ERROR_OUT(("Can't read code in 32-bit export"));
  187. DC_QUIT;
  188. }
  189. //
  190. // Does this have the 10-byte DEBUG prolog?
  191. //
  192. if (*lpfn32 == OPCODE32_PUSH)
  193. {
  194. // Yes, skip it
  195. lpfn32 += 5;
  196. // Make sure that next thing is a call
  197. if (*lpfn32 != OPCODE32_CALL)
  198. {
  199. ERROR_OUT(("Can't read code in 32-bit export"));
  200. DC_QUIT;
  201. }
  202. lpfn32 += 5;
  203. }
  204. //
  205. // This should either be mov cl, byte or mov cx, word
  206. //
  207. if (*lpfn32 == OPCODE32_MOVCL)
  208. {
  209. offsetThunk = *(lpfn32+1);
  210. }
  211. else if (*((LPWORD)lpfn32) == OPCODE32_MOVCX)
  212. {
  213. //
  214. // NOTE: Even though this is a CX offset, the thunk code only
  215. // looks at the low BYTE
  216. //
  217. offsetThunk = *(lpfn32+2);
  218. }
  219. else
  220. {
  221. ERROR_OUT(("Can't read code in 32-bit export"));
  222. DC_QUIT;
  223. }
  224. //
  225. // Now, can we read this value?
  226. //
  227. if (IsBadReadPtr(lpThunkTable+offsetThunk, sizeof(DWORD)) ||
  228. IsBadCodePtr((FARPROC)lpThunkTable[offsetThunk]))
  229. {
  230. ERROR_OUT(("Can't read thunk table entry"));
  231. DC_QUIT;
  232. }
  233. *lplpfn16 = (FARPROC)lpThunkTable[offsetThunk];
  234. DC_EXIT_POINT:
  235. if (SELECTOROF(lpfn32))
  236. {
  237. UnMapLS(lpfn32);
  238. }
  239. DebugExitBOOL(Get32BitOnlyExport16, (*lplpfn16 != NULL));
  240. return(*lplpfn16 != NULL);
  241. }
  242. //
  243. // CreateFnPatch()
  244. // This sets things up to be able to quickly patch/unpatch a system routine.
  245. // The patch is not originally enabled.
  246. //
  247. UINT CreateFnPatch
  248. (
  249. LPVOID lpfnToPatch,
  250. LPVOID lpfnJumpTo,
  251. LPFN_PATCH lpbPatch,
  252. UINT uCodeAlias
  253. )
  254. {
  255. SEGINFO segInfo;
  256. UINT ib;
  257. DebugEntry(CreateFnPatch);
  258. ASSERT(lpbPatch->lpCodeAlias == NULL);
  259. ASSERT(lpbPatch->wSegOrg == 0);
  260. //
  261. // Do NOT call IsBadReadPtr() here, that will cause the segment, if
  262. // not present, to get pulled in, and will masks problems in the debug
  263. // build that will show up in retail.
  264. //
  265. // Fortunately, PrestoChangoSelector() will set the linear address and
  266. // limit and attributes of our read/write selector properly.
  267. //
  268. //
  269. // Call GetCodeInfo() to check out bit-ness of code segment. If 32-bit
  270. // we need to use the 16-bit override opcode for a far 16:16 jump.
  271. //
  272. segInfo.flags = 0;
  273. GetCodeInfo(lpfnToPatch, &segInfo);
  274. if (segInfo.flags & NSUSE32)
  275. {
  276. WARNING_OUT(("Patching 32-bit code segment 0x%04x:0x%04x", SELECTOROF(lpfnToPatch), OFFSETOF(lpfnToPatch)));
  277. lpbPatch->f32Bit = TRUE;
  278. }
  279. //
  280. // We must fix the codeseg in linear memory, or our shadow will end up
  281. // pointing somewhere if the original moves. PolyBezier and SetPixel
  282. // are in moveable code segments for example.
  283. //
  284. // So save this away. We will fix it when the patch is enabled.
  285. //
  286. lpbPatch->wSegOrg = SELECTOROF(lpfnToPatch);
  287. if (uCodeAlias)
  288. {
  289. //
  290. // We are going to share an already allocated selector. Note that
  291. // this only works if the code segments of the two patched functions
  292. // are identical. We verify this by the base address in an assert
  293. // down below.
  294. //
  295. lpbPatch->fSharedAlias = TRUE;
  296. }
  297. else
  298. {
  299. //
  300. // Create a selector with read-write attributes to alias the read-only
  301. // code function. Using the original will set the limit of our
  302. // selector to the same as that of the codeseg, with the same
  303. // attributes but read-write.
  304. //
  305. uCodeAlias = AllocSelector(SELECTOROF(lpfnToPatch));
  306. if (!uCodeAlias)
  307. {
  308. ERROR_OUT(("CreateFnPatch: Unable to create alias selector"));
  309. DC_QUIT;
  310. }
  311. uCodeAlias = PrestoChangoSelector(SELECTOROF(lpfnToPatch), uCodeAlias);
  312. }
  313. lpbPatch->lpCodeAlias = MAKELP(uCodeAlias, OFFSETOF(lpfnToPatch));
  314. //
  315. // Create the N patch bytes (jmp far16 Seg:Function) of the patch
  316. //
  317. ib = 0;
  318. if (lpbPatch->f32Bit)
  319. {
  320. lpbPatch->rgbPatch[ib++] = OPCODE32_16OVERRIDE;
  321. }
  322. lpbPatch->rgbPatch[ib++] = OPCODE_FARJUMP16;
  323. lpbPatch->rgbPatch[ib++] = LOBYTE(OFFSETOF(lpfnJumpTo));
  324. lpbPatch->rgbPatch[ib++] = HIBYTE(OFFSETOF(lpfnJumpTo));
  325. lpbPatch->rgbPatch[ib++] = LOBYTE(SELECTOROF(lpfnJumpTo));
  326. lpbPatch->rgbPatch[ib++] = HIBYTE(SELECTOROF(lpfnJumpTo));
  327. lpbPatch->fActive = FALSE;
  328. lpbPatch->fEnabled = FALSE;
  329. DC_EXIT_POINT:
  330. DebugExitBOOL(CreateFnPatch, uCodeAlias);
  331. return(uCodeAlias);
  332. }
  333. //
  334. // DestroyFnPatch()
  335. // This frees any resources used when creating a function patch. The
  336. // alias data selector to the codeseg for writing purposes is it.
  337. //
  338. void DestroyFnPatch(LPFN_PATCH lpbPatch)
  339. {
  340. DebugEntry(DestroyFnPatch);
  341. //
  342. // First, disable the patch if in use
  343. //
  344. if (lpbPatch->fActive)
  345. {
  346. TRACE_OUT(("Destroying active patch"));
  347. EnableFnPatch(lpbPatch, PATCH_DEACTIVATE);
  348. }
  349. //
  350. // Second, free the alias selector if we allocated one
  351. //
  352. if (lpbPatch->lpCodeAlias)
  353. {
  354. if (!lpbPatch->fSharedAlias)
  355. {
  356. FreeSelector(SELECTOROF(lpbPatch->lpCodeAlias));
  357. }
  358. lpbPatch->lpCodeAlias = NULL;
  359. }
  360. //
  361. // Third, clear this to fine cleanup problems
  362. //
  363. lpbPatch->wSegOrg = 0;
  364. lpbPatch->f32Bit = FALSE;
  365. DebugExitVOID(DestroyFnPatch);
  366. }
  367. //
  368. // EnableFnPatch()
  369. // This actually patches the function to jump to our routine using the
  370. // info saved when created.
  371. //
  372. // THIS ROUTINE MAY GET CALLED AT INTERRUPT TIME. YOU CAN NOT USE ANY
  373. // EXTERNAL FUNCTION, INCLUDING DEBUG TRACING/ASSERTS.
  374. //
  375. #define SAFE_ASSERT(x) if (!lpbPatch->fInterruptable) { ASSERT(x); }
  376. void EnableFnPatch(LPFN_PATCH lpbPatch, UINT flags)
  377. {
  378. UINT ib;
  379. UINT cbPatch;
  380. SAFE_ASSERT(lpbPatch->lpCodeAlias);
  381. SAFE_ASSERT(lpbPatch->wSegOrg);
  382. //
  383. // Make sure the original and the alias are pointing to the same
  384. // linear memory. We don't do this when not enabling/disabling for calls,
  385. // only when starting/stopping the patches
  386. //
  387. //
  388. // If enabling for the first time, fix BEFORE bytes are copied
  389. //
  390. if ((flags & ENABLE_MASK) == ENABLE_ON)
  391. {
  392. //
  393. // We need to fix the original code segment so it won't move in
  394. // linear memory. Note that we set the selector base of our alias
  395. // even though several patches (not too many) may share one. The
  396. // extra times are harmless, and this prevents us from having to order
  397. // patches in our array in such a way that the original precedes the
  398. // shared ones, and walking our array in opposite orders to enable or
  399. // disable.
  400. //
  401. // And GlobalFix() just bumps up a lock count, so again, it's OK to do
  402. // this multiple times.
  403. //
  404. //
  405. // WE KNOW THIS CODE DOESN'T EXECUTE AT INTERRUPT TIME.
  406. //
  407. ASSERT(!lpbPatch->fEnabled);
  408. ASSERT(!lpbPatch->fActive);
  409. if (!lpbPatch->fActive)
  410. {
  411. lpbPatch->fActive = TRUE;
  412. //
  413. // Make sure this segment gets pulled in if discarded. GlobalFix()
  414. // fails if not, and worse we'll fault the second we try to write
  415. // to or read from the alias. We do this by grabbing the 1st word
  416. // from the original.
  417. //
  418. // GlobalFix will prevent the segment from being discarded until
  419. // GlobalUnfix() happens.
  420. //
  421. ib = *(LPUINT)MAKELP(lpbPatch->wSegOrg, OFFSETOF(lpbPatch->lpCodeAlias));
  422. GlobalFix((HGLOBAL)lpbPatch->wSegOrg);
  423. SetSelectorBase(SELECTOROF(lpbPatch->lpCodeAlias), GetSelectorBase(lpbPatch->wSegOrg));
  424. }
  425. }
  426. if (lpbPatch->fInterruptable)
  427. {
  428. //
  429. // If this is for starting/stopping the patch, we have to disable
  430. // interrupts around the byte copying. Or we could die if the
  431. // interrupt handler happens in the middle.
  432. //
  433. // We don't need to do this when disabling/enabling to call through
  434. // to the original, since we know that reflected interrupts aren't
  435. // nested, and the interrupt will complete before we come back to
  436. // the interrupted normal app code.
  437. //
  438. if (!(flags & ENABLE_FORCALL))
  439. {
  440. _asm cli
  441. }
  442. }
  443. SAFE_ASSERT(GetSelectorBase(SELECTOROF(lpbPatch->lpCodeAlias)) == GetSelectorBase(lpbPatch->wSegOrg));
  444. if (lpbPatch->f32Bit)
  445. {
  446. cbPatch = CB_PATCHBYTES32;
  447. }
  448. else
  449. {
  450. cbPatch = CB_PATCHBYTES16;
  451. }
  452. if (flags & ENABLE_ON)
  453. {
  454. SAFE_ASSERT(lpbPatch->fActive);
  455. SAFE_ASSERT(!lpbPatch->fEnabled);
  456. if (!lpbPatch->fEnabled)
  457. {
  458. //
  459. // Save the function's original first N bytes, and copy the jump far16
  460. // patch in.
  461. //
  462. for (ib = 0; ib < cbPatch; ib++)
  463. {
  464. lpbPatch->rgbOrg[ib] = lpbPatch->lpCodeAlias[ib];
  465. lpbPatch->lpCodeAlias[ib] = lpbPatch->rgbPatch[ib];
  466. }
  467. lpbPatch->fEnabled = TRUE;
  468. }
  469. }
  470. else
  471. {
  472. SAFE_ASSERT(lpbPatch->fActive);
  473. SAFE_ASSERT(lpbPatch->fEnabled);
  474. if (lpbPatch->fEnabled)
  475. {
  476. //
  477. // Put back the function's original first N bytes
  478. //
  479. for (ib = 0; ib < cbPatch; ib++)
  480. {
  481. lpbPatch->lpCodeAlias[ib] = lpbPatch->rgbOrg[ib];
  482. }
  483. lpbPatch->fEnabled = FALSE;
  484. }
  485. }
  486. SAFE_ASSERT(GetSelectorBase(SELECTOROF(lpbPatch->lpCodeAlias)) == GetSelectorBase(lpbPatch->wSegOrg));
  487. if (lpbPatch->fInterruptable)
  488. {
  489. //
  490. // Reenable interrupts
  491. //
  492. if (!(flags & ENABLE_FORCALL))
  493. {
  494. _asm sti
  495. }
  496. }
  497. //
  498. // If really stopping, unfix AFTER the bytes have been copied. This will
  499. // bump down a lock count, so when all patches are disabled, the original
  500. // code segment will be able to move.
  501. //
  502. if ((flags & ENABLE_MASK) == ENABLE_OFF)
  503. {
  504. //
  505. // WE KNOW THIS CODE DOESN'T EXECUTE AT INTERRUPT TIME.
  506. //
  507. ASSERT(!lpbPatch->fEnabled);
  508. ASSERT(lpbPatch->fActive);
  509. if (lpbPatch->fActive)
  510. {
  511. lpbPatch->fActive = FALSE;
  512. GlobalUnfix((HGLOBAL)lpbPatch->wSegOrg);
  513. }
  514. }
  515. }
  516. //
  517. // LIST MANIPULATION ROUTINES
  518. //
  519. //
  520. // COM_BasedListInsertBefore(...)
  521. //
  522. // See com.h for description.
  523. //
  524. void COM_BasedListInsertBefore(PBASEDLIST pExisting, PBASEDLIST pNew)
  525. {
  526. PBASEDLIST pTemp;
  527. DebugEntry(COM_BasedListInsertBefore);
  528. //
  529. // Check for bad parameters.
  530. //
  531. ASSERT((pNew != NULL));
  532. ASSERT((pExisting != NULL));
  533. //
  534. // Find the item before pExisting:
  535. //
  536. pTemp = COM_BasedPrevListField(pExisting);
  537. ASSERT((pTemp != NULL));
  538. TRACE_OUT(("Inserting item at %#lx into list between %#lx and %#lx",
  539. pNew, pTemp, pExisting));
  540. //
  541. // Set its <next> field to point to the new item
  542. //
  543. pTemp->next = PTRBASE_TO_OFFSET(pNew, pTemp);
  544. pNew->prev = PTRBASE_TO_OFFSET(pTemp, pNew);
  545. //
  546. // Set <prev> field of pExisting to point to new item:
  547. //
  548. pExisting->prev = PTRBASE_TO_OFFSET(pNew, pExisting);
  549. pNew->next = PTRBASE_TO_OFFSET(pExisting, pNew);
  550. DebugExitVOID(COM_BasedListInsertBefore);
  551. } // COM_BasedListInsertBefore
  552. //
  553. // COM_BasedListInsertAfter(...)
  554. //
  555. // See com.h for description.
  556. //
  557. void COM_BasedListInsertAfter(PBASEDLIST pExisting, PBASEDLIST pNew)
  558. {
  559. PBASEDLIST pTemp;
  560. DebugEntry(COM_BasedListInsertAfter);
  561. //
  562. // Check for bad parameters.
  563. //
  564. ASSERT((pNew != NULL));
  565. ASSERT((pExisting != NULL));
  566. //
  567. // Find the item after pExisting:
  568. //
  569. pTemp = COM_BasedNextListField(pExisting);
  570. ASSERT((pTemp != NULL));
  571. TRACE_OUT(("Inserting item at %#lx into list between %#lx and %#lx",
  572. pNew, pExisting, pTemp));
  573. //
  574. // Set its <prev> field to point to the new item
  575. //
  576. pTemp->prev = PTRBASE_TO_OFFSET(pNew, pTemp);
  577. pNew->next = PTRBASE_TO_OFFSET(pTemp, pNew);
  578. //
  579. // Set <next> field of pExisting to point to new item:
  580. //
  581. pExisting->next = PTRBASE_TO_OFFSET(pNew, pExisting);
  582. pNew->prev = PTRBASE_TO_OFFSET(pExisting, pNew);
  583. DebugExitVOID(COM_BasedListInsertAfter);
  584. } // COM_BasedListInsertAfter
  585. //
  586. // COM_BasedListRemove(...)
  587. //
  588. // See com.h for description.
  589. //
  590. void COM_BasedListRemove(PBASEDLIST pListItem)
  591. {
  592. PBASEDLIST pNext = NULL;
  593. PBASEDLIST pPrev = NULL;
  594. DebugEntry(COM_BasedListRemove);
  595. //
  596. // Check for bad parameters.
  597. //
  598. ASSERT((pListItem != NULL));
  599. pPrev = COM_BasedPrevListField(pListItem);
  600. pNext = COM_BasedNextListField(pListItem);
  601. ASSERT((pPrev != NULL));
  602. ASSERT((pNext != NULL));
  603. TRACE_OUT(("Removing item at %#lx from list", pListItem));
  604. pPrev->next = PTRBASE_TO_OFFSET(pNext, pPrev);
  605. pNext->prev = PTRBASE_TO_OFFSET(pPrev, pNext);
  606. DebugExitVOID(COM_BasedListRemove);
  607. } // COM_BasedListRemove
  608. //
  609. // NOTE:
  610. // Because this is small model 16-bit code, NULL (which is 0) gets turned
  611. // into ds:0 when casting to void FAR*. Therefore we use our own FAR_NULL
  612. // define, which is 0:0.
  613. //
  614. void FAR * COM_BasedListNext( PBASEDLIST pHead, void FAR * pEntry, UINT nOffset )
  615. {
  616. PBASEDLIST p;
  617. ASSERT(pHead != NULL);
  618. ASSERT(pEntry != NULL);
  619. p = COM_BasedNextListField(COM_BasedStructToField(pEntry, nOffset));
  620. return ((p == pHead) ? FAR_NULL : COM_BasedFieldToStruct(p, nOffset));
  621. }
  622. void FAR * COM_BasedListPrev ( PBASEDLIST pHead, void FAR * pEntry, UINT nOffset )
  623. {
  624. PBASEDLIST p;
  625. ASSERT(pHead != NULL);
  626. ASSERT(pEntry != NULL);
  627. p = COM_BasedPrevListField(COM_BasedStructToField(pEntry, nOffset));
  628. return ((p == pHead) ? FAR_NULL : COM_BasedFieldToStruct(p, nOffset));
  629. }
  630. void FAR * COM_BasedListFirst ( PBASEDLIST pHead, UINT nOffset )
  631. {
  632. return (COM_BasedListIsEmpty(pHead) ?
  633. FAR_NULL :
  634. COM_BasedFieldToStruct(COM_BasedNextListField(pHead), nOffset));
  635. }
  636. void FAR * COM_BasedListLast ( PBASEDLIST pHead, UINT nOffset )
  637. {
  638. return (COM_BasedListIsEmpty(pHead) ?
  639. FAR_NULL :
  640. COM_BasedFieldToStruct(COM_BasedPrevListField(pHead), nOffset));
  641. }