Why command routing?
Well, why not? A window can be a command target, after
all, so you won't lose anything for the classes you
already have that inherit from CWnd. But
classes that inherit from CDocument or CWinApp
can only be command targets; they cannot receive Windows
messages. If the socket class communicates with commands,
it can contact a document or application instance
directly rather than going through a faked-up window. If
you have a class in mind that is currently not a command
target, there is less overhead involved in creating
instances of a class that inherits from CCmdTarget
than in creating ones that inherit from CWnd.
What's the difference between a command and a
message?
A command is a certain kind of message. There is a
message called ON_COMMAND, which indicates that a command
is being routed. Many MFC objects inherit from CCmdTarget
and can have commands routed to them even though they
can't receive windows messages because they don't inherit
from CWnd. If you aren't willing to accept that as the
only really important difference between commands and
messages, read the article Meandering Through the
Maze of MFC Message and Command Routing in the
July 1995 issue of MSJ,
Microsoft Systems Journal.
How does that change the code?
A message has two parameters; a command has only one.
We had three pieces of information to pass along in our
socket messages before, but managed to fake up a way to
encode all three pieces in two parameters. If the first
parameter was positive, it was the length of the data
being transmitted and the second parameter was a pointer
to the data itself. If the first parameter was negative,
there was no data being transmitted but rather than
socket was telling the window about a status change; the
value of the first parameter was cast to an enum that
listed possible status change values. We just can't pull
off that kind of parameter-packing with only one
parameter, so we use that single parameter as a pointer
to a structure, QSocketInfo, that carries
all the relevant information. The vast majority of the
changes we will present below are to accomodate this
single fact; changes for other reasons are noted as we
go. We are assuming throughout that you are using the
version of QSocket that inherits from CSocket;
ie that you have Visual C++ version 2.1 or later. If not,
make these changes to the version of QSocket
that does not inherit from CSocket. You will
be changing the header and code files for two classes: QSocket
(socket.h and socket.cpp)
and CFingerView (fingevw.h
and fingevw.cpp) as detailed
below. If you'd rather not do all that typing, you can
just get the zip of the
four changed files. In the changed files, each change
is preceded by a comment showing you what the line was in
the message-passing version of Finger. These files will
not work alone; they are meant to replace the ones on the
CD that came with the book.
- Change the enum
SocketReceiveCmd:
add a SocketReceivedData and remove
the value specifiers ( =-100 and =-101)
- Before the definition of
QSocket,
add this code, which sets up a message macro to
be used in a message map later:#ifndef _WIN32
#define ON_SOCKET_COMMAND(id, memberFxn) \
{ CN_COMMAND, (WORD)id, AfxSig_bpv, \
(AFX_PMSG)(BOOL (AFX_MSG_CALL CCmdTarget::*)\
(void *))memberFxn },
#else
#define ON_SOCKET_COMMAND(id, memberFxn) \
{ WM_COMMAND, CN_COMMAND, (WORD)id, \
(WORD)id, AfxSig_bpv, \
(AFX_PMSG)(BOOL (AFX_MSG_CALL CCmdTarget::*)\
(void *))memberFxn },
#endif
Why do we use different versions of the macro
for 32 bit Windows (NT and 95) than for 16?
Because the 32-bit version has an extra parameter
at the start, set to WM_COMMAND
here, that defines what sort of messages will get
routed. The 16 bit version just routes WM_COMMAND
messages. The 32 bit version also has an
extra parameter in the middle: instead of taking
a single MessageID it takes two and catches all
messages in the range between those values. We
only want to catch a single message, so we use
the same value for both parameters.
Why AfxSig_bpv? It is a signature (hence Sig) for
functions that return a BOOL and are passed a
single pointer-to-void. There are many different
signatures, each used for a different combination
of return type and parameter type. Since the
function that will catch this message takes the
pointer to or QSocketInfo structure, and returns
a BOOL, this is the right signature for us.
- Change the default constructor so that it
initializes the renamed
ReceiveTarget
and ReceiveID
- At the end of
SetErrorVars(), remove
the call to ReceiveWindow->SendMessage and
the if surrounding it and replace it
with this code: if (ReceiveTarget)
{
QSocketInfo info;
info.cmd = SocketStatusChanged;
info.socket = this;
info.data = 0;
info.length = 0;
ReceiveTarget->OnCmdMsg(ReceiveID,
CN_COMMAND, &info, NULL);
}
- Change the parameters of
SetReceiveTarget
as in socket.h, and change the first two lines of
the function to use the passed parameters to set
the value of the member variables: ReceiveTarget = target;
ReceiveID = id;
- Change the first line of
GetLine() to
test ReceiveTarget rather than ReceiveWindow
- At the end of
OnReceive(), make
essentially the same change as in SetReceiveTarget,
except that the structure values are different: if (ReceiveTarget)
{
QSocketInfo info;
info.cmd = SocketReceivedData;
info.socket = this;
info.data = 0;
info.length = 0;
ReceiveTarget->OnCmdMsg(ReceiveID,
CN_COMMAND, &info, NULL); }
- At the beginning of
OnAccept(), make
that block change again: if (ReceiveTarget)
{
QSocketInfo info;
info.cmd = NewSocketAccepted;
info.socket = news_socket;
info.data = 0;
info.length = 0;
ReceiveTarget->OnCmdMsg(ReceiveID,
CN_COMMAND, &info, NULL); }
- At the end of
OnAccept(), make that
block change again: if (ReceiveTarget)
{
QSocketInfo info;
info.cmd = SocketStatusChanged;
info.socket = this;
info.data = 0;
info.length = 0;
ReceiveTarget->OnCmdMsg(ReceiveID,
CN_COMMAND, &info, NULL); }
- At the beginning of
OnConnect(),
make that block change again: if (ReceiveTarget)
{
QSocketInfo info;
info.cmd = SocketStatusChanged;
info.socket = this;
info.data = 0;
info.length = 0;
ReceiveTarget->OnCmdMsg(ReceiveID,
CN_COMMAND, &info, NULL); }
- At the end of
OnConnect(), make that
block change again: if (ReceiveTarget)
{
QSocketInfo info;
info.cmd = SocketStatusChanged;
info.socket = this;
info.data = 0;
info.length = 0;
ReceiveTarget->OnCmdMsg(ReceiveID,
CN_COMMAND, &info, NULL); }
- At the end of
OnClose(), make that
block change again: if (ReceiveTarget)
{
QSocketInfo info;
info.cmd = SocketStatusChanged;
info.socket = this;
info.data = 0;
info.length = 0;
ReceiveTarget->OnCmdMsg(ReceiveID,
CN_COMMAND, &info, NULL); }
- At the end of
ConnectHelper(), make
that block change one last time: if (ReceiveTarget)
{
QSocketInfo info;
info.cmd = SocketStatusChanged;
info.socket = this;
info.data = 0;
info.length = 0;
ReceiveTarget->OnCmdMsg(ReceiveID,
CN_COMMAND, &info, NULL); }
- Change the #define from
WM_SOCKET_RESPONSE to
just SOCKET_RESPONSE since it is no
longer a message, and the value from WM_USER+201
to 5000, which is in the range for user-defined
commands.
- Change the message map declaration of
OnSocket
to have a single parameter, void *pInfo
|