Laden...

Review meiner MemoryObserve Klasse

Erstellt von Tayuki vor 8 Jahren Letzter Beitrag vor 8 Jahren 2.134 Views
Thema geschlossen
T
Tayuki Themenstarter:in
21 Beiträge seit 2010
vor 8 Jahren
Review meiner MemoryObserve Klasse

Hallo zusammen,

erstmal eine kurze Einleitung: Ich bin dabei, mich mit dem Auslesen von Memory-Werten zu beschäftigen. Dazu verwende ich die WinAPI. Jetzt möchte ich die Werte immer aktuell halten, also habe ich mir folgende Klasse geschrieben, die einen Wert, aus der übergebenden Adresse ausliest und per Event mitteilt. (Event noch nicht implementiert)

(Lese momentan Werte von Notepad aus, damit ich üben kann, da ich selbst noch in der Ausbildung (Anfang 2 Lehrjahr) bin und mich interessiert, wie ich Werte aus dem Memory lesen kann)

Also hier meine Klasse:


using MojitoProject.MemoryLib.Observe.Converter;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace MojitoProject.MemoryLib.Observe
{
    public sealed class MojitoMemoryObserver<T>
    {

        /// <summary>
        /// Der Task, der die Überwachung durchführt
        /// </summary>
        private Task _Task;

        /// <summary>
        /// Den CancellationTakenSource
        /// </summary>
        private CancellationTokenSource _CancellationTokenSource;

        /// <summary>
        /// CancellationToken
        /// </summary>
        private CancellationToken _CancellationToken;

        /// <summary>
        /// UpdateInterval
        /// </summary>
        private int _UpdateInterval = 0;

        /// <summary>
        /// MojitoMemory
        /// </summary>
        public MojitoMemory MojitoMemory { get; private set; }

        /// <summary>
        /// Gibt die Addresse zurück, die überwacht wird
        /// </summary>
        public IntPtr Address { get; private set; }

        /// <summary>
        /// Gibt die Offsets zurück
        /// </summary>
        public int[] Offsets { get; private set; }


        /// <summary>
        /// Gibt True zurück, wenn diese MemoryObserver bereits aktiv ist, andernfalls false
        /// </summary>
        public bool IsObserving { get; private set; }

        /// <summary>
        /// Gibt den Converter zurück
        /// </summary>
        public IMojitoObserveConverter<T> Converter { get; private set; }

        /// <summary>
        /// Gibt oder setzt das UpdateIterval (min 0)
        /// </summary>
        public int UpdateInterval
        {
            get { return _UpdateInterval; }
            set
            {
                if (value < 0)
                    _UpdateInterval = 0;
                else
                    _UpdateInterval = value;
            }
        }



        /// <summary>
        /// Erstellt eine neuen MemoryObserver, mit einen DefaultConverter
        /// </summary>
        /// <param name="mojitoMemory">MojitoMemory Instanz</param>
        /// <param name="address">Addresse</param>
        /// <param name="offsets">Offsets, kann null sein</param>
        /// <param name="updateInterval">Das Update Iterval</param>
        public MojitoMemoryObserver(MojitoMemory mojitoMemory, IntPtr address, int[] offsets, int updateInterval) : this(mojitoMemory, address, offsets, updateInterval, new MojitoDefaultObserveConverter<T>())
        {}

        /// <summary>
        /// Erstellt eine neuen MemoryObserver
        /// </summary>
        /// <param name="mojitoMemory">MojitoMemory Instanz</param>
        /// <param name="address">Addresse</param>
        /// <param name="offsets">Offsets, kann null sein</param>
        /// <param name="updateInterval">Das Update Iterval</param>
        /// <param name="converter"Ein Konverter, der benutzt wird, um die gelesen bytes in T zu konvertieren></param>
        public MojitoMemoryObserver(MojitoMemory mojitoMemory, IntPtr address, int[] offsets, int updateInterval,  IMojitoObserveConverter<T> converter)
        {
            if (mojitoMemory == null)
                throw new ArgumentNullException(nameof(mojitoMemory));

            if (converter == null)
                throw new ArgumentNullException(nameof(converter));

            if (address == null)
                throw new ArgumentNullException(nameof(address));

            if (address == IntPtr.Zero)
                throw new ArgumentException("The Address cannot be IntPtr.Zero", nameof(address));

            MojitoMemory = mojitoMemory; //Hilfsklasse zum Lesen von Werten
            Address = address;
            Offsets = offsets;
            Converter = converter;
            UpdateInterval = updateInterval;
        }

        /// <summary>
        /// Startet das Überwachen
        /// </summary>
        public void Start()
        {
            Start(CancellationToken.None);
        }


        /// <summary>
        /// Startet das Überwachen
        /// </summary>
        /// <param name="cancellationToken">Der CancellationToken, der für den Abbruch verwendet werden soll</param>
        public void Start(CancellationToken cancellationToken)
        {
            if (IsObserving)
                return;

            if (cancellationToken == CancellationToken.None)
            {
                //Erstellen der inneren Abbruchsmöglichkeit
                CancellationTokenSource source = new CancellationTokenSource();
                CancellationToken token = source.Token;

                _CancellationTokenSource = source;
                _CancellationToken = token;
            }
            else
            {
                _CancellationToken = cancellationToken;
            }

            IsObserving = true;
            _Task = new Task(async () =>
            {
                try {
                    await StartObserve(_CancellationToken);
                }
                catch (TaskCanceledException)
                {
                    Stop();
                }
                catch(Exception ex) { throw ex; }
            }, _CancellationToken);

            _Task.Start();
        }

        private async Task StartObserve(CancellationToken token)
        {
            //Typ speichern
            Type type = typeof(T);

            while (IsObserving)
            {

                //TODO: Offsets berücksichtigen : this.MojitoMemory.ReadPointerAddress(address,offsets)
                byte[] objectBytes = MojitoMemory.ReadBytes(MojitoMemory.Handle, Address, Marshal.SizeOf<T>());
                T value = Converter.Convert(MojitoMemory, objectBytes);
                
                //TODO: Event Value Read


                _CancellationToken.ThrowIfCancellationRequested();
                await Task.Delay(_UpdateInterval);
            }
        }

        public void Stop()
        {
            if (!IsObserving)
                return;

            if (_CancellationTokenSource != null)
            {
                try {
                    _CancellationTokenSource.Cancel();
                }
                catch (TaskCanceledException)
                {}
                catch(Exception ex)
                {
                    //Rethrow eig. nicht gut, aber naja
                    throw ex;
                }
            }


            //Sauber machen
            _CancellationTokenSource = null;
            _CancellationToken = CancellationToken.None;
            _Task.Dispose();
            _Task = null;
            IsObserving = false;
        }
    }
}


Nun also zu meiner Frage, was haltet ihr davon ? (Bitte ehrlich sein, nur so lerne ich) Gibt es eine bessere Möglichkeit ?

Edit: Ich weiß, dass der Code Kompelieren muss, aber wollte jetzt nicht alle Klassen hier zeigen. Per Bedarf, kann ich auch gerne als Zip anhängen.

Edit: Hab den aktuellen Stand hinzugefügt, als Zip .net 4.6.1

Gruß
Tayuki

742 Beiträge seit 2005
vor 8 Jahren

s gibt viel Kleinkram anzumerken, der Hauptritikpunkt ist aber folgender:

Mal abgesehen von zwei Zeilen Code hast du einen Timer programmiert. Die Frage ist: Warum?

T
Tayuki Themenstarter:in
21 Beiträge seit 2010
vor 8 Jahren

Danke erstmal. Also zu deiner Frage: Aufgrund der CPU-Auslastung, die hochgeht, wenn ich ständig den Wert lesen würde. Deshalb ist der Interval da. Dieser soll frei setzbar sein. 100 ms reichen eigentlich vollkommen. Du hast ja recht, eigentlich ist es ein Timer. Sinn und Zweck war ja auch, dass ich soviel selber machen wollte, wie es geht, einfach zum Üben. Klar, sollte man Klassen verwenden die es schon gibt, dass ist mir alles bewusst.

Also würdest du mir einfach ein Timer empfehlen?

Gruß
Tayuki

742 Beiträge seit 2005
vor 8 Jahren

Ja, nehme einfach einen Timer. Wenn du einen zu Übungszwecken selbst schreiben willst (was sicher nicht verkehrt ist), dann würde ich diesen in eine wiederverwendbare Klasse auslagern.

Thema geschlossen