2

私は現在、C#のBinaryFormatterでデータを最初にフォーマットした後、ネットワーク経由でデータを送信するプログラムと相互運用しようとしています。それはばかげた考えであり、私はそれを嫌いますが、私はそれと相互運用する必要があります。

私はタイプがどのように見えるかを知っています、私はそれが正確なレイアウトであることを知っています。しかし、さまざまな理由から、プログラムにその特定のアセンブリへの参照を追加することはできません。

BinaryFormatterが特定のタイプ/バージョンにどれほど緊密に結合されているかを考えると、データ構造を知っているにもかかわらず、それを逆シリアル化する方法を見つけることができないようです。

私は現在、すべての適切な属性を使用して偽のアセンブリを作成し、それをリンクしようとするか(本当に厄介なようです)、手動でバイナリストリームを調べて、探している値を抽出しようとしています(私はこれに関するMSのドキュメントを見ると、レイアウトに関しては明らかに泥だらけです)。

他に素晴らしいアイデアはありますか、または過去にこれで成功した人はいますか?私は必要なすべての情報を知っているようで、BinaryFormatterはそれについて悪名高いほど壊れやすいだけです。

編集:

以下の質問に答えるには(これは良い点ですが)、いくつかの理由があります。

  1. プロジェクトの清潔さ。1つの機能の外部である.exeに5MBの参照を追加するのは少しずれています。

  2. 私が相互運用している機器には、世界中にさまざまなバージョンが展開されています。気になるアイテムの内部データ構造はすべて同じですが、アセンブリのバージョンが異なるため、BinaryFormatterで破損が発生します。バイナリストリームを文字列に変換し、バージョン番号を検索してから適切なバージョンをロードすることもできますが、今では1ダースの.exeが配置され、正しいバージョンがロードされるのを待っています。その場合、そのスキームは将来を保証するものではありません(まあ、このスキーム全体は実際には将来を保証するものではありませんが、少なくともBinaryFormatterの脆弱性の一部を抽象化して、仕事を容易にしたいです)。この応答を書くだけで、emitなどを使用して、カスタムアセンブリをその場で作成することを考えました。しかし、もっと簡単な方法が必要ですよね?私'

  3. オブジェクトの変数は、いくつかのロジックを持つget / setプロパティを介して公開され、関数呼び出しを行って、自分の側に存在しない可能性のある他のオブジェクトを更新しようとします(つまり、getは必要な値を取得しますが、トリガーもしますリンクされた依存関係を介して波及する通知で、アプリケーションに例外が発生する可能性があります。コードの臭いについて話してください!)。それは依存関係/実装のうさぎの穴に変わります。

edit2:メーカーは私と協力してシス​​テムを改善していますが、「Works with X」を宣伝するときは、特定のバージョンを必要とせずに、Xでのみ動作するようにしたいと考えています。特に、バージョン管理が厳密に行われている一部のお客様のシステムでは、問題のあるアプリケーションを更新するだけで大​​きな作業になります。

4

2 に答える 2

1

あなたがあなたの質問へのコメントで推測したように、SerializationSurrogateとSerializationBinderはあなたをそこへの道の一部にすることができます。いくつかのプロパティにのみ関心がある場合、SerializationSurrogateに渡されたSerializationInfoを列挙することで、データを設定するプロキシクラスにデシリアライズできます。残念ながら、それはあなたをそこへの道の一部にしか連れて行かないでしょう。

問題は、SerializationSurrogateを使用してデータにアクセスするだけなので、プロパティにアクセスしてもシリアル化されたオブジェクトに副作用が発生しないことです。バイナリコードは実行されません。以下のクイック&ダーティテストコードは、問題を示しています。

namespace BinaryProxy
{
    using System;
    using System.Collections.Generic;
    using System.IO;

    using System.Runtime.Serialization;
    using System.Runtime.Serialization.Formatters.Binary;

    [Serializable]
    class TestClass
    {


        public bool mvalue;

        public TestClass(bool value)
        {
            BoolValue = value;
        }

        public bool BoolValue
        {
            get
            {
                // won't happen
                SideEffect = DateTime.Now.ToString();
                return mvalue;
            }

            set
            {
                mvalue = value;
            }
        }

        public string SideEffect { get; set; }

    }

    class ProxyTestClass
    {
        private Dictionary<string, object> data = new Dictionary<string, object>();

        public Object GetData(string name)
        {
            if(data.ContainsKey(name))
            {
                return data[name];
            }
            return null;
        }
        public void SetData(string name, object value)
        {
            data[name] = value;
        }

        public IEnumerable<KeyValuePair<string, object>> Dump()
        {
            return data;
        }
    }

    class SurrogateTestClassConstructor : ISerializationSurrogate
    {
        private ProxyTestClass mProxy;
        /// <summary>
        /// Populates the provided <see cref="T:System.Runtime.Serialization.SerializationInfo"/> with the data needed to serialize the object.
        /// </summary>
        /// <param name="obj">The object to serialize. </param>
        /// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> to populate with data. </param>
        /// <param name="context">The destination (see <see cref="T:System.Runtime.Serialization.StreamingContext"/>) for this serialization. </param>
        /// <exception cref="T:System.Security.SecurityException">The caller does not have the required permission. </exception>
        public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Populates the object using the information in the <see cref="T:System.Runtime.Serialization.SerializationInfo"/>.
        /// </summary>
        /// <returns>
        /// The populated deserialized object.
        /// </returns>
        /// <param name="obj">The object to populate. </param>
        /// <param name="info">The information to populate the object. </param>
        /// <param name="context">The source from which the object is deserialized. </param>
        /// <param name="selector">The surrogate selector where the search for a compatible surrogate begins. </param>
        /// <exception cref="T:System.Security.SecurityException">The caller does not have the required permission. </exception>
        public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector)
        {
            if (mProxy == null) mProxy = new ProxyTestClass();
            var en = info.GetEnumerator();
            while (en.MoveNext())
            {
                mProxy.SetData(en.Current.Name, en.Current.Value);


            }
            return mProxy;

        }



    }

    sealed class DeserializeBinder : SerializationBinder
    {
        public override Type BindToType(string assemblyName, string typeName)
        {


            return typeof(ProxyTestClass);
        }
    }

    static class Program
    {

        static void Main()
        {
            var tc = new TestClass(true);
            byte[] serialized;
            using (var fs = new MemoryStream())
            {
                var formatter = new BinaryFormatter();
                formatter.Serialize(fs, tc);
                serialized = fs.ToArray();

                var surrSel = new SurrogateSelector();
                surrSel.AddSurrogate(typeof(ProxyTestClass),
                    new StreamingContext(StreamingContextStates.All), new SurrogateTestClassConstructor());

                using (var fs2 = new MemoryStream(serialized))
                {
                    var formatter2 = new BinaryFormatter();
                    formatter2.Binder = new DeserializeBinder();
                    formatter2.SurrogateSelector = surrSel;
                    var deser = formatter2.Deserialize(fs2) as ProxyTestClass;
                    foreach (var c in deser.Dump())
                    {
                        Console.WriteLine("{0} = {1}", c.Key, c.Value);
                    }
                }

            }

        }
    }
}

上記の例では、TestClass'SideEffectバッキングフィールドはnullのままになります。

副作用が不要で、一部のフィールドの値にアクセスしたいだけの場合、このアプローチはある程度実行可能です。そうでなければ、ジョン・スキートの提案に従う以外に、他の実行可能な解決策を考えることはできません。

于 2012-11-28T11:42:25.627 に答える
0

その特定のアセンブリへの参照を追加できる追加のレイヤー(または別のサービス)を追加できますか?その場合、その小さなレイヤーでバイナリ形式を逆シリアル化し、より移植性の高いもの(JSON、プロトコルバッファー、XML、適切なもの)に再形式化することができます。それを狂気からの隔離層と考えてくださいBinaryFormatter:)

于 2012-11-27T23:12:47.890 に答える