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