Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Formatted MessageBox/AfxMessageBox

0.00/5 (No votes)
21 Oct 2010 1  
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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here