Wrapper-iterator po snapshocie z CreateToolhelp32Snapshot

2

Nie mam bloga, więc wrzucaj tutaj.
Od jakiegoś czasu chodzi mi po głowie stworzenie wrapperów C++ na niektóre z systemowych API Windows.
Na próbę stworzyłem coś ułatwiającego korzystanie z Heap32ListNext / Module32Next / Process32Next / Thread32Next.

Kodu jest niewiele, ale spędziłem trochę czasu zastanawiając się jak to w ogóle powinno działać. Jak mogą (i jak powinny) wyglądać iteratory - co nie jest to z początku wcale takie oczywiste: np. co to znaczy, że dwa iteratory są sobie równe albo jak powinna zachowywać się jego kopia. Wszystko to starałem się zrealizować przy jak najmniejszej ilości logiki w samym kodzie.

Pytania, komentarze i krytyka jak najbardziej wskazane.

Wymaga boosta (tylko nagłówki). Sprawdzone w VS2012 i GCC 4.7.2. Szkoda, że ten pierwszy nie ma constexpr, bo obyłoby się bez kilku zbędnych linijek. Szkoda, że mingw jest w tyle z implementacją standardowego liba i musiałem zrezygnować z std::error_code.

Sposób użycia:

for(auto& process : Snapshot::snapshot()->range<SnapshotProcessIterator>())
    std::wcout << process.szExeFile << L"\n";

"Snapshot.hpp"

#ifndef __SNAPSHOT_ITERATOR_HPP__
#define __SNAPSHOT_ITERATOR_HPP__

#include <memory>
#include <stdexcept>

#include <boost/iterator/iterator_facade.hpp>
#include <boost/range/iterator_range.hpp>
#include <boost/noncopyable.hpp>

#include <Windows.h>
#include <TlHelp32.h>

namespace rev { namespace system {

template<typename T>
struct SnapshotFunc
{
    typedef BOOL (WINAPI *Func)(HANDLE, T*);
};

template<DWORD TFlag, typename T, typename SnapshotFunc<T>::Func TFuncFirst, typename SnapshotFunc<T>::Func TFuncNext>
struct SnapshotTraits
{
    typedef T Object;
    typedef typename SnapshotFunc<Object>::Func Func;

    static const DWORD flag = TFlag;
    static const Func funcFirst;
    static const Func funcNext;
};

template<DWORD TFlag, typename T, typename SnapshotFunc<T>::Func TFuncFirst, typename SnapshotFunc<T>::Func TFuncNext>
const typename SnapshotFunc<T>::Func SnapshotTraits<TFlag, T, TFuncFirst, TFuncNext>::funcFirst = TFuncFirst;

template<DWORD TFlag, typename T, typename SnapshotFunc<T>::Func TFuncFirst, typename SnapshotFunc<T>::Func TFuncNext>
const typename SnapshotFunc<T>::Func SnapshotTraits<TFlag, T, TFuncFirst, TFuncNext>::funcNext = TFuncNext;

typedef SnapshotTraits<TH32CS_SNAPHEAPLIST, HEAPLIST32,     Heap32ListFirst, Heap32ListNext> SnapshotHeaplistTraits;
typedef SnapshotTraits<TH32CS_SNAPMODULE,   MODULEENTRY32,  Module32First,   Module32Next  > SnapshotModuleTraits;
typedef SnapshotTraits<TH32CS_SNAPPROCESS,  PROCESSENTRY32, Process32First,  Process32Next > SnapshotProcessTraits;
typedef SnapshotTraits<TH32CS_SNAPTHREAD,   THREADENTRY32,  Thread32First,   Thread32Next  > SnapshotThreadTraits;

class Snapshot : boost::noncopyable, public std::enable_shared_from_this<Snapshot>
{
    template <typename TTraits>
    friend class SnapshotIterator;

    HANDLE handle;
    DWORD  flags;

    Snapshot(int processId, DWORD flags) : flags(flags) {
        handle = CreateToolhelp32Snapshot(flags, processId);

        if(!handle)
            throw std::runtime_error("Cannot create snapshot");
    }

public:
    static std::shared_ptr<Snapshot> snapshot() {
        return snapshot(0, TH32CS_SNAPPROCESS);
    }

    static std::shared_ptr<Snapshot> snapshot(int processId, DWORD flags = TH32CS_SNAPALL) {
        return std::shared_ptr<Snapshot>(new Snapshot(processId, flags));
    }

    ~Snapshot() {
        CloseHandle(handle);
    }

    template <typename TIterator>
    TIterator begin() {
        if((flags & TIterator::Traits::flag) == 0)
            throw std::runtime_error("Snapshot doesnt have data for specified iterator type");

        return TIterator(shared_from_this());
    }

    template <typename TIterator>
    TIterator end() {
        return TIterator();
    }

    template <typename TIterator>
    boost::iterator_range<TIterator> range() {
        return boost::make_iterator_range(begin<TIterator>(), end<TIterator>());
    }
};

template <typename TTraits>
class SnapshotIterator :
    public boost::iterator_facade
    <
        SnapshotIterator<TTraits>,
        const typename TTraits::Object,
        boost::single_pass_traversal_tag
    >
{
    friend class Snapshot;
    friend class boost::iterator_core_access;

    typedef typename TTraits::Object Object;
    
    std::shared_ptr<Snapshot> snapshot;
    Object object;

    const Object& dereference() const {
        return object;
    }

    void increment() {
        if(snapshot)
            advance(TTraits::funcNext);
    }

    void advance(typename TTraits::Func func) {
        object.dwSize = sizeof(Object);

        if(!func(snapshot->handle, &object)) {
            snapshot.reset();

            if(GetLastError() != ERROR_NO_MORE_FILES)
                throw std::runtime_error("Cannot get next value");
        }
    }

    bool equal(const SnapshotIterator<TTraits>& other) const {
        return snapshot == other.snapshot;
    }

    SnapshotIterator(std::shared_ptr<Snapshot> snapshot) : snapshot(snapshot) {
        advance(TTraits::funcFirst);
    }

public:
    typedef TTraits Traits;

    SnapshotIterator() { }
};

typedef SnapshotIterator<SnapshotHeaplistTraits> SnapshotHeaplistIterator;
typedef SnapshotIterator<SnapshotModuleTraits  > SnapshotModuleIterator;
typedef SnapshotIterator<SnapshotProcessTraits > SnapshotProcessIterator;
typedef SnapshotIterator<SnapshotThreadTraits  > SnapshotThreadIterator;

} } // namespace rev { namespace system {

#endif // ifdef __SNAPSHOT_ITERATOR_HPP__
0
  1. Strasznie duży narzut kodu. Nie dałoby się tego krócej zapisać? ;-)

  2. Rozumiem że każda iteracja to nowy Snapshot. To może jednak oznaczać, że iteracja po procesach przedstawia inny stan systemu niż iteracja po wątkach (na przykład).
    Przydałoby się raczej coś takiego:

SystemSnapshot snapshot; // tutaj jest CreateToolhelp32Snapshot(TH32CS_SNAPALL)
for(const auto& process : snapshot.getProcessRange())

1 użytkowników online, w tym zalogowanych: 0, gości: 1