home *** CD-ROM | disk | FTP | other *** search
/ Shareware Supreme Volume 6 #1 / swsii.zip / swsii / 215 / DDJ9302.ZIP / MESSAGE.ASC < prev    next >
Text File  |  1993-01-11  |  15KB  |  403 lines

  1. _INSIDE THE WINDOWS MESSAGING SYSTEM_
  2. by Matt Pietrek
  3.  
  4. [LISTING ONE]
  5.  
  6. 00h WORD    Selector of next message queue, (implements linked list).
  7. 02h WORD    hTask of task that owns this queue. 
  8. 04h WORD    Size of a message in this queue.  (In Windows 3.1, this is 22).
  9. 06h WORD    Number of messages waiting that have not been removed
  10.             by a GetMessage() or PeekMessage(PM_REMOVE).
  11. 08h WORD    Offset in the queue segment of next message to be retrieved.
  12. 0Ah WORD    Offset in the queue segment where next message will be written.
  13. 0Ch WORD    The length in bytes of the queue's segment. 
  14. 0Eh DWORD   DWORD value returned by GetMessageTime().
  15. 12h DWORD   DWORD value returned by GetMessagePos().
  16. 16h WORD    Unknown.  Sometimes contains 1.
  17. 18h DWORD   Information returned by GetMessageExtraInfo().
  18. 1Ch WORD    Unknown.
  19. 1Eh DWORD   Contains the LPARAM of a SendMessage() to another task.
  20. 22h WORD    Contains the WPARAM of a SendMessage() to another task.
  21. 24h WORD    Contains the MSG of a SendMessage() to another task.
  22. 26h WORD    Contains the HWND of a SendMessage() to another task.
  23. 28h WORD    Contains the DWORD result from the SendMessage().
  24. 2Ch WORD    PostQuitMessage() has been called by this program.
  25. 2Eh WORD    PostQuitMessage() exit code.
  26. 30h WORD    Flags of some sort.
  27. 32h DWORD   Unknown.
  28. 36h WORD    Expected Windows version, from NE file.
  29. 38h WORD    Queue handle of application that is sending a message to this app.
  30. 3Ah WORD    Used for an intertask SendMessage().
  31. 3Ch WORD    Used for an intertask SendMessage().
  32. 3Eh WORD    Number of "paints" needed by this application.
  33. 40h WORD    Number of timer events waiting for this application
  34. 42h WORD    QS_xxx bits that have changed since the last call to
  35.             GetMessage(), PeekMessage(), or GetQueueStatus().
  36. 44h WORD    QS_xxx bits indicating the kind of messages that are waiting 
  37.             for the application. 
  38. 46h WORD    Contains the QS_xxx bits that an application is
  39.             currently waiting for.
  40. 48h WORD    Used for intertask SendMessages(). 
  41.  
  42. 4Ah WORD    Used for intertask SendMessages(). 
  43. 4Ch WORD    Used for intertask SendMessages().  
  44. 4Eh WORD    Something having to do with hooks     
  45. 50h BYTE[1Eh]  Unknown.  Possibly having to do with hooks.
  46. 6Eh WORD    Start of the posted message storage area.  The
  47.             memory from here, to the end of the segment, can
  48.             be thought of as an array of messages, each message
  49.             being 22 bytes in length.
  50.  
  51.  
  52.  
  53. [LISTING TWO]
  54.  
  55. // Global variables: hQCursor   - The queue "associated" with the cursor
  56. //          hQActive   - The queue of the "active" window that has focus
  57. //          hQCapture  - The queue associated with the capture window
  58. //          hQSysModal - The queue associated with the system modal window
  59. // Local variables: best_queue - contains the current "best guess" as to which
  60. //                       queue should be woken up to receive the message
  61. //          wakebit - contains the QS_xxx message type (QS_MOUSEMOVE,
  62. //               QS_MOUSEBUTTON, or QS_KEY) that will be placed in the WakeBits
  63. //                    of whatever queue is selected to receive the message.
  64.     best_queue = hQCursor
  65.     if ( message is a not a key message )
  66.         goto mouse_event
  67.     wakebit = QS_KEY
  68.     if ( hQActive != NULL )
  69.         best_queue = hQActive
  70.     goto system_modal_check
  71. mouse_event:
  72.     if ( message == WM_MOUSEMOVE )
  73.         wakebit = QS_MOUSEMOVE
  74.     else
  75.         wakebit = QS_MOUSEBUTTON
  76.     if ( hQCapture != NULL )
  77.         best_queue = hQCapture
  78. system_modal_check:
  79.     if ( hQSysModal != NULL )
  80.         best_queue = hQSysModal
  81.     if ( best_queue != 0 )
  82.         goto wake_em_up
  83.     iterate through queue linked list
  84.     {
  85.         if ( queues WakeMask includes wakebit determined
  86.             previously )
  87.         {
  88.             best_queue = current queue under examination
  89.  
  90.             goto wake_em_up
  91.         }
  92.         if ( at end of queues linked list )
  93.             return
  94.     }
  95. wake_em_up:
  96.     SetWakeBit2();      // Sets WakeBits, and posts event
  97.     return
  98.  
  99.  
  100. [LISTING THREE]
  101.  
  102. // WakeMask contains QS_xxx OR'ed together. SleepHq() will not return until at
  103. // 1 of QS_xxx bits in the WakeMask parameter has been set in the ChangeBits.
  104.  
  105. void SleepHq( unsigned WakeMask )
  106. {
  107.     HANDLE currQ
  108. SleepHq_check_flags:
  109.     currQ = Get_current_task_queue
  110.     // If already have a message then go get it
  111.     if ( WakeMask & currQ.ChangeBits )
  112.         goto SleepHq_done
  113.     // Check for SendMessages and deal with them
  114.     if ( currQ.WakeBits & QS_SENDMESSAGE )
  115.         goto SleepHq_have_SendMessage
  116.     // Always check for SendMessages
  117.     currQ.WakeMask = WakeMask & QS_SENDMESSAGE
  118.     if ( WakeMask & currQ.ChangeBits )
  119.         goto SleepHq_done
  120.     WaitEvent()     // Kernel routine that waits for an event
  121.     goto SleepHq_check_flags:
  122. SleepHq_done:
  123.     zero_out_currQ.WakeMask
  124.     return
  125. SleepHq_have_SendMessage:
  126.     zero_out_qWakeMask
  127.     // Deal with the SendMessage(). Described in the section on SendMessage()
  128.     ReceiveMessage()
  129.     goto SleepHq_check_flags
  130.  
  131. }
  132.  
  133.  
  134. [LISTING FOUR]
  135.  
  136. void SetWakeBit2(HANDLE hQueue, UINT WakeBit)
  137. {
  138.     hQueue.ChangeBit |= WakeBit     // Turn on the QS_xxx flags
  139.     hQueue.WakeBit   |= WakeBit
  140.     // If we're setting a QS_xxx bit that the queue is waiting
  141.     // for, then force the scheduler to schedule the task
  142.     if ( WakeBit & hQueue.WakeMask )
  143.     {
  144.         hQueue.WakeMask = 0
  145.         PostEvent() to hQueue's task   
  146.     }
  147. }
  148.  
  149.  
  150.  
  151. [LISTING FIVE]
  152.  
  153. // "flags" are the "flags" parameter to PeekMessage(). "removeFlag" is a local 
  154. //  indicating whether a message will be read from the queue. "WakeMask" is a 
  155. //  local containing a QX_xxx mask of messages types GetMessage()/PeekMessage()
  156. //  are waiting for. "WakeBits" is a local containing the the QS_xxx bits that
  157. // indicate which types of messages are waiting for this task.
  158.  
  159. PeekMessage:
  160.     Is_GetMessage_call = 0
  161.     goto GetMessage2
  162. GetMessage:
  163.     Is_GetMessage_call = 1
  164.     Insert a flags WORD in the stack frame so that the stack
  165.     frame for GetMessage() is the same as for PeekMessage().
  166.     The flag is set to PM_REMOVE.
  167. GetMessage2:    // This is where GetMessage() and PeekMessage()
  168.                 // start sharing their code
  169.     if ( current task is locked )
  170.         set PM_NOYIELD in flags
  171.     removeFlag = flags & PM_REMOVE
  172.     Unlock the system queue if this task holds it.
  173.     if ( (msgMin != 0) or (msgMax != 0) )
  174.         Call function to set up WakeMask for the specified
  175.         message range
  176.     else
  177.         WakeMask = QS_MOUSE | QS_KEY | QS_POSTMESSAGE
  178.                     | QS_TIMER | QS_PAINT
  179.  
  180. begin_looking_for_msgs:
  181.     if ( !CheckForNewInput() )
  182.         goto wait_for_input
  183.     if ( system queue not locked )
  184.         goto not_in_system_queue
  185.     if ( system queue not locked by current queue )
  186.         goto not_in_system_queue
  187.     if ( (QS_MOUSE | QS_KEY) set in WakeMask and WakeMask )
  188.     {
  189.             if ( ScanSysQueue() )
  190.                 goto GetMessage_have_msg
  191.     }
  192. not_in_system_queue:
  193.     if ( QS_POSTMESSAGE set in WakeBits and WakeMask )
  194.         if ( ReadMessage() )
  195.             goto GetMessage_have_msg
  196.     if ( (QS_MOUSE or QS_KEY) set in WakeBits and WakeMask )
  197.         if ( ScanSysQueue() )
  198.             goto GetMessage_have_msg
  199.     if ( !CheckForNewInput() )
  200.         goto wait_for_input
  201.     if ( QS_PAINT set in WakeBits and WakeMask )
  202.         if ( DoPaint() )
  203.             goto GetMessage_have_msg
  204.     if ( PM_NOYIELD set in flags )
  205.         goto check_for_timer_msg
  206.     UserYield()
  207.     if ( !CheckForNewInput() )
  208.         goto wait_for_input
  209. check_for_timer_msg:
  210.     if ( QS_TIMER set in WakeBits and WakeMask )
  211.         if ( DoTimer() )
  212.             begin_looking_for_msgs
  213. wait_for_input:
  214.     if ( FSHRINKGDI )
  215.         ShrinkGDIheap()     ; Where is this defined???
  216.     // If not in GetMessage, we must be in PeekMessage
  217.     if ( Is_GetMessage_call == 0 )
  218.         goto PeekMessage_exit
  219.     SleepHq(wakemask)
  220.     goto begin_looking_for_msgs
  221.  
  222. GetMessage_have_message:
  223.     if ( a WH_GETMESSAGE hook is installed )
  224.         call the hook function
  225.     // If not in GetMessage, we must be in PeekMessage
  226.     if ( Is_GetMessage_call )
  227.         return 1
  228.     if ( returning msg == WM_QUIT )
  229.         return 0
  230.     else
  231.         return 1
  232. PeekMessage_exit:
  233.     if ( ! PM_NOYIELD )
  234.         UserYield()         // Yield to any higher priority app
  235.     return 0
  236.  
  237.  
  238. [LISTING SIX]
  239.  
  240. // Returns Zero Flag set if no desired input flag is set. WakeMask & WakeBits 
  241. // are in registers, and are same as WakeMask and WakeBits in GetMessage2().
  242. top:
  243.     Get handle of current queue
  244.     if ( QS_SENDMESSAGE set in the queues wakebits )
  245.     {
  246.         ReceiveMessage()
  247.         goto top
  248.     }
  249.     // AND instruction sets the Zero flag if any bits match
  250.     AND WakeMask, WakeBits together
  251.     Return
  252.  
  253.  
  254. [LISTING SEVEN]
  255.  
  256.     LPMSG lpMsg    // ptr to passed-in message, used as scratch variable.
  257.     if ( (msg != WM_TIMER) && (msg != WM_SYSTIMER) )
  258.         goto handle_normally
  259.     if ( msg.lParam == 0 )
  260.         goto handle_normally
  261.     GetTickCount()
  262.     push msg parameters on stack
  263.     lpMsg = msg.lParam  // Timer function callback address
  264.     AX = SS     // Something with MakeProcInstance thunk???
  265.     goto call_function
  266. handle_normally:
  267.     if ( msg.hwnd == 0 )
  268.         return;
  269.     push msg parameters on stack
  270.     if ( msg.msg == WM_PAINT )
  271.         set "paint" flag in WND structure
  272.     lpMsg = Window proc address // stored in WND data structure; 
  273.                                 // pointed to by msg.hwnd
  274.     AX = hInstance from WND structure   // For use by MakeProcInstance() thunks
  275. call_function:
  276.     ES = DS = SS    // Set all segment registers to hInstance of application
  277.     call [lpMsg]    // Call the window proceedure (or timer callback fn). 
  278.                     // lpMsg is now used to store the address of window
  279.                     // function (or timer callback function) to be called
  280.     if ( msg.msg != WM_PAINT )
  281.         goto DispatchMessage_done
  282.     // Check for destroyed window
  283.     if ( ! IsWindow(msg.msg) )
  284.         goto DispatchMessage_done
  285.     if ( "paint" flag in wnd structure still set )
  286.         goto No_BeginPaint
  287. DispatchMessage_done:
  288.     return
  289. No_BeginPaint:
  290.     Display debugging message "Missing BeginPaint..."
  291.     Call DoSyncPaint() to handle the painting correctly
  292.     goto DispatchMessage_done
  293.  
  294.  
  295. [LISTING EIGHT]
  296.  
  297.     if ( receiving HWnd == -1 )
  298.         goto BroadcastMessage   // Not included here
  299.     Verify sending app has a message queue
  300.     Get receiving apps queue from receiving hWnd
  301.    // Are the sending and receiving queues the same???
  302.     Intertask = ( receivingHQueue == sendingHQueue ) 
  303.     Call any installed WH_CALLWNDPROC hooks
  304.     if ( Intertask )
  305.         goto InterTaskSend
  306.    // Next section deals with calling a window proceedure within same program
  307.    // This is the simple case and is much easier than calling between two 
  308.    // different programs (below)
  309.     Push address of the wndproc of the receiving WND structure on stack
  310.     Push SendMessage params on stack
  311.     Put hInstance into AX
  312.     Load DS & ES from the SS register
  313.     Call through the wndproc address in the window structure
  314. SendMessage_done:
  315.     Return to caller
  316. SendMessage_error:  // Common JMP location when errors occurr
  317.     Put 0 in DX:AX
  318.     Goto SendMessage_done
  319.     // SendMessage()'s that go between different tasks come here.
  320.     // This is where the code gets complex.
  321. InterTaskSend:
  322.     if ( A task is locked )
  323.     {
  324.         display a diagnostic in debugging version
  325.         Goto SendMessage_Error
  326.     }
  327.     if ( sending task is terminating )
  328.     {
  329.         display a diagnostic in debugging version
  330.         Goto SendMessage_Error
  331.     }
  332.     if (SendMessage parameter area in sending app is already used)
  333.     {
  334.         display a diagnostic in debugging version
  335.         Sleep until the parameter area is free  // Uses SleepHq()
  336.     }
  337.     Grab parameter area in sending app
  338.     Save the address where the result of the call will be stored
  339.     Copy the SendMessage parameters off the stack into the sending hQueue
  340.     Put the receiving queue at the head of the SendMessage() list
  341.     // Set bits to wake up the receiving task
  342.  
  343.     SetWakeBit2( QS_SENDMESSAGE )
  344. SendMessage_wakeup_receiving_task:
  345.     if ( a previous SendMessage() has completed )
  346.         goto got_reply
  347.     Turn off "have result" flags in sending queue
  348.     Call DirectedYield() to force the child task to run next
  349.     // When the DirectedYield() returns, the receiving task should have awoken
  350.     // and called ReceiveMessage() and ReplyMessage(). Described below.
  351.     Sleep until result is back from child
  352.     // Uses SleepHq(). Probably redundant, because there already should be a 
  353.     // result available when the prior DirectedYield() returned.
  354. got_reply:
  355.     Copy the return value to the "result" area on the stack
  356.     Release parameter area in sending queue
  357.     if ( Not replied to )
  358.         goto SendMessage_wakeup_receiving_task
  359.     goto SendMessage_done
  360.  
  361.  
  362. [LISTING NINE]
  363.  
  364.     Make sure there is a SendMessage waiting for us.
  365.     Remove sending queue from SendMessage() list of queues.
  366.     Clear QS_SENDMSG bit if the list of queues is empty.
  367.     Save copies of the sending hQueue and pointer to area
  368.           where results should be saved in the sending task.
  369.     Free the the SMPARAMS area in the sending queue.
  370.     Make sure target window is still valid.
  371.     Copy the ExtraInfo data from sender to receiver.
  372.     Call the target window proc.
  373.     Call ReplyMessage.
  374.     Return.
  375.  
  376.  
  377. [LISTING TEN]
  378.  
  379.     // Reply message takes the value that should be returned to
  380.     // the sender as a parameter.  Here, it's called "return_value"
  381. ReplyMessage_start:
  382.     If ( message has already been replied to, or
  383.          if there is no sending queue )
  384.         return
  385.     if ( QS_SENDMESSAGE bit set in receiving queue)
  386.     {
  387.         ReceiveMessage()
  388.         Goto ReplyMessage_start
  389.  
  390.     }
  391.     if ( result area in use )
  392.     {
  393.         OldYield()
  394.         Goto ReplyMessage_start
  395.     }
  396.     Copy return_value into sending hQueue
  397.     Restore pointer to result area on stack in the sending hQueue 
  398.     Set AlreadyRepliedFlag
  399.     SetWakeBit2( QS_SMRESULT )
  400.     DirectedYield(SendingTask)
  401.     Return
  402.  
  403.