none
Unloadable Plugin Architecture RRS feed

  • Question

  • This is my first question here, so apologies if I have made a mistake.

    I am developing a collection of small games using WPF with C# but have been having real difficulty in being able to load and unload each game into my main viewer application.

    The structure of my program is I have a main view application GameViewer.EXE which basically consists of a Frame object to view pages.
    Each game is a custom class GamePage.CS, shown below:

    GameLibrary.DLL

    namespace GameLibrary
    {
     public class GamePage : Page
        {
            public Guid GameID;
            public string GameName;
            public string Description;
            public virtual void PauseGame(){}

           public event GameEndArgsEventHandler GameEnd;

            protected void OnGameEnd(GameEndArgs e)
            {
                if (GameEnd != null)
                    GameEnd(this, e);
            }
       }

     public class GameEndArgs : EventArgs
        {
            public double Score;
            public bool Finished;
            public int Level = 1;
        }
    }


    I also have a ControlLibrary.DLL , which contains a collection of stylized controls, i.e. customs, sliders, etc., which is used in all games and the main interface GameViewer.EXE.

    The structure of the application is:

    Home\
             GameViewer.EXE
             GameLibrary.DLL
             ControlLibrary.DLL
     
            Games\
                      Game1\
                                 game1.DLL
                                 game1Dependency1.DLL
                                 game1Dependency2.DLL
                      Game2\
                                game2.DLL
                      Game3\
                                game3.DLL
                                image\
                         ...
                     Game n


    My trouble is being able to load then successfully unload each game DLL into my main viewer. I have been reading dozen of threads, but I cannot seem to get anything to work. I am trying to create a new AppDomain load the assembly in there and create the GamePage object.
    - I can then unload the appdomain when the game is finished, but on repeated opening and closing the game I see the memory usuage shoot up, so something is going wrong.
    - I have also tried creating a separate loader, using the MarshalByRefObject, but on returning a GamePage I am told that GamePage is not Serializable.

    This is my current attempt:

    In the GameLibrary.DLL I have:

    namespace GameLibrary
    {
         [Serializable]
          public class Factory
          {
              public GamePage CreateGamePageObject(string path, string typename)
              {
                  return Activator.CreateInstanceFrom(path, typename).Unwrap() as GamePage;
               }
          }
    }

    Then in the my app I have:

            private AppDomain domain  =null;

             void LoadGame(string GameFile)
              {
                    domain = AppDomain.CreateDomain("Remote Load");

                    ObjectHandle oh = domain.CreateInstance("GameLibrary", "GameLibrary.Factory");
                    Object obj = oh.Unwrap();
                    GameLibrary.Factory f = obj as GameLibrary.Factory;
                    GameLibrary.GamePage addIn=null;

                    FileInfo fi = new FileInfo(GameFile);
                   addIn =f.CreateGamePageObject(fi.FullName, fi.Name.Substring(0, fi.Name.Length - 4) + ".Page1");

                    MainFrame.Content = addIn;
              }
             void UnloadGame()
             {
                   MainFrame.Content = null;
                   AppDomain.Unload(domain);
             }


    The trouble with this is the memory leak.

    I would greatly appreciate any help and suggestions.
    • Edited by Amit Popat Thursday, November 26, 2009 3:28 PM Sorry, I mis-typed the first time around
    Thursday, November 26, 2009 1:03 PM

Answers

  • I have since realised that the reason for the increasing memory usage was due to the use of a Frame, which would store a history of past pages. By setting     Frame.JournalOwnership  to UsesParentJournal, this problem is avoided.
    • Marked as answer by Amit Popat Friday, November 27, 2009 5:29 PM
    Friday, November 27, 2009 5:29 PM

All replies

  • You create a new appdomain but are still loading the assemblies into the main/default one, use domain.CreateInstance instead of Activator.CreateInstance. If you don't want to reinvent the weel you could look at MEF which does alot of this stuff as well
    Thursday, November 26, 2009 3:22 PM
  • Hi, sorry I mistyped my code above, I have corrected it now. Unfortuately the problem still remains. I will look into the MEF also.
    Thursday, November 26, 2009 3:30 PM
  • Cause you're still calling Activator in CreateGamePageObject which is not a bad thing on its own but you do have to supply the appdomain you want the object in if you don't want it in the default one.
    Thursday, November 26, 2009 3:43 PM
  • Hi, thanks for you replies.
    I've tried changing my code accordingly:

    In the GameLibrary.DLL I now have:

    namespace GameLibrary
    {
         [Serializable]
          public class Factory
          {
              public GamePage CreateGamePageObject(string path, string typename, ref AppDomain domain)
              {
                  return domain.CreateInstanceFrom(path, typename).Unwrap() as GamePage;
               }
          }
    }

    In the main app, I now have:

    void LoadGame(string GameFile)
              {
                    domain = AppDomain.CreateDomain("Remote Load");

                    ObjectHandle oh = domain.CreateInstance("GameLibrary", "GameLibrary.Factory");
                    Object obj = oh.Unwrap();
                    GameLibrary.Factory f = obj as GameLibrary.Factory;
                    GameLibrary.GamePage addIn=null;

                    FileInfo fi = new FileInfo(GameFile);
                   addIn =f.CreateGamePageObject(fi.FullName, fi.Name.Substring(0, fi.Name.Length - 4) + ".Page1",ref domain);

                    MainFrame.Content = addIn;
              }


    However now I receive the follwoing error at the f.CreateGamePageObject stage:
    "Type 'Game1.Page1' in assembly 'Game1, Version=0.0.1.0, Culture=neutral, PublicKeyToken=null' is not marked as serializable"

    Removing [Serializable] from Factory class does not help either, instead I receive the following exception at the oh.Unwrap() stage:
    "Type 'GameLibrary.Factory' in assembly 'GameLibrary, Version=0.0.3617.30390, Culture=neutral, PublicKeyToken=null' is not marked as serializable."


    I have had a look at the MEF, but I am reluctant to try it as it is in a prerelease stage and may still change.

    I would greatly appreciate your advice.
    Thursday, November 26, 2009 4:57 PM
  • I have since realised that the reason for the increasing memory usage was due to the use of a Frame, which would store a history of past pages. By setting     Frame.JournalOwnership  to UsesParentJournal, this problem is avoided.
    • Marked as answer by Amit Popat Friday, November 27, 2009 5:29 PM
    Friday, November 27, 2009 5:29 PM