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
};