BinaryFormatter 이벤트 원본

.NET 5부터 개체 BinaryFormatter 직렬화 또는 역직렬화가 발생하는 시기를 확인할 수 있는 기본 제공 EventSource 이 포함되어 있습니다. 앱은 EventListener 파생 형식을 사용하여 이러한 알림을 수신하고 기록할 수 있습니다.

이 기능은 SerializationBinder 또는 ISerializationSurrogate를 대체하지 않으며, 직렬화되거나 역직렬화되는 데이터를 수정하는 데 사용할 수 없습니다. 대신 이 이벤트 시스템은 직렬화 또는 역직렬화되는 형식에 대한 인사이트를 제공하기 위한 것입니다. 타사 라이브러리 코드에서 발생하는 호출 같이 BinaryFormatter 인프라에 대한 의도하지 않은 호출을 검색하는 데도 사용할 수 있습니다.

이벤트 설명

BinaryFormatter 이벤트 원본에는 잘 알려진 이름 System.Runtime.Serialization.Formatters.Binary.BinaryFormatterEventSource가 있습니다. 수신기는 6개의 이벤트를 구독할 수 있습니다.

SerializationStart 이벤트(id = 10)

BinaryFormatter.Serialize가 호출되고 직렬화 프로세스를 시작한 경우 발생합니다. 이 이벤트는 SerializationEnd 이벤트와 쌍을 이룹니다. 개체가 자체 직렬화 루틴 내에서 BinaryFormatter.Serialize를 호출하는 경우 SerializationStart 이벤트를 재귀적으로 호출할 수 있습니다.

이 이벤트에는 페이로드가 포함되지 않습니다.

SerializationEnd 이벤트(id = 11)

BinaryFormatter.Serialize가 작업을 완료했을 때 발생합니다. SerializationEnd의 각 항목은 짝이 없는 마지막 SerializationStart 이벤트의 완료를 나타냅니다.

이 이벤트에는 페이로드가 포함되지 않습니다.

SerializingObject 이벤트(id = 12)

BinaryFormatter.Serialize가 기본 형식이 아닌 형식을 직렬화하는 동안 발생합니다. BinaryFormatter 인프라는 stringint와 같은 특정 형식을 특수 처리하며, 이러한 형식이 발견될 때 이 이벤트를 발생시키지 않습니다. 이 이벤트는 사용자 정의 형식 및 BinaryFormatter가 고유하게 이해하지 못하는 다른 형식의 경우에 발생합니다.

이 이벤트는 SerializationStart 이벤트와 SerializationEnd 이벤트 사이에서 0번 이상 발생할 수 있습니다.

이 이벤트에는 인수가 하나 있는 페이로드가 포함되어 있습니다.

DeserializationStart 이벤트(id = 20)

BinaryFormatter.Deserialize가 호출되고 역직렬화 프로세스를 시작한 경우 발생합니다. 이 이벤트는 DeserializationEnd 이벤트와 쌍을 이룹니다. 개체가 자체 역직렬화 루틴 내에서 BinaryFormatter.Deserialize를 호출하는 경우 DeserializationStart 이벤트를 재귀적으로 호출할 수 있습니다.

이 이벤트에는 페이로드가 포함되지 않습니다.

DeserializationEnd 이벤트(id = 21)

BinaryFormatter.Deserialize가 작업을 완료했을 때 발생합니다. DeserializationEnd의 각 항목은 짝이 없는 마지막 DeserializationStart 이벤트의 완료를 나타냅니다.

이 이벤트에는 페이로드가 포함되지 않습니다.

DeserializingObject 이벤트(id = 22)

BinaryFormatter.Deserialize가 기본 형식이 아닌 형식을 역직렬화하는 동안 발생합니다. BinaryFormatter 인프라는 stringint와 같은 특정 형식을 특수 처리하며, 이러한 형식이 발견될 때 이 이벤트를 발생시키지 않습니다. 이 이벤트는 사용자 정의 형식 및 BinaryFormatter가 고유하게 이해하지 못하는 다른 형식의 경우에 발생합니다.

이 이벤트는 DeserializationStart 이벤트와 DeserializationEnd 이벤트 사이에서 0번 이상 발생할 수 있습니다.

이 이벤트에는 인수가 하나 있는 페이로드가 포함되어 있습니다.

[고급] 알림 하위 집합 구독

알림 하위 집합만 구독하려는 수신기는 사용할 키워드를 선택할 수 있습니다.

  • Serialization = (EventKeywords)1: SerializationStart, SerializationEndSerializingObject 이벤트를 발생시킵니다.
  • Deserialization = (EventKeywords)2: DeserializationStart, DeserializationEndDeserializingObject 이벤트를 발생시킵니다.

EventListener 등록 중에 키워드 필터를 제공하지 않으면 모든 이벤트가 발생합니다.

자세한 내용은 System.Diagnostics.Tracing.EventKeywords를 참조하세요.

예제 코드

코드는 다음과 같습니다.

  • System.Console에 쓰는 EventListener 파생 형식을 만듭니다.
  • 해당 수신기가 BinaryFormatter에서 생성된 알림을 구독합니다.
  • BinaryFormatter를 사용하여 단순 개체 그래프를 직렬화 및 역직렬화합니다.
  • 발생한 이벤트를 분석합니다.
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]}");
                }
            }
        }
    }
}

위 코드의 출력은 다음 예제와 유사합니다.

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

이 샘플에서 콘솔 기반 EventListener는 직렬화가 시작되고, PersonBook의 인스턴스가 직렬화된 다음 직렬화가 완료됨을 기록합니다. 마찬가지로 역직렬화가 시작되면 PersonBook 인스턴스가 역직렬화된 다음 역직렬화가 완료됩니다.

그런 다음 앱은 역직렬화된 Person에 포함된 값을 출력하여 실제로 개체가 올바로 직렬화 및 역직렬화되었음을 보여 줍니다.

참고 항목

EventListener를 사용하여 EventSource 기반 알림을 수신하는 방법에 대한 자세한 내용은 EventListener 클래스를 참조하세요.