Author: Mr. Ivan Gerencir BSc FIAP
        Eschersheimer Ldstr. 115
        60322 Frankfurt
        Germany

Tel. (office): +49 6171 641027
Tel. (home):   +49 69   5973526


                          MsgDisp

Displaying text messages in a separate application window


Introduction

MsgDisp presented here is a Windows tool for displaying
information text messages (strings) from any number of
applications in one window.  I have developed it as a utility to
help me monitor the behavior of the applications I am
developing.

There are several ways to use MsgDisp.  You can use it as a
simple debugging utility, something like placing MessageBox
calls in your source at strategic points.  Immediate advantage
in using MsgDisp over MessageBox is that you can examine the
text of your messages even after your application has ended.

You can also use MsgDisp when developing cooperative processing
applications (which communicate with each other using Windows
messages) to reveal the exact order of processing of these
messages, since MsgDisp displays messages in the order they are
received.

Finally, you can use MsgDisp where it would be very impractical
to use MessageBox instead: in loops.  Just imagine replying, by
pressing key Enter, to every MessageBox in a loop that runs a
couple of hundred of times!  Calling function MsgDisp does not
stop your program, does not ask you to press a key; it just
sends your text to be displayed in a completely different and
independent application.  You can also use MessageBox where it
is suitable or where it must attract users' attention.


What does MsgDisp contain of

MsgDisp (msgdisp.exe) is a Windows application that, once
started, awaits Windows messages.  Interface to MsgDisp is a
Dynamic Link Library (DLL) msgdispi.dll that contains function
MsgDisp which you call to send your text to be displayed in a
MsgDisp application.  An import library that you use to link
your code with is appropriately called msgdispi.lib. Declaration
of function MsgDisp is in header file called msgdispi.h.

I have included complete source code and resource file for both
msgdisp.exe and msgdispi.dll, as well as make file that will
compile, link and produce import library automatically.  Also,
you will find a small program that will test the whole process
of sending and displaying your text messages.


Using MsgDisp

Usage of MsgDisp is both standard and easy.  At the beginning of
your file(s) in which you want to use function MsgDisp you
#include "msgdispi.h" which stands for MsgDisp Interface.  Then
you insert MsgDisp calls into your source code.  The only
argument to this function is a pointer to the string that you
want to be sent to MsgDisp.  Afterwards you obviously have to
recompile and relink your code.  Since function MsgDisp is
contained in a DLL, you will have to link with an import library
msgdispi.lib that tells linker how to resolve these calls.

To be able to see your messages displayed, MsgDisp application
has to be active.  When your application executes MsgDisp calls,
the message texts will show up one by one in MsgDisp application
window.  If MsgDisp application is not running, you will not be
able to see messages displayed, but it will not stop your
application from running.  This enables you to start MsgDisp
application in any suitable moment while debugging.


MsgDisp application menu options

Here is a brief description of several standard menu options in
MsgDisp application:

File|Save enables you to save all received message texts into a
file.  Default file name extension is '.msg'.  File|Save uses
Common Dialog Box Library for selecting filename to save into.

File|Print enables you to print all received messages to printer
of your choice.  File|Print also uses Common Dialog Box Library
for selecting printer.

File|Exit terminates MsgDisp application.

There is a couple of specific menu options which deserve a more
detailed explanation.

Options|Insert Break enables you to insert a line of dashes
between messages in MsgDisp application while receiving messages
so that later you could easier distinguish particular blocks of
messages.  For instance, you can mark a place where you are
about to receive messages of interest, or you may mark some
significant event this way.

Options|PopUp enables you to toggle the behavior of MsgDisp.  If
this option is checked (on), then after every message received,
MsgDisp will show itself up as top window on a desktop.  This
can be useful if you have put only one or a few MsgDisp calls in
your code, awaiting when will your application execute it/them
for the first time.

On the other hand, if you are executing a lot of MsgDisp calls,
you may want to uncheck this option, and analyze generated
messages at some later time.

There is one more time when you will want to uncheck this
option.  Say you are debugging receiving of WM_PAINT message by
executing MsgDisp call.  If the window you are debugging and
MsgDisp application window would overlap, bringing MsgDisp
application window to the top of desktop will sooner or later
generate another WM_PAINT message to your window that would in
turn lead to another MsgDisp call and so on.  One solution might
be to move and/or resize MsgDisp application window after
starting it so that it does not overlap with your window.
Albeit, it might not be always possible, for instance when your
window is the size of the whole screen.

File|Repaint should be used if, for any reason, MsgDisp
application window gets partially obscured by some other active
application window.  This might sometimes happen when menu
option Options|PopUp is on.

Option|Clear lets you delete all received messages from MsgDisp
application window.


Atom Management Functions

The main task of transmitting a text string from one application
to another is most easily performed using Windows messages, for
they are the basic means of inter- process communication under
Windows.  By now probably everybody knows that Windows messages
can transfer only limited amount of data: one 16-bit and one
32-bit value.  These values are called WPARAM and LPARAM
respectively.  Each of these values can and often does represent
very different things, according to the message it is sent with.

When designing MsgDisp interface, I first thought of allocating
and locking memory on global heap, copying a string into it and
passing a pointer to it via Windows message LPARAM value to
MsgDisp application.  After returning from function SendMessage,
global memory would have to be unlocked and freed.  This looked
like classic Windows workable solution: with little care about
allocating and freeing memory and handling pointers, there
should be nothing to wary about.  But then, while examining
Windows SDK documentation on GlobalAlloc, on the opposite page I
noticed description of function GlobalAddAtom.  What might Atom
be, I asked myself and decided to take a quick look.  Here is
what kept my attention: "The GlobalAddAtom function adds a
string to the system atom table and returns a unique value
identifying the string." Now it seemed quite justified to do
some more research on atoms.  After a while, all I have found in
SDK documentation was half a page long list of Atom Management
Functions, and a suggestion that atoms can be used for
conserving memory and in applications using Dynamic Data
Exchange (DDE).  Not much, I thought to myself.

Anyway, it looked as if Atom Management Functions were made just
to be used for MsgDisp interface and I decided to give them a
try.  Now the transmitting procedure looked like this: use
GlobalAddAtom to add string to the global system table that is
available to all applications.  Next, send the Atom handle
returned by GlobalAddAtom to MsgDisp application via Windows
message.  After return from function SendMessage, use
GlobalDeleteAtom to release the space used in system Atom table.
MsgDisp application, on the other hand, uses GlobalGetAtomName
on Atom handle it receives to get a copy of string in its own
data segment.  Couldn't be simpler.


Sending and receiving a message

Now all that had to be done was to identify MsgDisp application
window as a target for sending a message.  May I remind you that
function SendMessage has to be provided a handle of the window
that is to receive the message.  I could have used function
FindWindow to retrieve MsgDisp application windows' handle for
future conversation based on application's name (MsgDisp). But
then I would have to monitor the status of sending every
message, for you might terminate MsgDisp application while your
application is running, or even start your application without
first starting MsgDisp.  In case of SendMessage failure (which
would mean that MsgDisp application is not running at that
particular moment), I would have to call FindWindow again on
every failure of sending the message, in order that if you
restart MsgDisp application at any point, it continues to
display messages.  If this sounds too complicated to you in
order to just send a string out, it sure did to me.

Hopefully there is a much simpler solution.  You can send a
message to all top-level active windows by using a constant
HWND_BROADCAST instead of window handle.  Since every top- level
window will receive our message, care must be taken for a
message not to be 'understood' by any window other than MsgDisp.
The simplest way to ensure this is to use RegisterWindowMessage
in both MsgDisp application and MsgDisp interface with the same
string given as argument.  RegisterWindowMessage returns a
message number in the range 0xC000 through 0xFFFF, according to
the string passed (note the range, it is important; more on it
later).  If two applications pass the same string, they get the
same message number.  Of course, the string has to be unique so
that no other application would use it and get the same message
number.  Just to be on the safe side, I have used an additional
security measure, passing a magic number in WPARAM value when
sending the string, which MsgDisp application expects along with
the message agreed upon, in order to process the message.

I have decided to have fixed (at compile time) maximum size of
string that can be transmitted by MsgDisp.  I could have passed
string length together with Atom in a message so that MsgDisp
application could allocate enough space from heap for each
string received, and deallocate it immediately after processing
the string.  However, there would be speed penalty for using
heap allocating/deallocating functions too often.  Other
solution, to allocate assumed maximum string size space and
re-allocate it only when the length of received string is
greater than the length of any string received before, can be
implemented, but I didn't want to make code more complicated
than necessary.  Besides, you have the source code so you can
always enlarge the maximum string size that can be sent.  I
have, however, checked the string length prior to sending it (in
function MsgDisp) to prevent buffer overflow in MsgDisp
application.


Implementation

I have written MsgDisp application and interface using Borland
C++ 3.1 & Application Frameworks.  I have used Object Windows
Library (OWL) and dialog for main window of MsgDisp application.

Listing 1 contains MsgDisp application skeleton.  I have decided
to allow only one instance of MsgDisp application running.  I do
not see any reason to enable more than one instance of MsgDisp
application;  doing so would probably only lead to confusion.
Function WinMain shows how to bring previous instance to the top
of the desktop, provided that it is already running.

Listing 1 is also a near-minimal OWL application.  Its only task
is to create MainWindow which then handles everything else.

Listing 2 contains functions pertaining to MainWindow of MsgDisp
application.  Since TMainWindow is derived class from class
TDialog (which is provided by OWL), its constructor must call
TDialog's constructor with appropriate arguments (function
TMainWindow).  Otherwise, TMainWindow's constructor just creates
interface to ListBox that is our only dialog's child control.

TMainWindow's destructor (function ~TMainWindow) makes sure this
interface to ListBox is destroyed when the MainWindow itself is
about to be destroyed.

Function SetupWindow is responsible for initial look and feel of
MainWindow.  It is called by OWL only once, just before the
MainWindow is to be shown.  First thing it does is to call base
class's SetupWindow to do its work.  Then it retrieves and
remembers window's menu handle and checks menu option
Options|PopUp.  Finally it calls function RegisterWindowMessage
and remembers the message's number; it will be used later for
receiving strings as previously described.  If for any reason
function RegisterWindowMessage fails, MessageBox with an error
is shown and MsgDisp application terminates.

The hart of receiving messages is function DefWndProc.  It
silently ignores all but the registered message number by
calling base class's DefWndProc.  However, if it receives 'our'
message and if furthermore magic number is what it should be
(additional precaution), then it processes the message by
calling RecvString to do the actual string handling.

It was not an easy task to find the right function in which to
handle 'our' message.  OWL has its own message loop for
receiving, processing and, if necessary, further dispatching
Windows messages.  OWL provides predefined names of functions
that receive and deal with particular messages (e.g.,
DefChildProc, DefCommandProc, DefNotificationProc, DefWndProc).
Furthermore, there is a special handling of dialogs throughout
OWL (our MainWindow is a dialog).  Anyway, I could not at first
have found a function in which to handle 'our' message; seemed
to me at that point that it is somehow filtered out from the
function DispatchAMessage where I was expecting to be able to
handle it.  There was time to do some digging into the OWL
source code.  And eventually I found it.

You will find in Table 1 how Windows documentation divides all
messages into four groups according to their number.  (WM_USER
is defined in windows.h to have a value 0x400, although in our
case it is of no importance.)  I did not expect OWL to treat
messages in the range WM_USER through 0x7FFF any different to
the range 0xC000 through 0xFFFF.  But for some reason,
StdWndProc (in owl.cpp) sends all messages greater or equal to
0x8000 to DefWndProc, while passing all other messages to
DispatchAMessage.  And since RegisterWindowMessage returns
message number in range 0xC000 through 0xFFFF, it is clear why
'our' message was not reaching function DispatchAMessage as I
expected.  Once I knew this, it was easy to put the right code
to the right place.

Function RecvString retrieves a string according to atom
received and inserts it to the ListBox as last item.  If
Option|PopUp option is checked, it brings MainWindow to the top
of the desktop.

Function WMSize handles WM_SIZE message which Windows sends when
MainWindow changes size.  LPARAM contains new X and Y sizes and
it is quite easy to resize ListBox accordingly to always fill
the whole MainWindow.

Function CMSave handles saving text messages received so far
into a file.  To get the filename, I have used Common Dialog Box
Library.  After preparing the OPENFILENAME structure with the
data required, I call GetSaveFileName function.  It returns zero
in case of error or if key Escape has been pressed.  To
distinguish among the two, I had to call CommDlgExtendedError
which returns error number (greater than zero), or zero if key
Escape has been pressed.  In first case I display MessageBox
with an error; in later case I do nothing so that key Escape can
be used to conveniently abort saving.

After I have obtained valid filename, I open file for writing in
text mode and dump contents of ListBox into it line by line.
Finally, I close the file and inform the user that the data has
been saved.

Function CMPrint handles printing of text messages to the
printer.  To get the printer on which to print, I have also used
Common Dialog Box Library.  After preparing the PRINTDLG
structure with the data required, I call PrintDlg.  Error
handling is essentially the same as in CMSave.  There is one
curiosity with Borland C++ compiler.  When preparing PRINTDLG
structure, I used constant PD_HIDEPRINTTOFILE not to display
"Print to File" option in Print Dialog.  However, this constant
(defined in commdlg.h) is long but does not end in L, so
Integrated Development Environment (IDE) compiler issues a
warning "Constant is long."  Nothing I tried (casting the
constant to long, inserting '#pragma warn -cln' to turn off that
particular warning) made IDE compiler quiet.  Personally I hate
to see a program compile with warnings, but there was nothing I
could do about this one, except changing original commdlg.h
which I did not want to do.  To my surprise, using command line
version of compiler (BCC) revealed that it handles both command
line option -w- cln and #pragma warn -cln properly.

After function PrintDlg successfully returns, one field of
PRINTDLG structure holds a Device Context (DC) which should be
used for subsequent printing.  To be able to skip to next page
at the end of current one, some housekeeping of vertical
coordinates is in order.  That's why I need vertical printer
page size, line height according to the current font and
vertical printing offset, all in pixels.  If I reduce vertical
page size by double vertical offset, I am left with printable
area.  Whenever I fill this area, I should skip to next page.
After printing is finished, I am careful to delete the DC, for
it is a scarce resource.

Function CMRepaint handles repainting the complete MsgDisp
application's window.  By using RedrawWindow with appropriate
pile of parameters, one can easily repaint applications' main
window as well as all the child windows.  I wish more Windows
programs would include this option, for from time to time
something goes wrong somewhere within Windows and windows on the
desktop end up (usually partially) displayed wrong.  It would be
very nice if Repaint option would be just a click away,
especially when it is so easy to implement it.

Function CMExitApp handles receiving MENU_EXITAPP message by
posting quit message which eventually terminates MsgDisp
application.

A few left CM... functions are quite small and I hope self
explanatory.  They all process particular menu-generated
message.

Listing 3 contains source for MsgDisp Interface DLL.  Function
LibMain initializes DLL by registering message for later
communication with MsgDisp application.  Function LibMain
registers a message only if it is not already registered;
although LibMain is called by Windows only once to initialize
DLL, I find that using that additional and most probably
unnecessary check (executed only once anyway) makes me sleep
well.  If any error occurs while initializing DLL, it is
important to return zero to Windows so that DLL is unloaded from
memory.

One particular advantage of having interface function(s)
contained in a DLL is that there is no need to define any
special interface initialization function.  All your
initialization that has to be done can be performed in LibMain,
and it occurs automatically when the first call to any of the
functions in a DLL is performed.  This also relives the user of
the interface the responsibility to properly initialize it
before using it, which at times can be quite easy to forget.

Function MsgDisp is the only interface function needed in our
case.  It has to be marked as exportable so that it can be
called from the outside of DLL.  It is also important to mark
the function itself, as well as all of its arguments as FAR.
Doing so lets you select any memory model according to what is
most suitable for this particular DLL.  You must not forget to
mark the function the same way in an appropriate header file
(here in listing 7) which you include in your code.

First thing MsgDisp function does is to check the length of the
string that should be sent to MsgDisp application.  If the
string is too long, appropriate message is issued and string is
truncated to the maximum length allowed.  Next step is to add
string to Global Atom Table so that it can be used from other
applications.  After that, a Windows message containing Atom
representing the string is broadcasted to all top-level
application windows.  SendMessage function will not return until
all the windows have processed the message, which means that the
copy of the text is already in MsgDisp application. When it does
return, it is safe and necessary to delete global atom,
otherwise it would use up atom table space.

Listing 4 is test program used to check the functioning of
MsgDisp and its' interface.  It is an unusual Windows program,
for it doesn't create and show any window.  It really does not
need one to test the communication.  It just sends two messages
using MsgDisp function before it terminates.

Listing 5 through 8 are header files with appropriate defines or
class or function declarations.  Most important in them are
maximum string length (listing 5) and declaration of MsgDisp
function (listing 7).

Listing 9 is a resource file used for defining the MsgDisp
application's MainWindow.

Listings 10 through 12 are Linker Definitions Files for
producing Windows compatible .exe or .dll files.  Although they
are the same, they need not be so I have provided each one in
its own file.

Listing 13 is a make file.  I used prj2mak utility to generate
make files from appropriate project files.  Since I had three
projects using Borland integrated development environment to
develop MsgDisp, MsgDisp Interface and Test, I came up with 3
similar but also significantly different make files.  However, I
have merged them into one make file for your convenience.

Listing 14 is a small batch file to make everything needed for
functioning and testing of MsgDisp application.

One last note for those of you who are not familiar with OWL:
the correspondence between particular function and Windows
message is established in the declaration of function (in
listing 6).  The syntax for this is nonstandard, but that is how
OWL does its' magic.


How to compile and test it

First you should create a subdirectory called msgdisp and put
all the files in it.  Now to create msgdisp.exe, msgdispi.dll,
msgdispi.lib and test.exe, type on DOS command line

makeall

which will start a batch file to do the job.

After everything is done, you should create a new Windows group;
call it MsgDisp.  Then create two program items for
c:\msgdisp\msgdisp.exe and c:\msgdisp\test.exe and you are ready
to test.  First start MsgDisp by double-clicking on MsgDisp
icon. An empty MsgDisp window will appear.  Then start the test
program.  It only sends two messages to the MsgDisp before it
terminates, so only these two messages will appear in MsgDisp
window.  Now you are ready to use MsgDisp in your applications
as described.


Conclusion

MsgDisp presented here is a simple, but quite useful tool for
getting more information from your application while it is
running. It can be used as the only means of analyzing the
behavior of your application, or it can supplement your favorite
debugger. One of its strongest points is the feature to present
received messages in chronological order, making it ideal tool
for analyzing behavior of cooperative processing applications.
Possibility to start/stop/restart MsgDisp at any time before and
during run of your application(s) enables you to receive and
analyze only those messages you are interested in.


About the Author

Ivan holds Bachelors Degree in Power Electronics from the
University of Belgrade, Yugoslavia and is a Fellow of the
Institution of Computer Analysts and Programmers in London,
England.  He is living and working in Germany, where (among
other activities) he develops Windows applications.  You can
reach him on Compuserve as 100135,1031.
