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.

886 lines
22 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. dllutil.c
  5. Abstract:
  6. This module contains utility procedures for the Windows Client DLL
  7. Author:
  8. Steve Wood (stevewo) 8-Oct-1990
  9. Revision History:
  10. --*/
  11. #include "csrdll.h"
  12. NTSTATUS
  13. CsrClientCallServer(
  14. IN OUT PCSR_API_MSG m,
  15. IN OUT PCSR_CAPTURE_HEADER CaptureBuffer OPTIONAL,
  16. IN CSR_API_NUMBER ApiNumber,
  17. IN ULONG ArgLength
  18. )
  19. /*++
  20. Routine Description:
  21. This function sends an API request to the Windows Emulation Subsystem
  22. Server and waits for a reply.
  23. Arguments:
  24. m - Pointer to the API request message to send.
  25. CaptureBuffer - Optional pointer to a capture buffer located in the
  26. Port Memory section that contains additional data being sent
  27. to the server. Since Port Memory is also visible to the server,
  28. no data needs to be copied, but pointers to locations within the
  29. capture buffer need to be converted into pointers valid in the
  30. server's process context, since the server's view of the Port Memory
  31. is not at the same virtual address as the client's view.
  32. ApiNumber - Small integer that is the number of the API being called.
  33. ArgLength - Length, in bytes, of the argument portion located at the
  34. end of the request message. Used to calculate the length of the
  35. request message.
  36. Return Value:
  37. Status Code from either client or server
  38. --*/
  39. {
  40. NTSTATUS Status;
  41. PULONG_PTR PointerOffsets;
  42. ULONG CountPointers;
  43. ULONG_PTR Pointer;
  44. //
  45. // Initialize the header of the message.
  46. //
  47. if ((LONG)ArgLength < 0) {
  48. ArgLength = (ULONG)(-(LONG)ArgLength);
  49. m->h.u2.s2.Type = 0;
  50. }
  51. else {
  52. m->h.u2.ZeroInit = 0;
  53. }
  54. ArgLength |= (ArgLength << 16);
  55. ArgLength += ((sizeof( CSR_API_MSG ) - sizeof( m->u )) << 16) |
  56. (FIELD_OFFSET( CSR_API_MSG, u ) - sizeof( m->h ));
  57. m->h.u1.Length = ArgLength;
  58. m->CaptureBuffer = NULL;
  59. m->ApiNumber = ApiNumber;
  60. //
  61. // if the caller is within the server process, do the API call directly
  62. // and skip the capture buffer fixups and LPC call.
  63. //
  64. if (CsrServerProcess == FALSE) {
  65. //
  66. // If the CaptureBuffer argument is present, then there is data located
  67. // in the Port Memory section that is being passed to the server. All
  68. // Port Memory pointers need to be converted so they are valid in the
  69. // Server's view of the Port Memory.
  70. //
  71. if (ARGUMENT_PRESENT( CaptureBuffer )) {
  72. //
  73. // Store a pointer to the capture buffer in the message that is valid
  74. // in the server process's context.
  75. //
  76. m->CaptureBuffer = (PCSR_CAPTURE_HEADER)
  77. ((PCHAR)CaptureBuffer + CsrPortMemoryRemoteDelta);
  78. //
  79. // Mark the fact that we are done allocating space from the end of
  80. // the capture buffer.
  81. //
  82. CaptureBuffer->FreeSpace = NULL;
  83. //
  84. // Loop over all of the pointers to Port Memory within the message
  85. // itself and convert them into server pointers. Also, convert
  86. // the pointers to pointers into offsets.
  87. //
  88. PointerOffsets = CaptureBuffer->MessagePointerOffsets;
  89. CountPointers = CaptureBuffer->CountMessagePointers;
  90. while (CountPointers--) {
  91. Pointer = *PointerOffsets++;
  92. if (Pointer != 0) {
  93. *(PULONG_PTR)Pointer += CsrPortMemoryRemoteDelta;
  94. PointerOffsets[ -1 ] = Pointer - (ULONG_PTR)m;
  95. }
  96. }
  97. }
  98. //
  99. // Send the request to the server and wait for a reply. The wait is
  100. // NOT alertable, because ? FIX,FIX
  101. //
  102. Status = NtRequestWaitReplyPort( CsrPortHandle,
  103. (PPORT_MESSAGE)m,
  104. (PPORT_MESSAGE)m
  105. );
  106. //
  107. // If the CaptureBuffer argument is present then reverse what we did
  108. // to the pointers above so that the client side code can use them
  109. // again.
  110. //
  111. if (ARGUMENT_PRESENT( CaptureBuffer )) {
  112. //
  113. // Convert the capture buffer pointer back to a client pointer.
  114. //
  115. m->CaptureBuffer = (PCSR_CAPTURE_HEADER)
  116. ((PCHAR)m->CaptureBuffer - CsrPortMemoryRemoteDelta);
  117. //
  118. // Loop over all of the pointers to Port Memory within the message
  119. // itself and convert them into client pointers. Also, convert
  120. // the offsets pointers to pointers into back into pointers
  121. //
  122. PointerOffsets = CaptureBuffer->MessagePointerOffsets;
  123. CountPointers = CaptureBuffer->CountMessagePointers;
  124. while (CountPointers--) {
  125. Pointer = *PointerOffsets++;
  126. if (Pointer != 0) {
  127. Pointer += (ULONG_PTR)m;
  128. PointerOffsets[ -1 ] = Pointer;
  129. *(PULONG_PTR)Pointer -= CsrPortMemoryRemoteDelta;
  130. }
  131. }
  132. }
  133. //
  134. // Check for failed status and do something.
  135. //
  136. if (!NT_SUCCESS( Status )) {
  137. IF_DEBUG {
  138. if (Status != STATUS_PORT_DISCONNECTED &&
  139. Status != STATUS_INVALID_HANDLE
  140. ) {
  141. DbgPrint( "CSRDLL: NtRequestWaitReplyPort failed - Status == %X\n",
  142. Status
  143. );
  144. }
  145. }
  146. m->ReturnValue = Status;
  147. }
  148. } else {
  149. m->h.ClientId = NtCurrentTeb()->ClientId;
  150. Status = (CsrServerApiRoutine)((PCSR_API_MSG)m,
  151. (PCSR_API_MSG)m
  152. );
  153. //
  154. // Check for failed status and do something.
  155. //
  156. if (!NT_SUCCESS( Status )) {
  157. IF_DEBUG {
  158. DbgPrint( "CSRDLL: Server side client call failed - Status == %X\n",
  159. Status
  160. );
  161. }
  162. m->ReturnValue = Status;
  163. }
  164. }
  165. //
  166. // The value of this function is whatever the server function returned.
  167. //
  168. return( m->ReturnValue );
  169. }
  170. HANDLE
  171. CsrGetProcessId (
  172. VOID
  173. )
  174. /*++
  175. Routine Description:
  176. This function gets the process ID of the CSR process (for the session)
  177. Arguments:
  178. None
  179. Return Value:
  180. Process ID of CSR
  181. --*/
  182. {
  183. return CsrProcessId;
  184. }
  185. PCSR_CAPTURE_HEADER
  186. CsrAllocateCaptureBuffer(
  187. IN ULONG CountMessagePointers,
  188. IN ULONG Size
  189. )
  190. /*++
  191. Routine Description:
  192. This function allocates a buffer from the Port Memory section for
  193. use by the client in capture arguments into Port Memory. In addition to
  194. specifying the size of the data that needs to be captured, the caller
  195. needs to specify how many pointers to captured data will be passed.
  196. Pointers can be located in either the request message itself, and/or
  197. the capture buffer.
  198. Arguments:
  199. CountMessagePointers - Number of pointers within the request message
  200. that will point to locations within the allocated capture buffer.
  201. Size - Total size of the data that will be captured into the capture
  202. buffer.
  203. Return Value:
  204. A pointer to the capture buffer header.
  205. --*/
  206. {
  207. PCSR_CAPTURE_HEADER CaptureBuffer;
  208. ULONG CountPointers;
  209. //
  210. // Calculate the total number of pointers that will be passed
  211. //
  212. CountPointers = CountMessagePointers;
  213. //
  214. // Calculate the total size of the capture buffer. This includes the
  215. // header, the array of pointer offsets and the data length. We round
  216. // the data length to a 32-bit boundary, assuming that each pointer
  217. // points to data whose length is not aligned on a 32-bit boundary.
  218. //
  219. if (Size >= MAXLONG) {
  220. //
  221. // Bail early if too big
  222. //
  223. return NULL;
  224. }
  225. Size += FIELD_OFFSET(CSR_CAPTURE_HEADER, MessagePointerOffsets) + (CountPointers * sizeof( PVOID ));
  226. Size = (Size + (3 * (CountPointers+1))) & ~3;
  227. //
  228. // Allocate the capture buffer from the Port Memory Heap.
  229. //
  230. CaptureBuffer = RtlAllocateHeap( CsrPortHeap, MAKE_CSRPORT_TAG( CAPTURE_TAG ), Size );
  231. if (CaptureBuffer == NULL) {
  232. //
  233. // FIX, FIX - need to attempt the receive lost reply messages to
  234. // to see if they contain CaptureBuffer pointers that can be freed.
  235. //
  236. return( NULL );
  237. }
  238. //
  239. // Initialize the capture buffer header
  240. //
  241. CaptureBuffer->Length = Size;
  242. CaptureBuffer->CountMessagePointers = 0;
  243. //
  244. // If there are pointers being passed then initialize the arrays of
  245. // pointer offsets to zero. In either case set the free space pointer
  246. // in the capture buffer header to point to the first 32-bit aligned
  247. // location after the header, the arrays of pointer offsets are considered
  248. // part of the header.
  249. //
  250. RtlZeroMemory( CaptureBuffer->MessagePointerOffsets,
  251. CountPointers * sizeof( ULONG_PTR )
  252. );
  253. CaptureBuffer->FreeSpace = (PCHAR)
  254. (CaptureBuffer->MessagePointerOffsets + CountPointers);
  255. //
  256. // Returned the address of the capture buffer.
  257. //
  258. return( CaptureBuffer );
  259. }
  260. VOID
  261. CsrFreeCaptureBuffer(
  262. IN PCSR_CAPTURE_HEADER CaptureBuffer
  263. )
  264. /*++
  265. Routine Description:
  266. This function frees a capture buffer allocated by CsrAllocateCaptureBuffer.
  267. Arguments:
  268. CaptureBuffer - Pointer to a capture buffer allocated by
  269. CsrAllocateCaptureBuffer.
  270. Return Value:
  271. None.
  272. --*/
  273. {
  274. //
  275. // Free the capture buffer back to the Port Memory heap.
  276. //
  277. RtlFreeHeap( CsrPortHeap, 0, CaptureBuffer );
  278. }
  279. ULONG
  280. CsrAllocateMessagePointer(
  281. IN OUT PCSR_CAPTURE_HEADER CaptureBuffer,
  282. IN ULONG Length,
  283. OUT PVOID *Pointer
  284. )
  285. /*++
  286. Routine Description:
  287. This function allocates space from the capture buffer along with a
  288. pointer to point to it. The pointer is presumed to be located in
  289. the request message structure.
  290. Arguments:
  291. CaptureBuffer - Pointer to a capture buffer allocated by
  292. CsrAllocateCaptureBuffer.
  293. Length - Size of data being allocated from the capture buffer.
  294. Pointer - Address of the pointer within the request message that
  295. is to point to the space allocated out of the capture buffer.
  296. Return Value:
  297. The actual length of the buffer allocated, after it has been rounded
  298. up to a multiple of 4.
  299. --*/
  300. {
  301. if (Length == 0) {
  302. *Pointer = NULL;
  303. Pointer = NULL;
  304. }
  305. else {
  306. //
  307. // Set the returned pointer value to point to the next free byte in
  308. // the capture buffer.
  309. //
  310. *Pointer = CaptureBuffer->FreeSpace;
  311. //
  312. // Round the length up to a multiple of 4
  313. //
  314. if (Length >= MAXLONG) {
  315. //
  316. // Bail early if too big
  317. //
  318. return 0;
  319. }
  320. Length = (Length + 3) & ~3;
  321. //
  322. // Update the free space pointer to point to the next available byte
  323. // in the capture buffer.
  324. //
  325. CaptureBuffer->FreeSpace += Length;
  326. }
  327. //
  328. // Remember the location of this pointer so that CsrClientCallServer can
  329. // convert it into a server pointer prior to sending the request to
  330. // the server.
  331. //
  332. CaptureBuffer->MessagePointerOffsets[ CaptureBuffer->CountMessagePointers++ ] =
  333. (ULONG_PTR)Pointer;
  334. //
  335. // Returned the actual length allocated.
  336. //
  337. return( Length );
  338. }
  339. VOID
  340. CsrCaptureMessageBuffer(
  341. IN OUT PCSR_CAPTURE_HEADER CaptureBuffer,
  342. IN PVOID Buffer OPTIONAL,
  343. IN ULONG Length,
  344. OUT PVOID *CapturedBuffer
  345. )
  346. /*++
  347. Routine Description:
  348. This function captures a buffer of bytes in an API request message.
  349. Arguments:
  350. CaptureBuffer - Pointer to a capture buffer allocated by
  351. CsrAllocateCaptureBuffer.
  352. Buffer - Optional pointer to the buffer. If this parameter is
  353. not present, then no data is copied into capture buffer.
  354. Length - Length of the buffer.
  355. CapturedBuffer - Pointer to the field in the message that will
  356. be filled in to point to the capture buffer.
  357. Return Value:
  358. None.
  359. --*/
  360. {
  361. //
  362. // Set the length fields of the captured string structure and allocated
  363. // the Length for the string from the capture buffer.
  364. //
  365. CsrAllocateMessagePointer( CaptureBuffer,
  366. Length,
  367. CapturedBuffer
  368. );
  369. //
  370. // If Buffer parameter is not present or the length of the data is zero,
  371. // return.
  372. //
  373. if (!ARGUMENT_PRESENT( Buffer ) || (Length == 0)) {
  374. return;
  375. }
  376. //
  377. // Copy the buffer data to the capture area.
  378. //
  379. RtlMoveMemory( *CapturedBuffer, Buffer, Length );
  380. return;
  381. }
  382. VOID
  383. CsrCaptureMessageString(
  384. IN OUT PCSR_CAPTURE_HEADER CaptureBuffer,
  385. IN PCSTR String OPTIONAL,
  386. IN ULONG Length,
  387. IN ULONG MaximumLength,
  388. OUT PSTRING CapturedString
  389. )
  390. /*++
  391. Routine Description:
  392. This function captures an ASCII string into a counted string data
  393. structure located in an API request message.
  394. Arguments:
  395. CaptureBuffer - Pointer to a capture buffer allocated by
  396. CsrAllocateCaptureBuffer.
  397. String - Optional pointer to the ASCII string. If this parameter is
  398. not present, then the counted string data structure is set to
  399. the null string.
  400. Length - Length of the ASCII string, ignored if String is NULL.
  401. MaximumLength - Maximum length of the string. Different for null
  402. terminated strings, where Length does not include the null and
  403. MaximumLength does. This is always how much space is allocated
  404. from the capture buffer.
  405. CaptureString - Pointer to the counted string data structure that will
  406. be filled in to point to the captured ASCII string.
  407. Return Value:
  408. None.
  409. --*/
  410. {
  411. ASSERT(CapturedString != NULL);
  412. //
  413. // If String parameter is not present, then set the captured string
  414. // to be the null string and returned.
  415. //
  416. if (!ARGUMENT_PRESENT( String )) {
  417. CapturedString->Length = 0;
  418. CapturedString->MaximumLength = (USHORT)MaximumLength;
  419. CsrAllocateMessagePointer( CaptureBuffer,
  420. MaximumLength,
  421. (PVOID *)&CapturedString->Buffer
  422. );
  423. //
  424. // Make it nul terminated if there is any room.
  425. //
  426. if (MaximumLength != 0) {
  427. CapturedString->Buffer[0] = 0;
  428. }
  429. return;
  430. }
  431. //
  432. // Set the length fields of the captured string structure and allocated
  433. // the MaximumLength for the string from the capture buffer.
  434. //
  435. CapturedString->Length = (USHORT)Length;
  436. CapturedString->MaximumLength = (USHORT)
  437. CsrAllocateMessagePointer( CaptureBuffer,
  438. MaximumLength,
  439. (PVOID *)&CapturedString->Buffer
  440. );
  441. //
  442. // If the Length of the ASCII string is non-zero then move it to the
  443. // capture area.
  444. //
  445. if (Length != 0) {
  446. RtlMoveMemory( CapturedString->Buffer, String, MaximumLength );
  447. }
  448. if (CapturedString->Length < CapturedString->MaximumLength) {
  449. CapturedString->Buffer[ CapturedString->Length ] = '\0';
  450. }
  451. return;
  452. }
  453. VOID
  454. CsrCaptureMessageUnicodeStringInPlace(
  455. IN OUT PCSR_CAPTURE_HEADER CaptureBuffer,
  456. IN OUT PUNICODE_STRING String
  457. )
  458. /*++
  459. Routine Description:
  460. This function captures an ASCII string into a counted string data
  461. structure located in an API request message.
  462. Arguments:
  463. CaptureBuffer - Pointer to a capture buffer allocated by
  464. CsrAllocateCaptureBuffer.
  465. String - Optional pointer to the Unicode string. If this parameter is
  466. not present, then the counted string data structure is set to
  467. the null string.
  468. Length - Length of the Unicode string in bytes, ignored if String is NULL.
  469. MaximumLength - Maximum length of the string. Different for null
  470. terminated strings, where Length does not include the null and
  471. MaximumLength does. This is always how much space is allocated
  472. from the capture buffer.
  473. CaptureString - Pointer to the counted string data structure that will
  474. be filled in to point to the captured Unicode string.
  475. Return Value:
  476. None, but if you don't trust the String parameter, use a __try block.
  477. --*/
  478. {
  479. ASSERT(String != NULL);
  480. CsrCaptureMessageString(
  481. CaptureBuffer,
  482. (PCSTR)String->Buffer,
  483. String->Length,
  484. String->MaximumLength,
  485. (PSTRING)String
  486. );
  487. // test > before substraction due to unsignedness
  488. if (String->MaximumLength > String->Length) {
  489. if ((String->MaximumLength - String->Length) >= sizeof(WCHAR)) {
  490. String->Buffer[ String->Length / sizeof(WCHAR) ] = 0;
  491. }
  492. }
  493. }
  494. NTSTATUS
  495. CsrCaptureMessageMultiUnicodeStringsInPlace(
  496. IN OUT PCSR_CAPTURE_HEADER* InOutCaptureBuffer,
  497. IN ULONG NumberOfStringsToCapture,
  498. IN const PUNICODE_STRING* StringsToCapture
  499. )
  500. /*++
  501. Routine Description:
  502. Capture multiple unicode strings.
  503. If the CaptureBuffer hasn't been allocated yet (passed as NULL), first
  504. allocate it.
  505. Arguments:
  506. CaptureBuffer - Pointer to a capture buffer allocated by
  507. CsrAllocateCaptureBuffer, or NULL, in which case we call CsrAllocateCaptureBuffer
  508. for you; this is the case if you are only capturing these strings
  509. and nothing else.
  510. NumberOfStringsToCapture -
  511. StringsToCapture -
  512. Return Value:
  513. NTSTATUS
  514. --*/
  515. {
  516. NTSTATUS Status = STATUS_SUCCESS;
  517. ULONG Length = 0;
  518. ULONG i = 0;
  519. PCSR_CAPTURE_HEADER CaptureBuffer = NULL;
  520. if (InOutCaptureBuffer == NULL) {
  521. Status = STATUS_INVALID_PARAMETER;
  522. goto Exit;
  523. }
  524. CaptureBuffer = *InOutCaptureBuffer;
  525. if (CaptureBuffer == NULL) {
  526. Length = 0;
  527. for (i = 0 ; i != NumberOfStringsToCapture ; ++i) {
  528. if (StringsToCapture[i] != NULL) {
  529. Length += StringsToCapture[i]->MaximumLength;
  530. }
  531. }
  532. CaptureBuffer = CsrAllocateCaptureBuffer(NumberOfStringsToCapture, Length);
  533. if (CaptureBuffer == NULL) {
  534. Status = STATUS_NO_MEMORY;
  535. goto Exit;
  536. }
  537. *InOutCaptureBuffer = CaptureBuffer;
  538. }
  539. for (i = 0 ; i != NumberOfStringsToCapture ; ++i) {
  540. if (StringsToCapture[i] != NULL) {
  541. CsrCaptureMessageUnicodeStringInPlace(
  542. CaptureBuffer,
  543. StringsToCapture[i]
  544. );
  545. } else {
  546. }
  547. }
  548. Status = STATUS_SUCCESS;
  549. Exit:
  550. return Status;
  551. }
  552. PLARGE_INTEGER
  553. CsrCaptureTimeout(
  554. IN ULONG MilliSeconds,
  555. OUT PLARGE_INTEGER Timeout
  556. )
  557. {
  558. if (MilliSeconds == -1) {
  559. return( NULL );
  560. }
  561. else {
  562. Timeout->QuadPart = Int32x32To64( MilliSeconds, -10000 );
  563. return( (PLARGE_INTEGER)Timeout );
  564. }
  565. }
  566. VOID
  567. CsrProbeForWrite(
  568. IN PVOID Address,
  569. IN ULONG Length,
  570. IN ULONG Alignment
  571. )
  572. /*++
  573. Routine Description:
  574. This function probes a structure for read accessibility.
  575. If the structure is not accessible, then an exception is raised.
  576. Arguments:
  577. Address - Supplies a pointer to the structure to be probed.
  578. Length - Supplies the length of the structure.
  579. Alignment - Supplies the required alignment of the structure expressed
  580. as the number of bytes in the primitive datatype (e.g., 1 for char,
  581. 2 for short, 4 for long, and 8 for quad).
  582. Return Value:
  583. None.
  584. --*/
  585. {
  586. volatile CHAR *StartAddress;
  587. volatile CHAR *EndAddress;
  588. CHAR Temp;
  589. //
  590. // If the structure has zero length, then do not probe the structure for
  591. // write accessibility or alignment.
  592. //
  593. if (Length != 0) {
  594. //
  595. // If the structure is not properly aligned, then raise a data
  596. // misalignment exception.
  597. //
  598. ASSERT((Alignment == 1) || (Alignment == 2) ||
  599. (Alignment == 4) || (Alignment == 8));
  600. StartAddress = (volatile CHAR *)Address;
  601. if (((ULONG_PTR)StartAddress & (Alignment - 1)) != 0) {
  602. RtlRaiseStatus(STATUS_DATATYPE_MISALIGNMENT);
  603. } else {
  604. //
  605. // BUG, BUG - this should not be necessary once the 386 kernel
  606. // makes system space inaccessable to user mode.
  607. //
  608. if ((ULONG_PTR)StartAddress > CsrNtSysInfo.MaximumUserModeAddress) {
  609. RtlRaiseStatus(STATUS_ACCESS_VIOLATION);
  610. }
  611. Temp = *StartAddress;
  612. *StartAddress = Temp;
  613. EndAddress = StartAddress + Length - 1;
  614. Temp = *EndAddress;
  615. *EndAddress = Temp;
  616. }
  617. }
  618. }
  619. VOID
  620. CsrProbeForRead(
  621. IN PVOID Address,
  622. IN ULONG Length,
  623. IN ULONG Alignment
  624. )
  625. /*++
  626. Routine Description:
  627. This function probes a structure for read accessibility.
  628. If the structure is not accessible, then an exception is raised.
  629. Arguments:
  630. Address - Supplies a pointer to the structure to be probed.
  631. Length - Supplies the length of the structure.
  632. Alignment - Supplies the required alignment of the structure expressed
  633. as the number of bytes in the primitive datatype (e.g., 1 for char,
  634. 2 for short, 4 for long, and 8 for quad).
  635. Return Value:
  636. None.
  637. --*/
  638. {
  639. volatile CHAR *StartAddress;
  640. volatile CHAR *EndAddress;
  641. CHAR Temp;
  642. //
  643. // If the structure has zero length, then do not probe the structure for
  644. // read accessibility or alignment.
  645. //
  646. if (Length != 0) {
  647. //
  648. // If the structure is not properly aligned, then raise a data
  649. // misalignment exception.
  650. //
  651. ASSERT((Alignment == 1) || (Alignment == 2) ||
  652. (Alignment == 4) || (Alignment == 8));
  653. StartAddress = (volatile CHAR *)Address;
  654. if (((ULONG_PTR)StartAddress & (Alignment - 1)) != 0) {
  655. RtlRaiseStatus(STATUS_DATATYPE_MISALIGNMENT);
  656. } else {
  657. Temp = *StartAddress;
  658. EndAddress = StartAddress + Length - 1;
  659. Temp = *EndAddress;
  660. }
  661. }
  662. }