[KERNEL32/CONSRV]
[reactos.git] / dll / win32 / kernel32 / client / console / readwrite.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS system libraries
4 * FILE: dll/win32/kernel32/client/console/readwrite.c
5 * PURPOSE: Win32 Console Client read-write functions
6 * PROGRAMMERS: Emanuele Aliberti
7 * Marty Dill
8 * Filip Navara (xnavara@volny.cz)
9 * Thomas Weidenmueller (w3seek@reactos.org)
10 * Jeffrey Morlan
11 */
12
13 /* INCLUDES *******************************************************************/
14
15 #include <k32.h>
16
17 #define NDEBUG
18 #include <debug.h>
19
20
21 /* PRIVATE FUNCTIONS **********************************************************/
22
23 /******************
24 * Read functions *
25 ******************/
26
27 static
28 BOOL
29 IntReadConsole(HANDLE hConsoleInput,
30 PVOID lpBuffer,
31 DWORD nNumberOfCharsToRead,
32 LPDWORD lpNumberOfCharsRead,
33 PCONSOLE_READCONSOLE_CONTROL pInputControl,
34 BOOL bUnicode)
35 {
36 NTSTATUS Status;
37 CONSOLE_API_MESSAGE ApiMessage;
38 PCSRSS_READ_CONSOLE ReadConsoleRequest = &ApiMessage.Data.ReadConsoleRequest;
39 PCSR_CAPTURE_BUFFER CaptureBuffer;
40 ULONG CharSize;
41
42 /* Determine the needed size */
43 CharSize = (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
44 ReadConsoleRequest->BufferSize = nNumberOfCharsToRead * CharSize;
45
46 /* Allocate a Capture Buffer */
47 CaptureBuffer = CsrAllocateCaptureBuffer(1, ReadConsoleRequest->BufferSize);
48 if (CaptureBuffer == NULL)
49 {
50 DPRINT1("CsrAllocateCaptureBuffer failed!\n");
51 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
52 return FALSE;
53 }
54
55 /* Allocate space in the Buffer */
56 CsrAllocateMessagePointer(CaptureBuffer,
57 ReadConsoleRequest->BufferSize,
58 (PVOID*)&ReadConsoleRequest->Buffer);
59
60 ReadConsoleRequest->ConsoleHandle = hConsoleInput;
61 ReadConsoleRequest->Unicode = bUnicode;
62 ReadConsoleRequest->NrCharactersToRead = (WORD)nNumberOfCharsToRead;
63 ReadConsoleRequest->NrCharactersRead = 0;
64 ReadConsoleRequest->CtrlWakeupMask = 0;
65 if (pInputControl && pInputControl->nLength == sizeof(CONSOLE_READCONSOLE_CONTROL))
66 {
67 ReadConsoleRequest->NrCharactersRead = pInputControl->nInitialChars;
68 memcpy(ReadConsoleRequest->Buffer,
69 lpBuffer,
70 pInputControl->nInitialChars * sizeof(WCHAR));
71 ReadConsoleRequest->CtrlWakeupMask = pInputControl->dwCtrlWakeupMask;
72 }
73
74 Status = CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
75 CaptureBuffer,
76 CSR_CREATE_API_NUMBER(CONSRV_SERVERDLL_INDEX, ConsolepReadConsole),
77 sizeof(CSRSS_READ_CONSOLE));
78 if (!NT_SUCCESS(Status) || !NT_SUCCESS(Status = ApiMessage.Status))
79 {
80 DPRINT1("CSR returned error in ReadConsole\n");
81 CsrFreeCaptureBuffer(CaptureBuffer);
82 BaseSetLastNTError(Status);
83 return FALSE;
84 }
85
86 memcpy(lpBuffer,
87 ReadConsoleRequest->Buffer,
88 ReadConsoleRequest->NrCharactersRead * CharSize);
89
90 if (lpNumberOfCharsRead != NULL)
91 *lpNumberOfCharsRead = ReadConsoleRequest->NrCharactersRead;
92
93 if (pInputControl && pInputControl->nLength == sizeof(CONSOLE_READCONSOLE_CONTROL))
94 pInputControl->dwControlKeyState = ReadConsoleRequest->ControlKeyState;
95
96 CsrFreeCaptureBuffer(CaptureBuffer);
97
98 return TRUE;
99 }
100
101
102 static
103 BOOL
104 IntGetConsoleInput(HANDLE hConsoleInput,
105 BOOL bRead,
106 PINPUT_RECORD lpBuffer,
107 DWORD nLength,
108 LPDWORD lpNumberOfEventsRead,
109 BOOL bUnicode)
110 {
111 NTSTATUS Status;
112 CONSOLE_API_MESSAGE ApiMessage;
113 PCSRSS_GET_CONSOLE_INPUT GetConsoleInputRequest = &ApiMessage.Data.GetConsoleInputRequest;
114 PCSR_CAPTURE_BUFFER CaptureBuffer;
115 ULONG Size;
116
117 if (lpBuffer == NULL)
118 {
119 SetLastError(ERROR_INVALID_PARAMETER);
120 return FALSE;
121 }
122
123 Size = nLength * sizeof(INPUT_RECORD);
124
125 DPRINT("IntGetConsoleInput: %lx %p\n", Size, lpNumberOfEventsRead);
126
127 /* Allocate a Capture Buffer */
128 CaptureBuffer = CsrAllocateCaptureBuffer(1, Size);
129 if (CaptureBuffer == NULL)
130 {
131 DPRINT1("CsrAllocateCaptureBuffer failed!\n");
132 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
133 return FALSE;
134 }
135
136 /* Allocate space in the Buffer */
137 CsrAllocateMessagePointer(CaptureBuffer,
138 Size,
139 (PVOID*)&GetConsoleInputRequest->InputRecord);
140
141 /* Set up the data to send to the Console Server */
142 GetConsoleInputRequest->ConsoleHandle = hConsoleInput;
143 GetConsoleInputRequest->Unicode = bUnicode;
144 GetConsoleInputRequest->bRead = bRead;
145 if (bRead == TRUE)
146 {
147 GetConsoleInputRequest->InputsRead = 0;
148 }
149 GetConsoleInputRequest->Length = nLength;
150
151 /* Call the server */
152 Status = CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
153 CaptureBuffer,
154 CSR_CREATE_API_NUMBER(CONSRV_SERVERDLL_INDEX, ConsolepGetConsoleInput),
155 sizeof(CSRSS_GET_CONSOLE_INPUT));
156 DPRINT("Server returned: %x\n", ApiMessage.Status);
157
158 /** For Read only **
159 if (!NT_SUCCESS(Status) || !NT_SUCCESS(Status = ApiMessage.Status))
160 {
161 // BaseSetLastNTError(Status); ????
162 if (GetConsoleInputRequest->InputsRead == 0)
163 {
164 /\* we couldn't read a single record, fail *\/
165 BaseSetLastNTError(Status);
166 return FALSE;
167 }
168 else
169 {
170 /\* FIXME - fail gracefully in case we already read at least one record? *\/
171 // break;
172 }
173 }
174 **/
175
176 /**
177 ** TODO: !! Simplify the function !!
178 **/
179 if (bRead == TRUE) // ReadConsoleInput call.
180 {
181 /* Check for success */
182 if (NT_SUCCESS(Status) || NT_SUCCESS(Status = ApiMessage.Status))
183 {
184 /* Return the number of events read */
185 DPRINT("Events read: %lx\n", GetConsoleInputRequest->InputsRead/*Length*/);
186
187 if (lpNumberOfEventsRead != NULL)
188 *lpNumberOfEventsRead = GetConsoleInputRequest->InputsRead/*Length*/;
189
190 /* Copy into the buffer */
191 DPRINT("Copying to buffer\n");
192 RtlCopyMemory(lpBuffer,
193 GetConsoleInputRequest->InputRecord,
194 sizeof(INPUT_RECORD) * GetConsoleInputRequest->InputsRead/*Length*/);
195 }
196 else
197 {
198 if (lpNumberOfEventsRead != NULL)
199 *lpNumberOfEventsRead = 0;
200
201 /* Error out */
202 BaseSetLastNTError(ApiMessage.Status);
203 }
204
205 /* Release the capture buffer */
206 CsrFreeCaptureBuffer(CaptureBuffer);
207
208 return (GetConsoleInputRequest->InputsRead > 0);
209 }
210 else // PeekConsoleInput call.
211 {
212 /* Check for success */
213 if (NT_SUCCESS(Status) || NT_SUCCESS(ApiMessage.Status))
214 {
215 /* Return the number of events read */
216 DPRINT("Events read: %lx\n", GetConsoleInputRequest->Length);
217
218 if (lpNumberOfEventsRead != NULL)
219 *lpNumberOfEventsRead = GetConsoleInputRequest->Length;
220
221 /* Copy into the buffer */
222 DPRINT("Copying to buffer\n");
223 RtlCopyMemory(lpBuffer,
224 GetConsoleInputRequest->InputRecord,
225 sizeof(INPUT_RECORD) * GetConsoleInputRequest->Length);
226 }
227 else
228 {
229 if (lpNumberOfEventsRead != NULL)
230 *lpNumberOfEventsRead = 0;
231
232 /* Error out */
233 BaseSetLastNTError(ApiMessage.Status);
234 }
235
236 /* Release the capture buffer */
237 CsrFreeCaptureBuffer(CaptureBuffer);
238
239 /* Return TRUE or FALSE */
240 return NT_SUCCESS(ApiMessage.Status);
241 }
242 }
243
244
245 static
246 BOOL
247 IntReadConsoleOutput(HANDLE hConsoleOutput,
248 PCHAR_INFO lpBuffer,
249 COORD dwBufferSize,
250 COORD dwBufferCoord,
251 PSMALL_RECT lpReadRegion,
252 BOOL bUnicode)
253 {
254 CONSOLE_API_MESSAGE ApiMessage;
255 PCSRSS_READ_CONSOLE_OUTPUT ReadConsoleOutputRequest = &ApiMessage.Data.ReadConsoleOutputRequest;
256 PCSR_CAPTURE_BUFFER CaptureBuffer;
257 DWORD Size, SizeX, SizeY;
258
259 if (lpBuffer == NULL)
260 {
261 SetLastError(ERROR_INVALID_PARAMETER);
262 return FALSE;
263 }
264
265 Size = dwBufferSize.X * dwBufferSize.Y * sizeof(CHAR_INFO);
266
267 DPRINT("IntReadConsoleOutput: %lx %p\n", Size, lpReadRegion);
268
269 /* Allocate a Capture Buffer */
270 CaptureBuffer = CsrAllocateCaptureBuffer(1, Size);
271 if (CaptureBuffer == NULL)
272 {
273 DPRINT1("CsrAllocateCaptureBuffer failed with size 0x%x!\n", Size);
274 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
275 return FALSE;
276 }
277
278 /* Allocate space in the Buffer */
279 CsrAllocateMessagePointer(CaptureBuffer,
280 Size,
281 (PVOID*)&ReadConsoleOutputRequest->CharInfo);
282
283 /* Set up the data to send to the Console Server */
284 ReadConsoleOutputRequest->ConsoleHandle = hConsoleOutput;
285 ReadConsoleOutputRequest->Unicode = bUnicode;
286 ReadConsoleOutputRequest->BufferSize = dwBufferSize;
287 ReadConsoleOutputRequest->BufferCoord = dwBufferCoord;
288 ReadConsoleOutputRequest->ReadRegion = *lpReadRegion;
289
290 /* Call the server */
291 CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
292 CaptureBuffer,
293 CSR_CREATE_API_NUMBER(CONSRV_SERVERDLL_INDEX, ConsolepReadConsoleOutput),
294 sizeof(CSRSS_READ_CONSOLE_OUTPUT));
295 DPRINT("Server returned: %x\n", ApiMessage.Status);
296
297 /* Check for success*/
298 if (NT_SUCCESS(ApiMessage.Status))
299 {
300 /* Copy into the buffer */
301 DPRINT("Copying to buffer\n");
302 SizeX = ReadConsoleOutputRequest->ReadRegion.Right -
303 ReadConsoleOutputRequest->ReadRegion.Left + 1;
304 SizeY = ReadConsoleOutputRequest->ReadRegion.Bottom -
305 ReadConsoleOutputRequest->ReadRegion.Top + 1;
306 RtlCopyMemory(lpBuffer,
307 ReadConsoleOutputRequest->CharInfo,
308 sizeof(CHAR_INFO) * SizeX * SizeY);
309 }
310 else
311 {
312 /* Error out */
313 BaseSetLastNTError(ApiMessage.Status);
314 }
315
316 /* Return the read region */
317 DPRINT("read region: %lx\n", ReadConsoleOutputRequest->ReadRegion);
318 *lpReadRegion = ReadConsoleOutputRequest->ReadRegion;
319
320 /* Release the capture buffer */
321 CsrFreeCaptureBuffer(CaptureBuffer);
322
323 /* Return TRUE or FALSE */
324 return NT_SUCCESS(ApiMessage.Status);
325 }
326
327
328 static
329 BOOL
330 IntReadConsoleOutputCode(HANDLE hConsoleOutput,
331 USHORT CodeType,
332 PVOID pCode,
333 DWORD nLength,
334 COORD dwReadCoord,
335 LPDWORD lpNumberOfCodesRead)
336 {
337 NTSTATUS Status;
338 CONSOLE_API_MESSAGE ApiMessage;
339 PCSRSS_READ_CONSOLE_OUTPUT_CODE ReadConsoleOutputCodeRequest = &ApiMessage.Data.ReadConsoleOutputCodeRequest;
340 PCSR_CAPTURE_BUFFER CaptureBuffer;
341 ULONG SizeBytes, CodeSize;
342 DWORD /*CodesRead = 0,*/ BytesRead;
343
344 /* Determine the needed size */
345 switch (CodeType)
346 {
347 case CODE_ASCII:
348 CodeSize = sizeof(CHAR);
349 break;
350
351 case CODE_UNICODE:
352 CodeSize = sizeof(WCHAR);
353 break;
354
355 case CODE_ATTRIBUTE:
356 CodeSize = sizeof(WORD);
357 break;
358
359 default:
360 SetLastError(ERROR_INVALID_PARAMETER);
361 return FALSE;
362 }
363 SizeBytes = nLength * CodeSize;
364
365 /* Allocate a Capture Buffer */
366 CaptureBuffer = CsrAllocateCaptureBuffer(1, SizeBytes);
367 if (CaptureBuffer == NULL)
368 {
369 DPRINT1("CsrAllocateCaptureBuffer failed!\n");
370 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
371 return FALSE;
372 }
373
374 /* Allocate space in the Buffer */
375 CsrAllocateMessagePointer(CaptureBuffer,
376 SizeBytes,
377 (PVOID*)&ReadConsoleOutputCodeRequest->pCode.pCode);
378
379 /* Start reading */
380 ReadConsoleOutputCodeRequest->ConsoleHandle = hConsoleOutput;
381 ReadConsoleOutputCodeRequest->CodeType = CodeType;
382 ReadConsoleOutputCodeRequest->ReadCoord = dwReadCoord;
383
384 // while (nLength > 0)
385 {
386 ReadConsoleOutputCodeRequest->NumCodesToRead = nLength;
387 // SizeBytes = ReadConsoleOutputCodeRequest->NumCodesToRead * CodeSize;
388
389 Status = CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
390 CaptureBuffer,
391 CSR_CREATE_API_NUMBER(CONSRV_SERVERDLL_INDEX, ConsolepReadConsoleOutputString),
392 sizeof(CSRSS_READ_CONSOLE_OUTPUT_CODE));
393 if (!NT_SUCCESS(Status) || !NT_SUCCESS(Status = ApiMessage.Status))
394 {
395 BaseSetLastNTError(Status);
396 CsrFreeCaptureBuffer(CaptureBuffer);
397 return FALSE;
398 }
399
400 BytesRead = ReadConsoleOutputCodeRequest->CodesRead * CodeSize;
401 memcpy(pCode, ReadConsoleOutputCodeRequest->pCode.pCode, BytesRead);
402 // pCode = (PVOID)((ULONG_PTR)pCode + /*(ULONG_PTR)*/BytesRead);
403 // nLength -= ReadConsoleOutputCodeRequest->CodesRead;
404 // CodesRead += ReadConsoleOutputCodeRequest->CodesRead;
405
406 ReadConsoleOutputCodeRequest->ReadCoord = ReadConsoleOutputCodeRequest->EndCoord;
407 }
408
409 if (lpNumberOfCodesRead != NULL)
410 *lpNumberOfCodesRead = /*CodesRead;*/ ReadConsoleOutputCodeRequest->CodesRead;
411
412 CsrFreeCaptureBuffer(CaptureBuffer);
413
414 return TRUE;
415 }
416
417
418 /*******************
419 * Write functions *
420 *******************/
421
422 static
423 BOOL
424 IntWriteConsole(HANDLE hConsoleOutput,
425 PVOID lpBuffer,
426 DWORD nNumberOfCharsToWrite,
427 LPDWORD lpNumberOfCharsWritten,
428 LPVOID lpReserved,
429 BOOL bUnicode)
430 {
431 PCSR_API_MESSAGE Request;
432 ULONG CsrRequest;
433 NTSTATUS Status;
434 USHORT nChars;
435 ULONG SizeBytes, CharSize;
436 DWORD Written = 0;
437
438 CharSize = (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
439 Request = RtlAllocateHeap(RtlGetProcessHeap(),
440 0,
441 max(sizeof(CSR_API_MESSAGE),
442 CSR_API_MESSAGE_HEADER_SIZE(CSRSS_WRITE_CONSOLE) + min(nNumberOfCharsToWrite,
443 CSRSS_MAX_WRITE_CONSOLE / CharSize) * CharSize));
444 if (Request == NULL)
445 {
446 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
447 return FALSE;
448 }
449
450 CsrRequest = CSR_CREATE_API_NUMBER(CSR_CONSOLE, WRITE_CONSOLE);
451
452 while (nNumberOfCharsToWrite > 0)
453 {
454 Request->Data.WriteConsoleRequest.ConsoleHandle = hConsoleOutput;
455 Request->Data.WriteConsoleRequest.Unicode = bUnicode;
456
457 nChars = (USHORT)min(nNumberOfCharsToWrite, CSRSS_MAX_WRITE_CONSOLE / CharSize);
458 Request->Data.WriteConsoleRequest.NrCharactersToWrite = nChars;
459
460 SizeBytes = nChars * CharSize;
461
462 memcpy(Request->Data.WriteConsoleRequest.Buffer, lpBuffer, SizeBytes);
463
464 Status = CsrClientCallServer(Request,
465 NULL,
466 CsrRequest,
467 max(sizeof(CSR_API_MESSAGE),
468 CSR_API_MESSAGE_HEADER_SIZE(CSRSS_WRITE_CONSOLE) + SizeBytes));
469
470 if (Status == STATUS_PENDING)
471 {
472 WaitForSingleObject(Request->Data.WriteConsoleRequest.UnpauseEvent, INFINITE);
473 CloseHandle(Request->Data.WriteConsoleRequest.UnpauseEvent);
474 continue;
475 }
476 if (!NT_SUCCESS(Status) || !NT_SUCCESS(Status = Request->Status))
477 {
478 RtlFreeHeap(RtlGetProcessHeap(), 0, Request);
479 BaseSetLastNTError(Status);
480 return FALSE;
481 }
482
483 nNumberOfCharsToWrite -= nChars;
484 lpBuffer = (PVOID)((ULONG_PTR)lpBuffer + (ULONG_PTR)SizeBytes);
485 Written += Request->Data.WriteConsoleRequest.NrCharactersWritten;
486 }
487
488 if (lpNumberOfCharsWritten != NULL)
489 {
490 *lpNumberOfCharsWritten = Written;
491 }
492 RtlFreeHeap(RtlGetProcessHeap(), 0, Request);
493
494 return TRUE;
495 }
496
497
498 static
499 BOOL
500 IntWriteConsoleInput(HANDLE hConsoleInput,
501 PINPUT_RECORD lpBuffer,
502 DWORD nLength,
503 LPDWORD lpNumberOfEventsWritten,
504 BOOL bUnicode)
505 {
506 CSR_API_MESSAGE Request;
507 PCSR_CAPTURE_BUFFER CaptureBuffer;
508 DWORD Size;
509
510 if (lpBuffer == NULL)
511 {
512 SetLastError(ERROR_INVALID_PARAMETER);
513 return FALSE;
514 }
515
516 Size = nLength * sizeof(INPUT_RECORD);
517
518 /* Allocate a Capture Buffer */
519 DPRINT("IntWriteConsoleInput: %lx %p\n", Size, lpNumberOfEventsWritten);
520 CaptureBuffer = CsrAllocateCaptureBuffer(1, Size);
521 if (CaptureBuffer == NULL)
522 {
523 DPRINT1("CsrAllocateCaptureBuffer failed!\n");
524 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
525 return FALSE;
526 }
527
528 /* Allocate space in the Buffer */
529 CsrCaptureMessageBuffer(CaptureBuffer,
530 lpBuffer,
531 Size,
532 (PVOID*)&Request.Data.WriteConsoleInputRequest.InputRecord);
533
534 /* Set up the data to send to the Console Server */
535 Request.Data.WriteConsoleInputRequest.ConsoleHandle = hConsoleInput;
536 Request.Data.WriteConsoleInputRequest.Unicode = bUnicode;
537 Request.Data.WriteConsoleInputRequest.Length = nLength;
538
539 /* Call the server */
540 CsrClientCallServer(&Request,
541 CaptureBuffer,
542 CSR_CREATE_API_NUMBER(CSR_CONSOLE, WRITE_CONSOLE_INPUT),
543 sizeof(CSR_API_MESSAGE));
544 DPRINT("Server returned: %x\n", Request.Status);
545
546 /* Check for success*/
547 if (NT_SUCCESS(Request.Status))
548 {
549 /* Return the number of events read */
550 DPRINT("Events read: %lx\n", Request.Data.WriteConsoleInputRequest.Length);
551 *lpNumberOfEventsWritten = Request.Data.WriteConsoleInputRequest.Length;
552 }
553 else
554 {
555 /* Error out */
556 *lpNumberOfEventsWritten = 0;
557 BaseSetLastNTError(Request.Status);
558 }
559
560 /* Release the capture buffer */
561 CsrFreeCaptureBuffer(CaptureBuffer);
562
563 /* Return TRUE or FALSE */
564 return NT_SUCCESS(Request.Status);
565 }
566
567
568 static
569 BOOL
570 IntWriteConsoleOutput(HANDLE hConsoleOutput,
571 CONST CHAR_INFO *lpBuffer,
572 COORD dwBufferSize,
573 COORD dwBufferCoord,
574 PSMALL_RECT lpWriteRegion,
575 BOOL bUnicode)
576 {
577 CSR_API_MESSAGE Request;
578 PCSR_CAPTURE_BUFFER CaptureBuffer;
579 ULONG Size;
580
581 Size = dwBufferSize.Y * dwBufferSize.X * sizeof(CHAR_INFO);
582
583 /* Allocate a Capture Buffer */
584 DPRINT("IntWriteConsoleOutput: %lx %p\n", Size, lpWriteRegion);
585 CaptureBuffer = CsrAllocateCaptureBuffer(1, Size);
586 if (CaptureBuffer == NULL)
587 {
588 DPRINT1("CsrAllocateCaptureBuffer failed!\n");
589 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
590 return FALSE;
591 }
592
593 /* Allocate space in the Buffer */
594 CsrCaptureMessageBuffer(CaptureBuffer,
595 NULL,
596 Size,
597 (PVOID*)&Request.Data.WriteConsoleOutputRequest.CharInfo);
598
599 /* Copy from the buffer */
600 RtlCopyMemory(Request.Data.WriteConsoleOutputRequest.CharInfo, lpBuffer, Size);
601
602 /* Set up the data to send to the Console Server */
603 Request.Data.WriteConsoleOutputRequest.ConsoleHandle = hConsoleOutput;
604 Request.Data.WriteConsoleOutputRequest.Unicode = bUnicode;
605 Request.Data.WriteConsoleOutputRequest.BufferSize = dwBufferSize;
606 Request.Data.WriteConsoleOutputRequest.BufferCoord = dwBufferCoord;
607 Request.Data.WriteConsoleOutputRequest.WriteRegion = *lpWriteRegion;
608
609 /* Call the server */
610 CsrClientCallServer(&Request,
611 CaptureBuffer,
612 CSR_CREATE_API_NUMBER(CSR_CONSOLE, WRITE_CONSOLE_OUTPUT),
613 sizeof(CSR_API_MESSAGE));
614 DPRINT("Server returned: %x\n", Request.Status);
615
616 /* Check for success*/
617 if (!NT_SUCCESS(Request.Status))
618 {
619 /* Error out */
620 BaseSetLastNTError(Request.Status);
621 }
622
623 /* Return the read region */
624 DPRINT("read region: %lx\n", Request.Data.WriteConsoleOutputRequest.WriteRegion);
625 *lpWriteRegion = Request.Data.WriteConsoleOutputRequest.WriteRegion;
626
627 /* Release the capture buffer */
628 CsrFreeCaptureBuffer(CaptureBuffer);
629
630 /* Return TRUE or FALSE */
631 return NT_SUCCESS(Request.Status);
632 }
633
634
635 static
636 BOOL
637 IntWriteConsoleOutputCharacter(HANDLE hConsoleOutput,
638 PVOID lpCharacter,
639 DWORD nLength,
640 COORD dwWriteCoord,
641 LPDWORD lpNumberOfCharsWritten,
642 BOOL bUnicode)
643 {
644 PCSR_API_MESSAGE Request;
645 ULONG CsrRequest;
646 NTSTATUS Status;
647 ULONG CharSize, nChars;
648 //ULONG SizeBytes;
649 DWORD Written = 0;
650
651 CharSize = (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
652
653 nChars = min(nLength, CSRSS_MAX_WRITE_CONSOLE_OUTPUT_CHAR / CharSize);
654 //SizeBytes = nChars * CharSize;
655
656 Request = RtlAllocateHeap(RtlGetProcessHeap(), 0,
657 max(sizeof(CSR_API_MESSAGE),
658 CSR_API_MESSAGE_HEADER_SIZE(CSRSS_WRITE_CONSOLE_OUTPUT_CHAR)
659 + min (nChars, CSRSS_MAX_WRITE_CONSOLE_OUTPUT_CHAR / CharSize) * CharSize));
660 if (Request == NULL)
661 {
662 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
663 return FALSE;
664 }
665
666 CsrRequest = CSR_CREATE_API_NUMBER(CSR_CONSOLE, WRITE_CONSOLE_OUTPUT_CHAR);
667 Request->Data.WriteConsoleOutputCharRequest.Coord = dwWriteCoord;
668
669 while (nLength > 0)
670 {
671 DWORD BytesWrite;
672
673 Request->Data.WriteConsoleOutputCharRequest.ConsoleHandle = hConsoleOutput;
674 Request->Data.WriteConsoleOutputCharRequest.Unicode = bUnicode;
675 Request->Data.WriteConsoleOutputCharRequest.Length = (WORD)min(nLength, nChars);
676 BytesWrite = Request->Data.WriteConsoleOutputCharRequest.Length * CharSize;
677
678 memcpy(Request->Data.WriteConsoleOutputCharRequest.String, lpCharacter, BytesWrite);
679
680 Status = CsrClientCallServer(Request,
681 NULL,
682 CsrRequest,
683 max(sizeof(CSR_API_MESSAGE),
684 CSR_API_MESSAGE_HEADER_SIZE(CSRSS_WRITE_CONSOLE_OUTPUT_CHAR) + BytesWrite));
685
686 if (!NT_SUCCESS(Status) || !NT_SUCCESS(Status = Request->Status))
687 {
688 RtlFreeHeap(RtlGetProcessHeap(), 0, Request);
689 BaseSetLastNTError(Status);
690 return FALSE;
691 }
692
693 nLength -= Request->Data.WriteConsoleOutputCharRequest.NrCharactersWritten;
694 lpCharacter = (PVOID)((ULONG_PTR)lpCharacter + (ULONG_PTR)(Request->Data.WriteConsoleOutputCharRequest.NrCharactersWritten * CharSize));
695 Written += Request->Data.WriteConsoleOutputCharRequest.NrCharactersWritten;
696
697 Request->Data.WriteConsoleOutputCharRequest.Coord = Request->Data.WriteConsoleOutputCharRequest.EndCoord;
698 }
699
700 if (lpNumberOfCharsWritten != NULL)
701 {
702 *lpNumberOfCharsWritten = Written;
703 }
704
705 RtlFreeHeap(RtlGetProcessHeap(), 0, Request);
706
707 return TRUE;
708 }
709
710
711 static
712 BOOL
713 IntFillConsoleOutputCharacter(HANDLE hConsoleOutput,
714 PVOID cCharacter,
715 DWORD nLength,
716 COORD dwWriteCoord,
717 LPDWORD lpNumberOfCharsWritten,
718 BOOL bUnicode)
719 {
720 CSR_API_MESSAGE Request;
721 NTSTATUS Status;
722
723 Request.Data.FillOutputRequest.ConsoleHandle = hConsoleOutput;
724 Request.Data.FillOutputRequest.Unicode = bUnicode;
725
726 if(bUnicode)
727 Request.Data.FillOutputRequest.Char.UnicodeChar = *((WCHAR*)cCharacter);
728 else
729 Request.Data.FillOutputRequest.Char.AsciiChar = *((CHAR*)cCharacter);
730
731 Request.Data.FillOutputRequest.Position = dwWriteCoord;
732 Request.Data.FillOutputRequest.Length = (WORD)nLength;
733
734 Status = CsrClientCallServer(&Request,
735 NULL,
736 CSR_CREATE_API_NUMBER(CSR_CONSOLE, FILL_OUTPUT),
737 sizeof(CSR_API_MESSAGE));
738
739 if (!NT_SUCCESS(Status) || !NT_SUCCESS(Status = Request.Status))
740 {
741 BaseSetLastNTError(Status);
742 return FALSE;
743 }
744
745 if(lpNumberOfCharsWritten != NULL)
746 {
747 *lpNumberOfCharsWritten = Request.Data.FillOutputRequest.NrCharactersWritten;
748 }
749
750 return TRUE;
751 }
752
753
754 /* FUNCTIONS ******************************************************************/
755
756 /******************
757 * Read functions *
758 ******************/
759
760 /*--------------------------------------------------------------
761 * ReadConsoleW
762 *
763 * @implemented
764 */
765 BOOL
766 WINAPI
767 ReadConsoleW(HANDLE hConsoleInput,
768 LPVOID lpBuffer,
769 DWORD nNumberOfCharsToRead,
770 LPDWORD lpNumberOfCharsRead,
771 PCONSOLE_READCONSOLE_CONTROL pInputControl)
772 {
773 return IntReadConsole(hConsoleInput,
774 lpBuffer,
775 nNumberOfCharsToRead,
776 lpNumberOfCharsRead,
777 pInputControl,
778 TRUE);
779 }
780
781
782 /*--------------------------------------------------------------
783 * ReadConsoleA
784 *
785 * @implemented
786 */
787 BOOL
788 WINAPI
789 ReadConsoleA(HANDLE hConsoleInput,
790 LPVOID lpBuffer,
791 DWORD nNumberOfCharsToRead,
792 LPDWORD lpNumberOfCharsRead,
793 PCONSOLE_READCONSOLE_CONTROL pInputControl)
794 {
795 return IntReadConsole(hConsoleInput,
796 lpBuffer,
797 nNumberOfCharsToRead,
798 lpNumberOfCharsRead,
799 NULL,
800 FALSE);
801 }
802
803
804 /*--------------------------------------------------------------
805 * PeekConsoleInputW
806 *
807 * @implemented
808 */
809 BOOL
810 WINAPI
811 PeekConsoleInputW(HANDLE hConsoleInput,
812 PINPUT_RECORD lpBuffer,
813 DWORD nLength,
814 LPDWORD lpNumberOfEventsRead)
815 {
816 return IntGetConsoleInput(hConsoleInput,
817 FALSE,
818 lpBuffer,
819 nLength,
820 lpNumberOfEventsRead,
821 TRUE);
822 }
823
824
825 /*--------------------------------------------------------------
826 * PeekConsoleInputA
827 *
828 * @implemented
829 */
830 BOOL
831 WINAPI
832 PeekConsoleInputA(HANDLE hConsoleInput,
833 PINPUT_RECORD lpBuffer,
834 DWORD nLength,
835 LPDWORD lpNumberOfEventsRead)
836 {
837 return IntGetConsoleInput(hConsoleInput,
838 FALSE,
839 lpBuffer,
840 nLength,
841 lpNumberOfEventsRead,
842 FALSE);
843 }
844
845
846 /*--------------------------------------------------------------
847 * ReadConsoleInputW
848 *
849 * @implemented
850 */
851 BOOL
852 WINAPI
853 ReadConsoleInputW(HANDLE hConsoleInput,
854 PINPUT_RECORD lpBuffer,
855 DWORD nLength,
856 LPDWORD lpNumberOfEventsRead)
857 {
858 return IntGetConsoleInput(hConsoleInput,
859 TRUE,
860 lpBuffer,
861 nLength,
862 lpNumberOfEventsRead,
863 TRUE);
864 }
865
866
867 /*--------------------------------------------------------------
868 * ReadConsoleInputA
869 *
870 * @implemented
871 */
872 BOOL
873 WINAPI
874 ReadConsoleInputA(HANDLE hConsoleInput,
875 PINPUT_RECORD lpBuffer,
876 DWORD nLength,
877 LPDWORD lpNumberOfEventsRead)
878 {
879 return IntGetConsoleInput(hConsoleInput,
880 TRUE,
881 lpBuffer,
882 nLength,
883 lpNumberOfEventsRead,
884 FALSE);
885 }
886
887
888 BOOL
889 WINAPI
890 ReadConsoleInputExW(HANDLE hConsole, LPVOID lpBuffer, DWORD dwLen, LPDWORD Unknown1, DWORD Unknown2)
891 {
892 STUB;
893 return FALSE;
894 }
895
896
897 BOOL
898 WINAPI
899 ReadConsoleInputExA(HANDLE hConsole, LPVOID lpBuffer, DWORD dwLen, LPDWORD Unknown1, DWORD Unknown2)
900 {
901 STUB;
902 return FALSE;
903 }
904
905
906 /*--------------------------------------------------------------
907 * ReadConsoleOutputW
908 *
909 * @implemented
910 */
911 BOOL
912 WINAPI
913 ReadConsoleOutputW(HANDLE hConsoleOutput,
914 PCHAR_INFO lpBuffer,
915 COORD dwBufferSize,
916 COORD dwBufferCoord,
917 PSMALL_RECT lpReadRegion)
918 {
919 return IntReadConsoleOutput(hConsoleOutput,
920 lpBuffer,
921 dwBufferSize,
922 dwBufferCoord,
923 lpReadRegion,
924 TRUE);
925 }
926
927
928 /*--------------------------------------------------------------
929 * ReadConsoleOutputA
930 *
931 * @implemented
932 */
933 BOOL
934 WINAPI
935 ReadConsoleOutputA(HANDLE hConsoleOutput,
936 PCHAR_INFO lpBuffer,
937 COORD dwBufferSize,
938 COORD dwBufferCoord,
939 PSMALL_RECT lpReadRegion)
940 {
941 return IntReadConsoleOutput(hConsoleOutput,
942 lpBuffer,
943 dwBufferSize,
944 dwBufferCoord,
945 lpReadRegion,
946 FALSE);
947 }
948
949
950 /*--------------------------------------------------------------
951 * ReadConsoleOutputCharacterW
952 *
953 * @implemented
954 */
955 BOOL
956 WINAPI
957 ReadConsoleOutputCharacterW(HANDLE hConsoleOutput,
958 LPWSTR lpCharacter,
959 DWORD nLength,
960 COORD dwReadCoord,
961 LPDWORD lpNumberOfCharsRead)
962 {
963 return IntReadConsoleOutputCode(hConsoleOutput,
964 CODE_UNICODE,
965 lpCharacter,
966 nLength,
967 dwReadCoord,
968 lpNumberOfCharsRead);
969 }
970
971
972 /*--------------------------------------------------------------
973 * ReadConsoleOutputCharacterA
974 *
975 * @implemented
976 */
977 BOOL
978 WINAPI
979 ReadConsoleOutputCharacterA(HANDLE hConsoleOutput,
980 LPSTR lpCharacter,
981 DWORD nLength,
982 COORD dwReadCoord,
983 LPDWORD lpNumberOfCharsRead)
984 {
985 return IntReadConsoleOutputCode(hConsoleOutput,
986 CODE_ASCII,
987 lpCharacter,
988 nLength,
989 dwReadCoord,
990 lpNumberOfCharsRead);
991 }
992
993
994 /*--------------------------------------------------------------
995 * ReadConsoleOutputAttribute
996 *
997 * @implemented
998 */
999 BOOL
1000 WINAPI
1001 ReadConsoleOutputAttribute(HANDLE hConsoleOutput,
1002 LPWORD lpAttribute,
1003 DWORD nLength,
1004 COORD dwReadCoord,
1005 LPDWORD lpNumberOfAttrsRead)
1006 {
1007 return IntReadConsoleOutputCode(hConsoleOutput,
1008 CODE_ATTRIBUTE,
1009 lpAttribute,
1010 nLength,
1011 dwReadCoord,
1012 lpNumberOfAttrsRead);
1013 }
1014
1015
1016 /*******************
1017 * Write functions *
1018 *******************/
1019
1020 /*--------------------------------------------------------------
1021 * WriteConsoleW
1022 *
1023 * @implemented
1024 */
1025 BOOL
1026 WINAPI
1027 WriteConsoleW(HANDLE hConsoleOutput,
1028 CONST VOID *lpBuffer,
1029 DWORD nNumberOfCharsToWrite,
1030 LPDWORD lpNumberOfCharsWritten,
1031 LPVOID lpReserved)
1032 {
1033 return IntWriteConsole(hConsoleOutput,
1034 (PVOID)lpBuffer,
1035 nNumberOfCharsToWrite,
1036 lpNumberOfCharsWritten,
1037 lpReserved,
1038 TRUE);
1039 }
1040
1041
1042 /*--------------------------------------------------------------
1043 * WriteConsoleA
1044 *
1045 * @implemented
1046 */
1047 BOOL
1048 WINAPI
1049 WriteConsoleA(HANDLE hConsoleOutput,
1050 CONST VOID *lpBuffer,
1051 DWORD nNumberOfCharsToWrite,
1052 LPDWORD lpNumberOfCharsWritten,
1053 LPVOID lpReserved)
1054 {
1055 return IntWriteConsole(hConsoleOutput,
1056 (PVOID)lpBuffer,
1057 nNumberOfCharsToWrite,
1058 lpNumberOfCharsWritten,
1059 lpReserved,
1060 FALSE);
1061 }
1062
1063
1064 /*--------------------------------------------------------------
1065 * WriteConsoleInputW
1066 *
1067 * @implemented
1068 */
1069 BOOL
1070 WINAPI
1071 WriteConsoleInputW(HANDLE hConsoleInput,
1072 CONST INPUT_RECORD *lpBuffer,
1073 DWORD nLength,
1074 LPDWORD lpNumberOfEventsWritten)
1075 {
1076 return IntWriteConsoleInput(hConsoleInput,
1077 (PINPUT_RECORD)lpBuffer,
1078 nLength,
1079 lpNumberOfEventsWritten,
1080 TRUE);
1081 }
1082
1083
1084 /*--------------------------------------------------------------
1085 * WriteConsoleInputA
1086 *
1087 * @implemented
1088 */
1089 BOOL
1090 WINAPI
1091 WriteConsoleInputA(HANDLE hConsoleInput,
1092 CONST INPUT_RECORD *lpBuffer,
1093 DWORD nLength,
1094 LPDWORD lpNumberOfEventsWritten)
1095 {
1096 return IntWriteConsoleInput(hConsoleInput,
1097 (PINPUT_RECORD)lpBuffer,
1098 nLength,
1099 lpNumberOfEventsWritten,
1100 FALSE);
1101 }
1102
1103
1104 /*--------------------------------------------------------------
1105 * WriteConsoleOutputW
1106 *
1107 * @implemented
1108 */
1109 BOOL
1110 WINAPI
1111 WriteConsoleOutputW(HANDLE hConsoleOutput,
1112 CONST CHAR_INFO *lpBuffer,
1113 COORD dwBufferSize,
1114 COORD dwBufferCoord,
1115 PSMALL_RECT lpWriteRegion)
1116 {
1117 return IntWriteConsoleOutput(hConsoleOutput,
1118 lpBuffer,
1119 dwBufferSize,
1120 dwBufferCoord,
1121 lpWriteRegion,
1122 TRUE);
1123 }
1124
1125
1126 /*--------------------------------------------------------------
1127 * WriteConsoleOutputA
1128 *
1129 * @implemented
1130 */
1131 BOOL
1132 WINAPI
1133 WriteConsoleOutputA(HANDLE hConsoleOutput,
1134 CONST CHAR_INFO *lpBuffer,
1135 COORD dwBufferSize,
1136 COORD dwBufferCoord,
1137 PSMALL_RECT lpWriteRegion)
1138 {
1139 return IntWriteConsoleOutput(hConsoleOutput,
1140 lpBuffer,
1141 dwBufferSize,
1142 dwBufferCoord,
1143 lpWriteRegion,
1144 FALSE);
1145 }
1146
1147
1148 /*--------------------------------------------------------------
1149 * WriteConsoleOutputCharacterW
1150 *
1151 * @implemented
1152 */
1153 BOOL
1154 WINAPI
1155 WriteConsoleOutputCharacterW(HANDLE hConsoleOutput,
1156 LPCWSTR lpCharacter,
1157 DWORD nLength,
1158 COORD dwWriteCoord,
1159 LPDWORD lpNumberOfCharsWritten)
1160 {
1161 return IntWriteConsoleOutputCharacter(hConsoleOutput,
1162 (PVOID)lpCharacter,
1163 nLength,
1164 dwWriteCoord,
1165 lpNumberOfCharsWritten,
1166 TRUE);
1167 }
1168
1169
1170 /*--------------------------------------------------------------
1171 * WriteConsoleOutputCharacterA
1172 *
1173 * @implemented
1174 */
1175 BOOL
1176 WINAPI
1177 WriteConsoleOutputCharacterA(HANDLE hConsoleOutput,
1178 LPCSTR lpCharacter,
1179 DWORD nLength,
1180 COORD dwWriteCoord,
1181 LPDWORD lpNumberOfCharsWritten)
1182 {
1183 return IntWriteConsoleOutputCharacter(hConsoleOutput,
1184 (PVOID)lpCharacter,
1185 nLength,
1186 dwWriteCoord,
1187 lpNumberOfCharsWritten,
1188 FALSE);
1189 }
1190
1191
1192 /*--------------------------------------------------------------
1193 * WriteConsoleOutputAttribute
1194 *
1195 * @implemented
1196 */
1197 BOOL
1198 WINAPI
1199 WriteConsoleOutputAttribute(HANDLE hConsoleOutput,
1200 CONST WORD *lpAttribute,
1201 DWORD nLength,
1202 COORD dwWriteCoord,
1203 LPDWORD lpNumberOfAttrsWritten)
1204 {
1205 PCSR_API_MESSAGE Request;
1206 ULONG CsrRequest;
1207 NTSTATUS Status;
1208 WORD Size;
1209
1210 Request = RtlAllocateHeap(RtlGetProcessHeap(),
1211 0,
1212 max(sizeof(CSR_API_MESSAGE),
1213 CSR_API_MESSAGE_HEADER_SIZE(CSRSS_WRITE_CONSOLE_OUTPUT_ATTRIB)
1214 + min(nLength, CSRSS_MAX_WRITE_CONSOLE_OUTPUT_ATTRIB / sizeof(WORD)) * sizeof(WORD)));
1215 if (Request == NULL)
1216 {
1217 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1218 return FALSE;
1219 }
1220
1221 CsrRequest = CSR_CREATE_API_NUMBER(CSR_CONSOLE, WRITE_CONSOLE_OUTPUT_ATTRIB);
1222 Request->Data.WriteConsoleOutputAttribRequest.Coord = dwWriteCoord;
1223
1224 if (lpNumberOfAttrsWritten)
1225 *lpNumberOfAttrsWritten = nLength;
1226 while (nLength)
1227 {
1228 Size = (WORD)min(nLength, CSRSS_MAX_WRITE_CONSOLE_OUTPUT_ATTRIB / sizeof(WORD));
1229 Request->Data.WriteConsoleOutputAttribRequest.ConsoleHandle = hConsoleOutput;
1230 Request->Data.WriteConsoleOutputAttribRequest.Length = Size;
1231 memcpy(Request->Data.WriteConsoleOutputAttribRequest.Attribute, lpAttribute, Size * sizeof(WORD));
1232
1233 Status = CsrClientCallServer(Request,
1234 NULL,
1235 CsrRequest,
1236 max(sizeof(CSR_API_MESSAGE),
1237 CSR_API_MESSAGE_HEADER_SIZE(CSRSS_WRITE_CONSOLE_OUTPUT_ATTRIB) + Size * sizeof(WORD)));
1238
1239 if (!NT_SUCCESS(Status) || !NT_SUCCESS(Status = Request->Status))
1240 {
1241 RtlFreeHeap(RtlGetProcessHeap(), 0, Request);
1242 BaseSetLastNTError (Status);
1243 return FALSE;
1244 }
1245 nLength -= Size;
1246 lpAttribute += Size;
1247 Request->Data.WriteConsoleOutputAttribRequest.Coord = Request->Data.WriteConsoleOutputAttribRequest.EndCoord;
1248 }
1249
1250 RtlFreeHeap(RtlGetProcessHeap(), 0, Request);
1251
1252 return TRUE;
1253 }
1254
1255
1256 /*--------------------------------------------------------------
1257 * FillConsoleOutputCharacterW
1258 *
1259 * @implemented
1260 */
1261 BOOL
1262 WINAPI
1263 FillConsoleOutputCharacterW(HANDLE hConsoleOutput,
1264 WCHAR cCharacter,
1265 DWORD nLength,
1266 COORD dwWriteCoord,
1267 LPDWORD lpNumberOfCharsWritten)
1268 {
1269 return IntFillConsoleOutputCharacter(hConsoleOutput,
1270 &cCharacter,
1271 nLength,
1272 dwWriteCoord,
1273 lpNumberOfCharsWritten,
1274 TRUE);
1275 }
1276
1277
1278 /*--------------------------------------------------------------
1279 * FillConsoleOutputCharacterA
1280 *
1281 * @implemented
1282 */
1283 BOOL
1284 WINAPI
1285 FillConsoleOutputCharacterA(HANDLE hConsoleOutput,
1286 CHAR cCharacter,
1287 DWORD nLength,
1288 COORD dwWriteCoord,
1289 LPDWORD lpNumberOfCharsWritten)
1290 {
1291 return IntFillConsoleOutputCharacter(hConsoleOutput,
1292 &cCharacter,
1293 nLength,
1294 dwWriteCoord,
1295 lpNumberOfCharsWritten,
1296 FALSE);
1297 }
1298
1299
1300 /*--------------------------------------------------------------
1301 * FillConsoleOutputAttribute
1302 *
1303 * @implemented
1304 */
1305 BOOL
1306 WINAPI
1307 FillConsoleOutputAttribute(HANDLE hConsoleOutput,
1308 WORD wAttribute,
1309 DWORD nLength,
1310 COORD dwWriteCoord,
1311 LPDWORD lpNumberOfAttrsWritten)
1312 {
1313 CSR_API_MESSAGE Request;
1314 NTSTATUS Status;
1315
1316 Request.Data.FillOutputAttribRequest.ConsoleHandle = hConsoleOutput;
1317 Request.Data.FillOutputAttribRequest.Attribute = (CHAR)wAttribute;
1318 Request.Data.FillOutputAttribRequest.Coord = dwWriteCoord;
1319 Request.Data.FillOutputAttribRequest.Length = (WORD)nLength;
1320
1321 Status = CsrClientCallServer(&Request,
1322 NULL,
1323 CSR_CREATE_API_NUMBER(CSR_CONSOLE, FILL_OUTPUT_ATTRIB),
1324 sizeof(CSR_API_MESSAGE));
1325 if (!NT_SUCCESS(Status) || !NT_SUCCESS(Status = Request.Status))
1326 {
1327 BaseSetLastNTError ( Status );
1328 return FALSE;
1329 }
1330
1331 if (lpNumberOfAttrsWritten)
1332 *lpNumberOfAttrsWritten = nLength;
1333
1334 return TRUE;
1335 }
1336
1337 /* EOF */