Sync with trunk r58033.
[reactos.git] / ntoskrnl / lpc / reply.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/lpc/reply.c
5 * PURPOSE: Local Procedure Call: Receive (Replies)
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 /* PRIVATE FUNCTIONS *********************************************************/
16
17 VOID
18 NTAPI
19 LpcpFreeDataInfoMessage(IN PLPCP_PORT_OBJECT Port,
20 IN ULONG MessageId,
21 IN ULONG CallbackId,
22 IN CLIENT_ID ClientId)
23 {
24 PLPCP_MESSAGE Message;
25 PLIST_ENTRY ListHead, NextEntry;
26
27 /* Check if the port we want is the connection port */
28 if ((Port->Flags & LPCP_PORT_TYPE_MASK) > LPCP_UNCONNECTED_PORT)
29 {
30 /* Use it */
31 Port = Port->ConnectionPort;
32 if (!Port) return;
33 }
34
35 /* Loop the list */
36 ListHead = &Port->LpcDataInfoChainHead;
37 NextEntry = ListHead->Flink;
38 while (ListHead != NextEntry)
39 {
40 /* Get the message */
41 Message = CONTAINING_RECORD(NextEntry, LPCP_MESSAGE, Entry);
42
43 /* Make sure it matches */
44 if ((Message->Request.MessageId == MessageId) &&
45 (Message->Request.ClientId.UniqueThread == ClientId.UniqueThread) &&
46 (Message->Request.ClientId.UniqueProcess == ClientId.UniqueProcess))
47 {
48 /* Unlink and free it */
49 RemoveEntryList(&Message->Entry);
50 InitializeListHead(&Message->Entry);
51 LpcpFreeToPortZone(Message, 1);
52 break;
53 }
54
55 /* Go to the next entry */
56 NextEntry = NextEntry->Flink;
57 }
58 }
59
60 VOID
61 NTAPI
62 LpcpSaveDataInfoMessage(IN PLPCP_PORT_OBJECT Port,
63 IN PLPCP_MESSAGE Message,
64 IN ULONG LockHeld)
65 {
66 PAGED_CODE();
67
68 /* Acquire the lock */
69 if (!LockHeld) KeAcquireGuardedMutex(&LpcpLock);
70
71 /* Check if the port we want is the connection port */
72 if ((Port->Flags & LPCP_PORT_TYPE_MASK) > LPCP_UNCONNECTED_PORT)
73 {
74 /* Use it */
75 Port = Port->ConnectionPort;
76 if (!Port)
77 {
78 /* Release the lock and return */
79 if (!LockHeld) KeReleaseGuardedMutex(&LpcpLock);
80 return;
81 }
82 }
83
84 /* Link the message */
85 InsertTailList(&Port->LpcDataInfoChainHead, &Message->Entry);
86
87 /* Release the lock */
88 if (!LockHeld) KeReleaseGuardedMutex(&LpcpLock);
89 }
90
91 VOID
92 NTAPI
93 LpcpMoveMessage(IN PPORT_MESSAGE Destination,
94 IN PPORT_MESSAGE Origin,
95 IN PVOID Data,
96 IN ULONG MessageType,
97 IN PCLIENT_ID ClientId)
98 {
99 /* Set the Message size */
100 LPCTRACE((LPC_REPLY_DEBUG | LPC_SEND_DEBUG),
101 "Destination/Origin: %p/%p. Data: %p. Length: %lx\n",
102 Destination,
103 Origin,
104 Data,
105 Origin->u1.Length);
106 Destination->u1.Length = Origin->u1.Length;
107
108 /* Set the Message Type */
109 Destination->u2.s2.Type = !MessageType ?
110 Origin->u2.s2.Type : MessageType & 0xFFFF;
111
112 /* Check if we have a Client ID */
113 if (ClientId)
114 {
115 /* Set the Client ID */
116 Destination->ClientId.UniqueProcess = ClientId->UniqueProcess;
117 Destination->ClientId.UniqueThread = ClientId->UniqueThread;
118 }
119 else
120 {
121 /* Otherwise, copy it */
122 Destination->ClientId.UniqueProcess = Origin->ClientId.UniqueProcess;
123 Destination->ClientId.UniqueThread = Origin->ClientId.UniqueThread;
124 }
125
126 /* Copy the MessageId and ClientViewSize */
127 Destination->MessageId = Origin->MessageId;
128 Destination->ClientViewSize = Origin->ClientViewSize;
129
130 /* Copy the Message Data */
131 RtlCopyMemory(Destination + 1,
132 Data,
133 ((Destination->u1.Length & 0xFFFF) + 3) &~3);
134 }
135
136 /* PUBLIC FUNCTIONS **********************************************************/
137
138 /*
139 * @implemented
140 */
141 NTSTATUS
142 NTAPI
143 NtReplyPort(IN HANDLE PortHandle,
144 IN PPORT_MESSAGE ReplyMessage)
145 {
146 PLPCP_PORT_OBJECT Port, ConnectionPort = NULL;
147 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
148 NTSTATUS Status;
149 PLPCP_MESSAGE Message;
150 PETHREAD Thread = PsGetCurrentThread(), WakeupThread;
151 //PORT_MESSAGE CapturedReplyMessage;
152
153 PAGED_CODE();
154 LPCTRACE(LPC_REPLY_DEBUG,
155 "Handle: %lx. Message: %p.\n",
156 PortHandle,
157 ReplyMessage);
158
159 if (KeGetPreviousMode() == UserMode)
160 {
161 _SEH2_TRY
162 {
163 ProbeForRead(ReplyMessage, sizeof(PORT_MESSAGE), sizeof(ULONG));
164 /*RtlCopyMemory(&CapturedReplyMessage, ReplyMessage, sizeof(PORT_MESSAGE));
165 ReplyMessage = &CapturedReplyMessage;*/
166 }
167 _SEH2_EXCEPT(ExSystemExceptionFilter())
168 {
169 DPRINT1("SEH crash [1]\n");
170 DbgBreakPoint();
171 _SEH2_YIELD(return _SEH2_GetExceptionCode());
172 }
173 _SEH2_END;
174 }
175
176 /* Validate its length */
177 if (((ULONG)ReplyMessage->u1.s1.DataLength + sizeof(PORT_MESSAGE)) >
178 (ULONG)ReplyMessage->u1.s1.TotalLength)
179 {
180 /* Fail */
181 return STATUS_INVALID_PARAMETER;
182 }
183
184 /* Make sure it has a valid ID */
185 if (!ReplyMessage->MessageId) return STATUS_INVALID_PARAMETER;
186
187 /* Get the Port object */
188 Status = ObReferenceObjectByHandle(PortHandle,
189 0,
190 LpcPortObjectType,
191 PreviousMode,
192 (PVOID*)&Port,
193 NULL);
194 if (!NT_SUCCESS(Status)) return Status;
195
196 /* Validate its length in respect to the port object */
197 if (((ULONG)ReplyMessage->u1.s1.TotalLength > Port->MaxMessageLength) ||
198 ((ULONG)ReplyMessage->u1.s1.TotalLength <=
199 (ULONG)ReplyMessage->u1.s1.DataLength))
200 {
201 /* Too large, fail */
202 ObDereferenceObject(Port);
203 return STATUS_PORT_MESSAGE_TOO_LONG;
204 }
205
206 /* Get the ETHREAD corresponding to it */
207 Status = PsLookupProcessThreadByCid(&ReplyMessage->ClientId,
208 NULL,
209 &WakeupThread);
210 if (!NT_SUCCESS(Status))
211 {
212 /* No thread found, fail */
213 ObDereferenceObject(Port);
214 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
215 return Status;
216 }
217
218 /* Allocate a message from the port zone */
219 Message = LpcpAllocateFromPortZone();
220 if (!Message)
221 {
222 /* Fail if we couldn't allocate a message */
223 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
224 ObDereferenceObject(WakeupThread);
225 ObDereferenceObject(Port);
226 return STATUS_NO_MEMORY;
227 }
228
229 /* Keep the lock acquired */
230 KeAcquireGuardedMutex(&LpcpLock);
231
232 /* Make sure this is the reply the thread is waiting for */
233 if ((WakeupThread->LpcReplyMessageId != ReplyMessage->MessageId) ||
234 ((LpcpGetMessageFromThread(WakeupThread)) &&
235 (LpcpGetMessageType(&LpcpGetMessageFromThread(WakeupThread)->
236 Request) != LPC_REQUEST)))
237 {
238 /* It isn't, fail */
239 LpcpFreeToPortZone(Message, 3);
240 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
241 ObDereferenceObject(WakeupThread);
242 ObDereferenceObject(Port);
243 return STATUS_REPLY_MESSAGE_MISMATCH;
244 }
245
246 /* Copy the message */
247 _SEH2_TRY
248 {
249 LpcpMoveMessage(&Message->Request,
250 ReplyMessage,
251 ReplyMessage + 1,
252 LPC_REPLY,
253 NULL);
254 }
255 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
256 {
257 /* Fail */
258 LpcpFreeToPortZone(Message, 3);
259 ObDereferenceObject(WakeupThread);
260 ObDereferenceObject(Port);
261 _SEH2_YIELD(return _SEH2_GetExceptionCode());
262 }
263 _SEH2_END;
264
265 /* Reference the thread while we use it */
266 ObReferenceObject(WakeupThread);
267 Message->RepliedToThread = WakeupThread;
268
269 /* Set this as the reply message */
270 WakeupThread->LpcReplyMessageId = 0;
271 WakeupThread->LpcReplyMessage = (PVOID)Message;
272
273 /* Check if we have messages on the reply chain */
274 if (!(WakeupThread->LpcExitThreadCalled) &&
275 !(IsListEmpty(&WakeupThread->LpcReplyChain)))
276 {
277 /* Remove us from it and reinitialize it */
278 RemoveEntryList(&WakeupThread->LpcReplyChain);
279 InitializeListHead(&WakeupThread->LpcReplyChain);
280 }
281
282 /* Check if this is the message the thread had received */
283 if ((Thread->LpcReceivedMsgIdValid) &&
284 (Thread->LpcReceivedMessageId == ReplyMessage->MessageId))
285 {
286 /* Clear this data */
287 Thread->LpcReceivedMessageId = 0;
288 Thread->LpcReceivedMsgIdValid = FALSE;
289 }
290
291 /* Free any data information */
292 LpcpFreeDataInfoMessage(Port,
293 ReplyMessage->MessageId,
294 ReplyMessage->CallbackId,
295 ReplyMessage->ClientId);
296
297 /* Release the lock and release the LPC semaphore to wake up waiters */
298 KeReleaseGuardedMutex(&LpcpLock);
299 LpcpCompleteWait(&WakeupThread->LpcReplySemaphore);
300
301 /* Now we can let go of the thread */
302 ObDereferenceObject(WakeupThread);
303
304 /* Dereference port object */
305 ObDereferenceObject(Port);
306 return Status;
307 }
308
309 /*
310 * @implemented
311 */
312 NTSTATUS
313 NTAPI
314 NtReplyWaitReceivePortEx(IN HANDLE PortHandle,
315 OUT PVOID *PortContext OPTIONAL,
316 IN PPORT_MESSAGE ReplyMessage OPTIONAL,
317 OUT PPORT_MESSAGE ReceiveMessage,
318 IN PLARGE_INTEGER Timeout OPTIONAL)
319 {
320 PLPCP_PORT_OBJECT Port, ReceivePort, ConnectionPort = NULL;
321 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(), WaitMode = PreviousMode;
322 NTSTATUS Status;
323 PLPCP_MESSAGE Message;
324 PETHREAD Thread = PsGetCurrentThread(), WakeupThread;
325 PLPCP_CONNECTION_MESSAGE ConnectMessage;
326 ULONG ConnectionInfoLength;
327 //PORT_MESSAGE CapturedReplyMessage;
328 LARGE_INTEGER CapturedTimeout;
329
330 PAGED_CODE();
331 LPCTRACE(LPC_REPLY_DEBUG,
332 "Handle: %lx. Messages: %p/%p. Context: %p\n",
333 PortHandle,
334 ReplyMessage,
335 ReceiveMessage,
336 PortContext);
337
338 if (KeGetPreviousMode() == UserMode)
339 {
340 _SEH2_TRY
341 {
342 if (ReplyMessage != NULL)
343 {
344 ProbeForRead(ReplyMessage, sizeof(PORT_MESSAGE), sizeof(ULONG));
345 /*RtlCopyMemory(&CapturedReplyMessage, ReplyMessage, sizeof(PORT_MESSAGE));
346 ReplyMessage = &CapturedReplyMessage;*/
347 }
348
349 if (Timeout != NULL)
350 {
351 ProbeForReadLargeInteger(Timeout);
352 RtlCopyMemory(&CapturedTimeout, Timeout, sizeof(LARGE_INTEGER));
353 Timeout = &CapturedTimeout;
354 }
355
356 if (PortContext != NULL)
357 ProbeForWritePointer(PortContext);
358 }
359 _SEH2_EXCEPT(ExSystemExceptionFilter())
360 {
361 DPRINT1("SEH crash [1]\n");
362 DbgBreakPoint();
363 _SEH2_YIELD(return _SEH2_GetExceptionCode());
364 }
365 _SEH2_END;
366 }
367 else
368 {
369 /* If this is a system thread, then let it page out its stack */
370 if (Thread->SystemThread) WaitMode = UserMode;
371 }
372
373 /* Check if caller has a reply message */
374 if (ReplyMessage)
375 {
376 /* Validate its length */
377 if (((ULONG)ReplyMessage->u1.s1.DataLength + sizeof(PORT_MESSAGE)) >
378 (ULONG)ReplyMessage->u1.s1.TotalLength)
379 {
380 /* Fail */
381 return STATUS_INVALID_PARAMETER;
382 }
383
384 /* Make sure it has a valid ID */
385 if (!ReplyMessage->MessageId) return STATUS_INVALID_PARAMETER;
386 }
387
388 /* Get the Port object */
389 Status = ObReferenceObjectByHandle(PortHandle,
390 0,
391 LpcPortObjectType,
392 PreviousMode,
393 (PVOID*)&Port,
394 NULL);
395 if (!NT_SUCCESS(Status)) return Status;
396
397 /* Check if the caller has a reply message */
398 if (ReplyMessage)
399 {
400 /* Validate its length in respect to the port object */
401 if (((ULONG)ReplyMessage->u1.s1.TotalLength > Port->MaxMessageLength) ||
402 ((ULONG)ReplyMessage->u1.s1.TotalLength <=
403 (ULONG)ReplyMessage->u1.s1.DataLength))
404 {
405 /* Too large, fail */
406 ObDereferenceObject(Port);
407 return STATUS_PORT_MESSAGE_TOO_LONG;
408 }
409 }
410
411 /* Check if this is anything but a client port */
412 if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CLIENT_PORT)
413 {
414 /* Check if this is the connection port */
415 if (Port->ConnectionPort == Port)
416 {
417 /* Use this port */
418 ConnectionPort = ReceivePort = Port;
419 ObReferenceObject(ConnectionPort);
420 }
421 else
422 {
423 /* Acquire the lock */
424 KeAcquireGuardedMutex(&LpcpLock);
425
426 /* Get the port */
427 ConnectionPort = ReceivePort = Port->ConnectionPort;
428 if (!ConnectionPort)
429 {
430 /* Fail */
431 KeReleaseGuardedMutex(&LpcpLock);
432 ObDereferenceObject(Port);
433 return STATUS_PORT_DISCONNECTED;
434 }
435
436 /* Release lock and reference */
437 ObReferenceObject(ConnectionPort);
438 KeReleaseGuardedMutex(&LpcpLock);
439 }
440 }
441 else
442 {
443 /* Otherwise, use the port itself */
444 ReceivePort = Port;
445 }
446
447 /* Check if the caller gave a reply message */
448 if (ReplyMessage)
449 {
450 /* Get the ETHREAD corresponding to it */
451 Status = PsLookupProcessThreadByCid(&ReplyMessage->ClientId,
452 NULL,
453 &WakeupThread);
454 if (!NT_SUCCESS(Status))
455 {
456 /* No thread found, fail */
457 ObDereferenceObject(Port);
458 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
459 return Status;
460 }
461
462 /* Allocate a message from the port zone */
463 Message = LpcpAllocateFromPortZone();
464 if (!Message)
465 {
466 /* Fail if we couldn't allocate a message */
467 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
468 ObDereferenceObject(WakeupThread);
469 ObDereferenceObject(Port);
470 return STATUS_NO_MEMORY;
471 }
472
473 /* Keep the lock acquired */
474 KeAcquireGuardedMutex(&LpcpLock);
475
476 /* Make sure this is the reply the thread is waiting for */
477 if ((WakeupThread->LpcReplyMessageId != ReplyMessage->MessageId) ||
478 ((LpcpGetMessageFromThread(WakeupThread)) &&
479 (LpcpGetMessageType(&LpcpGetMessageFromThread(WakeupThread)->
480 Request) != LPC_REQUEST)))
481 {
482 /* It isn't, fail */
483 LpcpFreeToPortZone(Message, 3);
484 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
485 ObDereferenceObject(WakeupThread);
486 ObDereferenceObject(Port);
487 return STATUS_REPLY_MESSAGE_MISMATCH;
488 }
489
490 /* Copy the message */
491 LpcpMoveMessage(&Message->Request,
492 ReplyMessage,
493 ReplyMessage + 1,
494 LPC_REPLY,
495 NULL);
496
497 /* Reference the thread while we use it */
498 ObReferenceObject(WakeupThread);
499 Message->RepliedToThread = WakeupThread;
500
501 /* Set this as the reply message */
502 WakeupThread->LpcReplyMessageId = 0;
503 WakeupThread->LpcReplyMessage = (PVOID)Message;
504
505 /* Check if we have messages on the reply chain */
506 if (!(WakeupThread->LpcExitThreadCalled) &&
507 !(IsListEmpty(&WakeupThread->LpcReplyChain)))
508 {
509 /* Remove us from it and reinitialize it */
510 RemoveEntryList(&WakeupThread->LpcReplyChain);
511 InitializeListHead(&WakeupThread->LpcReplyChain);
512 }
513
514 /* Check if this is the message the thread had received */
515 if ((Thread->LpcReceivedMsgIdValid) &&
516 (Thread->LpcReceivedMessageId == ReplyMessage->MessageId))
517 {
518 /* Clear this data */
519 Thread->LpcReceivedMessageId = 0;
520 Thread->LpcReceivedMsgIdValid = FALSE;
521 }
522
523 /* Free any data information */
524 LpcpFreeDataInfoMessage(Port,
525 ReplyMessage->MessageId,
526 ReplyMessage->CallbackId,
527 ReplyMessage->ClientId);
528
529 /* Release the lock and release the LPC semaphore to wake up waiters */
530 KeReleaseGuardedMutex(&LpcpLock);
531 LpcpCompleteWait(&WakeupThread->LpcReplySemaphore);
532
533 /* Now we can let go of the thread */
534 ObDereferenceObject(WakeupThread);
535 }
536
537 /* Now wait for someone to reply to us */
538 LpcpReceiveWait(ReceivePort->MsgQueue.Semaphore, WaitMode);
539 if (Status != STATUS_SUCCESS) goto Cleanup;
540
541 /* Wait done, get the LPC lock */
542 KeAcquireGuardedMutex(&LpcpLock);
543
544 /* Check if we've received nothing */
545 if (IsListEmpty(&ReceivePort->MsgQueue.ReceiveHead))
546 {
547 /* Check if this was a waitable port and wake it */
548 if (ReceivePort->Flags & LPCP_WAITABLE_PORT)
549 {
550 /* Reset its event */
551 KeResetEvent(&ReceivePort->WaitEvent);
552 }
553
554 /* Release the lock and fail */
555 KeReleaseGuardedMutex(&LpcpLock);
556 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
557 ObDereferenceObject(Port);
558 return STATUS_UNSUCCESSFUL;
559 }
560
561 /* Get the message on the queue */
562 Message = CONTAINING_RECORD(RemoveHeadList(&ReceivePort->
563 MsgQueue.ReceiveHead),
564 LPCP_MESSAGE,
565 Entry);
566
567 /* Check if the queue is empty now */
568 if (IsListEmpty(&ReceivePort->MsgQueue.ReceiveHead))
569 {
570 /* Check if this was a waitable port */
571 if (ReceivePort->Flags & LPCP_WAITABLE_PORT)
572 {
573 /* Reset its event */
574 KeResetEvent(&ReceivePort->WaitEvent);
575 }
576 }
577
578 /* Re-initialize the message's list entry */
579 InitializeListHead(&Message->Entry);
580
581 /* Set this as the received message */
582 Thread->LpcReceivedMessageId = Message->Request.MessageId;
583 Thread->LpcReceivedMsgIdValid = TRUE;
584
585 _SEH2_TRY
586 {
587 /* Check if this was a connection request */
588 if (LpcpGetMessageType(&Message->Request) == LPC_CONNECTION_REQUEST)
589 {
590 /* Get the connection message */
591 ConnectMessage = (PLPCP_CONNECTION_MESSAGE)(Message + 1);
592 LPCTRACE(LPC_REPLY_DEBUG,
593 "Request Messages: %p/%p\n",
594 Message,
595 ConnectMessage);
596
597 /* Get its length */
598 ConnectionInfoLength = Message->Request.u1.s1.DataLength -
599 sizeof(LPCP_CONNECTION_MESSAGE);
600
601 /* Return it as the receive message */
602 *ReceiveMessage = Message->Request;
603
604 /* Clear our stack variable so the message doesn't get freed */
605 Message = NULL;
606
607 /* Setup the receive message */
608 ReceiveMessage->u1.s1.TotalLength = (CSHORT)(sizeof(LPCP_MESSAGE) +
609 ConnectionInfoLength);
610 ReceiveMessage->u1.s1.DataLength = (CSHORT)ConnectionInfoLength;
611 RtlCopyMemory(ReceiveMessage + 1,
612 ConnectMessage + 1,
613 ConnectionInfoLength);
614
615 /* Clear the port context if the caller requested one */
616 if (PortContext) *PortContext = NULL;
617 }
618 else if (LpcpGetMessageType(&Message->Request) != LPC_REPLY)
619 {
620 /* Otherwise, this is a new message or event */
621 LPCTRACE(LPC_REPLY_DEBUG,
622 "Non-Reply Messages: %p/%p\n",
623 &Message->Request,
624 (&Message->Request) + 1);
625
626 /* Copy it */
627 LpcpMoveMessage(ReceiveMessage,
628 &Message->Request,
629 (&Message->Request) + 1,
630 0,
631 NULL);
632
633 /* Return its context */
634 if (PortContext) *PortContext = Message->PortContext;
635
636 /* And check if it has data information */
637 if (Message->Request.u2.s2.DataInfoOffset)
638 {
639 /* It does, save it, and don't free the message below */
640 LpcpSaveDataInfoMessage(Port, Message, 1);
641 Message = NULL;
642 }
643 }
644 else
645 {
646 /* This is a reply message, should never happen! */
647 ASSERT(FALSE);
648 }
649 }
650 _SEH2_EXCEPT(ExSystemExceptionFilter())
651 {
652 DPRINT1("SEH crash [2]\n");
653 DbgBreakPoint();
654 Status = _SEH2_GetExceptionCode();
655 }
656 _SEH2_END;
657
658 /* Check if we have a message pointer here */
659 if (Message)
660 {
661 /* Free it and release the lock */
662 LpcpFreeToPortZone(Message, 3);
663 }
664 else
665 {
666 /* Just release the lock */
667 KeReleaseGuardedMutex(&LpcpLock);
668 }
669
670 Cleanup:
671 /* All done, dereference the port and return the status */
672 LPCTRACE(LPC_REPLY_DEBUG,
673 "Port: %p. Status: %p\n",
674 Port,
675 Status);
676 if (ConnectionPort) ObDereferenceObject(ConnectionPort);
677 ObDereferenceObject(Port);
678 return Status;
679 }
680
681 /*
682 * @implemented
683 */
684 NTSTATUS
685 NTAPI
686 NtReplyWaitReceivePort(IN HANDLE PortHandle,
687 OUT PVOID *PortContext OPTIONAL,
688 IN PPORT_MESSAGE ReplyMessage OPTIONAL,
689 OUT PPORT_MESSAGE ReceiveMessage)
690 {
691 /* Call the newer API */
692 return NtReplyWaitReceivePortEx(PortHandle,
693 PortContext,
694 ReplyMessage,
695 ReceiveMessage,
696 NULL);
697 }
698
699 /*
700 * @unimplemented
701 */
702 NTSTATUS
703 NTAPI
704 NtReplyWaitReplyPort(IN HANDLE PortHandle,
705 IN PPORT_MESSAGE ReplyMessage)
706 {
707 UNIMPLEMENTED;
708 return STATUS_NOT_IMPLEMENTED;
709 }
710
711 /*
712 * @unimplemented
713 */
714 NTSTATUS
715 NTAPI
716 NtReadRequestData(IN HANDLE PortHandle,
717 IN PPORT_MESSAGE Message,
718 IN ULONG Index,
719 IN PVOID Buffer,
720 IN ULONG BufferLength,
721 OUT PULONG Returnlength)
722 {
723 UNIMPLEMENTED;
724 return STATUS_NOT_IMPLEMENTED;
725 }
726
727 /*
728 * @unimplemented
729 */
730 NTSTATUS
731 NTAPI
732 NtWriteRequestData(IN HANDLE PortHandle,
733 IN PPORT_MESSAGE Message,
734 IN ULONG Index,
735 IN PVOID Buffer,
736 IN ULONG BufferLength,
737 OUT PULONG ReturnLength)
738 {
739 UNIMPLEMENTED;
740 return STATUS_NOT_IMPLEMENTED;
741 }
742
743 /* EOF */