[KERNEL32][CONSRV]
[reactos.git] / win32ss / user / winsrv / consrv / coninput.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Console Server DLL
4 * FILE: win32ss/user/winsrv/consrv/coninput.c
5 * PURPOSE: Console Input functions
6 * PROGRAMMERS: Jeffrey Morlan
7 * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
8 */
9
10 /* INCLUDES *******************************************************************/
11
12 #include "consrv.h"
13
14 #define NDEBUG
15 #include <debug.h>
16
17 /* GLOBALS ********************************************************************/
18
19 #define ConSrvGetInputBuffer(ProcessData, Handle, Ptr, Access, LockConsole) \
20 ConSrvGetObject((ProcessData), (Handle), (PCONSOLE_IO_OBJECT*)(Ptr), NULL, \
21 (Access), (LockConsole), INPUT_BUFFER)
22 #define ConSrvGetInputBufferAndHandleEntry(ProcessData, Handle, Ptr, Entry, Access, LockConsole) \
23 ConSrvGetObject((ProcessData), (Handle), (PCONSOLE_IO_OBJECT*)(Ptr), (Entry), \
24 (Access), (LockConsole), INPUT_BUFFER)
25 #define ConSrvReleaseInputBuffer(Buff, IsConsoleLocked) \
26 ConSrvReleaseObject(&(Buff)->Header, (IsConsoleLocked))
27
28
29 typedef struct _GET_INPUT_INFO
30 {
31 PCSR_THREAD CallingThread; // The thread which called the input API.
32 PVOID HandleEntry; // The handle data associated with the wait thread.
33 PCONSOLE_INPUT_BUFFER InputBuffer; // The input buffer corresponding to the handle.
34 } GET_INPUT_INFO, *PGET_INPUT_INFO;
35
36
37 /* PRIVATE FUNCTIONS **********************************************************/
38
39 static NTSTATUS
40 WaitBeforeReading(IN PGET_INPUT_INFO InputInfo,
41 IN PCSR_API_MESSAGE ApiMessage,
42 IN CSR_WAIT_FUNCTION WaitFunction OPTIONAL,
43 IN BOOLEAN CreateWaitBlock OPTIONAL)
44 {
45 if (CreateWaitBlock)
46 {
47 PGET_INPUT_INFO CapturedInputInfo;
48
49 CapturedInputInfo = ConsoleAllocHeap(0, sizeof(GET_INPUT_INFO));
50 if (!CapturedInputInfo) return STATUS_NO_MEMORY;
51
52 RtlMoveMemory(CapturedInputInfo, InputInfo, sizeof(GET_INPUT_INFO));
53
54 if (!CsrCreateWait(&InputInfo->InputBuffer->Header.Console->ReadWaitQueue,
55 WaitFunction,
56 InputInfo->CallingThread,
57 ApiMessage,
58 CapturedInputInfo))
59 {
60 ConsoleFreeHeap(CapturedInputInfo);
61 return STATUS_NO_MEMORY;
62 }
63 }
64
65 /* Wait for input */
66 return STATUS_PENDING;
67 }
68
69 static NTSTATUS
70 ReadChars(IN PGET_INPUT_INFO InputInfo,
71 IN PCSR_API_MESSAGE ApiMessage,
72 IN BOOLEAN CreateWaitBlock OPTIONAL);
73
74 // Wait function CSR_WAIT_FUNCTION
75 static BOOLEAN
76 NTAPI
77 ReadCharsThread(IN PLIST_ENTRY WaitList,
78 IN PCSR_THREAD WaitThread,
79 IN PCSR_API_MESSAGE WaitApiMessage,
80 IN PVOID WaitContext,
81 IN PVOID WaitArgument1,
82 IN PVOID WaitArgument2,
83 IN ULONG WaitFlags)
84 {
85 NTSTATUS Status;
86 PGET_INPUT_INFO InputInfo = (PGET_INPUT_INFO)WaitContext;
87
88 PVOID InputHandle = WaitArgument2;
89
90 DPRINT("ReadCharsThread - WaitContext = 0x%p, WaitArgument1 = 0x%p, WaitArgument2 = 0x%p, WaitFlags = %lu\n", WaitContext, WaitArgument1, WaitArgument2, WaitFlags);
91
92 /*
93 * If we are notified of the process termination via a call
94 * to CsrNotifyWaitBlock triggered by CsrDestroyProcess or
95 * CsrDestroyThread, just return.
96 */
97 if (WaitFlags & CsrProcessTerminating)
98 {
99 Status = STATUS_THREAD_IS_TERMINATING;
100 goto Quit;
101 }
102
103 /*
104 * Somebody is closing a handle to this input buffer,
105 * by calling ConSrvCloseHandleEntry.
106 * See whether we are linked to that handle (ie. we
107 * are a waiter for this handle), and if so, return.
108 * Otherwise, ignore the call and continue waiting.
109 */
110 if (InputHandle != NULL)
111 {
112 Status = (InputHandle == InputInfo->HandleEntry ? STATUS_ALERTED
113 : STATUS_PENDING);
114 goto Quit;
115 }
116
117 /*
118 * If we go there, that means we are notified for some new input.
119 * The console is therefore already locked.
120 */
121 Status = ReadChars(InputInfo, WaitApiMessage, FALSE);
122
123 Quit:
124 if (Status != STATUS_PENDING)
125 {
126 WaitApiMessage->Status = Status;
127 ConsoleFreeHeap(InputInfo);
128 }
129
130 return (Status == STATUS_PENDING ? FALSE : TRUE);
131 }
132
133 NTSTATUS NTAPI
134 ConDrvReadConsole(IN PCONSOLE Console,
135 IN PCONSOLE_INPUT_BUFFER InputBuffer,
136 /**/IN PUNICODE_STRING ExeName /**/OPTIONAL/**/,/**/
137 IN BOOLEAN Unicode,
138 OUT PVOID Buffer,
139 IN OUT PCONSOLE_READCONSOLE_CONTROL ReadControl,
140 IN ULONG NumCharsToRead,
141 OUT PULONG NumCharsRead OPTIONAL);
142 static NTSTATUS
143 ReadChars(IN PGET_INPUT_INFO InputInfo,
144 IN PCSR_API_MESSAGE ApiMessage,
145 IN BOOLEAN CreateWaitBlock OPTIONAL)
146 {
147 NTSTATUS Status;
148 PCONSOLE_READCONSOLE ReadConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ReadConsoleRequest;
149 PCONSOLE_INPUT_BUFFER InputBuffer = InputInfo->InputBuffer;
150 CONSOLE_READCONSOLE_CONTROL ReadControl;
151
152 UNICODE_STRING ExeName;
153
154 PVOID Buffer;
155 ULONG NrCharactersRead = 0;
156 ULONG CharSize = (ReadConsoleRequest->Unicode ? sizeof(WCHAR) : sizeof(CHAR));
157
158 /* Compute the executable name, if needed */
159 if (ReadConsoleRequest->InitialNumBytes == 0 &&
160 ReadConsoleRequest->ExeLength <= sizeof(ReadConsoleRequest->StaticBuffer))
161 {
162 ExeName.Length = ExeName.MaximumLength = ReadConsoleRequest->ExeLength;
163 ExeName.Buffer = (PWCHAR)ReadConsoleRequest->StaticBuffer;
164 }
165 else
166 {
167 ExeName.Length = ExeName.MaximumLength = 0;
168 ExeName.Buffer = NULL;
169 }
170
171 /* Build the ReadControl structure */
172 ReadControl.nLength = sizeof(CONSOLE_READCONSOLE_CONTROL);
173 ReadControl.nInitialChars = ReadConsoleRequest->InitialNumBytes / CharSize;
174 ReadControl.dwCtrlWakeupMask = ReadConsoleRequest->CtrlWakeupMask;
175 ReadControl.dwControlKeyState = ReadConsoleRequest->ControlKeyState;
176
177 /*
178 * For optimization purposes, Windows (and hence ReactOS, too, for
179 * compatibility reasons) uses a static buffer if no more than eighty
180 * bytes are read. Otherwise a new buffer is used.
181 * The client-side expects that we know this behaviour.
182 */
183 if (ReadConsoleRequest->CaptureBufferSize <= sizeof(ReadConsoleRequest->StaticBuffer))
184 {
185 /*
186 * Adjust the internal pointer, because its old value points to
187 * the static buffer in the original ApiMessage structure.
188 */
189 // ReadConsoleRequest->Buffer = ReadConsoleRequest->StaticBuffer;
190 Buffer = ReadConsoleRequest->StaticBuffer;
191 }
192 else
193 {
194 Buffer = ReadConsoleRequest->Buffer;
195 }
196
197 DPRINT1("Calling ConDrvReadConsole(%wZ)\n", &ExeName);
198 Status = ConDrvReadConsole(InputBuffer->Header.Console,
199 InputBuffer,
200 &ExeName,
201 ReadConsoleRequest->Unicode,
202 Buffer,
203 &ReadControl,
204 ReadConsoleRequest->NumBytes / CharSize, // NrCharactersToRead
205 &NrCharactersRead);
206 DPRINT1("ConDrvReadConsole returned (%d ; Status = 0x%08x)\n",
207 NrCharactersRead, Status);
208
209 // ReadConsoleRequest->ControlKeyState = ReadControl.dwControlKeyState;
210
211 if (Status == STATUS_PENDING)
212 {
213 /* We haven't completed a read, so start a wait */
214 return WaitBeforeReading(InputInfo,
215 ApiMessage,
216 ReadCharsThread,
217 CreateWaitBlock);
218 }
219 else
220 {
221 /*
222 * We read all what we wanted. Set the number of bytes read and
223 * return the error code we were given.
224 */
225 ReadConsoleRequest->NumBytes = NrCharactersRead * CharSize;
226 ReadConsoleRequest->ControlKeyState = ReadControl.dwControlKeyState;
227
228 return Status;
229 // return STATUS_SUCCESS;
230 }
231 }
232
233 static NTSTATUS
234 ReadInputBuffer(IN PGET_INPUT_INFO InputInfo,
235 IN PCSR_API_MESSAGE ApiMessage,
236 IN BOOLEAN CreateWaitBlock OPTIONAL);
237
238 // Wait function CSR_WAIT_FUNCTION
239 static BOOLEAN
240 NTAPI
241 ReadInputBufferThread(IN PLIST_ENTRY WaitList,
242 IN PCSR_THREAD WaitThread,
243 IN PCSR_API_MESSAGE WaitApiMessage,
244 IN PVOID WaitContext,
245 IN PVOID WaitArgument1,
246 IN PVOID WaitArgument2,
247 IN ULONG WaitFlags)
248 {
249 NTSTATUS Status;
250 PGET_INPUT_INFO InputInfo = (PGET_INPUT_INFO)WaitContext;
251
252 PVOID InputHandle = WaitArgument2;
253
254 DPRINT("ReadInputBufferThread - WaitContext = 0x%p, WaitArgument1 = 0x%p, WaitArgument2 = 0x%p, WaitFlags = %lu\n", WaitContext, WaitArgument1, WaitArgument2, WaitFlags);
255
256 /*
257 * If we are notified of the process termination via a call
258 * to CsrNotifyWaitBlock triggered by CsrDestroyProcess or
259 * CsrDestroyThread, just return.
260 */
261 if (WaitFlags & CsrProcessTerminating)
262 {
263 Status = STATUS_THREAD_IS_TERMINATING;
264 goto Quit;
265 }
266
267 /*
268 * Somebody is closing a handle to this input buffer,
269 * by calling ConSrvCloseHandleEntry.
270 * See whether we are linked to that handle (ie. we
271 * are a waiter for this handle), and if so, return.
272 * Otherwise, ignore the call and continue waiting.
273 */
274 if (InputHandle != NULL)
275 {
276 Status = (InputHandle == InputInfo->HandleEntry ? STATUS_ALERTED
277 : STATUS_PENDING);
278 goto Quit;
279 }
280
281 /*
282 * If we go there, that means we are notified for some new input.
283 * The console is therefore already locked.
284 */
285 Status = ReadInputBuffer(InputInfo, WaitApiMessage, FALSE);
286
287 Quit:
288 if (Status != STATUS_PENDING)
289 {
290 WaitApiMessage->Status = Status;
291 ConsoleFreeHeap(InputInfo);
292 }
293
294 return (Status == STATUS_PENDING ? FALSE : TRUE);
295 }
296
297 NTSTATUS NTAPI
298 ConDrvGetConsoleInput(IN PCONSOLE Console,
299 IN PCONSOLE_INPUT_BUFFER InputBuffer,
300 IN BOOLEAN KeepEvents,
301 IN BOOLEAN WaitForMoreEvents,
302 IN BOOLEAN Unicode,
303 OUT PINPUT_RECORD InputRecord,
304 IN ULONG NumEventsToRead,
305 OUT PULONG NumEventsRead OPTIONAL);
306 static NTSTATUS
307 ReadInputBuffer(IN PGET_INPUT_INFO InputInfo,
308 IN PCSR_API_MESSAGE ApiMessage,
309 IN BOOLEAN CreateWaitBlock OPTIONAL)
310 {
311 NTSTATUS Status;
312 PCONSOLE_GETINPUT GetInputRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetInputRequest;
313 PCONSOLE_INPUT_BUFFER InputBuffer = InputInfo->InputBuffer;
314 ULONG NumEventsRead;
315
316 PINPUT_RECORD InputRecord;
317
318 /*
319 * For optimization purposes, Windows (and hence ReactOS, too, for
320 * compatibility reasons) uses a static buffer if no more than five
321 * input records are read. Otherwise a new buffer is used.
322 * The client-side expects that we know this behaviour.
323 */
324 if (GetInputRequest->NumRecords <= sizeof(GetInputRequest->RecordStaticBuffer)/sizeof(INPUT_RECORD))
325 {
326 /*
327 * Adjust the internal pointer, because its old value points to
328 * the static buffer in the original ApiMessage structure.
329 */
330 // GetInputRequest->RecordBufPtr = GetInputRequest->RecordStaticBuffer;
331 InputRecord = GetInputRequest->RecordStaticBuffer;
332 }
333 else
334 {
335 InputRecord = GetInputRequest->RecordBufPtr;
336 }
337
338 NumEventsRead = 0;
339 Status = ConDrvGetConsoleInput(InputBuffer->Header.Console,
340 InputBuffer,
341 (GetInputRequest->Flags & CONSOLE_READ_KEEPEVENT) != 0,
342 (GetInputRequest->Flags & CONSOLE_READ_CONTINUE ) == 0,
343 GetInputRequest->Unicode,
344 InputRecord,
345 GetInputRequest->NumRecords,
346 &NumEventsRead);
347
348 if (Status == STATUS_PENDING)
349 {
350 /* We haven't completed a read, so start a wait */
351 return WaitBeforeReading(InputInfo,
352 ApiMessage,
353 ReadInputBufferThread,
354 CreateWaitBlock);
355 }
356 else
357 {
358 /*
359 * We read all what we wanted. Set the number of events read and
360 * return the error code we were given.
361 */
362 GetInputRequest->NumRecords = NumEventsRead;
363
364 return Status;
365 // return STATUS_SUCCESS;
366 }
367 }
368
369
370 /* PUBLIC SERVER APIS *********************************************************/
371
372 CSR_API(SrvReadConsole)
373 {
374 NTSTATUS Status;
375 PCONSOLE_READCONSOLE ReadConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ReadConsoleRequest;
376 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
377 PVOID HandleEntry;
378 PCONSOLE_INPUT_BUFFER InputBuffer;
379 GET_INPUT_INFO InputInfo;
380
381 DPRINT("SrvReadConsole\n");
382
383 /*
384 * For optimization purposes, Windows (and hence ReactOS, too, for
385 * compatibility reasons) uses a static buffer if no more than eighty
386 * bytes are read. Otherwise a new buffer is used.
387 * The client-side expects that we know this behaviour.
388 */
389 if (ReadConsoleRequest->CaptureBufferSize <= sizeof(ReadConsoleRequest->StaticBuffer))
390 {
391 /*
392 * Adjust the internal pointer, because its old value points to
393 * the static buffer in the original ApiMessage structure.
394 */
395 // ReadConsoleRequest->Buffer = ReadConsoleRequest->StaticBuffer;
396 }
397 else
398 {
399 if (!CsrValidateMessageBuffer(ApiMessage,
400 (PVOID*)&ReadConsoleRequest->Buffer,
401 ReadConsoleRequest->CaptureBufferSize,
402 sizeof(BYTE)))
403 {
404 return STATUS_INVALID_PARAMETER;
405 }
406 }
407
408 if (ReadConsoleRequest->InitialNumBytes > ReadConsoleRequest->NumBytes)
409 {
410 return STATUS_INVALID_PARAMETER;
411 }
412
413 Status = ConSrvGetInputBufferAndHandleEntry(ProcessData, ReadConsoleRequest->InputHandle, &InputBuffer, &HandleEntry, GENERIC_READ, TRUE);
414 if (!NT_SUCCESS(Status)) return Status;
415
416 InputInfo.CallingThread = CsrGetClientThread();
417 InputInfo.HandleEntry = HandleEntry;
418 InputInfo.InputBuffer = InputBuffer;
419
420 Status = ReadChars(&InputInfo, ApiMessage, TRUE);
421
422 ConSrvReleaseInputBuffer(InputBuffer, TRUE);
423
424 if (Status == STATUS_PENDING) *ReplyCode = CsrReplyPending;
425
426 return Status;
427 }
428
429 CSR_API(SrvGetConsoleInput)
430 {
431 NTSTATUS Status;
432 PCONSOLE_GETINPUT GetInputRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetInputRequest;
433 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
434 PVOID HandleEntry;
435 PCONSOLE_INPUT_BUFFER InputBuffer;
436 GET_INPUT_INFO InputInfo;
437
438 DPRINT("SrvGetConsoleInput\n");
439
440 if (GetInputRequest->Flags & ~(CONSOLE_READ_KEEPEVENT | CONSOLE_READ_CONTINUE))
441 return STATUS_INVALID_PARAMETER;
442
443 /*
444 * For optimization purposes, Windows (and hence ReactOS, too, for
445 * compatibility reasons) uses a static buffer if no more than five
446 * input records are read. Otherwise a new buffer is used.
447 * The client-side expects that we know this behaviour.
448 */
449 if (GetInputRequest->NumRecords <= sizeof(GetInputRequest->RecordStaticBuffer)/sizeof(INPUT_RECORD))
450 {
451 /*
452 * Adjust the internal pointer, because its old value points to
453 * the static buffer in the original ApiMessage structure.
454 */
455 // GetInputRequest->RecordBufPtr = GetInputRequest->RecordStaticBuffer;
456 }
457 else
458 {
459 if (!CsrValidateMessageBuffer(ApiMessage,
460 (PVOID*)&GetInputRequest->RecordBufPtr,
461 GetInputRequest->NumRecords,
462 sizeof(INPUT_RECORD)))
463 {
464 return STATUS_INVALID_PARAMETER;
465 }
466 }
467
468 Status = ConSrvGetInputBufferAndHandleEntry(ProcessData, GetInputRequest->InputHandle, &InputBuffer, &HandleEntry, GENERIC_READ, TRUE);
469 if (!NT_SUCCESS(Status)) return Status;
470
471 InputInfo.CallingThread = CsrGetClientThread();
472 InputInfo.HandleEntry = HandleEntry;
473 InputInfo.InputBuffer = InputBuffer;
474
475 Status = ReadInputBuffer(&InputInfo, ApiMessage, TRUE);
476
477 ConSrvReleaseInputBuffer(InputBuffer, TRUE);
478
479 if (Status == STATUS_PENDING) *ReplyCode = CsrReplyPending;
480
481 return Status;
482 }
483
484 NTSTATUS NTAPI
485 ConDrvWriteConsoleInput(IN PCONSOLE Console,
486 IN PCONSOLE_INPUT_BUFFER InputBuffer,
487 IN BOOLEAN Unicode,
488 IN BOOLEAN AppendToEnd,
489 IN PINPUT_RECORD InputRecord,
490 IN ULONG NumEventsToWrite,
491 OUT PULONG NumEventsWritten OPTIONAL);
492 CSR_API(SrvWriteConsoleInput)
493 {
494 NTSTATUS Status;
495 PCONSOLE_WRITEINPUT WriteInputRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.WriteInputRequest;
496 PCONSOLE_INPUT_BUFFER InputBuffer;
497 ULONG NumEventsWritten;
498
499 PINPUT_RECORD InputRecord;
500
501 DPRINT("SrvWriteConsoleInput\n");
502
503 /*
504 * For optimization purposes, Windows (and hence ReactOS, too, for
505 * compatibility reasons) uses a static buffer if no more than five
506 * input records are written. Otherwise a new buffer is used.
507 * The client-side expects that we know this behaviour.
508 */
509 if (WriteInputRequest->NumRecords <= sizeof(WriteInputRequest->RecordStaticBuffer)/sizeof(INPUT_RECORD))
510 {
511 /*
512 * Adjust the internal pointer, because its old value points to
513 * the static buffer in the original ApiMessage structure.
514 */
515 // WriteInputRequest->RecordBufPtr = WriteInputRequest->RecordStaticBuffer;
516 InputRecord = WriteInputRequest->RecordStaticBuffer;
517 }
518 else
519 {
520 if (!CsrValidateMessageBuffer(ApiMessage,
521 (PVOID*)&WriteInputRequest->RecordBufPtr,
522 WriteInputRequest->NumRecords,
523 sizeof(INPUT_RECORD)))
524 {
525 return STATUS_INVALID_PARAMETER;
526 }
527
528 InputRecord = WriteInputRequest->RecordBufPtr;
529 }
530
531 Status = ConSrvGetInputBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
532 WriteInputRequest->InputHandle,
533 &InputBuffer, GENERIC_WRITE, TRUE);
534 if (!NT_SUCCESS(Status))
535 {
536 WriteInputRequest->NumRecords = 0;
537 return Status;
538 }
539
540 NumEventsWritten = 0;
541 Status = ConDrvWriteConsoleInput(InputBuffer->Header.Console,
542 InputBuffer,
543 WriteInputRequest->Unicode,
544 WriteInputRequest->AppendToEnd,
545 InputRecord,
546 WriteInputRequest->NumRecords,
547 &NumEventsWritten);
548 WriteInputRequest->NumRecords = NumEventsWritten;
549
550 ConSrvReleaseInputBuffer(InputBuffer, TRUE);
551 return Status;
552 }
553
554 NTSTATUS NTAPI
555 ConDrvFlushConsoleInputBuffer(IN PCONSOLE Console,
556 IN PCONSOLE_INPUT_BUFFER InputBuffer);
557 CSR_API(SrvFlushConsoleInputBuffer)
558 {
559 NTSTATUS Status;
560 PCONSOLE_FLUSHINPUTBUFFER FlushInputBufferRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.FlushInputBufferRequest;
561 PCONSOLE_INPUT_BUFFER InputBuffer;
562
563 DPRINT("SrvFlushConsoleInputBuffer\n");
564
565 Status = ConSrvGetInputBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
566 FlushInputBufferRequest->InputHandle,
567 &InputBuffer, GENERIC_WRITE, TRUE);
568 if (!NT_SUCCESS(Status)) return Status;
569
570 Status = ConDrvFlushConsoleInputBuffer(InputBuffer->Header.Console,
571 InputBuffer);
572
573 ConSrvReleaseInputBuffer(InputBuffer, TRUE);
574 return Status;
575 }
576
577 NTSTATUS NTAPI
578 ConDrvGetConsoleNumberOfInputEvents(IN PCONSOLE Console,
579 IN PCONSOLE_INPUT_BUFFER InputBuffer,
580 OUT PULONG NumberOfEvents);
581 CSR_API(SrvGetConsoleNumberOfInputEvents)
582 {
583 NTSTATUS Status;
584 PCONSOLE_GETNUMINPUTEVENTS GetNumInputEventsRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetNumInputEventsRequest;
585 PCONSOLE_INPUT_BUFFER InputBuffer;
586
587 DPRINT("SrvGetConsoleNumberOfInputEvents\n");
588
589 Status = ConSrvGetInputBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
590 GetNumInputEventsRequest->InputHandle,
591 &InputBuffer, GENERIC_READ, TRUE);
592 if (!NT_SUCCESS(Status)) return Status;
593
594 Status = ConDrvGetConsoleNumberOfInputEvents(InputBuffer->Header.Console,
595 InputBuffer,
596 &GetNumInputEventsRequest->NumberOfEvents);
597
598 ConSrvReleaseInputBuffer(InputBuffer, TRUE);
599 return Status;
600 }
601
602 /* EOF */