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;
}
}
}