The WinBuf Class Tutorial

This is a tutorial showing how one would port some console-based code (externalcode.cpp) to windows.
  1. Make a new project workspace, called "Tutorial". (Just click "finish".)
  2. Copy winbuff.cpp, winbuff.h, promptdialog.h, promptdialog.cpp, and externalcode.cpp to your source code directory.
  3. Go to the Class Wizard and select Add Class from a file. The class name is "PromptDialog". Be sure to enter the header and implementation filenames.
  4. Insert the files promptdialog.cpp, winbuff.cpp, and externalcode.cpp into your project.
  5. In Insert|Resources, add a Dialog box called IDD_DIALOG1. Keep the OK and Cancel buttons. Add two edit boxes (IDC_EDIT1 for output and IDC_EDIT2 for input). Change the properties of the output box so that it is read only, has multiline output, and a vertical scrollbar.
  6. Select Layout|Tab Order, and click the input box so that it is first.
  7. Make the following modifications, so that externalcode.cpp looks like ported.cpp. Add the following code to the beginning of the file:
    	#ifdef WIN32
    	#include "resource.h"
    	#include "PromptDialog.h"
    	#include "StdAfx.h"
    	#include "winbuff.h"
    	#endif // WIN32
    Add this code before any use of cout or cin:
    	#ifdef WIN32
    	PromptDialog* md = new PromptDialog(AfxGetMainWnd());
    	EnableWindow(AfxGetMainWnd()->GetSafeHwnd(),FALSE);
    	md->m_displaytext = "Initial output string.\r\n";
    	md->m_strEdit1 = ""; // No input default.
    	
    	md->Create();
    	
    	// Store the old stream handles
    	istream *in = &cin;
    	ostream *out = &cout;
    	ostream *err = &cerr;
    	
    	// You can also specify the size of the input buffer you want. (No dire consequences for
    	// a small buffer.)
    	inWinStream win(md,3);
    	cin = win;
    	
    	// You can also specify the size of the output buffer you want. (It will dump output as it
    	// is filled.) The default is 256
    	outWinStream wout(md);
    	cout = wout;
    	
    	// You can also specify the size of the error buffer you want. (It will dynamically grow
    	// as needed.)
    	errWinStream werr(3);
    	cerr = werr;
    	
    	// Tie the input to the output so that pending output will be flushed when input is
    	// needed.
    	cin.tie(&cout);
    	
    	#endif // WIN32
    Add this code after all the cout's and cin's:
    	#ifdef WIN32
    	
    	// destroy our window
    	md->DestroyWindow();
    	EnableWindow(AfxGetMainWnd()->GetSafeHwnd(),TRUE);
    	
    	// restore the streams.
    	cout = *out;
    	cin = *in;
    	cerr = *err;
    	
    	#endif // WIN32
  8. Go to the Class Wizard, and in the Class CTutorialDoc, add a function for the ID_FILE_NEW command. Edit the code so that it calls externalCode(). Also add extern void externalCode(); to the top of the TutorialDoc.cpp file.
  9. Under Build|Settings|C/C++|Precompiled Headers, select automatic use.
Now that everything is done, compile and run your application. When you click File|New, your dialog should pop up, with the initial output string "Initial Output String", followed by "test", "one", and "two" that were "cout'ed". The dialog waits for you to enter text into the input box, since the line "cin >> w;" is being executed. After you enter something and press enter or click OK, a warning box pops up when the string "Oh no!" is sent to cerr. I've also included in ported.cpp to show how to wait for the user to click OK or Cancel. The code is:
#ifdef WIN32
// Here's how to wait for an OK.
// It's an unfortunate fact of life that Microsoft's implementation
// of cout doesn't automatically flush on newlines.
cout.flush();

md->WaitForOK();
#endif // WIN32
Unfortunately, when using buffering, Microsoft's implementation of cout doesn't call my code to flush the output whenever a newline is encountered. You have to manually flush it before waiting for and OK.