Click here to Skip to main content
14,872,753 members
Please Sign up or sign in to vote.
4.00/5 (2 votes)
See more:
Dear All,

Scenario:
WPF: contains a 3d viewport and some images with a interface implemented to access the information in WinForms. It is a usercontrol generating "*.dll" which is used in winforms.

WinForms: Has ElementHost which adds the WPF usercontrol and manipulates data.

Problem: I am having a memory leak problem.

C#
public void Dispose(){

/*
Disposes 4 images which were saved in cached.
This section works fine.
*/

            if (elementHost != null)
            {
                elementHost.Child = null;
                elementHost.Dispose();
                elementHost.Parent = null;
                elementHost = null;
            }

// Clearing the information from the interfaced usercontrol
            pluginService.AvailablePlugins.Clear();

            if (dll != null)
            {
                if (File.Exists(dll))
                {
// I am not able to delete the dll from the dll. The dll is not fully released.
                    try { File.Delete(dll); }
                    catch (Exception e) { MessageBox.Show(e.Message); }
                }
            }
        }


Update:
Plugin Service: Gets the information of the WPF to WInForms

Plugin Service Class:
C#
class PluginService : IPluginHost
    {
        private Types.AvailablePlugins colAvailablePlugins = new Types.AvailablePlugins();
        public Types.AvailablePlugins AvailablePlugins
        {
            get { return colAvailablePlugins; }
            set { colAvailablePlugins = value; }
        }

        public void AddPlugin(string filePath)
        {
            try
            {
                Assembly pluginAssembly = Assembly.LoadFrom(filePath);
                foreach (Type pluginType in pluginAssembly.GetTypes())
                {
                    try
                    {
                        if (pluginType.IsPublic)
                        {
                            if (!pluginType.IsAbstract)
                            {
                                Type typeInterface = pluginType.GetInterface("PluginInterface.Iplugin", true);
                                if (typeInterface != null)
                                {
                                    Types.AvailablePlugin newPlugin = new Types.AvailablePlugin();
                                    newPlugin.AssemblyPath = filePath;
                                    newPlugin.Instance = (IPlugin)Activator.CreateInstance(pluginAssembly.GetType(pluginType.ToString()));
                                    newPlugin.Instance.Host = this;
                                    this.colAvailablePlugins.Add(newPlugin);
                                    newPlugin = null;
                                }
                                typeInterface = null;
                            }
                        }
                    }
                    catch (Exception)
                    {
                        continue;
                    }
                }
                pluginAssembly = null;
            }
            catch (Exception)
            {
                //errorPath.Add(filePath);
            }
        }

        public void Feedback(string Feedback, IPlugin Plugin) { }
    }

    namespace Types
    {
        public class AvailablePlugins : System.Collections.CollectionBase
        {
            public void Add(Types.AvailablePlugin pluginToAdd)
            {
                if (pluginToAdd != null)
                    this.List.Add(pluginToAdd);
            }
        }

        public class AvailablePlugin
        {
            private IPlugin myInstance = null;
            private string myAssemblyPath = "";

            public IPlugin Instance
            {
                get { return myInstance; }
                set { myInstance = value; }
            }
            public string AssemblyPath
            {
                get { return myAssemblyPath; }
                set { myAssemblyPath = value; }
            }
        }
    }
}


[Interface]Plugin
XML
namespace PluginInterface
{
    public interface IPlugin
    {
        IPluginHost Host { get; set; }

        /// <summary>
        /// Gets the WPF usercontrol
        /// </summary>
        UserControl MainInterface { get; }

        /// <summary>
        /// Gets the viewport of the 3D object.
        /// </summary>
        Viewport3D MainViewport { get; }

        /// <summary>
        /// Gets the backgroud image.
        /// </summary>
        Image BackImage { get; }

        /// <summary>
        /// Gets the mask image.
        /// </summary>
        Image MaskImage { get; }

        /// <summary>
        ///
        /// </summary>
        Image ActiveImage { get; }
        Image ShadowImage { get; }
        BitmapImage BackBitmap { get; }
        BitmapImage MaskBitmap { get; }
        BitmapImage ActiveBitmap { get; }
        BitmapImage ShadowBitmap { get; }

        BitmapImage ViewportBitmap();

        ImageBrush ImgBrush { get; }
        string CurrentImage { get; set; }
        BitmapImage GetBitmapImage { get; set; }
    }

    public interface IPluginHost
    {
        void Feedback(string Feedback, IPlugin Plugin);
    }
}


[Update]
After looking into the help sites that you all suggested i wrote my code

This is the class inheriting MarshalByRefObject.
C#
[Serializable]
    public class RemoteLoader : MarshalByRefObject, IPluginHost
    {
        private Types.AvailablePlugins colAvailablePlugins = new Types.AvailablePlugins();
        
        public Types.AvailablePlugins AvailablePlugins
        {
            get { return colAvailablePlugins; }
            set { colAvailablePlugins = value; }
        }

        public void LoadAndExecute(string assemblyName)
        {
            
            Assembly pluginAassembly = Assembly.LoadFrom(assemblyName); //AppDomain.CurrentDomain.Load(assemblyName);
            //AppDomain ad = AppDomain.CurrentDomain;

            foreach (Type pluginType in pluginAassembly.GetTypes())
            {
                try
                {
                    if (pluginType.IsPublic)
                    {
                        if (!pluginType.IsAbstract)
                        {
                            Type typeInterface = pluginType.GetInterface("PluginInterface.Iplugin", true);
                            if (typeInterface != null)
                            {
                                Types.AvailablePlugin newPlugin = new Types.AvailablePlugin();
                                newPlugin.AssemblyPath = assemblyName;
                                newPlugin.Instance = (IPlugin)Activator.CreateInstance(pluginAassembly.GetType(pluginType.ToString()));
                                newPlugin.Instance.Host = this;
                                this.colAvailablePlugins.Add(newPlugin);
                                newPlugin = null;
                            }
                            typeInterface = null;
                        }
                    }
                }
                catch (Exception)
                {
                    continue;
                }
                
            }
            pluginAassembly = null;
        }
        public void Feedback(string Feedback, IPlugin Plugin) { }

    }

The other two supporting class are
C#
namespace Types
   {
       [Serializable]
       public class AvailablePlugins : System.Collections.CollectionBase
       {
           public void Add(Types.AvailablePlugin pluginToAdd)
           {
               if (pluginToAdd != null)
                   this.List.Add(pluginToAdd);
           }
       }

       [Serializable]
       public class AvailablePlugin
       {
           private IPlugin myInstance = null;
           private string myAssemblyPath = "";

           public IPlugin Instance
           {
               get { return myInstance; }
               set { myInstance = value; }
           }
           public string AssemblyPath
           {
               get { return myAssemblyPath; }
               set { myAssemblyPath = value; }
           }
       }
   }


After i ran the code, it gave an error that the class is not serialized so i added it.

But after adding it, it still refers that the WPF usercontrol is not serialized, but i have added it there also.

C#
[Serializable]
    public partial class UserControl1 : UserControl, IPlugin
    {
        public UserControl1()
        {
            InitializeComponent();
        }
        
//IPlugin code goes here


DO you think i am doing something wrong?
Is there a solution in which i donot have to update my WPF usercontrol


Can anyone give suggestions
Posted
Updated 1-Aug-12 0:34am
v3
Comments
Zoltán Zörgő 31-Jul-12 7:23am
   
To me it is not obvious that it leaks. Might happen, that the plugin manager you use is not unloading the assamblies properly, or you are not using it properly. What exactly is your pluginService?
Sergey Alexandrovich Kryukov 31-Jul-12 12:41pm
   
Pretty interesting question, but you have to do a big work to fix the design. I voted 4.
--SA

Ok, now that you have posted your plugin manager, it is clear, that you load plugin assamblies, but never unload them. Clearing the list is not enough. But unloading assamblies is not simple (can't do this directly). Start from here: http://adrianvintu.com/blogengine/post/Unloadable-plugins.aspx[^]

Or use the features of the framework:
MAF: AddIn Enabled Applications[^]
MEF: http://msdn.microsoft.com/en-us/library/dd460648.aspx[^]
or Prism: http://msdn.microsoft.com/en-us/library/ff648611.aspx[^]
   
v3
Comments
Sergey Alexandrovich Kryukov 31-Jul-12 12:52pm
   
This is great that you managed to figure this out! I agree, my 5.
I've added some more explanation of the idea of the solution and other useful information, please see.
--SA
Zoltán Zörgő 31-Jul-12 13:00pm
   
You are right. This topic is far more complicated as it could be addressed in several sentences.
le.Festin 1-Aug-12 6:12am
   
I looked into the link that you suggested but i am having a little problem.
Could you looks into my updated problem
Zoltán Zörgő 1-Aug-12 8:00am
   
I see that you load the plugin and instantiate a class based on type. Good. But you still load it into the current appdoamin, still can't unload it, and thus won't be able to delete the file (while I still don't understand why you want to delete it).
But please be more precise about what your little problem is.
I don't see anything that jumps off the page as a memory leak here, but I might have missed it. However, I did see one of your comments that says that you cannot delete the dll. This is how .NET works. Once it loads a dll, you have to entirely unload the AppDomain for the dll file to be unlocked. Simply closing out the calls to the dll will not be enough.

If you still cannot find the memory leak (and the dll issue wasn't what you were referring to), I would recommend you check out the Ants Memory Profiler[^]. It will tell you exactly where the problem is. Even if you don't want to purchase it, you can get a 30-day trial that will give you the information you need.
   
In addition to the good answer by Zoltán Zörgő, who pointed out the root of the problem: loading the assemblies dynamically and inability to unload them.

The idea of the solution is this: using Application Domain. The Application Domains have totally isolated address spaces, much like separate processes do. So, unloading assemblies is not allowed, because it could be unsafe: what if some code is still calling a method/property/operator in already unloaded assembly? As Application Domains are isolated, this problem does not exist: no code ever calls anything from the different Application Domain. The Application Domains of the same process communicate exclusively via IPC.

So, an assembly to be unloaded should be loaded in a separate Application Domain which can be unloaded. But now the problem is: how to use such assembly? Through the Application Domain boundary, which always involves IPC. This is a big complication which will entail considerable change in your design. However, Application Domains have its own simplified IPC facilities. Please see:
http://msdn.microsoft.com/en-us/library/system.appdomain.aspx[^].

For further ideas, please see my past answers on the plug-in architecture and plug-in unloading problem:
Create WPF Application that uses Reloadable Plugins...[^],
AppDomain refuses to load an assembly[^],
code generating using CodeDom[^],
C# Reflection InvokeMember on existing instance[^].

Good luck,
—SA
   
v4

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)




CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900