Click here to Skip to main content
15,885,309 members
Articles / Desktop Programming / MFC

Writing MFC Console Applications

Rate me:
Please Sign up or sign in to vote.
3.00/5 (3 votes)
5 Mar 2009CPOL4 min read 40.2K   11   2
An article to shed light on the basics of MFC.

Introduction

Stated loosely, writing MFC console applications help us to better gain experience with the MFC utility classes and the C++ class concepts in a familiar setting. Rather than creating a dialog-based, SDI, or an MDI application, this paper will focus on the C++ constructs that are used in MFC. The reason for this is that if we were to truly explain how windows are created in MFC, the reader’s focus could leave the “class” and look out the window. A Microsoft Foundation Classes (MFC) window is a hybrid of C++ and Windows API calls. In effect, an MFC window gives you a C++ wrapper to much (but not all) of the Windows API, but an MFC window does not have any more direct control over a window object than you do in the API world. That is, if you can’t do something in the API world, then you can’t do it in the MFC world. Creating an MFC window can be tricky, as you first create an instance of CWnd to then call a member function of CWnd, which calls CreateWindow() in the API. CreateWindow() is a function contained in user32.dll. The returned window handle (which is just an indirect pointer to the window object) is stored in the CWnd member variable m_hWnd. But, take care to note that because qindows are created in memory that is constantly being churned, its address may constantly change. Therefore, a window handle, instead of pointing directly to the window object, points to another pointer that keeps track of the window object location. So far, this sounds complicated and unnecessary, but there is a strong logic to it. In order to shed light on how to use MFC, I will develop a simple console program by writing a simple class called the CIndicator.

An indicator shows the status of something. When we are told that a flag is set, then we understand that a flag is really an algorithm to indicate the status of an operation, as indicated by a bit setting. Indicators can be gauges, thermometers, speedometers, and so on. The minimum and maximum values on these various types of indicators can therefore vary widely. A circular gauge such as a direction indicator would have values that may vary from 0 to 360; for an odometer from 0.0 to 99,999.9. So let’s try a fuel gauge. This will require an Indicator.h file that not only contains the predefined functions and so forth but also acts as an interface for the implementation of the CIndicator class.

The Default Constructor and Default Parameters

By definition, the default constructor in the interface (header) file is the constructor that is to be invoked when no parameters are specified. Normally, the declaration of the default constructor could look like this:

public:
CIndicator::CIndicator();

// then the definition in the implementation might look like this:
CIndicator::CIndicator()
: mMaxLimit(100.0),  // the line that begins with
                     // the colon is the initialization list
mCurValue(0.0),
mMinLimit(0.0)
{
}

MFC makes use of default parameters in many of its functions, so it’s a good idea to try and see how we might use the default parameters in the constructor:

CIndicator  FuelGauge();
CIndicator  DirectionIndicator(359.9)
CIndicator FuelGauge(25.0, 10.0)
CIndicator TempGauge(225.0, 50.0, 31.0)

If an invocation of a function does not have the full complement of parameters, the default parameter values starting from the right side are used. Here is the Indicator.h header file:

// Indicator.h: interface for the CIndicator class.
#include <afxwin.h >
#include <iostream >   // notice that we do not use the .h file extension
using namespace std; // we are using the Standard Template Library
  
#if !defined(AFX_INDICATOR_H__9BA950E2_5320_11D3_B00B_ECA1B8D52B36__INCLUDED_)
#define AFX_INDICATOR_H__9BA950E2_5320_11D3_B00B_ECA1B8D52B36__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

class CIndicator  
{
private:
    double   mMaxLimit;
    double   mCurValue;
    double   mMinLimit;
public:
             CIndicator(double MaxLimit   = 100.0,
                      double startValue = 0.0,
                      double MinLimit   = 0.0);
            ~CIndicator();
             CIndicator (const CIndicator& ind);
    void     Display   (CString s) const;
    double   SetValue  (double value);
    double   GetValue  () const;
    BOOL     IncreaseBy(double value = 1.0);
    BOOL     DecreaseBy(double value = 1.0);
};

#endif // !defined(AFX_INDICATOR_H__9BA950E2_5320_11D3_B00B_ECA1B8D52B36__INCLUDED_)

And, here is the Indicator.cpp file, which is the implementation of the CIndicator class. Note that this is an important distinction in COM programming. An implementation of an interface does not necessarily mean the implementation of a class.

// Indicator.cpp: implementation of the CIndicator class.


#include "Indicator.h"

// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CIndicator::CIndicator(double MaxLimit,
                       double startValue,
                       double MinLimit)
: mMaxLimit(MaxLimit), 
  mCurValue(startValue),
  mMinLimit (MinLimit)
{ 
  // If out of sequence, set to default.
  if ((mMinLimit <= mCurValue) && (mCurValue <= mMaxLimit))
  {  // Ok. in sequence
  }
  else
  {
    mMaxLimit = 100.0;
    mCurValue = 0.0;
    mMinLimit = 0.0;
  }
  cout << "\n     Construct Indicator";
 }

CIndicator::CIndicator (const CIndicator& ind)
: mMaxLimit (ind.mMaxLimit),
  mCurValue (ind.mCurValue),
  mMinLimit (ind.mMinLimit)
{
  cout << "\n     Copy construct Indicator";
}


CIndicator::~CIndicator()
{ cout << "\n     Destruct Indicator"; }

void CIndicator::Display(CString prefix) const
{ CString s;
  s.Format (s+" Current value:%4.1f  Max. limit:%4.1f"
            "  Min. limit: %4.1f",
               mCurValue, mMaxLimit, mMinLimit);
  cout << prefix << s;
}

double CIndicator::SetValue (double value)
{ double temp = mCurValue;
  mCurValue = value;
  return  temp;  // previous CurValue
}

double CIndicator::GetValue() const
{ return mCurValue; }

BOOL CIndicator::IncreaseBy (double value)
{ if (mMaxLimit < mCurValue + value)
    return FALSE;
  else
  { mCurValue = mCurValue + value;
    return TRUE;
  }
}

BOOL CIndicator::DecreaseBy (double value)
{ if (mCurValue + value < mMinLimit)
    return FALSE;
  else
  { mCurValue = mCurValue - value;
    return TRUE;
  }
}

Here is the main.cpp program that builds the solution. Notice that I use the getc(stdin) function on the bottom to force the console window to remain after running this project. In fact, to build using Visual Studio, you must first start a new C++ Win32 Console project that has the “empty project” check box checked in the application settings box. Then, go to the configuration properties and ensure that “Use MFC as a Shared DLL” is checked. The code already references the Standard Template Library, but now the project can behave and be built as an MFC project. From there, add the three new items: the header file, and the two source code files.

#include <afxwin.h>
#include <iostream >
#include "Indicator.h"

void DoSomething (CIndicator x)
{
  x.Display("\nIn DoSomething:");
  return;
}

int main()
{
  CString prefix("\nFuel gauge:");

  CIndicator FuelGauge(10.0, 3.0);
  FuelGauge.Display(prefix);

  FuelGauge.IncreaseBy(2.0);
  FuelGauge.Display(prefix);

  FuelGauge.DecreaseBy(1.4);
  FuelGauge.Display(prefix);

  // Test two things:
  CIndicator Temp;
  Temp = FuelGauge;        // Assignment operator
  Temp.Display("\nValue of Temp: ");

  DoSomething (FuelGauge); // Copy constructor
  getc(stdin);
  return 0;
}

The header file and the two source files will do it in an empty Win32 console project that has the "Use MFC in a Shared DLL" property set in the project's settings. MFC is a powerful framework that can get complicated, and sometimes can leave any application or business logic to the programmer. It is different from the good old .NET Framework, but if we take the time to learn it, we can use MFC when it is actually the best framework for certain situations.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer Monroe Community
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
Questionreally? Pin
dmihailescu18-Jun-10 4:55
dmihailescu18-Jun-10 4:55 
GeneralHmmmm... Pin
Maximilien6-Mar-09 1:05
Maximilien6-Mar-09 1:05 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.