简体   繁体   中英

C# TargetInvocationException and FormatException

Ok, I have a really weird situation happening here. First I need to give some background. I'm creating AI agents for a game that was made on the XNA engine. The way things are set up, people are supposed to use the agent's framework to generate a .dll that the game then uses to load the agents when it runs.

I have access to the code of the game (so I can see what's happening) and at this point I'm using someone else's agents as a starting point for my own. Recently, there were a few changes to the game (and consequentially, the framework), mostly in names of classes and interfaces which means I have to bring the agents up to speed. So, after I made all the updates necessary to be able to compile the agents with the new version of the framework, I came up with a problem. This is the code for the game loading the .dll

 // dynamically load assembly from file GeometryFriendsAgents.dll
 Assembly agentsDLL = Assembly.LoadFile(path);

 // get type of classes BallAgent and SquareAgent from just loaded Assembly
 Type circleType = AgentsDLL.GetType("GeometryFriendsAgents.CircleAgent");
 Type rectangleType = AgentsDLL.GetType("GeometryFriendsAgents.RectangleAgent");
 try { 
     // create instances of classes BallAgent and SquareAgent
     npcCircle = (ICircleAgent)Activator.CreateInstance(circleType);
     npcRectangle = (IRectangleAgent)Activator.CreateInstance(rectangleType);
 }catch(TargetInvocationException e){
     throw e.InnerException;
 }

I can confirm that the path is correct. The lines inside the try/catch will throw a TargetInvocationException when I try to run the game (Which will automatically load the agents). I added the try/catch to see the inner exception, which is a FormatException, and VisualStudio gives the aditional information that the input string was not in the correct format.

I don't know what part of the agents code would be relevant for this, but I have yet to get to the weird part. In the implementation I'm using, the agents make use of a LearningCenter class. This class essentially reads and writes the learning files of the agents. at the start of the class it stores the path for the learning files:

protected const string path = @"..\..\..\..\Agents\";

So here's where things get weird. This is the correct path for the learning files. When earlier I made a mistake, I had this path (which before was repeated many times throughout the code) as

protected const string path = @"..\..\..\..\Agents";

When I build the .dll with the incorrect path, I can sucessfully load the agents and it will run the game. The problem then is that the path is incorrect, and when the LearningCenter tries to write the learning file, it will evidently fail with a DirectoryNotFoundException. The method in question is:

public void EndGame(float knownStatesRatio) {
    if (_toSave) {
        FileStream fileStream = new FileStream(path + _learningFolder + "\\Ratios.csv", FileMode.Append);
        StreamWriter sw = new StreamWriter(fileStream);
        sw.WriteLine(knownStatesRatio);
        sw.Close();
        fileStream.Close();
        fileStream = new FileStream(path + _learningFolder + "\\IntraPlatformLearning.csv", FileMode.Create);
        DumpLearning(fileStream, _intraplatformPlayedStates);
        fileStream.Close();
        if (interPlatform) {
            fileStream = new FileStream(path + _learningFolder + "\\InterPlatformLearning.csv", FileMode.Create);
            DumpLearning(fileStream, _interplatformPlayedStates);
            fileStream.Close();
            }
        }
    }

The exception occurs immediatly when creating the new filestream. I've tried shifting the missing \\ to the _learningFolder variable, but when I do it goes back to the first problem. So long as the path is incorrect, I can run the game...

I should also mention that before this I initially encountered another TargetInvocationException at the same location. At the time the problem was fixed by changing the visibility of the agent classes to public.

I realize that the thing with the path is probably hiding the actual problem, but I just don't know where to look next.

edit: Here's the stack trace for the first problem

GeometryFriends.exe!GeometryFriends.AI.AgentsManager.LoadAgents() Line 396
GeometryFriends.exe!GeometryFriends.Levels.SinglePlayerLevel.LoadLevelContent() Line 78
GeometryFriends.exe!GeometryFriends.Levels.Level.LoadContent() Line 262
GeometryFriends.exe!GeometryFriends.ScreenSystem.ScreenManager.LoadContent() Line 253
Microsoft.Xna.Framework.Game.dll!Microsoft.Xna.Framework.DrawableGameComponent.Initialize() 
GeometryFriends.exe!GeometryFriends.ScreenSystem.ScreenManager.Initialize() Line 221 
Microsoft.Xna.Framework.Game.dll!Microsoft.Xna.Framework.Game.Initialize() 
GeometryFriends.exe!GeometryFriends.Engine.Initialize() Line 203
Microsoft.Xna.Framework.Game.dll!Microsoft.Xna.Framework.Game.RunGame(bool useBlockingRun)
Microsoft.Xna.Framework.Game.dll!Microsoft.Xna.Framework.Game.Run()
GeometryFriends.exe!GeometryFriends.Program.Main(string[] args) Line 16

The agent that's failing first is the CircleAgent, here's the constructor:

public CircleAgent() {
    //Change flag if agent is not to be used
    SetImplementedAgent(true);

    lastMoveTime = DateTime.Now;
    lastRefreshTime = DateTime.Now;
    currentAction = 0;
    rnd = new Random(DateTime.Now.Millisecond);

    model = new CircleWorldModel(this);

    learningCenter = new CircleLearningCenter(model);
    learningCenter.InitializeLearning();

    startTime = DateTime.Now;
}

edit 2: Ok, I managed to zone in on the source of the FormatException. The error occurs in this method of the CircleLearningCenter (the statement in the first if):

public override void addStateMovementValue(string[] lineSplit, string stateId, ref Dictionary<string, Dictionary<int, double>> lessons) {
    if (!lineSplit[1].Equals("0")) {
        lessons[stateId].Add(Moves.ROLL_LEFT, double.Parse(lineSplit[1]));
    }
    if (!lineSplit[2].Equals("0")) {
        lessons[stateId].Add(Moves.ROLL_RIGHT, double.Parse(lineSplit[2]));
    }
    if (!lineSplit[3].Equals("0")) {
        lessons[stateId].Add(Moves.JUMP, double.Parse(lineSplit[3]));
    }
}

Which is called by this method in the LearningCenter:

private void createLearningFromFile(FileStream fileStream, ref Dictionary<string, Dictionary<int, double>> lessons) {
    lessons = new Dictionary<string, Dictionary<int, double>>();
    StreamReader sr = new StreamReader(fileStream);
    string line;
    while ((line = sr.ReadLine()) != null) {
        string[] lineSplit = line.Split(',');
        string stateId = lineSplit[0];
        lessons.Add(stateId, new Dictionary<int, double>());
        addStateMovementValue(lineSplit, stateId, ref lessons);
    }
}

which in turn is called by this method (which it's called in the constructor of the circle):

public void InitializeLearning() {
    if (File.Exists(Path.Combine(Path.Combine(path, _learningFolder), "IntraPlatformLearning.csv"))) {
        FileStream fileStream = new FileStream(Path.Combine(Path.Combine(path, _learningFolder),"IntraPlatformLearning.csv"), FileMode.Open);
        createLearningFromFile(fileStream, ref _intraplatformLessonsLearnt);
        fileStream.Close();
    } else {
        createEmptyLearning(ref _intraplatformLessonsLearnt);
    }
    if (File.Exists(Path.Combine(Path.Combine(path, _learningFolder), "InterPlatformLearning.csv"))) {
        FileStream fileStream = new FileStream(Path.Combine(Path.Combine(path, _learningFolder), "InterPlatformLearning.csv"), FileMode.Open);
        createLearningFromFile(fileStream, ref _interplatformLessonsLearnt);
        fileStream.Close();
     } else {
         createEmptyLearning(ref _interplatformLessonsLearnt);
     }
}

In case it's not apparent, CircleLearningCenter is a subclass of LearningCenter. Also, sorry for the text wall, but I'm at my wits end.

Use System.IO.Path.Combine() to con-cat path parts. For example:

instead of :

FileStream(path + _learningFolder + "\\Ratios.csv")

use :

FileStream(Path.Combine(Path.Combine(path , _learningFolder) , "Ratios.csv"))

Just don't forget to remove \\\\ from each part. And do the same for other FileStream paths.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM