Topic : MFC Introduction
Author : Prof Devi
Page : << Previous 3  Next >>
Go to page :


  perror("Unable to init MFC\n");  return 1; }


   Person a("Jill"),b("Hill","123 Willow","123-45678");

#ifdef _DEBUG
   a.Dump(afxDump);      // Call Dump Directly
   b.Dump(afxDump);      // Call Dump Directly
#endif _DEBUG

   return 0;
   }



As the library came into existance before RTTI, the MFC library has its own run time type identification.  To use run time type identication, you need to declare two macros: DECLARE_DYNAMIC and IMPLEMENT_DYNAMIC.  The DECLARE_DYNAMIC takes a single argument of the name of the current class.   It is placed inside the declaration of any class derived from CObject.   The IMPLEMENT_DYNAMIC takes two arguments.  The first is the name of the class,and  the second is the name of the base class that the current class is derived from.  Place the IMPLEMENT_DYNAMIC macro in the implementation file which contains the definition of the methods of the class.  Box #4 and Box #5 contain a declaration and implementation of a class called counter.

// Box #4: DECLARE_DYNAMIC Example
// File Name: Counter.h
#ifndef Counter_h
#define Counter_h

#include <afxwin.h>

class Counter:public CObject
   {
   int mCounter;
   public:
   Counter();
   ~Counter();
   void Inc(void);
   int GetCount(void) const;

   DECLARE_DYNAMIC(Counter);
   };


#endif Counter_h



// Box #5 : IMPLEMENT_DYNAMIC Example
// File Name: Counter.cpp

#include <afxwin.h>
#include "Counter.h"

/////////////////////////////
IMPLEMENT_DYNAMIC(Counter,CObject);

/////////////////////////////
Counter::Counter()
   { mCounter=0; }

/////////////////////////////
Counter::~Counter()
   { }

/////////////////////////////
void Counter::Inc(void)
   {  ++mCounter; }

/////////////////////////////
int Counter::GetCount(void) const
   { return mCounter; }




Once DECLARE_DYNAMIC and IMPLEMENT_DYNAMIC have been declared, you can get the run time type information by using either the GetRuntimeClass method or the macro RUNTIME_CLASS.   The GetRuntimeClass method is a member method of all objects that are of type CObject.  The RUNTIME_CLASS is a macro applicable only to class names.  Both the method and macro return a pointer to a CRuntimeClass instance.  A CRuntimeClass object holds the type information of the class or object.  The CRuntimeClass has several methods and attributes, but, the most relevant ones for type identification are the method BOOL IsDerivedFrom(const CRuntimeClass *other) and the attribute const char *m_lpszClassName.  The IsDerivedFrom is used to compare to check if the current object or class is of another type (specified by the CRuntimeClass).   The m_lpszClassName returns the name of the class that the CRuntimeClass holds type information for.  The Box #6 program demonstrates the usage of these methods.

// Box #6: MFC Run time Type Identification

#include <afxwin.h>
#include <stdio.h>
#include "Counter.h"

void PrintClassName(const CObject *obj)
   {   //Use Object method GetRuntimeClass
   printf("Class Information: %s\n",obj->GetRuntimeClass()->m_lpszClassName);
   }


//////////////////////////////////////
int main(void)
   {
   if(AfxWinInit(::GetModuleHandle(NULL),NULL,::GetCommandLine(),0)==FALSE)
      {  perror("Unable to init MFC\n");  return 1; }


   CRuntimeClass *typeinfo=RUNTIME_CLASS(Counter);

   printf("Class Name: %s\n",typeinfo->m_lpszClassName);

      // Check if Counter is derived from the base class CObject
   if(typeinfo->IsDerivedFrom(RUNTIME_CLASS(CObject))==TRUE)
      {
      printf("Class Counter is derived from class CObject\n");  //
      }
   

      // Check if Counter is derived from another MFC Class called CWnd
   if(typeinfo->IsDerivedFrom(RUNTIME_CLASS(CWnd))==TRUE)
      {  printf("Class Counter is derived from class CWnd\n");  }
   else
      {  printf("Class Counter is not derived from class CWnd\n");  }

   
   CWnd a;  Counter b;

   PrintClassName(&a);
   PrintClassName(&b);
   


   return 0;
   }



Although the implementation of DECLARE_DYNAMIC and IMPLEMENT_DYNAMIC is very interesting, these notes do not explain them.  Knowing the expansion does not help in using the macros.  If you are interested, you can quickly search for the definitions of the macros in your code through the browse database.




Exceptions


Since version 1.0, the MFC class library has had exception handling done through special macros(TRY, CATCH, AND_CATCH and END_CATCH).   You may still use these macros in MFC for exception handling, but it would be better to use the standard C++ exception handling keywords instead.  Starting from MFC 3.0, you could use regular C++ for exception handling.  These notes assume that you are using standard C++ exception handling. All MFC classes throw exceptions in terms of pointers to instances of classes that are derived from the CException class.  The CException is an abstract class, and as such, it cannot be instantiated.  Be careful when you read the documentation.  The documentation is meant to be read as if you are using MFC Exception Macros.  If the documentation states that the a MFC function throws an exception of CException, your regular C++ exception handler should handle the  pointer to CException.   If the documentation states the the function may through a CFileException, then your handler should for either a pointer to CFileException or a pointer to CException(ie all classes are derived from CException).  The Box #1 program demonstrates the correct coding for handling an exception of type CException.  Notice that all the catch statements for MFC exceptions are pointers.

// Box #1: Exception

   try  // Try Some Code
      {
      // Assume that the MFC code here may throw a CFileException
      // or a CException.  The Non MFC code may throw any exception
      }
   catch(CFileException *ex) // CFileException is derived from CException
      {
      // Handle CFile Exception!!
      }
   catch(CException *ex) // Catch all other CException derived exceptions
      {
      // Handle all other CExceptions here!!
      }
   catch(...)
      {
      // Catch all Non MFC Exceptions here!!
      }



When you get an  reference to an object in your handler, you may be required to destroy the exception object  If the exception object has been created on the heap, then you need to use delete to destroy it.  If you do not, you will get a memory leak.  But, if the exception object was created on the stack, then calling delete may crash your application.  To ease this tricky coding, all exception objects have a method called virtual void Delete(void).  This method will automatically call delete this, if the exception object was created on the heap.  Otherwise, if the object was created on the stack, then the Delete method does nothing.  The Box #2 code fragment is the same as Box #1, except that it has the correct code for deletion of the exception objects.

// Box #2: Deletion of MFC Exception Objects

   try  // Try Some Code
      {
      // Assume that the MFC code here may throw a CFileException
      // or a CException.  The Non MFC code may throw any exception
      }
   catch(CFileException *ex) // CFileException is derived from CException
      {
      // Handle CFile Exception!!
      ex->Delete(); // Will delete exception object correctly
      }
   catch(CException *ex) // Catch all other CException derived exceptions
      {
      // Handle all other CExceptions here!!
      ex->Delete(); // Will delete exception object correctly
      }
   catch(...)
      {
      // Catch all Non MFC Exceptions here!!
      }



As the CException object is derived from CObject, you can catch all MFC Exceptions using a single catch for CException *.   You can then use the MFC Run Time Type Identification(RTTI) to find out what the derived exception really is.  

Other than the Delete method, The CException has two more useful methods.  The first method is virtual void ReportError(UINT type=MB_OK,UINT MesgId=0).  This method will pop up a message box displaying the exception error.  The first argument type is a constant that specifies the selected style that the message box is displayed as. This argument take the same values as that of the standard Win32 MessageBox style argument.   The second argument for ReportError is for a string resource identifier of a string that will displayed when no error information is available for the exception.  The second useful method found in CException is  virtual BOOL GetErrorMessage(LPTSTR errorbuf, UINT errorbufsize,PUINT helpcontext=NULL).   This method will place the exception information string into a buffer specified by the first argument.  The second argument is the maximum size of the buffer.  The third argument is a returned context identifier of a help file which explains the error better.  This help context id is returned by reference.   The return value of the function is TRUE, if the function was successful. The Box #3 fragment extends the Box #2 code.

// Box #3: ReportError and GetErrorMessage

   try  // Try Some Code
      {
      // Assume that the MFC code here may throw a CFileException
      // or a CException.  The Non MFC code may throw any exception
      }
   catch(CFileException *ex)
      {
      ex->ReportError(); // Display a Message Box with the error
      ex->Delete();
      }
   catch(CException *ex) // Catch all other CException derived exceptions
      {  char info[300];
      if(ex->GetErrorMessage(info,300)==TRUE)  // Get Error and place string into info
         {  printf("Error: %s\n",info); }
      ex->Delete(); // Will delete exception object correctly
      }
   catch(...)
      {
      // Catch all Non MFC Exceptions here!!
      }






Dynamic Object Creation


Instances of classes that are derived from CObject can be created from the type information class CRuntimeClass.  To use this feature, you need to use the special macros DECLARE_DYNCREATE and IMPLEMENT_DYNCREATE.   These macros have to used in exactly the same way as were the macros DECLARE_DYNAMIC and  IMPLEMENT_DYNAMIC.  When you use DECLARE_DYNCREATE and IMPLEMENT_DYNCREATE, you can use the CRuntimeClass method CObject *CreateObject(void) to create a new instance of the typed class.  Although it does not seem very useful in our code,this dynamic creation is used extensively in the MFC Document-View architecture.  Compounded with the new object creation ability, the new macros give the same Run Time Type Identification abilities found in the DECLARE_DYNAMIC and IMPLEMENT_DYNAMIC macros.  If you do use DECLARE_DYNCREATE and IMPLEMENT_DYNCREATEB, you can no longer use the macros DECLARE_DYNAMIC and IMPLEMENT_DYNAMIC for the same class.

The arguments for the DECLARE_DYNCREATE and IMPLEMENT_DYNCREATE are the same as DECLARE_DYNAMIC and IMPLEMENT_DYNAMIC.   The DECLARE_DYNCREATE takes a single argument of the name of the class.  The IMPLEMENT_DYNCREATE takes two arguments of name of the current class and the name

Page : << Previous 3  Next >>