Tutorials
|
|
Menu of tutorialsTutorial 1: The Simplest Window
Tutorial 11: Finishing TouchesIn 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 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 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 Command Line Arguments Command line arguments are passed to the program when it is started. The
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
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:
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. |