Overview

When using STEP Tools® software in MFC applications, you are likely to be writing a GUI, rather than a command-line application, so you may want to capture the warning and error messages from STEP files and display them in a special way. Also, you will need to fix a debugging macro in code generated by Visual Studio wizards.

Conflict with Debug New Macro

If you create an MFC project with Visual Studio wizards, the start of generated .CPP files will have the debugging preprocessor macros shown below. These macros replace every call to new with DEBUG_NEW:

#ifdef _DEBUG
#define new DEBUG_NEW   <-- This causes problems!
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

DEBUG_NEW is itself a macro that calls a special debug version of the new operator with extra filename and line number arguments. This debug version is specific to CObject classes and when called on STEP Tools® C++ classes (which define their own new/pnew operators), the extra arguments cause compiler syntax errors.

To correct this, just comment out the macro that replaces every call to new:

// CORRECTED VERSION
#ifdef _DEBUG
// define new DEBUG_NEW   <-- Commented out
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

You can still call DEBUG_NEW explicitly on CObjects if you want to test for memory allocation. In non-debug configurations, the this macro is just defined as the normal new operator. See the Windows AFX.H header file for the exact definitions.

Log Window for Error Reporting

ROSE applications normally print status and debugging messages to stdout, but GUI applications do not have a console, so they will not be seen. You can provide your own message handling as described below, but STEP Tools® software also contains the rose_logwin extension that provides a window to hold a cumulative log of status messages. The messages can be cleared, copied, and saved, and the window can be dismissed when not needed.

Simply include rose_logwin.h and call rose_logwin_init() at the start of your program to register the message window. You must also link against the rose_logwin.lib library.

After this call, all of the ROSE messages are redirected to the window. No other changes are needed. Calling rose_logwin_wait() at the end of your program will block until the user dismisses the message box. This gives the user a chance to view the messages, particularly in short-lived programs.

#include <rose.h>
#include <rose_logwin.h>
 
int main (int argc, char ** argv) 
{
    rose_logwin_init();
    /* your code */
    rose_logwin_wait();
}

The rose_logwin extension provides its own Windows message pump in a dedicated thread, and can be used by MFC, raw Windows API, or even console applications. The screendump below shows the message window and some diagnostic messages from a sample application.

ST-Developer Message Window

The roselog DLL must be in your path for the log window to appear. It is installed in the STEP Tools® software bin directory, which should be in your path. If the DLL cannot be found, rose_logwin_init() will return zero and the default error handling used.

Custom Error Reporting

You can provide your own functions to display status and debugging messages in your own way. All messages are handled by an instance of the RoseErrorReporter class, so you can create a subtype of this class to customize your display.

The log window described above overrides the reporting class to append to a running log of messages in a text window. You can do something similar. We did not use MessageBox() because the report function would be called many times if you read a STEP file with errors in it, and the user would have to hit OK each time!

The class header file should look like the following. You can give the new class whatever name you see fit. It is derived from RoseErrorReporter and overrides the do_report() and do_quit() virtual functions as shown below.

#ifndef _CCustomErrorReporter_H
#define _CCustomErrorReporter_H

#include <rose.h>

class CCustomErrorReporter : public RoseErrorReporter 
{
public:
    CCustomErrorReporter();	
	
    virtual void do_report (char * pfx, RoseError* err, va_list ap);
    virtual void do_quit (int err_code);
};

#endif // _CCustomErrorReporter_H

The class implementation file should look like the following. We only need to create one instance of this class, so we create a static instance called theErrorReporter and register it with the ROSE library to use for error reporting.

#include "stdafx.h"
#include "CustomErrorReporter.h"

// The only instance of this class
CCustomErrorReporter theErrorReporter;

CCustomErrorReporter::CCustomErrorReporter() : RoseErrorReporter()
{ 
    // Register this to use as the error reporter.
    ROSE.error_reporter(this);
}

void CCustomErrorReporter::do_report(
    char * pfx, RoseError* err, va_list ap
    )
{
    // Save the last error code in case anyone asks.
    f_errcode = err->f_errcode;

    // You might do different things based on the severity,
    // like print errors in red and status messages in green.
    switch (err->f_severity) {
    case ROSE_SEV_NULL:
    case ROSE_SEV_MESSAGE:
    case ROSE_SEV_WARNING:
    case ROSE_SEV_ERROR:
    case ROSE_SEV_FATAL:
	break;
    }

    // The RoseError object contains a message as a printf format
    // string, so we expand it out with the variable argument list
    // that we have been passed.  The pfx string may contain a file
    // and line number if we are reading a file.
    //
    char buf[1024]; // or use a CString

    strcpy(buf, pfx);
    vsprintf (buf + strlen(buf), err->f_message, ap);
    strcat(buf, "\r\n");

#ifdef USE_CONSOLE
    printf (buf);
#else
    // TODO: Do your own error-reporting for GUI applications.  This
    // may be called many times if you read a STEP file with errors in
    // it, so do not just call MessageBox (or else the user may have
    // to hit OK dozens of times).  Better to append the message to a
    // text window so that the user can see a running log of messages.
    // 
#endif

#ifdef _DEBUG
    TRACE(buf);
#endif
}

void CCustomErrorReporter::do_quit(int err_code)
{
    AfxMessageBox(
	"Fatal error. Can't proceed.", MB_OK | MB_ICONSTOP | MB_APPLMODAL
	);

    // TODO: Abort the application here if needed
}