-Fixed MESSAGE functions that were thunking down to 16 bits implementation.
[wine] / windows / queue.c
1 /* * Message queues related functions
2  *
3  * Copyright 1993, 1994 Alexandre Julliard
4  */
5
6 #include <stdlib.h>
7 #include <signal.h>
8 #include "miscemu.h"
9 #include "module.h"
10 #include "queue.h"
11 #include "task.h"
12 #include "win.h"
13 #include "clipboard.h"
14 #include "hook.h"
15 #include "heap.h"
16 #include "thread.h"
17 #include "process.h"
18 #include <assert.h>
19 #include "debug.h"
20
21 #define MAX_QUEUE_SIZE   120  /* Max. size of a message queue */
22
23 static HQUEUE16 hFirstQueue = 0;
24 static HQUEUE16 hExitingQueue = 0;
25 static HQUEUE16 hmemSysMsgQueue = 0;
26 static MESSAGEQUEUE *sysMsgQueue = NULL;
27 static PERQUEUEDATA *pQDataWin16 = NULL;  /* Global perQData for Win16 tasks */
28
29 static MESSAGEQUEUE *pMouseQueue = NULL;  /* Queue for last mouse message */
30 static MESSAGEQUEUE *pKbdQueue = NULL;    /* Queue for last kbd message */
31
32 HQUEUE16 hCursorQueue = 0;
33 HQUEUE16 hActiveQueue = 0;
34
35
36 /***********************************************************************
37  *           PERQDATA_CreateInstance
38  *
39  * Creates an instance of a reference counted PERQUEUEDATA element
40  * for the message queue. perQData is stored globally for 16 bit tasks.
41  *
42  * Note: We don't implement perQdata exactly the same way Windows does.
43  * Each perQData element is reference counted since it may be potentially
44  * shared by multiple message Queues (via AttachThreadInput).
45  * We only store the current values for Active, Capture and focus windows
46  * currently.
47  */
48 PERQUEUEDATA * PERQDATA_CreateInstance( )
49 {
50     PERQUEUEDATA *pQData;
51     
52     BOOL16 bIsWin16 = 0;
53     
54     TRACE(msg,"()\n");
55
56     /* Share a single instance of perQData for all 16 bit tasks */
57     if ( ( bIsWin16 = THREAD_IsWin16( THREAD_Current() ) ) )
58     {
59         /* If previously allocated, just bump up ref count */
60         if ( pQDataWin16 )
61         {
62             PERQDATA_Addref( pQDataWin16 );
63             return pQDataWin16;
64         }
65     }
66
67     /* Allocate PERQUEUEDATA from the system heap */
68     if (!( pQData = (PERQUEUEDATA *) HeapAlloc( SystemHeap, 0,
69                                                     sizeof(PERQUEUEDATA) ) ))
70         return 0;
71
72     /* Initialize */
73     pQData->hWndCapture = pQData->hWndFocus = pQData->hWndActive = 0;
74     pQData->ulRefCount = 1;
75     pQData->nCaptureHT = HTCLIENT;
76
77     /* Note: We have an independent critical section for the per queue data
78      * since this may be shared by different threads. see AttachThreadInput()
79      */
80     InitializeCriticalSection( &pQData->cSection );
81
82     /* Save perQData globally for 16 bit tasks */
83     if ( bIsWin16 )
84         pQDataWin16 = pQData;
85         
86     return pQData;
87 }
88
89
90 /***********************************************************************
91  *           PERQDATA_Addref
92  *
93  * Increment reference count for the PERQUEUEDATA instance
94  * Returns reference count for debugging purposes
95  */
96 ULONG PERQDATA_Addref( PERQUEUEDATA *pQData )
97 {
98     assert(pQData != 0 );
99     TRACE(msg,"(): current refcount %lu ...\n", pQData->ulRefCount);
100
101     EnterCriticalSection( &pQData->cSection );
102     ++pQData->ulRefCount;
103     LeaveCriticalSection( &pQData->cSection );
104
105     return pQData->ulRefCount;
106 }
107
108
109 /***********************************************************************
110  *           PERQDATA_Release
111  *
112  * Release a reference to a PERQUEUEDATA instance.
113  * Destroy the instance if no more references exist
114  * Returns reference count for debugging purposes
115  */
116 ULONG PERQDATA_Release( PERQUEUEDATA *pQData )
117 {
118     assert(pQData != 0 );
119     TRACE(msg,"(): current refcount %lu ...\n",
120           (LONG)pQData->ulRefCount );
121
122     EnterCriticalSection( &pQData->cSection );
123     if ( --pQData->ulRefCount == 0 )
124     {
125         LeaveCriticalSection( &pQData->cSection );
126         DeleteCriticalSection( &pQData->cSection );
127
128         TRACE(msg,"(): deleting PERQUEUEDATA instance ...\n" );
129
130         /* Deleting our global 16 bit perQData? */
131         if ( pQData == pQDataWin16 )
132             pQDataWin16 = 0;
133             
134         /* Free the PERQUEUEDATA instance */
135         HeapFree( SystemHeap, 0, pQData );
136
137         return 0;
138     }
139     LeaveCriticalSection( &pQData->cSection );
140
141     return pQData->ulRefCount;
142 }
143
144
145 /***********************************************************************
146  *           PERQDATA_GetFocusWnd
147  *
148  * Get the focus hwnd member in a threadsafe manner
149  */
150 HWND32 PERQDATA_GetFocusWnd( PERQUEUEDATA *pQData )
151 {
152     HWND32 hWndFocus;
153     assert(pQData != 0 );
154
155     EnterCriticalSection( &pQData->cSection );
156     hWndFocus = pQData->hWndFocus;
157     LeaveCriticalSection( &pQData->cSection );
158
159     return hWndFocus;
160 }
161
162
163 /***********************************************************************
164  *           PERQDATA_SetFocusWnd
165  *
166  * Set the focus hwnd member in a threadsafe manner
167  */
168 HWND32 PERQDATA_SetFocusWnd( PERQUEUEDATA *pQData, HWND32 hWndFocus )
169 {
170     HWND32 hWndFocusPrv;
171     assert(pQData != 0 );
172
173     EnterCriticalSection( &pQData->cSection );
174     hWndFocusPrv = pQData->hWndFocus;
175     pQData->hWndFocus = hWndFocus;
176     LeaveCriticalSection( &pQData->cSection );
177
178     return hWndFocusPrv;
179 }
180
181
182 /***********************************************************************
183  *           PERQDATA_GetActiveWnd
184  *
185  * Get the active hwnd member in a threadsafe manner
186  */
187 HWND32 PERQDATA_GetActiveWnd( PERQUEUEDATA *pQData )
188 {
189     HWND32 hWndActive;
190     assert(pQData != 0 );
191
192     EnterCriticalSection( &pQData->cSection );
193     hWndActive = pQData->hWndActive;
194     LeaveCriticalSection( &pQData->cSection );
195
196     return hWndActive;
197 }
198
199
200 /***********************************************************************
201  *           PERQDATA_SetActiveWnd
202  *
203  * Set the active focus hwnd member in a threadsafe manner
204  */
205 HWND32 PERQDATA_SetActiveWnd( PERQUEUEDATA *pQData, HWND32 hWndActive )
206 {
207     HWND32 hWndActivePrv;
208     assert(pQData != 0 );
209
210     EnterCriticalSection( &pQData->cSection );
211     hWndActivePrv = pQData->hWndActive;
212     pQData->hWndActive = hWndActive;
213     LeaveCriticalSection( &pQData->cSection );
214
215     return hWndActivePrv;
216 }
217
218
219 /***********************************************************************
220  *           PERQDATA_GetCaptureWnd
221  *
222  * Get the capture hwnd member in a threadsafe manner
223  */
224 HWND32 PERQDATA_GetCaptureWnd( PERQUEUEDATA *pQData )
225 {
226     HWND32 hWndCapture;
227     assert(pQData != 0 );
228
229     EnterCriticalSection( &pQData->cSection );
230     hWndCapture = pQData->hWndCapture;
231     LeaveCriticalSection( &pQData->cSection );
232
233     return hWndCapture;
234 }
235
236
237 /***********************************************************************
238  *           PERQDATA_SetCaptureWnd
239  *
240  * Set the capture hwnd member in a threadsafe manner
241  */
242 HWND32 PERQDATA_SetCaptureWnd( PERQUEUEDATA *pQData, HWND32 hWndCapture )
243 {
244     HWND32 hWndCapturePrv;
245     assert(pQData != 0 );
246
247     EnterCriticalSection( &pQData->cSection );
248     hWndCapturePrv = pQData->hWndCapture;
249     pQData->hWndCapture = hWndCapture;
250     LeaveCriticalSection( &pQData->cSection );
251
252     return hWndCapturePrv;
253 }
254
255
256 /***********************************************************************
257  *           PERQDATA_GetCaptureInfo
258  *
259  * Get the capture info member in a threadsafe manner
260  */
261 INT16 PERQDATA_GetCaptureInfo( PERQUEUEDATA *pQData )
262 {
263     INT16 nCaptureHT;
264     assert(pQData != 0 );
265
266     EnterCriticalSection( &pQData->cSection );
267     nCaptureHT = pQData->nCaptureHT;
268     LeaveCriticalSection( &pQData->cSection );
269
270     return nCaptureHT;
271 }
272
273
274 /***********************************************************************
275  *           PERQDATA_SetCaptureInfo
276  *
277  * Set the capture info member in a threadsafe manner
278  */
279 INT16 PERQDATA_SetCaptureInfo( PERQUEUEDATA *pQData, INT16 nCaptureHT )
280 {
281     INT16 nCaptureHTPrv;
282     assert(pQData != 0 );
283
284     EnterCriticalSection( &pQData->cSection );
285     nCaptureHTPrv = pQData->nCaptureHT;
286     pQData->nCaptureHT = nCaptureHT;
287     LeaveCriticalSection( &pQData->cSection );
288
289     return nCaptureHTPrv;
290 }
291
292
293 /***********************************************************************
294  *           QUEUE_Lock
295  *
296  * Function for getting a 32 bit pointer on queue strcture. For thread
297  * safeness programmers should use this function instead of GlobalLock to
298  * retrieve a pointer on the structure. QUEUE_Unlock should also be called
299  * when access to the queue structure is not required anymore.
300  */
301 MESSAGEQUEUE *QUEUE_Lock( HQUEUE16 hQueue )
302 {
303     MESSAGEQUEUE *queue;
304
305     SYSTEM_LOCK();
306     queue = GlobalLock16( hQueue );
307     if ( !queue || (queue->magic != QUEUE_MAGIC) )
308     {
309         SYSTEM_UNLOCK();
310         return NULL;
311     }
312
313     queue->lockCount++;
314     SYSTEM_UNLOCK();
315
316     return queue;
317 }
318
319
320 /***********************************************************************
321  *           QUEUE_Unlock
322  *
323  * Use with QUEUE_Lock to get a thread safe acces to message queue
324  * structure
325  */
326 void QUEUE_Unlock( MESSAGEQUEUE *queue )
327 {
328     if (queue)
329     {
330         SYSTEM_LOCK();
331
332         if ( --queue->lockCount == 0 )
333         {
334             DeleteCriticalSection ( &queue->cSection );
335             if (queue->hEvent)
336                 CloseHandle( queue->hEvent );
337             GlobalFree16( queue->self );
338         }
339     
340         SYSTEM_UNLOCK();
341     }
342 }
343
344
345 /***********************************************************************
346  *           QUEUE_DumpQueue
347  */
348 void QUEUE_DumpQueue( HQUEUE16 hQueue )
349 {
350     MESSAGEQUEUE *pq; 
351
352     if (!(pq = (MESSAGEQUEUE*) QUEUE_Lock( hQueue )) )
353     {
354         WARN(msg, "%04x is not a queue handle\n", hQueue );
355         return;
356     }
357
358     DUMP(    "next: %12.4x  Intertask SendMessage:\n"
359              "thread: %10p  ----------------------\n"
360              "hWnd: %12.8x\n"
361              "firstMsg: %8p   msg:     %11.8x\n"
362              "lastMsg:  %8p   wParam:   %10.8x\n"
363              "msgCount: %8.4x   lParam:   %10.8x\n"
364              "lockCount: %7.4x   lRet:   %12.8x\n"
365              "wWinVer: %9.4x  ISMH: %10.4x\n"
366              "paints: %10.4x  hSendTask: %5.4x\n"
367              "timers: %10.4x  hPrevSend: %5.4x\n"
368              "wakeBits: %8.4x\n"
369              "wakeMask: %8.4x\n"
370              "hCurHook: %8.4x\n",
371              pq->next, pq->thdb, pq->hWnd32, pq->firstMsg, pq->msg32,
372              pq->lastMsg, pq->wParam32, pq->msgCount, (unsigned)pq->lParam,
373              (unsigned)pq->lockCount, (unsigned)pq->SendMessageReturn,
374              pq->wWinVersion, pq->InSendMessageHandle,
375              pq->wPaintCount, pq->hSendingTask, pq->wTimerCount,
376              pq->hPrevSendingTask, pq->wakeBits, pq->wakeMask, pq->hCurHook);
377
378     QUEUE_Unlock( pq );
379 }
380
381
382 /***********************************************************************
383  *           QUEUE_WalkQueues
384  */
385 void QUEUE_WalkQueues(void)
386 {
387     char module[10];
388     HQUEUE16 hQueue = hFirstQueue;
389
390     DUMP( "Queue Msgs Thread   Task Module\n" );
391     while (hQueue)
392     {
393         MESSAGEQUEUE *queue = (MESSAGEQUEUE *)QUEUE_Lock( hQueue );
394         if (!queue)
395         {
396             WARN( msg, "Bad queue handle %04x\n", hQueue );
397             return;
398         }
399         if (!GetModuleName( queue->thdb->process->task, module, sizeof(module )))
400             strcpy( module, "???" );
401         DUMP( "%04x %4d %p %04x %s\n", hQueue,queue->msgCount,
402               queue->thdb, queue->thdb->process->task, module );
403         hQueue = queue->next;
404         QUEUE_Unlock( queue );
405     }
406     DUMP( "\n" );
407 }
408
409
410 /***********************************************************************
411  *           QUEUE_IsExitingQueue
412  */
413 BOOL32 QUEUE_IsExitingQueue( HQUEUE16 hQueue )
414 {
415     return (hExitingQueue && (hQueue == hExitingQueue));
416 }
417
418
419 /***********************************************************************
420  *           QUEUE_SetExitingQueue
421  */
422 void QUEUE_SetExitingQueue( HQUEUE16 hQueue )
423 {
424     hExitingQueue = hQueue;
425 }
426
427
428 /***********************************************************************
429  *           QUEUE_CreateMsgQueue
430  *
431  * Creates a message queue. Doesn't link it into queue list!
432  */
433 static HQUEUE16 QUEUE_CreateMsgQueue( BOOL16 bCreatePerQData )
434 {
435     HQUEUE16 hQueue;
436     MESSAGEQUEUE * msgQueue;
437     TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
438
439     TRACE(msg,"(): Creating message queue...\n");
440
441     if (!(hQueue = GlobalAlloc16( GMEM_FIXED | GMEM_ZEROINIT,
442                                   sizeof(MESSAGEQUEUE) )))
443         return 0;
444
445     msgQueue = (MESSAGEQUEUE *) GlobalLock16( hQueue );
446     if ( !msgQueue )
447         return 0;
448
449     msgQueue->self        = hQueue;
450     msgQueue->wakeBits    = msgQueue->changeBits = QS_SMPARAMSFREE;
451     msgQueue->wWinVersion = pTask ? pTask->version : 0;
452     
453     InitializeCriticalSection( &msgQueue->cSection );
454
455     /* Create an Event object for waiting on message, used by win32 thread
456        only */
457     if ( !THREAD_IsWin16( THREAD_Current() ) )
458     {
459         msgQueue->hEvent = CreateEvent32A( NULL, FALSE, FALSE, NULL);
460
461         if (msgQueue->hEvent == 0)
462         {
463             WARN(msg, "CreateEvent32A is not able to create an event object");
464             return 0;
465         }
466         msgQueue->hEvent = ConvertToGlobalHandle( msgQueue->hEvent );
467     }
468     else
469         msgQueue->hEvent = 0;
470          
471     msgQueue->lockCount = 1;
472     msgQueue->magic = QUEUE_MAGIC;
473     
474     /* Create and initialize our per queue data */
475     msgQueue->pQData = bCreatePerQData ? PERQDATA_CreateInstance() : NULL;
476     
477     return hQueue;
478 }
479
480
481 /***********************************************************************
482  *           QUEUE_DeleteMsgQueue
483  *
484  * Unlinks and deletes a message queue.
485  *
486  * Note: We need to mask asynchronous events to make sure PostMessage works
487  * even in the signal handler.
488  */
489 BOOL32 QUEUE_DeleteMsgQueue( HQUEUE16 hQueue )
490 {
491     MESSAGEQUEUE * msgQueue = (MESSAGEQUEUE*)QUEUE_Lock(hQueue);
492     HQUEUE16  senderQ;
493     HQUEUE16 *pPrev;
494
495     TRACE(msg,"(): Deleting message queue %04x\n", hQueue);
496
497     if (!hQueue || !msgQueue)
498     {
499         WARN(msg, "invalid argument.\n");
500         return 0;
501     }
502
503     msgQueue->magic = 0;
504     
505     if( hCursorQueue == hQueue ) hCursorQueue = 0;
506     if( hActiveQueue == hQueue ) hActiveQueue = 0;
507
508     /* flush sent messages */
509     senderQ = msgQueue->hSendingTask;
510     while( senderQ )
511     {
512       MESSAGEQUEUE* sq = (MESSAGEQUEUE*)QUEUE_Lock(senderQ);
513       if( !sq ) break;
514       sq->SendMessageReturn = 0L;
515       QUEUE_SetWakeBit( sq, QS_SMRESULT );
516       senderQ = sq->hPrevSendingTask;
517       QUEUE_Unlock(sq);
518     }
519
520     SYSTEM_LOCK();
521
522     /* Release per queue data if present */
523     if ( msgQueue->pQData )
524     {
525         PERQDATA_Release( msgQueue->pQData );
526         msgQueue->pQData = 0;
527     }
528     
529     /* remove the message queue from the global link list */
530     pPrev = &hFirstQueue;
531     while (*pPrev && (*pPrev != hQueue))
532     {
533         MESSAGEQUEUE *msgQ = (MESSAGEQUEUE*)GlobalLock16(*pPrev);
534
535         /* sanity check */
536         if ( !msgQ || (msgQ->magic != QUEUE_MAGIC) )
537         {
538             /* HQUEUE link list is corrupted, try to exit gracefully */
539             WARN( msg, "HQUEUE link list corrupted!\n");
540             pPrev = 0;
541             break;
542         }
543         pPrev = &msgQ->next;
544     }
545     if (pPrev && *pPrev) *pPrev = msgQueue->next;
546     msgQueue->self = 0;
547
548     SYSTEM_UNLOCK();
549
550     /* free up resource used by MESSAGEQUEUE strcture */
551     msgQueue->lockCount--;
552     QUEUE_Unlock( msgQueue );
553     
554     return 1;
555 }
556
557
558 /***********************************************************************
559  *           QUEUE_CreateSysMsgQueue
560  *
561  * Create the system message queue, and set the double-click speed.
562  * Must be called only once.
563  */
564 BOOL32 QUEUE_CreateSysMsgQueue( int size )
565 {
566     /* Note: We dont need perQ data for the system message queue */
567     if (!(hmemSysMsgQueue = QUEUE_CreateMsgQueue( FALSE )))
568         return FALSE;
569     
570     sysMsgQueue = (MESSAGEQUEUE *) GlobalLock16( hmemSysMsgQueue );
571     return TRUE;
572 }
573
574
575 /***********************************************************************
576  *           QUEUE_GetSysQueue
577  */
578 MESSAGEQUEUE *QUEUE_GetSysQueue(void)
579 {
580     return sysMsgQueue;
581 }
582
583
584 /***********************************************************************
585  *           QUEUE_Wait
586  */
587 static void QUEUE_Wait( DWORD wait_mask )
588 {
589     if ( THREAD_IsWin16( THREAD_Current() ) )
590         WaitEvent( 0 );
591     else
592     {
593         TRACE(msg, "current task is 32-bit, calling SYNC_DoWait\n");
594         MsgWaitForMultipleObjects( 0, NULL, FALSE, INFINITE32, wait_mask );
595     }
596 }
597
598
599 /***********************************************************************
600  *           QUEUE_SetWakeBit
601  *
602  * See "Windows Internals", p.449
603  */
604 void QUEUE_SetWakeBit( MESSAGEQUEUE *queue, WORD bit )
605 {
606     TRACE(msg,"queue = %04x (wm=%04x), bit = %04x\n", 
607                         queue->self, queue->wakeMask, bit );
608
609     if (bit & QS_MOUSE) pMouseQueue = queue;
610     if (bit & QS_KEY) pKbdQueue = queue;
611     queue->changeBits |= bit;
612     queue->wakeBits   |= bit;
613     if (queue->wakeMask & bit)
614     {
615         queue->wakeMask = 0;
616         
617         /* Wake up thread waiting for message */
618         if ( THREAD_IsWin16( queue->thdb ) )
619             PostEvent( queue->thdb->process->task );
620         else
621         {
622             SetEvent( queue->hEvent );
623         }
624     }
625 }
626
627
628 /***********************************************************************
629  *           QUEUE_ClearWakeBit
630  */
631 void QUEUE_ClearWakeBit( MESSAGEQUEUE *queue, WORD bit )
632 {
633     queue->changeBits &= ~bit;
634     queue->wakeBits   &= ~bit;
635 }
636
637
638 /***********************************************************************
639  *           QUEUE_WaitBits
640  *
641  * See "Windows Internals", p.447
642  */
643 void QUEUE_WaitBits( WORD bits )
644 {
645     MESSAGEQUEUE *queue;
646
647     TRACE(msg,"q %04x waiting for %04x\n", GetFastQueue(), bits);
648
649     for (;;)
650     {
651         if (!(queue = (MESSAGEQUEUE *)QUEUE_Lock( GetFastQueue() ))) return;
652
653         if (queue->changeBits & bits)
654         {
655             /* One of the bits is set; we can return */
656             queue->wakeMask = 0;
657             QUEUE_Unlock( queue );
658             return;
659         }
660         if (queue->wakeBits & QS_SENDMESSAGE)
661         {
662             /* Process the sent message immediately */
663
664             queue->wakeMask = 0;
665             QUEUE_ReceiveMessage( queue );
666             QUEUE_Unlock( queue );
667             continue;                           /* nested sm crux */
668         }
669
670         queue->wakeMask = bits | QS_SENDMESSAGE;
671         if(queue->changeBits & bits)
672         {
673             QUEUE_Unlock( queue );
674             continue;
675         }
676         
677         TRACE(msg,"%04x) wakeMask is %04x, waiting\n", queue->self, queue->wakeMask);
678
679         QUEUE_Wait( queue->wakeMask );
680
681         QUEUE_Unlock( queue );
682     }
683 }
684
685
686 /***********************************************************************
687  *           QUEUE_ReceiveMessage
688  *
689  * This routine is called when a sent message is waiting for the queue.
690  */
691 void QUEUE_ReceiveMessage( MESSAGEQUEUE *queue )
692 {
693     MESSAGEQUEUE *senderQ = NULL;
694     HQUEUE16      prevSender = 0;
695     QSMCTRL*      prevCtrlPtr = NULL;
696     LRESULT       result = 0;
697
698     TRACE(msg, "ReceiveMessage, queue %04x\n", queue->self );
699     if (!(queue->wakeBits & QS_SENDMESSAGE) ||
700         !(senderQ = (MESSAGEQUEUE*)QUEUE_Lock( queue->hSendingTask)))
701         { TRACE(msg,"\trcm: nothing to do\n"); return; }
702
703     if( !senderQ->hPrevSendingTask )
704         QUEUE_ClearWakeBit( queue, QS_SENDMESSAGE );   /* no more sent messages */
705
706     /* Save current state on stack */
707     prevSender                 = queue->InSendMessageHandle;
708     prevCtrlPtr                = queue->smResultCurrent;
709
710     /* Remove sending queue from the list */
711     queue->InSendMessageHandle = queue->hSendingTask;
712     queue->smResultCurrent     = senderQ->smResultInit;
713     queue->hSendingTask        = senderQ->hPrevSendingTask;
714
715     TRACE(msg, "\trcm: smResultCurrent = %08x, prevCtrl = %08x\n", 
716                                 (unsigned)queue->smResultCurrent, (unsigned)prevCtrlPtr );
717     QUEUE_SetWakeBit( senderQ, QS_SMPARAMSFREE );
718
719     TRACE(msg, "\trcm: calling wndproc - %08x %08x %08x %08x\n",
720                 senderQ->hWnd32, senderQ->msg32,
721                 senderQ->wParam32, (unsigned)senderQ->lParam );
722
723     if (IsWindow32( senderQ->hWnd32 ))
724     {
725         WND *wndPtr = WIN_FindWndPtr( senderQ->hWnd32 );
726         DWORD extraInfo = queue->GetMessageExtraInfoVal;
727         queue->GetMessageExtraInfoVal = senderQ->GetMessageExtraInfoVal;
728
729         if (senderQ->flags & QUEUE_SM_WIN32)
730         {
731             TRACE(msg, "\trcm: msg is Win32\n" );
732             if (senderQ->flags & QUEUE_SM_UNICODE)
733                 result = CallWindowProc32W( wndPtr->winproc,
734                                             senderQ->hWnd32, senderQ->msg32,
735                                             senderQ->wParam32, senderQ->lParam );
736             else
737                 result = CallWindowProc32A( wndPtr->winproc,
738                                             senderQ->hWnd32, senderQ->msg32,
739                                             senderQ->wParam32, senderQ->lParam );
740         }
741         else  /* Win16 message */
742             result = CallWindowProc16( (WNDPROC16)wndPtr->winproc,
743                                        (HWND16) senderQ->hWnd32,
744                                        (UINT16) senderQ->msg32,
745                                        LOWORD (senderQ->wParam32),
746                                        senderQ->lParam );
747
748         queue->GetMessageExtraInfoVal = extraInfo;  /* Restore extra info */
749         TRACE(msg,"\trcm: result =  %08x\n", (unsigned)result );
750     }
751     else WARN(msg, "\trcm: bad hWnd\n");
752
753     QUEUE_Unlock( senderQ );
754     
755     /* Return the result to the sender task */
756     ReplyMessage16( result );
757
758     queue->InSendMessageHandle = prevSender;
759     queue->smResultCurrent     = prevCtrlPtr;
760
761     TRACE(msg,"done!\n");
762 }
763
764 /***********************************************************************
765  *           QUEUE_FlushMessage
766  * 
767  * Try to reply to all pending sent messages on exit.
768  */
769 void QUEUE_FlushMessages( HQUEUE16 hQueue )
770 {
771   MESSAGEQUEUE *queue = (MESSAGEQUEUE*)QUEUE_Lock( hQueue );
772
773   if( queue )
774   {
775     MESSAGEQUEUE *senderQ = (MESSAGEQUEUE*)QUEUE_Lock( queue->hSendingTask );
776     QSMCTRL*      CtrlPtr = queue->smResultCurrent;
777
778     TRACE(msg,"Flushing queue %04x:\n", hQueue );
779
780     while( senderQ )
781     {
782       if( !CtrlPtr )
783            CtrlPtr = senderQ->smResultInit;
784
785       TRACE(msg,"\tfrom queue %04x, smResult %08x\n", queue->hSendingTask, (unsigned)CtrlPtr );
786
787       if( !(queue->hSendingTask = senderQ->hPrevSendingTask) )
788         QUEUE_ClearWakeBit( queue, QS_SENDMESSAGE );
789
790       QUEUE_SetWakeBit( senderQ, QS_SMPARAMSFREE );
791       
792       queue->smResultCurrent = CtrlPtr;
793       while( senderQ->wakeBits & QS_SMRESULT ) OldYield();
794
795       senderQ->SendMessageReturn = 0;
796       senderQ->smResult = queue->smResultCurrent;
797       QUEUE_SetWakeBit( senderQ, QS_SMRESULT);
798
799       QUEUE_Unlock( senderQ );
800
801       senderQ = (MESSAGEQUEUE*)QUEUE_Lock( queue->hSendingTask );
802       CtrlPtr = NULL;
803     }
804     queue->InSendMessageHandle = 0;
805     
806     QUEUE_Unlock( queue );
807   }  
808
809 }
810
811 /***********************************************************************
812  *           QUEUE_AddMsg
813  *
814  * Add a message to the queue. Return FALSE if queue is full.
815  */
816 BOOL32 QUEUE_AddMsg( HQUEUE16 hQueue, MSG32 *msg, DWORD extraInfo )
817 {
818     MESSAGEQUEUE *msgQueue;
819     QMSG         *qmsg;
820
821
822     if (!(msgQueue = (MESSAGEQUEUE *)QUEUE_Lock( hQueue ))) return FALSE;
823
824     /* allocate new message in global heap for now */
825     if (!(qmsg = (QMSG *) HeapAlloc( SystemHeap, 0, sizeof(QMSG) ) ))
826     {
827         QUEUE_Unlock( msgQueue );
828         return 0;
829     }
830
831     EnterCriticalSection( &msgQueue->cSection );
832
833       /* Store message */
834     qmsg->msg = *msg;
835     qmsg->extraInfo = extraInfo;
836
837     /* insert the message in the link list */
838     qmsg->nextMsg = 0;
839     qmsg->prevMsg = msgQueue->lastMsg;
840
841     if (msgQueue->lastMsg)
842         msgQueue->lastMsg->nextMsg = qmsg;
843
844     /* update first and last anchor in message queue */
845     msgQueue->lastMsg = qmsg;
846     if (!msgQueue->firstMsg)
847         msgQueue->firstMsg = qmsg;
848     
849     msgQueue->msgCount++;
850
851     LeaveCriticalSection( &msgQueue->cSection );
852
853     QUEUE_SetWakeBit( msgQueue, QS_POSTMESSAGE );
854     QUEUE_Unlock( msgQueue );
855     
856     return TRUE;
857 }
858
859
860
861 /***********************************************************************
862  *           QUEUE_FindMsg
863  *
864  * Find a message matching the given parameters. Return -1 if none available.
865  */
866 QMSG* QUEUE_FindMsg( MESSAGEQUEUE * msgQueue, HWND32 hwnd, int first, int last )
867 {
868     QMSG* qmsg;
869
870     EnterCriticalSection( &msgQueue->cSection );
871
872     if (!msgQueue->msgCount)
873         qmsg = 0;
874     else if (!hwnd && !first && !last)
875         qmsg = msgQueue->firstMsg;
876     else
877     {
878         /* look in linked list for message matching first and last criteria */
879         for (qmsg = msgQueue->firstMsg; qmsg; qmsg = qmsg->nextMsg)
880     {
881             MSG32 *msg = &(qmsg->msg);
882
883         if (!hwnd || (msg->hwnd == hwnd))
884         {
885                 if (!first && !last)
886                     break;   /* found it */
887                 
888                 if ((msg->message >= first) && (msg->message <= last))
889                     break;   /* found it */
890             }
891         }
892     }
893     
894     LeaveCriticalSection( &msgQueue->cSection );
895
896     return qmsg;
897 }
898
899
900
901 /***********************************************************************
902  *           QUEUE_RemoveMsg
903  *
904  * Remove a message from the queue (pos must be a valid position).
905  */
906 void QUEUE_RemoveMsg( MESSAGEQUEUE * msgQueue, QMSG *qmsg )
907 {
908     EnterCriticalSection( &msgQueue->cSection );
909
910     /* set the linked list */
911     if (qmsg->prevMsg)
912         qmsg->prevMsg->nextMsg = qmsg->nextMsg;
913
914     if (qmsg->nextMsg)
915         qmsg->nextMsg->prevMsg = qmsg->prevMsg;
916
917     if (msgQueue->firstMsg == qmsg)
918         msgQueue->firstMsg = qmsg->nextMsg;
919
920     if (msgQueue->lastMsg == qmsg)
921         msgQueue->lastMsg = qmsg->prevMsg;
922
923     /* deallocate the memory for the message */
924     HeapFree( SystemHeap, 0, qmsg );
925     
926     msgQueue->msgCount--;
927     if (!msgQueue->msgCount) msgQueue->wakeBits &= ~QS_POSTMESSAGE;
928
929     LeaveCriticalSection( &msgQueue->cSection );
930 }
931
932
933 /***********************************************************************
934  *           QUEUE_WakeSomeone
935  *
936  * Wake a queue upon reception of a hardware event.
937  */
938 static void QUEUE_WakeSomeone( UINT32 message )
939 {
940     WND*          wndPtr = NULL;
941     WORD          wakeBit;
942     HWND32 hwnd;
943     HQUEUE16     hQueue = 0;
944     MESSAGEQUEUE *queue = NULL;
945
946     if (hCursorQueue)
947         hQueue = hCursorQueue;
948
949     if( (message >= WM_KEYFIRST) && (message <= WM_KEYLAST) )
950     {
951        wakeBit = QS_KEY;
952        if( hActiveQueue )
953            hQueue = hActiveQueue;
954     }
955     else 
956     {
957        wakeBit = (message == WM_MOUSEMOVE) ? QS_MOUSEMOVE : QS_MOUSEBUTTON;
958        if( (hwnd = GetCapture32()) )
959          if( (wndPtr = WIN_FindWndPtr( hwnd )) ) 
960            {
961                hQueue = wndPtr->hmemTaskQ;
962            }
963     }
964
965     if( (hwnd = GetSysModalWindow16()) )
966     {
967       if( (wndPtr = WIN_FindWndPtr( hwnd )) )
968             hQueue = wndPtr->hmemTaskQ;
969     }
970
971     if (hQueue)
972         queue = QUEUE_Lock( hQueue );
973     
974     if( !queue ) 
975     {
976         queue = QUEUE_Lock( hFirstQueue );
977       while( queue )
978       {
979         if (queue->wakeMask & wakeBit) break;
980           
981             QUEUE_Unlock(queue);
982             queue = QUEUE_Lock( queue->next );
983       }
984       if( !queue )
985       { 
986         WARN(msg, "couldn't find queue\n"); 
987         return; 
988       }
989     }
990
991     QUEUE_SetWakeBit( queue, wakeBit );
992
993     QUEUE_Unlock( queue );
994 }
995
996
997 /***********************************************************************
998  *           hardware_event
999  *
1000  * Add an event to the system message queue.
1001  * Note: the position is relative to the desktop window.
1002  */
1003 void hardware_event( WORD message, WORD wParam, LONG lParam,
1004                      int xPos, int yPos, DWORD time, DWORD extraInfo )
1005 {
1006     MSG32 *msg;
1007     QMSG  *qmsg = sysMsgQueue->lastMsg;
1008     int  mergeMsg = 0;
1009
1010     if (!sysMsgQueue) return;
1011
1012       /* Merge with previous event if possible */
1013
1014     if ((message == WM_MOUSEMOVE) && sysMsgQueue->lastMsg)
1015     {
1016         msg = &(sysMsgQueue->lastMsg->msg);
1017         
1018         if ((msg->message == message) && (msg->wParam == wParam))
1019         {
1020             /* Merge events */
1021             qmsg = sysMsgQueue->lastMsg;
1022             mergeMsg = 1;
1023     }
1024     }
1025
1026     if (!mergeMsg)
1027     {
1028         /* Should I limit the number of message in
1029           the system message queue??? */
1030
1031         /* Don't merge allocate a new msg in the global heap */
1032         
1033         if (!(qmsg = (QMSG *) HeapAlloc( SystemHeap, 0, sizeof(QMSG) ) ))
1034         return;
1035         
1036         /* put message at the end of the linked list */
1037         qmsg->nextMsg = 0;
1038         qmsg->prevMsg = sysMsgQueue->lastMsg;
1039
1040         if (sysMsgQueue->lastMsg)
1041             sysMsgQueue->lastMsg->nextMsg = qmsg;
1042
1043         /* set last and first anchor index in system message queue */
1044         sysMsgQueue->lastMsg = qmsg;
1045         if (!sysMsgQueue->firstMsg)
1046             sysMsgQueue->firstMsg = qmsg;
1047         
1048         sysMsgQueue->msgCount++;
1049     }
1050
1051       /* Store message */
1052     msg = &(qmsg->msg);
1053     msg->hwnd    = 0;
1054     msg->message = message;
1055     msg->wParam  = wParam;
1056     msg->lParam  = lParam;
1057     msg->time    = time;
1058     msg->pt.x    = xPos;
1059     msg->pt.y    = yPos;
1060     qmsg->extraInfo = extraInfo;
1061
1062     QUEUE_WakeSomeone( message );
1063 }
1064
1065                     
1066 /***********************************************************************
1067  *           QUEUE_GetQueueTask
1068  */
1069 HTASK16 QUEUE_GetQueueTask( HQUEUE16 hQueue )
1070 {
1071     HTASK16 hTask = 0;
1072     
1073     MESSAGEQUEUE *queue = QUEUE_Lock( hQueue );
1074
1075     if (queue)
1076 {
1077         hTask = queue->thdb->process->task;
1078         QUEUE_Unlock( queue );
1079 }
1080
1081     return hTask;
1082 }
1083
1084
1085
1086 /***********************************************************************
1087  *           QUEUE_IncPaintCount
1088  */
1089 void QUEUE_IncPaintCount( HQUEUE16 hQueue )
1090 {
1091     MESSAGEQUEUE *queue;
1092
1093     if (!(queue = (MESSAGEQUEUE *)QUEUE_Lock( hQueue ))) return;
1094     queue->wPaintCount++;
1095     QUEUE_SetWakeBit( queue, QS_PAINT );
1096     QUEUE_Unlock( queue );
1097 }
1098
1099
1100 /***********************************************************************
1101  *           QUEUE_DecPaintCount
1102  */
1103 void QUEUE_DecPaintCount( HQUEUE16 hQueue )
1104 {
1105     MESSAGEQUEUE *queue;
1106
1107     if (!(queue = (MESSAGEQUEUE *)QUEUE_Lock( hQueue ))) return;
1108     queue->wPaintCount--;
1109     if (!queue->wPaintCount) queue->wakeBits &= ~QS_PAINT;
1110     QUEUE_Unlock( queue );
1111 }
1112
1113
1114 /***********************************************************************
1115  *           QUEUE_IncTimerCount
1116  */
1117 void QUEUE_IncTimerCount( HQUEUE16 hQueue )
1118 {
1119     MESSAGEQUEUE *queue;
1120
1121     if (!(queue = (MESSAGEQUEUE *)QUEUE_Lock( hQueue ))) return;
1122     queue->wTimerCount++;
1123     QUEUE_SetWakeBit( queue, QS_TIMER );
1124     QUEUE_Unlock( queue );
1125 }
1126
1127
1128 /***********************************************************************
1129  *           QUEUE_DecTimerCount
1130  */
1131 void QUEUE_DecTimerCount( HQUEUE16 hQueue )
1132 {
1133     MESSAGEQUEUE *queue;
1134
1135     if (!(queue = (MESSAGEQUEUE *)QUEUE_Lock( hQueue ))) return;
1136     queue->wTimerCount--;
1137     if (!queue->wTimerCount) queue->wakeBits &= ~QS_TIMER;
1138     QUEUE_Unlock( queue );
1139 }
1140
1141
1142 /***********************************************************************
1143  *           PostQuitMessage16   (USER.6)
1144  */
1145 void WINAPI PostQuitMessage16( INT16 exitCode )
1146 {
1147     PostQuitMessage32( exitCode );
1148 }
1149
1150
1151 /***********************************************************************
1152  *           PostQuitMessage32   (USER32.421)
1153  *
1154  * PostQuitMessage() posts a message to the system requesting an
1155  * application to terminate execution. As a result of this function,
1156  * the WM_QUIT message is posted to the application, and
1157  * PostQuitMessage() returns immediately.  The exitCode parameter
1158  * specifies an application-defined exit code, which appears in the
1159  * _wParam_ parameter of the WM_QUIT message posted to the application.  
1160  *
1161  * CONFORMANCE
1162  *
1163  *  ECMA-234, Win32
1164  */
1165 void WINAPI PostQuitMessage32( INT32 exitCode )
1166 {
1167     MESSAGEQUEUE *queue;
1168
1169     if (!(queue = (MESSAGEQUEUE *)QUEUE_Lock( GetFastQueue() ))) return;
1170     queue->wPostQMsg = TRUE;
1171     queue->wExitCode = (WORD)exitCode;
1172     QUEUE_Unlock( queue );
1173 }
1174
1175
1176 /***********************************************************************
1177  *           GetWindowTask16   (USER.224)
1178  */
1179 HTASK16 WINAPI GetWindowTask16( HWND16 hwnd )
1180 {
1181     WND *wndPtr = WIN_FindWndPtr( hwnd );
1182
1183     if (!wndPtr) return 0;
1184     return QUEUE_GetQueueTask( wndPtr->hmemTaskQ );
1185 }
1186
1187 /***********************************************************************
1188  *           GetWindowThreadProcessId   (USER32.313)
1189  */
1190 DWORD WINAPI GetWindowThreadProcessId( HWND32 hwnd, LPDWORD process )
1191 {
1192     HTASK16 htask;
1193     TDB *tdb;
1194
1195     WND *wndPtr = WIN_FindWndPtr( hwnd );
1196
1197     if (!wndPtr) return 0;
1198     htask=QUEUE_GetQueueTask( wndPtr->hmemTaskQ );
1199     tdb = (TDB*)GlobalLock16(htask);
1200     if (!tdb || !tdb->thdb) return 0;
1201     if (process) *process = PDB_TO_PROCESS_ID( tdb->thdb->process );
1202     return THDB_TO_THREAD_ID( tdb->thdb );
1203 }
1204
1205
1206 /***********************************************************************
1207  *           SetMessageQueue16   (USER.266)
1208  */
1209 BOOL16 WINAPI SetMessageQueue16( INT16 size )
1210 {
1211     return SetMessageQueue32( size );
1212 }
1213
1214
1215 /***********************************************************************
1216  *           SetMessageQueue32   (USER32.494)
1217  */
1218 BOOL32 WINAPI SetMessageQueue32( INT32 size )
1219 {
1220     /* now obsolete the message queue will be expanded dynamically
1221      as necessary */
1222
1223     /* access the queue to create it if it's not existing */
1224     GetFastQueue();
1225
1226     return TRUE;
1227 }
1228
1229 /***********************************************************************
1230  *           InitThreadInput   (USER.409)
1231  */
1232 HQUEUE16 WINAPI InitThreadInput( WORD unknown, WORD flags )
1233 {
1234     HQUEUE16 hQueue;
1235     MESSAGEQUEUE *queuePtr;
1236
1237     THDB *thdb = THREAD_Current();
1238
1239     if (!thdb)
1240         return 0;
1241
1242     hQueue = thdb->teb.queue;
1243     
1244     if ( !hQueue )
1245     {
1246         /* Create thread message queue */
1247         if( !(hQueue = QUEUE_CreateMsgQueue( TRUE )))
1248         {
1249             WARN(msg, "failed!\n");
1250             return FALSE;
1251     }
1252         
1253         /* Link new queue into list */
1254         queuePtr = (MESSAGEQUEUE *)QUEUE_Lock( hQueue );
1255         queuePtr->thdb = THREAD_Current();
1256
1257         SYSTEM_LOCK();
1258         SetThreadQueue( 0, hQueue );
1259         thdb->teb.queue = hQueue;
1260             
1261         queuePtr->next  = hFirstQueue;
1262         hFirstQueue = hQueue;
1263         SYSTEM_UNLOCK();
1264         
1265         QUEUE_Unlock( queuePtr );
1266     }
1267
1268     return hQueue;
1269 }
1270
1271 /***********************************************************************
1272  *           GetQueueStatus16   (USER.334)
1273  */
1274 DWORD WINAPI GetQueueStatus16( UINT16 flags )
1275 {
1276     MESSAGEQUEUE *queue;
1277     DWORD ret;
1278
1279     if (!(queue = (MESSAGEQUEUE *)QUEUE_Lock( GetFastQueue() ))) return 0;
1280     ret = MAKELONG( queue->changeBits, queue->wakeBits );
1281     queue->changeBits = 0;
1282     QUEUE_Unlock( queue );
1283     
1284     return ret & MAKELONG( flags, flags );
1285 }
1286
1287 /***********************************************************************
1288  *           GetQueueStatus32   (USER32.283)
1289  */
1290 DWORD WINAPI GetQueueStatus32( UINT32 flags )
1291 {
1292     MESSAGEQUEUE *queue;
1293     DWORD ret;
1294
1295     if (!(queue = (MESSAGEQUEUE *)QUEUE_Lock( GetFastQueue() ))) return 0;
1296     ret = MAKELONG( queue->changeBits, queue->wakeBits );
1297     queue->changeBits = 0;
1298     QUEUE_Unlock( queue );
1299     
1300     return ret & MAKELONG( flags, flags );
1301 }
1302
1303
1304 /***********************************************************************
1305  *           GetInputState16   (USER.335)
1306  */
1307 BOOL16 WINAPI GetInputState16(void)
1308 {
1309     return GetInputState32();
1310 }
1311
1312 /***********************************************************************
1313  *           WaitForInputIdle   (USER32.577)
1314  */
1315 DWORD WINAPI WaitForInputIdle (HANDLE32 hProcess, DWORD dwTimeOut)
1316 {
1317   FIXME (msg, "(hProcess=%d, dwTimeOut=%ld): stub\n", hProcess, dwTimeOut);
1318
1319   return WAIT_TIMEOUT;
1320 }
1321
1322
1323 /***********************************************************************
1324  *           GetInputState32   (USER32.244)
1325  */
1326 BOOL32 WINAPI GetInputState32(void)
1327 {
1328     MESSAGEQUEUE *queue;
1329     BOOL32 ret;
1330
1331     if (!(queue = (MESSAGEQUEUE *)QUEUE_Lock( GetFastQueue() )))
1332         return FALSE;
1333     ret = queue->wakeBits & (QS_KEY | QS_MOUSEBUTTON);
1334     QUEUE_Unlock( queue );
1335
1336     return ret;
1337 }
1338
1339 /***********************************************************************
1340  *           UserYield  (USER.332)
1341  */
1342 void WINAPI UserYield(void)
1343 {
1344     TDB *pCurTask = (TDB *)GlobalLock16( GetCurrentTask() );
1345     MESSAGEQUEUE *queue = (MESSAGEQUEUE *)QUEUE_Lock( pCurTask->hQueue );
1346
1347     if ( !THREAD_IsWin16( THREAD_Current() ) )
1348     {
1349         FIXME(task, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
1350         QUEUE_Unlock( queue );
1351         return;
1352     }
1353
1354     /* Handle sent messages */
1355     while (queue && (queue->wakeBits & QS_SENDMESSAGE))
1356         QUEUE_ReceiveMessage( queue );
1357
1358     QUEUE_Unlock( queue );
1359     
1360     OldYield();
1361
1362     queue = (MESSAGEQUEUE *)QUEUE_Lock( pCurTask->hQueue );
1363     while (queue && (queue->wakeBits & QS_SENDMESSAGE))
1364         QUEUE_ReceiveMessage( queue );
1365
1366     QUEUE_Unlock( queue );
1367 }
1368
1369 /***********************************************************************
1370  *           GetMessagePos   (USER.119) (USER32.272)
1371  * 
1372  * The GetMessagePos() function returns a long value representing a
1373  * cursor position, in screen coordinates, when the last message
1374  * retrieved by the GetMessage() function occurs. The x-coordinate is
1375  * in the low-order word of the return value, the y-coordinate is in
1376  * the high-order word. The application can use the MAKEPOINT()
1377  * macro to obtain a POINT structure from the return value. 
1378  *
1379  * For the current cursor position, use GetCursorPos().
1380  *
1381  * RETURNS
1382  *
1383  * Cursor position of last message on success, zero on failure.
1384  *
1385  * CONFORMANCE
1386  *
1387  * ECMA-234, Win32
1388  *
1389  */
1390 DWORD WINAPI GetMessagePos(void)
1391 {
1392     MESSAGEQUEUE *queue;
1393     DWORD ret;
1394
1395     if (!(queue = (MESSAGEQUEUE *)QUEUE_Lock( GetFastQueue() ))) return 0;
1396     ret = queue->GetMessagePosVal;
1397     QUEUE_Unlock( queue );
1398
1399     return ret;
1400 }
1401
1402
1403 /***********************************************************************
1404  *           GetMessageTime   (USER.120) (USER32.273)
1405  *
1406  * GetMessageTime() returns the message time for the last message
1407  * retrieved by the function. The time is measured in milliseconds with
1408  * the same offset as GetTickCount().
1409  *
1410  * Since the tick count wraps, this is only useful for moderately short
1411  * relative time comparisons.
1412  *
1413  * RETURNS
1414  *
1415  * Time of last message on success, zero on failure.
1416  *
1417  * CONFORMANCE
1418  *
1419  * ECMA-234, Win32
1420  *  
1421  */
1422 LONG WINAPI GetMessageTime(void)
1423 {
1424     MESSAGEQUEUE *queue;
1425     LONG ret;
1426
1427     if (!(queue = (MESSAGEQUEUE *)QUEUE_Lock( GetFastQueue() ))) return 0;
1428     ret = queue->GetMessageTimeVal;
1429     QUEUE_Unlock( queue );
1430     
1431     return ret;
1432 }
1433
1434
1435 /***********************************************************************
1436  *           GetMessageExtraInfo   (USER.288) (USER32.271)
1437  */
1438 LONG WINAPI GetMessageExtraInfo(void)
1439 {
1440     MESSAGEQUEUE *queue;
1441     LONG ret;
1442
1443     if (!(queue = (MESSAGEQUEUE *)QUEUE_Lock( GetFastQueue() ))) return 0;
1444     ret = queue->GetMessageExtraInfoVal;
1445     QUEUE_Unlock( queue );
1446
1447     return ret;
1448 }