1

Windows Phone 7 でゲームの状態をシリアル化しようとしているので、「保存」構造体を作成して、DataContractSerializer を使用してすべての状態を XML ドキュメントに簡単に変換できるようにしました。

Gamestate の各データ メンバーを個別にシリアル化したところ、Save() コードは正常に機能しました。ただし、これにより複数のxmlルートエラーが発生したため、「保存」クラスを唯一のルートとして機能させるようにしました。これで、何かに対して DataContractSerializer.WriteObject() を呼び出そうとするたびに、セキュリティ例外が発生します。

古いコードと新しいコードを比較してきましたが、何も問題が見つかりません。
問題が Save() メソッドの外にある場合に備えて、すべてのコードを以下に貼り付けました。
最初に Save() メソッドを見てください。


例外の詳細:
ここに画像の説明を入力

スタックトレース:

   at System.Runtime.Serialization.DataContract.DataContractCriticalHelper.CreateDataContract(Int32 id, RuntimeTypeHandle typeHandle, Type type)
   at System.Runtime.Serialization.DataContract.DataContractCriticalHelper.GetDataContractSkipValidation(Int32 id, RuntimeTypeHandle typeHandle, Type type)
   at System.Runtime.Serialization.DataContract.GetDataContractSkipValidation(Int32 id, RuntimeTypeHandle typeHandle, Type type)
   at System.Runtime.Serialization.DataContract.GetDataContract(RuntimeTypeHandle typeHandle, Type type, SerializationMode mode)
   at System.Runtime.Serialization.DataContract.GetDataContract(RuntimeTypeHandle typeHandle, Type type)
   at System.Runtime.Serialization.DataContract.GetDataContract(Type type)
   at System.Runtime.Serialization.DataContractSerializer.get_RootContract()
   at System.Runtime.Serialization.DataContractSerializer.InternalWriteStartObject(XmlWriterDelegator writer, Object graph)
   at System.Runtime.Serialization.DataContractSerializer.InternalWriteObject(XmlWriterDelegator writer, Object graph)
   at System.Runtime.Serialization.XmlObjectSerializer.WriteObjectHandleExceptions(XmlWriterDelegator writer, Object graph)
   at System.Runtime.Serialization.XmlObjectSerializer.WriteObject(XmlDictionaryWriter writer, Object graph)
   at System.Runtime.Serialization.XmlObjectSerializer.WriteObject(Stream stream, Object graph)
   at GameState_test.GameState.Save(String filename)
   at GameState_test.MainPage.SaveButton_Click(Object sender, RoutedEventArgs e)
   at System.Windows.Controls.Primitives.ButtonBase.OnClick()
   at System.Windows.Controls.Button.OnClick()
   at System.Windows.Controls.Primitives.ButtonBase.OnMouseLeftButtonUp(MouseButtonEventArgs e)
   at System.Windows.Controls.Control.OnMouseLeftButtonUp(Control ctrl, EventArgs e)
   at MS.Internal.JoltHelper.FireEvent(IntPtr unmanagedObj, IntPtr unmanagedObjArgs, Int32 argsTypeIndex, Int32 actualArgsTypeIndex, String eventName)

新しいコード:

    namespace GameState_test
    {                                       //TODO: Find a way to not have this ignored
        [DataContract] public class Hazard { [IgnoreDataMember] public Planet CurrentPlanet;} 

        public class Connections
        {
            public Connections(List<List<int>> connections = null) { this.cons = connections; }

            public bool AreConnected(int Planet1, int Planet2)
            {
                for (int i = 0; i < cons[Planet1].Count; i++)
                    if (cons[Planet1][i] == Planet2) return true;

                return false;
            }

            public List<int> this [int index] { get { return cons[index]; } }

            internal readonly List<List<int>> cons; //internal so it can be read by serializer
        }

        [DataContract]
        public class GameState
        {
            public GameState(List<List<int>> connections) { this.Connections = new Connections(connections); }
            public GameState(Position pos, List<List<int>> con) { Position = pos; Connections = new Connections(con); }
            public GameState(string filename) //load a game
            {   
                using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    if (!store.DirectoryExists("SavedGames")) store.CreateDirectory("SavedGames");

                    using (IsolatedStorageFileStream stream = store.OpenFile("SavedGames/" + filename + ".xml", System.IO.FileMode.Open, System.IO.FileAccess.Read))
                    {
                        DataContractSerializer serializer = new DataContractSerializer(typeof(GameStateSave), new List<Type> {typeof(Hazard), typeof(Inventory), typeof(List<List<int>>), typeof(List<Planet>), typeof(Planet) });
                        GameStateSave Save = (GameStateSave)serializer.ReadObject(stream);

                        Position position = new Position(Save.planets, Save.connections, Save.playerPosition);

                        this.Position = position;
                        this.PlayerInventory = Save.playerInventory;
                        this.Connections = new Connections(Save.connections);
                    }
                }
            }

            [DataMember] public readonly Connections Connections;
            [DataMember] public Position Position;
            [DataMember] public Inventory PlayerInventory;

            public void Save(string filename) 
            {
                using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    if (!store.DirectoryExists("SavedGames")) store.CreateDirectory("SavedGames");

                    using (IsolatedStorageFileStream stream = store.OpenFile("SavedGames/" + filename + ".xml", System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.Write))
                    {
                        DataContractSerializer serializer = new DataContractSerializer(typeof (GameStateSave), new List<Type> {typeof(Hazard), typeof(Inventory), typeof(List<List<int>>), typeof(List<Planet>), typeof(Planet)} );

                        GameStateSave Save = GenerateSave();
                        serializer.WriteObject(stream, Save);

                        //NOTE: Even when I comment everything out but this, I still get an exception:
                        //serializer.WriteObject(stream, this.Position.Player); 
                    }
                }
            }

            private GameStateSave GenerateSave()
            {
                GameStateSave Save = new GameStateSave();

                Save.connections = this.Connections.cons;
                Save.playerInventory = this.PlayerInventory;
                Save.planets = this.Position.Planets;
                Save.playerPosition = this.Position.Player;

                return Save;
            }

        }

        [DataContract]
        internal struct GameStateSave //only to be used here
        {
            [DataMember]
            public List<List<int>> connections;
            [DataMember]
            public Inventory playerInventory;
            [DataMember]
            public List<Planet> planets;
            [DataMember]
            public int playerPosition;
        }

    }

古いコード:

namespace GameState_test
{
    [DataContract] public class Hazard { [IgnoreDataMember] public Planet CurrentPlanet;}

    public class Connections
    {
        public Connections(List<List<int>> connections = null) { this.cons = connections; }

        public bool AreConnected(int Planet1, int Planet2)
        {
            for (int i = 0; i < cons[Planet1].Count; i++)
                if (cons[Planet1][i] == Planet2) return true;

            return false;
        }

        public List<int> this [int index] { get { return cons[index]; } }

        internal readonly List<List<int>> cons; //internal so it can be read by serializer
    }

    [DataContract]
    public class GameState
    {
        public GameState(List<List<int>> connections) { this.Connections = new Connections(connections); }
        public GameState(Position pos, List<List<int>> con) { Position = pos; Connections = new Connections(con); }
        public GameState(string filename) 
        {   //load a game
            using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
            {
                if (!store.DirectoryExists("SavedGames")) store.CreateDirectory("SavedGames");

                using (IsolatedStorageFileStream stream = store.OpenFile("SavedGames/" + filename + ".xml", System.IO.FileMode.Open, System.IO.FileAccess.Read))
                {
                    DataContractSerializer serializer = new DataContractSerializer(typeof(Hazard), new List<Type> { typeof(Inventory), typeof(List<List<int>>), typeof(List<Planet>), typeof(Planet) });

                    List<List<int>> Connections = (List<List<int>>) serializer.ReadObject(stream);
                    Inventory PlayerInventory = (Inventory) serializer.ReadObject(stream);
                    List<Planet> Planets = (List<Planet>) serializer.ReadObject(stream);
                    int PlayerPosition = (int)serializer.ReadObject(stream);

                    Position position = new Position(Planets, Connections, PlayerPosition);

                    this.Position = position;
                    this.PlayerInventory = PlayerInventory;
                    this.Connections = new Connections(Connections);
                }
            }
        }

        [DataMember] public readonly Connections Connections;
        [DataMember] public Position Position;
        [DataMember] public Inventory PlayerInventory;

        public void Save(string filename) 
        {
            using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
            {
                if (!store.DirectoryExists("SavedGames")) store.CreateDirectory("SavedGames");

                using (IsolatedStorageFileStream stream = store.OpenFile("SavedGames/" + filename + ".xml", System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.Write))
                {
                    DataContractSerializer serializer = new DataContractSerializer(typeof (Hazard), new List<Type> {typeof(Inventory), typeof(List<List<int>>), typeof(List<Planet>), typeof(Planet)} );
                    serializer.WriteObject(stream, this.Connections.cons);
                    serializer.WriteObject(stream, this.PlayerInventory);
                    serializer.WriteObject(stream, this.Position.Planets);
                    serializer.WriteObject(stream, this.Position.Player);
                }
            }
        }

        internal class SerializableGameState //only to be used here
        {
            public List<List<int>> connections;
            public Inventory playerInventory;
            public List<Planet> planets;
            public int playerPosition;
        }

    }
}
4

3 に答える 3

2

Silverlight コードは部分信頼で実行されます。つまり、システム内のオブジェクト (パブリックでない型やメンバーを含む) への完全なアクセス権はありません。そのままでは機能しない内部クラスをシリアライズしようとしています。内部クラスをシリアル化しようとすると、次の例外が発生します。

Exception: System.Security.SecurityException: The data contract type 'SL_ButtonAndText.SerializableGameState' is not serializable because it is not public. Making the type public will fix this error. Alternatively, you can make it internal, and use the InternalsVisibleToAttribute attribute on your assembly in order to enable serialization of internal members - see documentation for more details. Be aware that doing so has certain security implications. ---> System.Security.SecurityException: Security error.
   at System.Runtime.Serialization.CodeGenerator.BeginMethod(Type returnType, String methodName, Type[] argTypes, Boolean allowPrivateMemberAccess)
   at System.Runtime.Serialization.CodeGenerator.BeginMethod(String methodName, Type delegateType, Boolean allowPrivateMemberAccess)
   at System.Runtime.Serialization.XmlFormatWriterGenerator.CriticalHelper.GenerateClassWriter(ClassDataContract classContract)
   --- End of inner exception stack trace ---
   at System.Runtime.Serialization.ClassDataContract.RequiresMemberAccessForWrite(SecurityException securityException, String[] serializationAssemblyPatterns)
   at System.Runtime.Serialization.XmlFormatWriterGenerator.CriticalHelper.GenerateClassWriter(ClassDataContract classContract)
   at System.Runtime.Serialization.ClassDataContract.get_XmlFormatWriterDelegate()
   at System.Runtime.Serialization.ClassDataContract.WriteXmlValue(XmlWriterDelegator xmlWriter, Object obj, XmlObjectSerializerWriteContext context)
   at System.Runtime.Serialization.XmlObjectSerializerWriteContext.WriteDataContractValue(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle)
   at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithoutXsiType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle)
   at System.Runtime.Serialization.DataContractSerializer.InternalWriteObjectContent(XmlWriterDelegator writer, Object graph)
   at System.Runtime.Serialization.DataContractSerializer.InternalWriteObject(XmlWriterDelegator writer, Object graph)
   at System.Runtime.Serialization.XmlObjectSerializer.WriteObjectHandleExceptions(XmlWriterDelegator writer, Object graph)
   at System.Runtime.Serialization.XmlObjectSerializer.WriteObject(XmlDictionaryWriter writer, Object graph)
   at System.Runtime.Serialization.XmlObjectSerializer.WriteObject(Stream stream, Object graph)
   at SL_ButtonAndText.MainPage.Button_Click(Object sender, RoutedEventArgs e)

例外メッセージは、私の問題が何であるかを正確に示しています (例外メッセージは似ていますか?)。これを解決する 1 つの方法は、クラスを public にすることです。これが最も簡単な解決策です。エラー メッセージで示されている別の方法として、属性DataContractSerializerを使用して、 が内部型を参照できるように明示的に許可することもできます。[InternalsVisibleTo]以下のコードには[IVT]属性があり、エラーが表示されなくなったことを追加しています。

using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Text;
using System.Windows;
using System.Windows.Controls;

[assembly: InternalsVisibleTo("System.Runtime.Serialization")]

namespace SL_ButtonAndText
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            AddToDebug(typeof(DataContractSerializer).Assembly.FullName);
            MemoryStream ms = new MemoryStream();
            DataContractSerializer dcs = new DataContractSerializer(typeof(SerializableGameState));
            SerializableGameState sgs = new SerializableGameState
            {
                connections = new List<List<int>>
                {
                    new List<int> { 1, 2, 3 },
                    new List<int> { 4, 5 },
                },
                playerPosition = 0
            };
            try
            {
                dcs.WriteObject(ms, sgs);
                AddToDebug("Serialized: {0}", Encoding.UTF8.GetString(ms.GetBuffer(), 0, (int)ms.Position));
            }
            catch (Exception ex)
            {
                AddToDebug("Exception: {0}", ex);
            }
        }

        private void AddToDebug(string text, params object[] args)
        {
            if (args != null && args.Length > 0) text = string.Format(text, args);
            this.Dispatcher.BeginInvoke(() => this.txtDebug.Text = this.txtDebug.Text + text + Environment.NewLine);
        }
    }

    [DataContract]
    internal class SerializableGameState
    {
        [DataMember]
        public List<List<int>> connections;
        [DataMember]
        public int playerPosition;
    }
}
于 2012-05-10T20:22:27.897 に答える
1

Carlosfigueira の言うとおりです。Silverlight では内部が見えないからです。これを修正するには、AssemblyInfo.cs に次を追加します。

[assembly: InternalsVisibleTo("System.Runtime.Serialization, PublicKey="
+ "00240000048000009400000006020000002400005253413100040000010001008D56C76F9E86493"
+ "83049F383C44BE0EC204181822A6C31CF5EB7EF486944D032188EA1D3920763712CCB12D75FB77E"
+ "9811149E6148E5D32FBAAB37611C1878DDC19E20EF135D0CB2CFF2BFEC3D115810C3D9069638FE4"
+ "BE215DBF795861920E5AB6F7DB2E2CEEF136AC23D5DD2BF031700AEC232F6C6B1C785B4305C123B"
+ "37AB")]
[assembly: InternalsVisibleTo("System.ServiceModel.Web, PublicKey="
+ "00240000048000009400000006020000002400005253413100040000010001008D56C76F9E86493"
+ "83049F383C44BE0EC204181822A6C31CF5EB7EF486944D032188EA1D3920763712CCB12D75FB77E"
+ "9811149E6148E5D32FBAAB37611C1878DDC19E20EF135D0CB2CFF2BFEC3D115810C3D9069638FE4"
+ "BE215DBF795861920E5AB6F7DB2E2CEEF136AC23D5DD2BF031700AEC232F6C6B1C785B4305C123B"
+ "37AB")]
[assembly: InternalsVisibleTo("System.Runtime.Serialization.Json, PublicKey="
+ "00240000048000009400000006020000002400005253413100040000010001008D56C76F9E86493"
+ "83049F383C44BE0EC204181822A6C31CF5EB7EF486944D032188EA1D3920763712CCB12D75FB77E"
+ "9811149E6148E5D32FBAAB37611C1878DDC19E20EF135D0CB2CFF2BFEC3D115810C3D9069638FE4"
+ "BE215DBF795861920E5AB6F7DB2E2CEEF136AC23D5DD2BF031700AEC232F6C6B1C785B4305C123B"
+ "37AB")]

ソース: http://systemmetaphor.blogspot.fr/2010/04/silverlight-serialization-avoiding.html

于 2014-03-31T07:50:40.817 に答える
0

保存先または読み込み先のファイルの権限を確認してください。単純なファイル許可エラーかもしれません。フォルダの権限や管理者権限などを確認してください。

于 2012-05-10T06:26:17.880 に答える