Click here to Skip to main content
15,879,184 members
Articles / Desktop Programming / MFC
Alternative
Tip/Trick

Formatted MessageBox/AfxMessageBox

Rate me:
Please Sign up or sign in to vote.
5.00/5 (4 votes)
21 Oct 2010CPOL3 min read 22.6K   1   7
printf is so 1980s. If you don't want to dig out your shoulder pads and big hair why not go for a more modern C++ approach?Instead of a function try a stream buffer:class message_box_stream_buf : public std::basic_streambuf{ public: message_box_stream_buf( const...
printf is so 1980s. If you don't want to dig out your shoulder pads and big hair why not go for a more modern C++ approach?

Instead of a function try a stream buffer:

class message_box_stream_buf : public std::basic_streambuf<char>
{
    public:
        message_box_stream_buf( const std::string &caption )
            : buffer_( 513 ), caption_( caption )
        {
            setp( &buffer_[ 0 ], &buffer_[ 512 ] );
        }

        ~message_box_stream_buf()
        {
            sync();
        }

    protected:
        traits_type::int_type overflow( traits_type::int_type to_write )
        {
            sync();
            return sputc( traits_type::char_type( to_write ) );
        }

        traits_type::int_type sync()
        {
            if( pptr() != pbase() )
            {
                MessageBoxA( NULL, &buffer_[ 0 ], caption_.c_str(), MB_OK );
                std::fill( buffer_.begin(), buffer_.end(), 0 );
                setp( &buffer_[ 0 ], &buffer_[ 0 ] + 512 );
            }
            return traits_type::not_eof( ' ' );
        }

    private:
        std::vector<char> buffer_;
        const std::string caption_;
};


I won't go into any detail as to how this lot works, if you want to know I'd suggest reading chapter 3 of "Standard C++ IOStreams and Locales" by Kreft and Langer.

To use it you need to plug it into an ostream:

    message_box_stream_buf sb( "Test App" );
std::ostream message_box_stream( &sb );

and once you do that it's just like using std::cout:

message_box_stream << "Hello Cruel World!" << std::endl;


which means you don't have to mess about with things like variable parameter lists and risk making a mistake that'll crash your program. No mucking about with format strings and making sure they match the order of your arguments either.

In fact you can ever rewire std::cout to use one of these stream buffers and lob all it's output to a message box:

message_box_stream_buf sb( "Test App" );
std::cout.rdbuf( &sb );
std::cout << "This is an output test. Have an integer: "
          << 87 << std::endl;


While this isn't too handy for cout it can be really handy for cerr.

Finally you get all the other goodness that comes with using iostreams. As well as being able to use them in C++ algorithms (via stream and streambuf iterators) they can handle types your write:

class vector3
{
	public:
		vector3( int x, int y, int z ) : x_( x ), y_( y ), z_( z ) {}

		friend std::ostream &operator<<(
                     std::ostream &str, const vector3 &to_display )
		{
			return str << "x=" << to_display.x_
                                   << " y=" << to_display.y_
                                   << " z=" << to_display.z_;
		}

	private:
		int x_, y_, z_;
};


a bit of object creation and voila, vector3s can be output to a message box:

message_box_stream_buf sb( "Test App" );
std::ostream message_box_stream( &sb );
    message_box_str << vector3( 1, 2, 3 ) << std::endl;


A couple of final points. The implementation above will only work for narrow characters. It's trivial to templatise it or convert it to use wide characters. Secondly the only OS specific bit is the call to MessageBoxA. That's asking to be parameterised as well. In fact if you use boost you'll see that there are several streambuffer classes in there that already do this sort of thing - all you have to do is adapt MessageBox to the signature the boost stream buffers expect and the job's done. I only didn't do that because I've not got boost installed on the system I'm writing this on!

So in conclusion: Don't fanny about with unsafe C idioms from years ago. You've got a C++ compiler and library, embrace it and use it. And as you do that look out for other abstractions that you can pull out and use elsewhere.

Cheers,

Ash

PS: Response to Ajay's comment below...

Writes to a stream are sent to the output device when one of two things happen: It runs out of buffer space OR you explicitly flush it. So you can pile up text to be output either in the same statement:

message_box_stream << " First bit " << " Second bit ";

or in different statements:

message_box_stream << " First bit ";
message_box_stream << " Second bit";


and nothing will be displayed (Depending on what you've already buffered.) When you flush the stream (std::endl flushes) it'll all come flooding out:

message_box_stream << "Hello";
message_box_stream << " Cruel ";
message_box_stream << " World!!";
message_box_stream << std::flush; // MessageBoxA called


It's no different to the way a stream to the console or writing to a file works - nothing comes out until it's flushed.

As to the comment that it's extraneous I'd suggest that you wouldn't create a new stream and stream buffer everytime you wanted to do some output, anymore than you'd open a console window. While I wouldn't advocate a global (like std::cout is) there's generally no problem with parameterising from above and passing a reference to the stream with a function call. This has the added bonus of allowing different levels of the program to compose things like error messages and only the top level pulls the trigger to do the display.

Another option, even though it's a global in disguise, is to make an instance part of an MFC application object and provide a simple wrapper around it to make it easier to use. To me:

application_message_box() << "It's all gone wrong! " << e.what() << std::endl;


is safer than any solution involving printf style functions as whatever disasters I inflict on the formatting it's unlikely to crash the process. However your milage may (and probably will) vary!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United Kingdom United Kingdom
I've been programming since 1985 - starting with Fortran 77, then moving onto assembler, C and C++ in about 1991. I also know enough Java and Python to read code but you probably wouldn't want me writing it.

I've worked in a wide variety of application areas - defense, banking, games and security with the longest stints being in security. I also seem to end up programming devices far too often. This time I'm programming terahertz band body scanners.

Comments and Discussions

 
Generalnot bad,some times i will use messagebox to test my project.... Pin
fronz@sohu.com18-Jan-12 0:00
fronz@sohu.com18-Jan-12 0:00 
GeneralReason for my vote of 5 5. ...as always. Pin
Niklas L16-Jan-11 22:14
Niklas L16-Jan-11 22:14 
GeneralAnd Bad Bad Bad design is when you advocate using variable a... Pin
Aescleal23-Oct-10 10:45
Aescleal23-Oct-10 10:45 
GeneralAnd seriously, from my perspertive, streaming, buffering and... Pin
Ajay Vijayvargiya23-Oct-10 9:05
Ajay Vijayvargiya23-Oct-10 9:05 
GeneralI just read the added stuff. My point was *just* to avoid fo... Pin
Ajay Vijayvargiya23-Oct-10 8:59
Ajay Vijayvargiya23-Oct-10 8:59 
GeneralQuite good, and powerful. But not adaptable easily. My point... Pin
Ajay Vijayvargiya21-Oct-10 2:56
Ajay Vijayvargiya21-Oct-10 2:56 
GeneralReason for my vote of 5 Wonderful: an elegant and powerful s... Pin
Sauro Viti21-Oct-10 2:25
professionalSauro Viti21-Oct-10 2:25 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.