home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Shareware Supreme Volume 6 #1
/
swsii.zip
/
swsii
/
215
/
DDJ9302.ZIP
/
MESSAGE.ASC
< prev
next >
Wrap
Text File
|
1993-01-11
|
15KB
|
403 lines
_INSIDE THE WINDOWS MESSAGING SYSTEM_
by Matt Pietrek
[LISTING ONE]
00h WORD Selector of next message queue, (implements linked list).
02h WORD hTask of task that owns this queue.
04h WORD Size of a message in this queue. (In Windows 3.1, this is 22).
06h WORD Number of messages waiting that have not been removed
by a GetMessage() or PeekMessage(PM_REMOVE).
08h WORD Offset in the queue segment of next message to be retrieved.
0Ah WORD Offset in the queue segment where next message will be written.
0Ch WORD The length in bytes of the queue's segment.
0Eh DWORD DWORD value returned by GetMessageTime().
12h DWORD DWORD value returned by GetMessagePos().
16h WORD Unknown. Sometimes contains 1.
18h DWORD Information returned by GetMessageExtraInfo().
1Ch WORD Unknown.
1Eh DWORD Contains the LPARAM of a SendMessage() to another task.
22h WORD Contains the WPARAM of a SendMessage() to another task.
24h WORD Contains the MSG of a SendMessage() to another task.
26h WORD Contains the HWND of a SendMessage() to another task.
28h WORD Contains the DWORD result from the SendMessage().
2Ch WORD PostQuitMessage() has been called by this program.
2Eh WORD PostQuitMessage() exit code.
30h WORD Flags of some sort.
32h DWORD Unknown.
36h WORD Expected Windows version, from NE file.
38h WORD Queue handle of application that is sending a message to this app.
3Ah WORD Used for an intertask SendMessage().
3Ch WORD Used for an intertask SendMessage().
3Eh WORD Number of "paints" needed by this application.
40h WORD Number of timer events waiting for this application
42h WORD QS_xxx bits that have changed since the last call to
GetMessage(), PeekMessage(), or GetQueueStatus().
44h WORD QS_xxx bits indicating the kind of messages that are waiting
for the application.
46h WORD Contains the QS_xxx bits that an application is
currently waiting for.
48h WORD Used for intertask SendMessages().
4Ah WORD Used for intertask SendMessages().
4Ch WORD Used for intertask SendMessages().
4Eh WORD Something having to do with hooks
50h BYTE[1Eh] Unknown. Possibly having to do with hooks.
6Eh WORD Start of the posted message storage area. The
memory from here, to the end of the segment, can
be thought of as an array of messages, each message
being 22 bytes in length.
[LISTING TWO]
// Global variables: hQCursor - The queue "associated" with the cursor
// hQActive - The queue of the "active" window that has focus
// hQCapture - The queue associated with the capture window
// hQSysModal - The queue associated with the system modal window
// Local variables: best_queue - contains the current "best guess" as to which
// queue should be woken up to receive the message
// wakebit - contains the QS_xxx message type (QS_MOUSEMOVE,
// QS_MOUSEBUTTON, or QS_KEY) that will be placed in the WakeBits
// of whatever queue is selected to receive the message.
best_queue = hQCursor
if ( message is a not a key message )
goto mouse_event
wakebit = QS_KEY
if ( hQActive != NULL )
best_queue = hQActive
goto system_modal_check
mouse_event:
if ( message == WM_MOUSEMOVE )
wakebit = QS_MOUSEMOVE
else
wakebit = QS_MOUSEBUTTON
if ( hQCapture != NULL )
best_queue = hQCapture
system_modal_check:
if ( hQSysModal != NULL )
best_queue = hQSysModal
if ( best_queue != 0 )
goto wake_em_up
iterate through queue linked list
{
if ( queues WakeMask includes wakebit determined
previously )
{
best_queue = current queue under examination
goto wake_em_up
}
if ( at end of queues linked list )
return
}
wake_em_up:
SetWakeBit2(); // Sets WakeBits, and posts event
return
[LISTING THREE]
// WakeMask contains QS_xxx OR'ed together. SleepHq() will not return until at
// 1 of QS_xxx bits in the WakeMask parameter has been set in the ChangeBits.
void SleepHq( unsigned WakeMask )
{
HANDLE currQ
SleepHq_check_flags:
currQ = Get_current_task_queue
// If already have a message then go get it
if ( WakeMask & currQ.ChangeBits )
goto SleepHq_done
// Check for SendMessages and deal with them
if ( currQ.WakeBits & QS_SENDMESSAGE )
goto SleepHq_have_SendMessage
// Always check for SendMessages
currQ.WakeMask = WakeMask & QS_SENDMESSAGE
if ( WakeMask & currQ.ChangeBits )
goto SleepHq_done
WaitEvent() // Kernel routine that waits for an event
goto SleepHq_check_flags:
SleepHq_done:
zero_out_currQ.WakeMask
return
SleepHq_have_SendMessage:
zero_out_qWakeMask
// Deal with the SendMessage(). Described in the section on SendMessage()
ReceiveMessage()
goto SleepHq_check_flags
}
[LISTING FOUR]
void SetWakeBit2(HANDLE hQueue, UINT WakeBit)
{
hQueue.ChangeBit |= WakeBit // Turn on the QS_xxx flags
hQueue.WakeBit |= WakeBit
// If we're setting a QS_xxx bit that the queue is waiting
// for, then force the scheduler to schedule the task
if ( WakeBit & hQueue.WakeMask )
{
hQueue.WakeMask = 0
PostEvent() to hQueue's task
}
}
[LISTING FIVE]
// "flags" are the "flags" parameter to PeekMessage(). "removeFlag" is a local
// indicating whether a message will be read from the queue. "WakeMask" is a
// local containing a QX_xxx mask of messages types GetMessage()/PeekMessage()
// are waiting for. "WakeBits" is a local containing the the QS_xxx bits that
// indicate which types of messages are waiting for this task.
PeekMessage:
Is_GetMessage_call = 0
goto GetMessage2
GetMessage:
Is_GetMessage_call = 1
Insert a flags WORD in the stack frame so that the stack
frame for GetMessage() is the same as for PeekMessage().
The flag is set to PM_REMOVE.
GetMessage2: // This is where GetMessage() and PeekMessage()
// start sharing their code
if ( current task is locked )
set PM_NOYIELD in flags
removeFlag = flags & PM_REMOVE
Unlock the system queue if this task holds it.
if ( (msgMin != 0) or (msgMax != 0) )
Call function to set up WakeMask for the specified
message range
else
WakeMask = QS_MOUSE | QS_KEY | QS_POSTMESSAGE
| QS_TIMER | QS_PAINT
begin_looking_for_msgs:
if ( !CheckForNewInput() )
goto wait_for_input
if ( system queue not locked )
goto not_in_system_queue
if ( system queue not locked by current queue )
goto not_in_system_queue
if ( (QS_MOUSE | QS_KEY) set in WakeMask and WakeMask )
{
if ( ScanSysQueue() )
goto GetMessage_have_msg
}
not_in_system_queue:
if ( QS_POSTMESSAGE set in WakeBits and WakeMask )
if ( ReadMessage() )
goto GetMessage_have_msg
if ( (QS_MOUSE or QS_KEY) set in WakeBits and WakeMask )
if ( ScanSysQueue() )
goto GetMessage_have_msg
if ( !CheckForNewInput() )
goto wait_for_input
if ( QS_PAINT set in WakeBits and WakeMask )
if ( DoPaint() )
goto GetMessage_have_msg
if ( PM_NOYIELD set in flags )
goto check_for_timer_msg
UserYield()
if ( !CheckForNewInput() )
goto wait_for_input
check_for_timer_msg:
if ( QS_TIMER set in WakeBits and WakeMask )
if ( DoTimer() )
begin_looking_for_msgs
wait_for_input:
if ( FSHRINKGDI )
ShrinkGDIheap() ; Where is this defined???
// If not in GetMessage, we must be in PeekMessage
if ( Is_GetMessage_call == 0 )
goto PeekMessage_exit
SleepHq(wakemask)
goto begin_looking_for_msgs
GetMessage_have_message:
if ( a WH_GETMESSAGE hook is installed )
call the hook function
// If not in GetMessage, we must be in PeekMessage
if ( Is_GetMessage_call )
return 1
if ( returning msg == WM_QUIT )
return 0
else
return 1
PeekMessage_exit:
if ( ! PM_NOYIELD )
UserYield() // Yield to any higher priority app
return 0
[LISTING SIX]
// Returns Zero Flag set if no desired input flag is set. WakeMask & WakeBits
// are in registers, and are same as WakeMask and WakeBits in GetMessage2().
top:
Get handle of current queue
if ( QS_SENDMESSAGE set in the queues wakebits )
{
ReceiveMessage()
goto top
}
// AND instruction sets the Zero flag if any bits match
AND WakeMask, WakeBits together
Return
[LISTING SEVEN]
LPMSG lpMsg // ptr to passed-in message, used as scratch variable.
if ( (msg != WM_TIMER) && (msg != WM_SYSTIMER) )
goto handle_normally
if ( msg.lParam == 0 )
goto handle_normally
GetTickCount()
push msg parameters on stack
lpMsg = msg.lParam // Timer function callback address
AX = SS // Something with MakeProcInstance thunk???
goto call_function
handle_normally:
if ( msg.hwnd == 0 )
return;
push msg parameters on stack
if ( msg.msg == WM_PAINT )
set "paint" flag in WND structure
lpMsg = Window proc address // stored in WND data structure;
// pointed to by msg.hwnd
AX = hInstance from WND structure // For use by MakeProcInstance() thunks
call_function:
ES = DS = SS // Set all segment registers to hInstance of application
call [lpMsg] // Call the window proceedure (or timer callback fn).
// lpMsg is now used to store the address of window
// function (or timer callback function) to be called
if ( msg.msg != WM_PAINT )
goto DispatchMessage_done
// Check for destroyed window
if ( ! IsWindow(msg.msg) )
goto DispatchMessage_done
if ( "paint" flag in wnd structure still set )
goto No_BeginPaint
DispatchMessage_done:
return
No_BeginPaint:
Display debugging message "Missing BeginPaint..."
Call DoSyncPaint() to handle the painting correctly
goto DispatchMessage_done
[LISTING EIGHT]
if ( receiving HWnd == -1 )
goto BroadcastMessage // Not included here
Verify sending app has a message queue
Get receiving apps queue from receiving hWnd
// Are the sending and receiving queues the same???
Intertask = ( receivingHQueue == sendingHQueue )
Call any installed WH_CALLWNDPROC hooks
if ( Intertask )
goto InterTaskSend
// Next section deals with calling a window proceedure within same program
// This is the simple case and is much easier than calling between two
// different programs (below)
Push address of the wndproc of the receiving WND structure on stack
Push SendMessage params on stack
Put hInstance into AX
Load DS & ES from the SS register
Call through the wndproc address in the window structure
SendMessage_done:
Return to caller
SendMessage_error: // Common JMP location when errors occurr
Put 0 in DX:AX
Goto SendMessage_done
// SendMessage()'s that go between different tasks come here.
// This is where the code gets complex.
InterTaskSend:
if ( A task is locked )
{
display a diagnostic in debugging version
Goto SendMessage_Error
}
if ( sending task is terminating )
{
display a diagnostic in debugging version
Goto SendMessage_Error
}
if (SendMessage parameter area in sending app is already used)
{
display a diagnostic in debugging version
Sleep until the parameter area is free // Uses SleepHq()
}
Grab parameter area in sending app
Save the address where the result of the call will be stored
Copy the SendMessage parameters off the stack into the sending hQueue
Put the receiving queue at the head of the SendMessage() list
// Set bits to wake up the receiving task
SetWakeBit2( QS_SENDMESSAGE )
SendMessage_wakeup_receiving_task:
if ( a previous SendMessage() has completed )
goto got_reply
Turn off "have result" flags in sending queue
Call DirectedYield() to force the child task to run next
// When the DirectedYield() returns, the receiving task should have awoken
// and called ReceiveMessage() and ReplyMessage(). Described below.
Sleep until result is back from child
// Uses SleepHq(). Probably redundant, because there already should be a
// result available when the prior DirectedYield() returned.
got_reply:
Copy the return value to the "result" area on the stack
Release parameter area in sending queue
if ( Not replied to )
goto SendMessage_wakeup_receiving_task
goto SendMessage_done
[LISTING NINE]
Make sure there is a SendMessage waiting for us.
Remove sending queue from SendMessage() list of queues.
Clear QS_SENDMSG bit if the list of queues is empty.
Save copies of the sending hQueue and pointer to area
where results should be saved in the sending task.
Free the the SMPARAMS area in the sending queue.
Make sure target window is still valid.
Copy the ExtraInfo data from sender to receiver.
Call the target window proc.
Call ReplyMessage.
Return.
[LISTING TEN]
// Reply message takes the value that should be returned to
// the sender as a parameter. Here, it's called "return_value"
ReplyMessage_start:
If ( message has already been replied to, or
if there is no sending queue )
return
if ( QS_SENDMESSAGE bit set in receiving queue)
{
ReceiveMessage()
Goto ReplyMessage_start
}
if ( result area in use )
{
OldYield()
Goto ReplyMessage_start
}
Copy return_value into sending hQueue
Restore pointer to result area on stack in the sending hQueue
Set AlreadyRepliedFlag
SetWakeBit2( QS_SMRESULT )
DirectedYield(SendingTask)
Return