Moved server.h to include/wine.
[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 "wine/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)
636  *              GetMessagePos (USER32.@)
637  * 
638  * The GetMessagePos() function returns a long value representing a
639  * cursor position, in screen coordinates, when the last message
640  * retrieved by the GetMessage() function occurs. The x-coordinate is
641  * in the low-order word of the return value, the y-coordinate is in
642  * the high-order word. The application can use the MAKEPOINT()
643  * macro to obtain a POINT structure from the return value. 
644  *
645  * For the current cursor position, use GetCursorPos().
646  *
647  * RETURNS
648  *
649  * Cursor position of last message on success, zero on failure.
650  *
651  * CONFORMANCE
652  *
653  * ECMA-234, Win32
654  *
655  */
656 DWORD WINAPI GetMessagePos(void)
657 {
658     MESSAGEQUEUE *queue;
659     DWORD ret;
660
661     if (!(queue = QUEUE_Lock( GetFastQueue16() ))) return 0;
662     ret = queue->GetMessagePosVal;
663     QUEUE_Unlock( queue );
664
665     return ret;
666 }
667
668
669 /***********************************************************************
670  *              GetMessageTime (USER.120)
671  *              GetMessageTime (USER32.@)
672  *
673  * GetMessageTime() returns the message time for the last message
674  * retrieved by the function. The time is measured in milliseconds with
675  * the same offset as GetTickCount().
676  *
677  * Since the tick count wraps, this is only useful for moderately short
678  * relative time comparisons.
679  *
680  * RETURNS
681  *
682  * Time of last message on success, zero on failure.
683  *
684  * CONFORMANCE
685  *
686  * ECMA-234, Win32
687  *  
688  */
689 LONG WINAPI GetMessageTime(void)
690 {
691     MESSAGEQUEUE *queue;
692     LONG ret;
693
694     if (!(queue = QUEUE_Lock( GetFastQueue16() ))) return 0;
695     ret = queue->GetMessageTimeVal;
696     QUEUE_Unlock( queue );
697     
698     return ret;
699 }
700
701
702 /***********************************************************************
703  *              GetMessageExtraInfo (USER.288)
704  *              GetMessageExtraInfo (USER32.@)
705  */
706 LONG WINAPI GetMessageExtraInfo(void)
707 {
708     MESSAGEQUEUE *queue;
709     LONG ret;
710
711     if (!(queue = QUEUE_Lock( GetFastQueue16() ))) return 0;
712     ret = queue->GetMessageExtraInfoVal;
713     QUEUE_Unlock( queue );
714
715     return ret;
716 }
717
718
719 /**********************************************************************
720  *              AttachThreadInput (USER32.@) Attaches input of 1 thread to other
721  *
722  * Attaches the input processing mechanism of one thread to that of
723  * another thread.
724  *
725  * RETURNS
726  *    Success: TRUE
727  *    Failure: FALSE
728  *
729  * TODO:
730  *    1. Reset the Key State (currenly per thread key state is not maintained)
731  */
732 BOOL WINAPI AttachThreadInput( 
733     DWORD idAttach,   /* [in] Thread to attach */
734     DWORD idAttachTo, /* [in] Thread to attach to */
735     BOOL fAttach)   /* [in] Attach or detach */
736 {
737     MESSAGEQUEUE *pSrcMsgQ = 0, *pTgtMsgQ = 0;
738     BOOL16 bRet = 0;
739
740     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
741
742     /* A thread cannot attach to itself */
743     if ( idAttach == idAttachTo )
744         goto CLEANUP;
745
746     /* According to the docs this method should fail if a
747      * "Journal record" hook is installed. (attaches all input queues together)
748      */
749     if ( HOOK_IsHooked( WH_JOURNALRECORD ) )
750         goto CLEANUP;
751         
752     /* Retrieve message queues corresponding to the thread id's */
753     pTgtMsgQ = QUEUE_Lock( GetThreadQueue16( idAttach ) );
754     pSrcMsgQ = QUEUE_Lock( GetThreadQueue16( idAttachTo ) );
755
756     /* Ensure we have message queues and that Src and Tgt threads
757      * are not system threads.
758      */
759     if ( !pSrcMsgQ || !pTgtMsgQ || !pSrcMsgQ->pQData || !pTgtMsgQ->pQData )
760         goto CLEANUP;
761
762     if (fAttach)   /* Attach threads */
763     {
764         /* Only attach if currently detached  */
765         if ( pTgtMsgQ->pQData != pSrcMsgQ->pQData )
766         {
767             /* First release the target threads perQData */
768             PERQDATA_Release( pTgtMsgQ->pQData );
769         
770             /* Share a reference to the source threads perQDATA */
771             PERQDATA_Addref( pSrcMsgQ->pQData );
772             pTgtMsgQ->pQData = pSrcMsgQ->pQData;
773         }
774     }
775     else    /* Detach threads */
776     {
777         /* Only detach if currently attached */
778         if ( pTgtMsgQ->pQData == pSrcMsgQ->pQData )
779         {
780             /* First release the target threads perQData */
781             PERQDATA_Release( pTgtMsgQ->pQData );
782         
783             /* Give the target thread its own private perQDATA once more */
784             pTgtMsgQ->pQData = PERQDATA_CreateInstance();
785         }
786     }
787
788     /* TODO: Reset the Key State */
789
790     bRet = 1;      /* Success */
791     
792 CLEANUP:
793
794     /* Unlock the queues before returning */
795     if ( pSrcMsgQ )
796         QUEUE_Unlock( pSrcMsgQ );
797     if ( pTgtMsgQ )
798         QUEUE_Unlock( pTgtMsgQ );
799     
800     return bRet;
801 }