Figure 3 CPrimeCalculator
Prime.h
#pragma once

#include <vector>
#include <list>
using namespace std;

/////////////////
// Interface for prime events. Not an interface in the .NET or COM sense,
// just an ordinary old abstract base class. Clients that handle prime 
// events must derive from this and implement the virtual functions.
//
class IPrimeEvents {
protected:
   IPrimeEvents() { }
   virtual ~IPrimeEvents() { };
public:
   virtual void OnProgress(UINT nPrimes) = 0;
   virtual void OnDone() = 0;
};

// List (vector) of prime numbers.
typedef vector<UINT> PRIMELIST;

//////////////////////////////////////////////////////////////////
// Class to calculate 1st n prime numbers.
//
class CPrimeCalculator {
protected:
   list<IPrimeEvents*> m_clients; // list of client objects to notify
   PRIMELIST m_primes;            // list (vector) of primes numbers found

   // Helpers
   BOOL IsPrime(UINT p);          // is prime?
   void AddPrime(UINT p)          // add prime to list
   {
      m_primes.push_back(p);
   }

   // Raise events
   void NotifyProgress(UINT nFound);
   void NotifyDone();

public:
   CPrimeCalculator() { }
   ~CPrimeCalculator() { }

   // Find 1st n prime numbers.
   UINT FindPrimes(UINT n);

   // Get number of primes found so far.
   UINT GetNumberOfPrimes() {
      return (UINT)m_primes.size();
   }

   // Get all the primes found.
   const PRIMELIST GetPrimes() {
      return m_primes;
   }

   // Register client to receive events
   void Register(IPrimeEvents* client)
   {
      m_clients.push_back(client);
   }

   // Unregister client from receiving events.
   void Unregister(IPrimeEvents* client)
   {
      m_clients.remove(client);
   }
};
Prime.cpp
#include "StdAfx.h"
#include "Prime.h"

const NOTIFYINCR = 100;                 // notify every 100 tries
const FIRSTPRIME = 2;                   // important!

//////////////////
// Find 1st n prime numbers.
//
UINT CPrimeCalculator::FindPrimes(UINT nFind)
{
   m_primes.reserve(nFind);
   for (UINT p=FIRSTPRIME; GetNumberOfPrimes()<nFind; p++) {
      if (IsPrime(p)) {                 // if prime:
         AddPrime(p);
      }
      if (p % NOTIFYINCR == 0) {
         // report progress by raising event
         NotifyProgress(GetNumberOfPrimes()); 
         Sleep(30); // simulate work; otherwise too fast!
      }
   }
   NotifyDone(); 
   return GetNumberOfPrimes();
}

//////////////////
// Helper fn to test if number p is prime:
// Try dividing by all primes found so far, up to SQRT(p).
//
BOOL CPrimeCalculator::IsPrime(UINT p)
{
   PRIMELIST::iterator it;
   for (it=m_primes.begin(); it!=m_primes.end(); it++) {
      UINT factor = *it;
      if (p%factor == 0) {              // if divisible:
         return FALSE;                  // ..it's not prime
      }
      if (factor*factor > p)            // if no more primes to try:
         return TRUE;                   // ..number must be prime
   }
   // Should only get here when p==2 (list is empty).
   ASSERT(p==2);
   return TRUE;
}

//////////////////
// Notify clients of progress so far.
//
void CPrimeCalculator::NotifyProgress(UINT nFound)
{
   list<IPrimeEvents*>::iterator it;
   for (it=m_clients.begin(); it!=m_clients.end(); it++) {
      (*it)->OnProgress(nFound);
   }
}

//////////////////
// Notify clients I'm done.
//
void CPrimeCalculator::NotifyDone()
{
   list<IPrimeEvents*>::iterator it;
   for (it=m_clients.begin(); it!=m_clients.end(); it++) {
      (*it)->OnDone();
   }
}

Figure 4 PrimeCalc.cpp
#include "stdafx.h"
#include "StatLink.h"
#include "resource.h"
#include "Prime.h"
#include "TraceWin.h"

const NFIND = 1000; // find first 1000 primes

//////////////////
// This class displays IPrimeEvents in the diagnostic (TRACE) stream. Used 
// to show that multiple objects can register for the same event stream.
//
class CTracePrimeEvents : public IPrimeEvents
{
public:
   void OnProgress(UINT nPrimes)
   {
      UNREFERENCED_PARAMETER(nPrimes);
      TRACE(_T(" PrimeCalc: Found %d primes.\n"), nPrimes);
   }
   virtual void OnDone()
   {
      TRACE(_T(" PrimeCalc: Done\n"));
   }
};

//////////////////
// Main dialog: implements IPrimveEvents to handle events from
// CPrimeCalculator.
//
class CMyDlg : public CDialog, public IPrimeEvents {
protected:
   .
   .
   CEdit m_wndFeedback;

   // IPrimeEvents handlers
   void OnProgress(UINT nPrimes);
   void OnDone();

   // message handler
   void CMyDlg::OnCompute()
};

//////////////////
// User pressed "Compute" button.
//
void CMyDlg::OnCompute()
{
   CWaitCursor wc;                      // display wait cursor

   m_wndFeedback.SetWindowText(_T("Calculating..."));

   // create prime calculator and hook events
   CPrimeCalculator pc;       // Prime calculator
   CTracePrimeEvents tn;      // TRACE notifier
   pc.Register(this);         // register myself for events
   pc.Register(&tn);          // .. and trace notifier too
   pc.FindPrimes(NFIND);      // find prime numbers

   // Build message showing prime numbers.
   CString msg,temp;
   int any = 0;
   PRIMELIST primes = pc.GetPrimes();
   PRIMELIST::iterator it;
   for (it=primes.begin(); it!=primes.end(); it++) {
      UINT p = *it;
      if (any)
         msg += _T(", ");
      else
         any++;
      temp.Format(_T("%d"),p);
      msg += temp;
   }

   // show results in feedback window
   m_wndFeedback.SetWindowText(msg);
}

//////////////////
// Event handler: Show progress.
//
void CMyDlg::OnProgress(UINT nPrimes)
{
   CString s;
   s.Format(_T(«Found %d primes so far...»), nPrimes);
   m_wndFeedback.SetWindowText(s);
   m_wndFeedback.UpdateWindow();
}

//////////////////
// Event handler: Beep when done.
//
void CMyDlg::OnDone() {
   MessageBeep(0);
}