Tutorials

 

Home
About
How it Works
Documentation
Tutorials

Examples

Links
Contact

 

 

Menu of tutorials

Tutorial 1:   The Simplest Window
Tutorial 2:   Using Classes and Inheritance
Tutorial 3:   Using Messages to Create a Scribble Window
Tutorial 4:   Repainting the Window
Tutorial 5:   Wrapping a Frame around our Scribble Window
Tutorial 6:   Customising Window Creation
Tutorial 7:   Customising the ToolBar
Tutorial 8:   Loading and Saving Files
Tutorial 9:   Printing
Tutorial 10: Print Previewing
Tutorial 11: Finishing Touches

Tutorial 11:  Finishing Touches

In this final tutorial we will show how to store the application's settings in the registry, implement tracing and handle exceptions.  The code for this final tutorial is the scribble demo application.

Saving the Window Position

Users will expect modern applications to save their settings, such as the position and size of the frame window.  These settings are stored in the registry. LoadRegistrySettings is used to set the name of the registry key.  Typically the name takes the form of  "CompanyName\\ApplicationName" as demonstrated below.

HWND CMainFrame::Create(HWND parent)
{
  // Set the registry key name, and load the initial window position.
  // Use a registry key name like "CompanyName\\Application".
  LoadRegistrySettings(_T("Win32++\\Scribble Sample"));

  return CFrame::Create(parent);
}

If a registry key name has been set using the LoadRegistrySetting function, the registry settings will be loaded from the registry when the application starts, and stored in the registry when the application ends. Override the LoadRegistrySettings and SaveRegistrySettings functions in CMainFrame if you wish to store other settings in the registry.

Saving the Most Recently Used (MRU) List

Applications that load and store files typically allow the user to choose from a list of recently used file names to load from. This MRU list is also stored in the registry. To add this capability to your Win32++ application, use the LoadRegistryMRUSettings function to specify the number of entries in the MRU list (up to a maximum of 4) as shown below.

HWND CMainFrame::Create(HWND parent)
{
  // Set the registry key name, and load the initial window position.
  // Use a registry key name like "CompanyName\\Application".
  LoadRegistrySettings(_T("Win32++\\Scribble Sample"));

  // Load the settings from the registry with 4 MRU entries.
  LoadRegistryMRUSettings(4);

  return CFrame::Create(parent);
}

To see the MRU entries listed in the menu, add "Recent Files" menu item to the menu definitions in Resource.rc as follows:

MENUITEM "Recent Files",                IDW_FILE_MRU_FILE1, GRAYED

MRU entries are added with AddMRUEntry, and removed with RemoveMRUEntry.

Command Line Arguments

Command line arguments are passed to the program when it is started. The GetCommandLine function is used to retrieve the the command line string. The CommandLineToArgvW can parse this string and convert it to an array of LPWSTR pointers.

The following code demonstrates how to load a file when the filename is supplied as a command line argument.

void CMainFrame::OnInitialUpdate()
{
  // Here we process the command line arguments, and automatically load a file if one is specified.
  // GetCommandLineArgs retrieves our command line arguments and stores them in a vector of CString.

  std::vector<CString> args = GetCommandLineArgs();
  // The second argument (if any) contains our file name.
  if (args.size() > 1)
  {
    GetDoc().FileOpen(args[1]);
  }
}

This allows the application to start and load the file when it is selected and opened from within Windows Explorer. This also provides drag and drop support for running the application by dropping a data file on the program's icon or .exe file.

Debugging your Application with Tracing

One important debugging technique is to trace what is happening with the application while it is running.  The tracing allows you to display messages or the contents of variables in the output pane of Visual Studio (or whichever Integrated Development Environment you use).

In order to take advantage of this, you will need do the following

  • Run your application in debug mode (see below)
  • Use the TRACE function to display the messages.

To run the application in debug mode, you need to have the _DEBUG variable defined.  Microsoft's Visual Studio products define this variable for you when you compile in debug mode.  For other compilers you should use the appropriate compiler option to set this variable.  The source code for this example, as well as the samples in the download section, include project files for debug modes for both DevC++ and CodeBlocks  to make this task easier.

If you are not running in debug mode, TRACE statements have no effect.  You can leave them in place for Release mode if you wish.

In the sample code for this section we add tracing to our scribble application to display the position of the mouse for the lines we draw.

This is the code added to CView::OnMouseMove for tracing.

CString str;
str.Format( _T("Draw Point: %hd, %hd\n"), GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) );
TRACE(str);

Handling Exceptions

When an exception is thrown, it should be handled, otherwise our program will terminate. A program is terminated whenever an exception isn't handled.

The Win32++ framework will throw exceptions when it can't perform certain tasks. Examples of tasks that can raise exceptions include:

  • Failure to create a window or dialog.
  • Failure to create a thread.
  • Failure to create a GDI resource such as a pen or brush.
  • Failure to select a GDI resource such as a bitmap into a device context.
  • Failure to open or read from a file.

We can use exception handling to warn of these problems and possibly allow our program to continue running. To handle all exceptions we should place a try/catch block in our program's entry point (WinMain or wWinMain). We also need and a try/catch block in the WndProc function for each CWnd class and the DialogProc for each CDialog class, even those that don't do special message handling.

The following code show how to display exceptions that might be raised when our program starts.

// WinMain is the program's entry point. The program starts here.
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
  try
  {
    // Start Win32++.
    CScribbleApp theApp;

    // Run the application.
    return theApp.Run();
  }

  // Catch all unhandled CException types.
  catch (const CException& e)
  {
    // Display the exception and continue.
    CString str1;
    str1 << e.GetText() << _T("\n") << e.GetErrorString();
    CString str2;
    str2 << "Error: " << e.what();
    ::MessageBox(NULL, str1, str2, MB_ICONERROR);
  }

  // Catch all unhandled std::exception types.
  catch (const std::exception& e)
  {
    // Display the exception and continue.
    CString str1 = e.what();
    ::MessageBox(NULL, str1, _T("Error: std::exception"), MB_ICONERROR);
  }

  return -1;
}

The following code shows how to handle exceptions that might be raised when our program handles window messages.

// Handle the frame's messages.
LRESULT CMainFrame::WndProc(UINT msg, WPARAM wparam, LPARAM lparam)
{
  try
  {
    switch (msg)
    {
      case UWM_DROPFILE:          OnDropFile(wparam); break;
      case UWM_PREVIEWCLOSE:      OnPreviewClose();   break;
      case UWM_PRINTNOW:          OnPreviewPrint();   break;
      case UWM_PRINTSETUP:        OnPreviewSetup();   break;
    }

    return WndProcDefault(msg, wparam, lparam);
  }

  // Catch all unhandled CException types.
  catch (const CException& e)
  {
    // Display the exception and continue.
    CString str1;
    str1 << e.GetText() << _T("\n") << e.GetErrorString();
    CString str2;
    str2 << "Error: " << e.what();
    ::MessageBox(NULL, str1, str2, MB_ICONERROR);
  }

  // Catch all unhandled std::exception types.
  catch (const std::exception& e)
  {
    // Display the exception and continue.
    CString str1 = e.what();
    ::MessageBox(NULL, str1, _T("Error: std::exception"), MB_ICONERROR);
  }

  return 0;
}

Note that this code will also handle exceptions raised in functions called by WndProcDefault, such as OnCommand, OnDraw, OnNotify etc.

Code that is expected to raise exceptions should also have its own exception handling. Code that uses files, archives and printing can throw exceptions. The following code shows how we might handle exception when opening a file.

// Load the PlotPoint data from the file.
BOOL CMainFrame::OnFileOpen()
{
  CFileDialog fileDlg(TRUE, _T("dat"), NULL, OFN_FILEMUSTEXIST, _T("Scribble Files (*.dat)\0*.dat\0\0"));
  fileDlg.SetTitle(_T("Open File"));

  try
  {
    // Bring up the file open dialog retrieve the selected file name.
    if (fileDlg.DoModal(*this) == IDOK)
    {
      // Load the file
      LoadFile(fileDlg.GetPathName());
    }
  }

  catch (const CFileException &e)
  {
    // An exception occurred. Display the relevant information.
    MessageBox(e.GetErrorString(), e.GetText(), MB_ICONWARNING);

    m_view.GetAllPoints().clear();
  }

  return TRUE;
}

The source code for this tutorial is located within the Tutorial folder of the software available from SourceForge at http://sourceforge.net/projects/win32-framework.