Listing 2: child_window_sequence class.

/* /////////////////////////////////////////////////////////////
 * Extracts from child_window_sequence_test.cpp
 * www:   http://www.synesis.com.au/winstl
 *        http://www.winstl.org/
 * Copyright (C) 2002, Synesis Software Pty Ltd.
 * (Licensed under the Synesis Software Standard Source License:
 *  http://www.synesis.com.au/licenses/ssssl.html)
 * ////////////////////////////////////////////////////////// */

/* Holds state of enumeration and relationship between fibers */
struct cws_fiber_data
{
  LPVOID  main_fiber, worker_fiber;
  HWND    hwndParent, hwndChild;

  cws_fiber_data()
    : main_fiber(0), worker_fiber(0)
    , hwndParent(0), hwndChild(0)
  {}
};
/* The iterator class */
class child_window_sequence_const_iterator
{
public:
  typedef child_window_sequence_const_iterator  class_type;
// Construction
public:
  child_window_sequence_const_iterator(cws_fiber_data *data)
    : m_data(data)
  {}
  // Only Input-Iterator Concept supported, so  implement move semantics.
  child_window_sequence_const_iterator(class_type &rhs);
  class_type &operator =(class_type &);
  ~child_window_sequence_const_iterator()
  {
    // Clean up here, in case enumeration not completed.
    if(m_data != 0)
    {
      ::DeleteFiber(m_data->worker_fiber);
      delete m_data;
    }
  }
  HWND operator *() const
  {
    return m_data->hwndChild;
  }
  child_window_sequence_const_iterator &operator ++()
  {
    // Switch to the enumeration fiber
    ::SwitchToFiber(m_data->worker_fiber);
    // Having switched back, hwndChild will contain next
    // child, or NULL, in which case enumeration complete.
    if(m_data->hwndChild == 0)
    {
      ::DeleteFiber(m_data->worker_fiber);
      delete m_data;
      m_data = 0;
    }
    return *this;
  }
// Comparison
public:
  bool operator ==(class_type const &rhs) const
  {
    // == if both NULL, or both non-NULL and have same child.
    return  ( m_data == 0 && 
              rhs.m_data == 0) || 
            ( m_data != 0 && 
              rhs.m_data != 0 && 
              m_data->hwndChild == rhs.m_data->hwndChild);
  }
  bool operator !=(class_type const &rhs) const;
// Members
protected:
  cws_fiber_data  *m_data;
};
class child_window_sequence
{
public:
  typedef child_window_sequence                 class_type;
  typedef child_window_sequence_const_iterator  const_iterator;
// Construction
public:
  child_window_sequence(HWND hwnd)
    : m_fiberMain(GetMainFiber()), m_hwnd(hwnd)
  {}
// Iteration
public:
  const_iterator begin() const
  {
    // (i) Create a shared area
    cws_fiber_data  *data = new cws_fiber_data;
    // (ii) Create the callback fiber
    data->main_fiber   = m_fiberMain;
    data->worker_fiber = ::CreateFiber(0, WorkerProc, data);
    data->hwndParent   = m_hwnd;
    // (iii) Switch to fiber and start the enumeration
    ::SwitchToFiber(data->worker_fiber);
    // (iv) set the first window and the fiber data into the iterator
    if(data->hwndChild == 0)
    {
      ::DeleteFiber(data->worker_fiber);
      delete data;
      data = 0;
    }
    return child_window_sequence_const_iterator(data);
  }
  const_iterator end() const
  {
    return const_iterator(0);
  }
// Implementation
protected:
  // This reduces fragility of Fibers somewhat, by ensuring all
  // instances of sequence class share a particular main fiber.
  static LPVOID GetMainFiber()
  {
    static LPVOID s_fiberMain = ::ConvertThreadToFiber(0);
    return s_fiberMain;
  }
  static void WINAPI WorkerProc(PVOID lpParam)
  {
    // This makes enumeration call, then scopes enumeration via fiber 
    // exchanges between this fiber and main fiber (where iterators are used)
    cws_fiber_data  &data = *(cws_fiber_data*)lpParam;
    // Start the enumeration
    ::EnumChildWindows(data.hwndParent, EnumChildProc, (LPARAM)lpParam);
    // End enumeration by setting child to NULL ...
    data.hwndChild = 0;
    // ... and switch back for the last time (main will delete this fiber).
    ::SwitchToFiber(data.main_fiber);
  }
  static BOOL CALLBACK EnumChildProc(HWND hwnd, LPARAM lParam)
  {
    cws_fiber_data  &data = *(cws_fiber_data*)lParam;
    // Place this result in the data ...
    data.hwndChild = hwnd;
    // ... and switch back to main fiber
    ::SwitchToFiber(data.main_fiber);
    return true;
  }
// Members
protected:
  LPVOID  m_fiberMain;  // The main (/ ctor caller) fiber
  HWND    m_hwnd;       // Window whose children are enumerated
};