- Remove cooked hardware messages when they are dropped (reported by
[wine] / windows / queue.c
1 /*
2  * Message queues related functions
3  *
4  * Copyright 1993, 1994 Alexandre Julliard
5  */
6
7 #include <string.h>
8 #include <signal.h>
9 #include <assert.h>
10 #include "windef.h"
11 #include "wingdi.h"
12 #include "winerror.h"
13 #include "wine/winbase16.h"
14 #include "wine/winuser16.h"
15 #include "queue.h"
16 #include "win.h"
17 #include "user.h"
18 #include "hook.h"
19 #include "thread.h"
20 #include "debugtools.h"
21 #include "server.h"
22 #include "spy.h"
23
24 DEFAULT_DEBUG_CHANNEL(msg);
25
26
27 static HQUEUE16 hExitingQueue = 0;
28 static PERQUEUEDATA *pQDataWin16 = NULL;  /* Global perQData for Win16 tasks */
29
30 HQUEUE16 hActiveQueue = 0;
31
32
33 /***********************************************************************
34  *           PERQDATA_Addref
35  *
36  * Increment reference count for the PERQUEUEDATA instance
37  * Returns reference count for debugging purposes
38  */
39 static void PERQDATA_Addref( PERQUEUEDATA *pQData )
40 {
41     assert(pQData != 0 );
42     TRACE_(msg)("(): current refcount %lu ...\n", pQData->ulRefCount);
43
44     InterlockedIncrement( &pQData->ulRefCount );
45 }
46
47
48 /***********************************************************************
49  *           PERQDATA_Release
50  *
51  * Release a reference to a PERQUEUEDATA instance.
52  * Destroy the instance if no more references exist
53  * Returns reference count for debugging purposes
54  */
55 static void PERQDATA_Release( PERQUEUEDATA *pQData )
56 {
57     assert(pQData != 0 );
58     TRACE_(msg)("(): current refcount %lu ...\n",
59           (LONG)pQData->ulRefCount );
60
61     if (!InterlockedDecrement( &pQData->ulRefCount ))
62     {
63         DeleteCriticalSection( &pQData->cSection );
64
65         TRACE_(msg)("(): deleting PERQUEUEDATA instance ...\n" );
66
67         /* Deleting our global 16 bit perQData? */
68         if ( pQData == pQDataWin16 ) pQDataWin16 = 0;
69
70         /* Free the PERQUEUEDATA instance */
71         HeapFree( GetProcessHeap(), 0, pQData );
72     }
73 }
74
75
76 /***********************************************************************
77  *           PERQDATA_CreateInstance
78  *
79  * Creates an instance of a reference counted PERQUEUEDATA element
80  * for the message queue. perQData is stored globally for 16 bit tasks.
81  *
82  * Note: We don't implement perQdata exactly the same way Windows does.
83  * Each perQData element is reference counted since it may be potentially
84  * shared by multiple message Queues (via AttachThreadInput).
85  * We only store the current values for Active, Capture and focus windows
86  * currently.
87  */
88 static PERQUEUEDATA * PERQDATA_CreateInstance(void)
89 {
90     PERQUEUEDATA *pQData;
91     
92     BOOL16 bIsWin16 = 0;
93     
94     TRACE_(msg)("()\n");
95
96     /* Share a single instance of perQData for all 16 bit tasks */
97     if ( ( bIsWin16 = !(NtCurrentTeb()->tibflags & TEBF_WIN32) ) )
98     {
99         /* If previously allocated, just bump up ref count */
100         if ( pQDataWin16 )
101         {
102             PERQDATA_Addref( pQDataWin16 );
103             return pQDataWin16;
104         }
105     }
106
107     /* Allocate PERQUEUEDATA from the system heap */
108     if (!( pQData = (PERQUEUEDATA *) HeapAlloc( GetProcessHeap(), 0,
109                                                     sizeof(PERQUEUEDATA) ) ))
110         return 0;
111
112     /* Initialize */
113     pQData->hWndCapture = pQData->hWndFocus = pQData->hWndActive = 0;
114     pQData->ulRefCount = 1;
115     pQData->nCaptureHT = HTCLIENT;
116
117     /* Note: We have an independent critical section for the per queue data
118      * since this may be shared by different threads. see AttachThreadInput()
119      */
120     InitializeCriticalSection( &pQData->cSection );
121     /* FIXME: not all per queue data critical sections should be global */
122     MakeCriticalSectionGlobal( &pQData->cSection );
123
124     /* Save perQData globally for 16 bit tasks */
125     if ( bIsWin16 )
126         pQDataWin16 = pQData;
127         
128     return pQData;
129 }
130
131
132 /***********************************************************************
133  *           PERQDATA_GetFocusWnd
134  *
135  * Get the focus hwnd member in a threadsafe manner
136  */
137 HWND PERQDATA_GetFocusWnd( PERQUEUEDATA *pQData )
138 {
139     HWND hWndFocus;
140     assert(pQData != 0 );
141
142     EnterCriticalSection( &pQData->cSection );
143     hWndFocus = pQData->hWndFocus;
144     LeaveCriticalSection( &pQData->cSection );
145
146     return hWndFocus;
147 }
148
149
150 /***********************************************************************
151  *           PERQDATA_SetFocusWnd
152  *
153  * Set the focus hwnd member in a threadsafe manner
154  */
155 HWND PERQDATA_SetFocusWnd( PERQUEUEDATA *pQData, HWND hWndFocus )
156 {
157     HWND hWndFocusPrv;
158     assert(pQData != 0 );
159
160     EnterCriticalSection( &pQData->cSection );
161     hWndFocusPrv = pQData->hWndFocus;
162     pQData->hWndFocus = hWndFocus;
163     LeaveCriticalSection( &pQData->cSection );
164
165     return hWndFocusPrv;
166 }
167
168
169 /***********************************************************************
170  *           PERQDATA_GetActiveWnd
171  *
172  * Get the active hwnd member in a threadsafe manner
173  */
174 HWND PERQDATA_GetActiveWnd( PERQUEUEDATA *pQData )
175 {
176     HWND hWndActive;
177     assert(pQData != 0 );
178
179     EnterCriticalSection( &pQData->cSection );
180     hWndActive = pQData->hWndActive;
181     LeaveCriticalSection( &pQData->cSection );
182
183     return hWndActive;
184 }
185
186
187 /***********************************************************************
188  *           PERQDATA_SetActiveWnd
189  *
190  * Set the active focus hwnd member in a threadsafe manner
191  */
192 HWND PERQDATA_SetActiveWnd( PERQUEUEDATA *pQData, HWND hWndActive )
193 {
194     HWND hWndActivePrv;
195     assert(pQData != 0 );
196
197     EnterCriticalSection( &pQData->cSection );
198     hWndActivePrv = pQData->hWndActive;
199     pQData->hWndActive = hWndActive;
200     LeaveCriticalSection( &pQData->cSection );
201
202     return hWndActivePrv;
203 }
204
205
206 /***********************************************************************
207  *           PERQDATA_GetCaptureWnd
208  *
209  * Get the capture hwnd member in a threadsafe manner
210  */
211 HWND PERQDATA_GetCaptureWnd( INT *hittest )
212 {
213     MESSAGEQUEUE *queue;
214     PERQUEUEDATA *pQData;
215     HWND hWndCapture;
216
217     if (!(queue = QUEUE_Lock( GetFastQueue16() ))) return 0;
218     pQData = queue->pQData;
219
220     EnterCriticalSection( &pQData->cSection );
221     hWndCapture = pQData->hWndCapture;
222     *hittest = pQData->nCaptureHT;
223     LeaveCriticalSection( &pQData->cSection );
224
225     QUEUE_Unlock( queue );
226     return hWndCapture;
227 }
228
229
230 /***********************************************************************
231  *           PERQDATA_SetCaptureWnd
232  *
233  * Set the capture hwnd member in a threadsafe manner
234  */
235 HWND PERQDATA_SetCaptureWnd( HWND hWndCapture, INT hittest )
236 {
237     MESSAGEQUEUE *queue;
238     PERQUEUEDATA *pQData;
239     HWND hWndCapturePrv;
240
241     if (!(queue = QUEUE_Lock( GetFastQueue16() ))) return 0;
242     pQData = queue->pQData;
243
244     EnterCriticalSection( &pQData->cSection );
245     hWndCapturePrv = pQData->hWndCapture;
246     pQData->hWndCapture = hWndCapture;
247     pQData->nCaptureHT = hittest;
248     LeaveCriticalSection( &pQData->cSection );
249
250     QUEUE_Unlock( queue );
251     return hWndCapturePrv;
252 }
253
254
255
256 /***********************************************************************
257  *           QUEUE_Lock
258  *
259  * Function for getting a 32 bit pointer on queue structure. For thread
260  * safeness programmers should use this function instead of GlobalLock to
261  * retrieve a pointer on the structure. QUEUE_Unlock should also be called
262  * when access to the queue structure is not required anymore.
263  */
264 MESSAGEQUEUE *QUEUE_Lock( HQUEUE16 hQueue )
265 {
266     MESSAGEQUEUE *queue;
267
268     HeapLock( GetProcessHeap() );  /* FIXME: a bit overkill */
269     queue = GlobalLock16( hQueue );
270     if ( !queue || (queue->magic != QUEUE_MAGIC) )
271     {
272         HeapUnlock( GetProcessHeap() );
273         return NULL;
274     }
275
276     queue->lockCount++;
277     HeapUnlock( GetProcessHeap() );
278     return queue;
279 }
280
281
282 /***********************************************************************
283  *           QUEUE_Unlock
284  *
285  * Use with QUEUE_Lock to get a thread safe access to message queue
286  * structure
287  */
288 void QUEUE_Unlock( MESSAGEQUEUE *queue )
289 {
290     if (queue)
291     {
292         HeapLock( GetProcessHeap() );  /* FIXME: a bit overkill */
293
294         if ( --queue->lockCount == 0 )
295         {
296             if (queue->server_queue)
297                 CloseHandle( queue->server_queue );
298             GlobalFree16( queue->self );
299         }
300     
301         HeapUnlock( GetProcessHeap() );
302     }
303 }
304
305
306 /***********************************************************************
307  *           QUEUE_IsExitingQueue
308  */
309 BOOL QUEUE_IsExitingQueue( HQUEUE16 hQueue )
310 {
311     return (hExitingQueue && (hQueue == hExitingQueue));
312 }
313
314
315 /***********************************************************************
316  *           QUEUE_SetExitingQueue
317  */
318 void QUEUE_SetExitingQueue( HQUEUE16 hQueue )
319 {
320     hExitingQueue = hQueue;
321 }
322
323
324 /***********************************************************************
325  *           QUEUE_CreateMsgQueue
326  *
327  * Creates a message queue. Doesn't link it into queue list!
328  */
329 static HQUEUE16 QUEUE_CreateMsgQueue( BOOL16 bCreatePerQData )
330 {
331     HQUEUE16 hQueue;
332     HANDLE handle;
333     MESSAGEQUEUE * msgQueue;
334
335     TRACE_(msg)("(): Creating message queue...\n");
336
337     if (!(hQueue = GlobalAlloc16( GMEM_FIXED | GMEM_ZEROINIT,
338                                   sizeof(MESSAGEQUEUE) )))
339         return 0;
340
341     msgQueue = (MESSAGEQUEUE *) GlobalLock16( hQueue );
342     if ( !msgQueue )
343         return 0;
344
345     if (bCreatePerQData)
346     {
347         SERVER_START_REQ( get_msg_queue )
348         {
349             SERVER_CALL_ERR();
350             handle = req->handle;
351         }
352         SERVER_END_REQ;
353         if (!handle)
354         {
355             ERR_(msg)("Cannot get thread queue");
356             GlobalFree16( hQueue );
357             return 0;
358         }
359         msgQueue->server_queue = handle;
360     }
361
362     msgQueue->self = hQueue;
363     msgQueue->lockCount = 1;
364     msgQueue->magic = QUEUE_MAGIC;
365     
366     /* Create and initialize our per queue data */
367     msgQueue->pQData = bCreatePerQData ? PERQDATA_CreateInstance() : NULL;
368     
369     return hQueue;
370 }
371
372
373 /***********************************************************************
374  *           QUEUE_DeleteMsgQueue
375  *
376  * Unlinks and deletes a message queue.
377  *
378  * Note: We need to mask asynchronous events to make sure PostMessage works
379  * even in the signal handler.
380  */
381 BOOL QUEUE_DeleteMsgQueue( HQUEUE16 hQueue )
382 {
383     MESSAGEQUEUE * msgQueue = QUEUE_Lock(hQueue);
384
385     TRACE_(msg)("(): Deleting message queue %04x\n", hQueue);
386
387     if (!hQueue || !msgQueue)
388     {
389         ERR_(msg)("invalid argument.\n");
390         return 0;
391     }
392
393     msgQueue->magic = 0;
394
395     if( hActiveQueue == hQueue ) hActiveQueue = 0;
396     if (hExitingQueue == hQueue) hExitingQueue = 0;
397
398     HeapLock( GetProcessHeap() );  /* FIXME: a bit overkill */
399
400     /* Release per queue data if present */
401     if ( msgQueue->pQData )
402     {
403         PERQDATA_Release( msgQueue->pQData );
404         msgQueue->pQData = 0;
405     }
406
407     msgQueue->self = 0;
408
409     HeapUnlock( GetProcessHeap() );
410
411     /* free up resource used by MESSAGEQUEUE structure */
412     msgQueue->lockCount--;
413     QUEUE_Unlock( msgQueue );
414     
415     return 1;
416 }
417
418
419 /***********************************************************************
420  *           QUEUE_CleanupWindow
421  *
422  * Cleanup the queue to account for a window being deleted.
423  */
424 void QUEUE_CleanupWindow( HWND hwnd )
425 {
426     SERVER_START_REQ( cleanup_window_queue )
427     {
428         req->win = hwnd;
429         SERVER_CALL();
430     }
431     SERVER_END_REQ;
432 }
433
434
435 /***********************************************************************
436  *           QUEUE_GetQueueTask
437  */
438 HTASK16 QUEUE_GetQueueTask( HQUEUE16 hQueue )
439 {
440     HTASK16 hTask = 0;
441     
442     MESSAGEQUEUE *queue = QUEUE_Lock( hQueue );
443
444     if (queue)
445     {
446         hTask = queue->teb->htask16;
447         QUEUE_Unlock( queue );
448     }
449
450     return hTask;
451 }
452
453
454 /***********************************************************************
455  *              PostQuitMessage (USER.6)
456  */
457 void WINAPI PostQuitMessage16( INT16 exitCode )
458 {
459     PostQuitMessage( exitCode );
460 }
461
462
463 /***********************************************************************
464  *              PostQuitMessage (USER32.@)
465  *
466  * PostQuitMessage() posts a message to the system requesting an
467  * application to terminate execution. As a result of this function,
468  * the WM_QUIT message is posted to the application, and
469  * PostQuitMessage() returns immediately.  The exitCode parameter
470  * specifies an application-defined exit code, which appears in the
471  * _wParam_ parameter of the WM_QUIT message posted to the application.  
472  *
473  * CONFORMANCE
474  *
475  *  ECMA-234, Win32
476  */
477 void WINAPI PostQuitMessage( INT exitCode )
478 {
479     PostThreadMessageW( GetCurrentThreadId(), WM_QUIT, exitCode, 0 );
480 }
481
482
483 /***********************************************************************
484  *              GetWindowTask (USER.224)
485  */
486 HTASK16 WINAPI GetWindowTask16( HWND16 hwnd )
487 {
488     HTASK16 retvalue;
489     WND *wndPtr = WIN_FindWndPtr( hwnd );
490
491     if (!wndPtr) return 0;
492     retvalue = QUEUE_GetQueueTask( wndPtr->hmemTaskQ );
493     WIN_ReleaseWndPtr(wndPtr);
494     return retvalue;
495 }
496
497 /***********************************************************************
498  *              GetWindowThreadProcessId (USER32.@)
499  */
500 DWORD WINAPI GetWindowThreadProcessId( HWND hwnd, LPDWORD process )
501 {
502     DWORD retvalue;
503     MESSAGEQUEUE *queue;
504
505     WND *wndPtr = WIN_FindWndPtr( hwnd );
506     if (!wndPtr) return 0;
507
508     queue = QUEUE_Lock( wndPtr->hmemTaskQ );
509     WIN_ReleaseWndPtr(wndPtr);
510
511     if (!queue) return 0;
512
513     if ( process ) *process = (DWORD)queue->teb->pid;
514     retvalue = (DWORD)queue->teb->tid;
515
516     QUEUE_Unlock( queue );
517     return retvalue;
518 }
519
520
521 /***********************************************************************
522  *              SetMessageQueue (USER.266)
523  */
524 BOOL16 WINAPI SetMessageQueue16( INT16 size )
525 {
526     return SetMessageQueue( size );
527 }
528
529
530 /***********************************************************************
531  *              SetMessageQueue (USER32.@)
532  */
533 BOOL WINAPI SetMessageQueue( INT size )
534 {
535     /* now obsolete the message queue will be expanded dynamically
536      as necessary */
537
538     /* access the queue to create it if it's not existing */
539     GetFastQueue16();
540
541     return TRUE;
542 }
543
544 /***********************************************************************
545  *              InitThreadInput (USER.409)
546  */
547 HQUEUE16 WINAPI InitThreadInput16( WORD unknown, WORD flags )
548 {
549     HQUEUE16 hQueue;
550     MESSAGEQUEUE *queuePtr;
551
552     TEB *teb = NtCurrentTeb();
553
554     if (!teb)
555         return 0;
556
557     hQueue = teb->queue;
558     
559     if ( !hQueue )
560     {
561         /* Create thread message queue */
562         if( !(hQueue = QUEUE_CreateMsgQueue( TRUE )))
563         {
564             ERR_(msg)("failed!\n");
565             return FALSE;
566         }
567         
568         /* Link new queue into list */
569         queuePtr = QUEUE_Lock( hQueue );
570         queuePtr->teb = NtCurrentTeb();
571
572         HeapLock( GetProcessHeap() );  /* FIXME: a bit overkill */
573         SetThreadQueue16( 0, hQueue );
574         teb->queue = hQueue;
575         HeapUnlock( GetProcessHeap() );
576         
577         QUEUE_Unlock( queuePtr );
578     }
579
580     return hQueue;
581 }
582
583 /***********************************************************************
584  *              GetQueueStatus (USER.334)
585  */
586 DWORD WINAPI GetQueueStatus16( UINT16 flags )
587 {
588     return GetQueueStatus( flags );
589 }
590
591 /***********************************************************************
592  *              GetQueueStatus (USER32.@)
593  */
594 DWORD WINAPI GetQueueStatus( UINT flags )
595 {
596     DWORD ret = 0;
597
598     SERVER_START_REQ( get_queue_status )
599     {
600         req->clear = 1;
601         SERVER_CALL();
602         ret = MAKELONG( req->changed_bits & flags, req->wake_bits & flags );
603     }
604     SERVER_END_REQ;
605     return ret;
606 }
607
608
609 /***********************************************************************
610  *              GetInputState (USER.335)
611  */
612 BOOL16 WINAPI GetInputState16(void)
613 {
614     return GetInputState();
615 }
616
617 /***********************************************************************
618  *              GetInputState   (USER32.@)
619  */
620 BOOL WINAPI GetInputState(void)
621 {
622     DWORD ret = 0;
623
624     SERVER_START_REQ( get_queue_status )
625     {
626         req->clear = 0;
627         SERVER_CALL();
628         ret = req->wake_bits & (QS_KEY | QS_MOUSEBUTTON);
629     }
630     SERVER_END_REQ;
631     return ret;
632 }
633
634 /***********************************************************************
635  *              GetMessagePos (USER.119) (USER32.@)
636  * 
637  * The GetMessagePos() function returns a long value representing a
638  * cursor position, in screen coordinates, when the last message
639  * retrieved by the GetMessage() function occurs. The x-coordinate is
640  * in the low-order word of the return value, the y-coordinate is in
641  * the high-order word. The application can use the MAKEPOINT()
642  * macro to obtain a POINT structure from the return value. 
643  *
644  * For the current cursor position, use GetCursorPos().
645  *
646  * RETURNS
647  *
648  * Cursor position of last message on success, zero on failure.
649  *
650  * CONFORMANCE
651  *
652  * ECMA-234, Win32
653  *
654  */
655 DWORD WINAPI GetMessagePos(void)
656 {
657     MESSAGEQUEUE *queue;
658     DWORD ret;
659
660     if (!(queue = QUEUE_Lock( GetFastQueue16() ))) return 0;
661     ret = queue->GetMessagePosVal;
662     QUEUE_Unlock( queue );
663
664     return ret;
665 }
666
667
668 /***********************************************************************
669  *              GetMessageTime (USER.120) (USER32.@)
670  *
671  * GetMessageTime() returns the message time for the last message
672  * retrieved by the function. The time is measured in milliseconds with
673  * the same offset as GetTickCount().
674  *
675  * Since the tick count wraps, this is only useful for moderately short
676  * relative time comparisons.
677  *
678  * RETURNS
679  *
680  * Time of last message on success, zero on failure.
681  *
682  * CONFORMANCE
683  *
684  * ECMA-234, Win32
685  *  
686  */
687 LONG WINAPI GetMessageTime(void)
688 {
689     MESSAGEQUEUE *queue;
690     LONG ret;
691
692     if (!(queue = QUEUE_Lock( GetFastQueue16() ))) return 0;
693     ret = queue->GetMessageTimeVal;
694     QUEUE_Unlock( queue );
695     
696     return ret;
697 }
698
699
700 /***********************************************************************
701  *              GetMessageExtraInfo (USER.288) (USER32.@)
702  */
703 LONG WINAPI GetMessageExtraInfo(void)
704 {
705     MESSAGEQUEUE *queue;
706     LONG ret;
707
708     if (!(queue = QUEUE_Lock( GetFastQueue16() ))) return 0;
709     ret = queue->GetMessageExtraInfoVal;
710     QUEUE_Unlock( queue );
711
712     return ret;
713 }
714
715
716 /**********************************************************************
717  *              AttachThreadInput (USER32.@) Attaches input of 1 thread to other
718  *
719  * Attaches the input processing mechanism of one thread to that of
720  * another thread.
721  *
722  * RETURNS
723  *    Success: TRUE
724  *    Failure: FALSE
725  *
726  * TODO:
727  *    1. Reset the Key State (currenly per thread key state is not maintained)
728  */
729 BOOL WINAPI AttachThreadInput( 
730     DWORD idAttach,   /* [in] Thread to attach */
731     DWORD idAttachTo, /* [in] Thread to attach to */
732     BOOL fAttach)   /* [in] Attach or detach */
733 {
734     MESSAGEQUEUE *pSrcMsgQ = 0, *pTgtMsgQ = 0;
735     BOOL16 bRet = 0;
736
737     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
738
739     /* A thread cannot attach to itself */
740     if ( idAttach == idAttachTo )
741         goto CLEANUP;
742
743     /* According to the docs this method should fail if a
744      * "Journal record" hook is installed. (attaches all input queues together)
745      */
746     if ( HOOK_IsHooked( WH_JOURNALRECORD ) )
747         goto CLEANUP;
748         
749     /* Retrieve message queues corresponding to the thread id's */
750     pTgtMsgQ = QUEUE_Lock( GetThreadQueue16( idAttach ) );
751     pSrcMsgQ = QUEUE_Lock( GetThreadQueue16( idAttachTo ) );
752
753     /* Ensure we have message queues and that Src and Tgt threads
754      * are not system threads.
755      */
756     if ( !pSrcMsgQ || !pTgtMsgQ || !pSrcMsgQ->pQData || !pTgtMsgQ->pQData )
757         goto CLEANUP;
758
759     if (fAttach)   /* Attach threads */
760     {
761         /* Only attach if currently detached  */
762         if ( pTgtMsgQ->pQData != pSrcMsgQ->pQData )
763         {
764             /* First release the target threads perQData */
765             PERQDATA_Release( pTgtMsgQ->pQData );
766         
767             /* Share a reference to the source threads perQDATA */
768             PERQDATA_Addref( pSrcMsgQ->pQData );
769             pTgtMsgQ->pQData = pSrcMsgQ->pQData;
770         }
771     }
772     else    /* Detach threads */
773     {
774         /* Only detach if currently attached */
775         if ( pTgtMsgQ->pQData == pSrcMsgQ->pQData )
776         {
777             /* First release the target threads perQData */
778             PERQDATA_Release( pTgtMsgQ->pQData );
779         
780             /* Give the target thread its own private perQDATA once more */
781             pTgtMsgQ->pQData = PERQDATA_CreateInstance();
782         }
783     }
784
785     /* TODO: Reset the Key State */
786
787     bRet = 1;      /* Success */
788     
789 CLEANUP:
790
791     /* Unlock the queues before returning */
792     if ( pSrcMsgQ )
793         QUEUE_Unlock( pSrcMsgQ );
794     if ( pTgtMsgQ )
795         QUEUE_Unlock( pTgtMsgQ );
796     
797     return bRet;
798 }