Freigeben über


BinaryFormatter-Ereignisquelle

Ab .NET 5 umfasst BinaryFormatter eine integrierte EventSource, die Ihnen den Einblick verschafft, wann eine Objektserialisierung oder -deserialisierung stattfindet. Apps können mit von EventListener abgeleiteten Typen auf diese Benachrichtigungen lauschen und sie protokollieren.

Diese Funktion ist kein Ersatz für einen SerializationBinder oder ein ISerializationSurrogate und kann nicht verwendet werden, um die Daten zu ändern, die serialisiert oder deserialisiert werden. Vielmehr soll dieses Ereignissystem einen Einblick in die serialisierten oder deserialisierten Typen bieten. Damit können auch unbeabsichtigte in die BinaryFormatter-Infrastruktur gerichtete Aufrufe erkannt werden, z. B. Aufrufe aus einem Bibliothekscode von Drittanbietern.

Beschreibung von Ereignissen

Die BinaryFormatter-Ereignisquelle hat den bekannten Namen System.Runtime.Serialization.Formatters.Binary.BinaryFormatterEventSource. Listener können sechs Ereignisse abonnieren.

SerializationStart-Ereignis (ID = 10)

Wird ausgelöst, wenn BinaryFormatter.Serialize aufgerufen wurde und den Serialisierungsprozess gestartet hat. Dieses Ereignis ist mit dem SerializationEnd-Ereignis gekoppelt. Das SerializationStart-Ereignis kann rekursiv aufgerufen werden, wenn ein Objekt BinaryFormatter.Serialize in seiner eigenen Serialisierungsroutine aufruft.

Dieses Ereignis enthält keine Nutzdaten.

SerializationEnd-Ereignis (ID = 11)

Wird ausgelöst, wenn BinaryFormatter.Serialize seine Arbeit abgeschlossen hat. Jedes Vorkommen von SerializationEnd gibt den Abschluss des letzten ungekoppelten SerializationStart-Ereignisses an.

Dieses Ereignis enthält keine Nutzdaten.

SerializingObject-Ereignis (ID = 12)

Wird ausgelöst, wenn BinaryFormatter.Serialize gerade einen nicht primitiven Typ serialisiert. Die BinaryFormatter-Infrastruktur behandelt bestimmte Typen (z. B. string und int) als Sonderfälle und löst dieses Ereignis nicht aus, wenn diese Typen gefunden werden. Dieses Ereignis wird für benutzerdefinierte Typen und andere Typen ausgelöst, die BinaryFormatter nicht nativ versteht.

Es kann sein, dass dieses Ereignis zwischen den Ereignissen SerializationStart und SerializationEnd nicht oder mehrmals ausgelöst wird.

Dieses Ereignis enthält eine Nutzlast mit einem Argument:

DeserializationStart-Ereignis (ID = 20)

Wird ausgelöst, wenn BinaryFormatter.Deserialize aufgerufen wurde und den Deserialisierungsprozess gestartet hat. Dieses Ereignis ist mit dem DeserializationEnd-Ereignis gekoppelt. Das DeserializationStart-Ereignis kann rekursiv aufgerufen werden, wenn ein Objekt BinaryFormatter.Deserialize in seiner eigenen Deserialisierungsroutine aufruft.

Dieses Ereignis enthält keine Nutzdaten.

DeserializationEnd-Ereignis (ID = 21)

Wird ausgelöst, wenn BinaryFormatter.Deserialize seine Arbeit abgeschlossen hat. Jedes Vorkommen von DeserializationEnd gibt den Abschluss des letzten ungekoppelten DeserializationStart-Ereignisses an.

Dieses Ereignis enthält keine Nutzdaten.

DeserializingObject-Ereignis (ID = 22)

Wird ausgelöst, wenn BinaryFormatter.Deserialize gerade einen nicht primitiven Typ deserialisiert. Die BinaryFormatter-Infrastruktur behandelt bestimmte Typen (z. B. string und int) als Sonderfälle und löst dieses Ereignis nicht aus, wenn diese Typen gefunden werden. Dieses Ereignis wird für benutzerdefinierte Typen und andere Typen ausgelöst, die BinaryFormatter nicht nativ versteht.

Es kann sein, dass dieses Ereignis zwischen den Ereignissen DeserializationStart und DeserializationEnd nicht oder mehrmals ausgelöst wird.

Dieses Ereignis enthält eine Nutzlast mit einem Argument.

[Erweitert] Abonnieren einer Teilmenge von Benachrichtigungen

Listener, die nur eine Teilmenge von Benachrichtigungen abonnieren möchten, können auswählen, welche Schlüsselwörter aktiviert werden sollen.

  • Serialization = (EventKeywords)1: Löst die Ereignisse SerializationStart, SerializationEnd und SerializingObject aus.
  • Deserialization = (EventKeywords)2: Löst die Ereignisse DeserializationStart, DeserializationEnd und DeserializingObject aus.

Wenn während der EventListener-Registrierung keine Schlüsselwortfilter bereitgestellt werden, werden alle Ereignisse ausgelöst.

Weitere Informationen finden Sie unter System.Diagnostics.Tracing.EventKeywords.

Beispielcode

Der folgende Code

  • erstellt einen vom EventListener abgeleiteten Typ, der in die System.Console schreibt,
  • abonniert diesen Listener für Benachrichtigungen, die der BinaryFormatter erstellt,
  • serialisiert und deserialisiert einen einfachen Objektgraphen mit dem BinaryFormatter und
  • analysiert die ausgelösten Ereignisse.
using System;
using System.Diagnostics.Tracing;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace BinaryFormatterEventSample
{
    class Program
    {
        static EventListener? _globalListener = null;

        static void Main(string[] args)
        {
            // First, set up the event listener.
            // Note: We assign it to a static field so that it doesn't get GCed.
            // We also provide a callback that subscribes this listener to all
            // events produced by the well-known BinaryFormatter source.

            _globalListener = new ConsoleEventListener();
            _globalListener.EventSourceCreated += (sender, args) =>
            {
                if (args.EventSource?.Name ==
                    "System.Runtime.Serialization.Formatters.Binary.BinaryFormatterEventSource")
                {
                    ((EventListener?)sender)?
                        .EnableEvents(args.EventSource, EventLevel.LogAlways);
                }
            };

            // Next, create the Person object and serialize it.

            Person originalPerson = new Person()
            {
                FirstName = "Logan",
                LastName = "Edwards",
                FavoriteBook = new Book()
                {
                    Title = "A Tale of Two Cities",
                    Author = "Charles Dickens",
                    Price = 10.25m
                }
            };

            byte[] serializedPerson = SerializePerson(originalPerson);

            // Finally, deserialize the Person object.

            Person rehydratedPerson = DeserializePerson(serializedPerson);

            Console.WriteLine
                ($"Rehydrated person {rehydratedPerson.FirstName} {rehydratedPerson.LastName}");
            Console.Write
                ($"Favorite book: {rehydratedPerson.FavoriteBook?.Title} ");
            Console.Write
                ($"by {rehydratedPerson.FavoriteBook?.Author}, ");
            Console.WriteLine
                ($"list price {rehydratedPerson.FavoriteBook?.Price}");
        }

        private static byte[] SerializePerson(Person p)
        {
            MemoryStream memStream = new MemoryStream();
            BinaryFormatter formatter = new BinaryFormatter();
#pragma warning disable SYSLIB0011 // BinaryFormatter.Serialize is obsolete
            formatter.Serialize(memStream, p);
#pragma warning restore SYSLIB0011

            return memStream.ToArray();
        }

        private static Person DeserializePerson(byte[] serializedData)
        {
            MemoryStream memStream = new MemoryStream(serializedData);
            BinaryFormatter formatter = new BinaryFormatter();

#pragma warning disable SYSLIB0011 // Danger: BinaryFormatter.Deserialize is insecure for untrusted input
            return (Person)formatter.Deserialize(memStream);
#pragma warning restore SYSLIB0011
        }
    }

    [Serializable]
    public class Person
    {
        public string? FirstName;
        public string? LastName;
        public Book? FavoriteBook;
    }

    [Serializable]
    public class Book
    {
        public string? Title;
        public string? Author;
        public decimal? Price;
    }

    // A sample EventListener that writes data to System.Console.
    public class ConsoleEventListener : EventListener
    {
        protected override void OnEventWritten(EventWrittenEventArgs eventData)
        {
            base.OnEventWritten(eventData);

            Console.WriteLine($"Event {eventData.EventName} (id={eventData.EventId}) received.");
            if (eventData.PayloadNames != null)
            {
                for (int i = 0; i < eventData.PayloadNames.Count; i++)
                {
                    Console.WriteLine($"{eventData.PayloadNames[i]} = {eventData.Payload?[i]}");
                }
            }
        }
    }
}

Der obige Code erzeugt eine Ausgabe, die folgendem Beispiel ähnelt:

Event SerializationStart (id=10) received.
Event SerializingObject (id=12) received.
typeName = BinaryFormatterEventSample.Person, BinaryFormatterEventSample, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Event SerializingObject (id=12) received.
typeName = BinaryFormatterEventSample.Book, BinaryFormatterEventSample, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Event SerializationStop (id=11) received.
Event DeserializationStart (id=20) received.
Event DeserializingObject (id=22) received.
typeName = BinaryFormatterEventSample.Person, BinaryFormatterEventSample, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Event DeserializingObject (id=22) received.
typeName = BinaryFormatterEventSample.Book, BinaryFormatterEventSample, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Event DeserializationStop (id=21) received.
Rehydrated person Logan Edwards
Favorite book: A Tale of Two Cities by Charles Dickens, list price 10.25

In diesem Beispiel protokolliert der konsolenbasierte EventListener, dass die Serialisierung beginnt, Instanzen von Person und Book serialisiert werden und die Serialisierung abgeschlossen wird. Entsprechend werden Instanzen von Person und Book deserialisiert, sobald die Deserialisierung begonnen hat, und anschließend die Deserialisierung abgeschlossen.

Die App druckt dann die Werte, die im deserialisierten Person enthalten sind, um zu veranschaulichen, dass das Objekt tatsächlich ordnunsgemäß serialisiert und deserialisiert wurde.

Weitere Informationen

Weitere Informationen zur Verwendung des EventListener zum Empfang EventSource-basierter Benachrichtigungen finden Sie unter EventListener-Klasse.