//
// Radegast Metaverse Client
-// Copyright (c) 2009, Radegast Development Team
+// Copyright (c) 2009-2013, Radegast Development Team
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
return PluginManager.GetAttributes(Plugin);
}
}
+
+ public AppDomain Domain;
}
/// <summary>
/// </summary>
public class PluginManager : IDisposable
{
+ /// <summary>List of files that should not be scanned for plugins</summary>
+ public static readonly List<string> PluginBlackList = new List<string>(new string[]
+ {
+ "AIMLbot.dll",
+ "CommandLine.dll",
+ "Meebey.SmartIrc4net.dll",
+ "Monobjc.Cocoa.dll",
+ "Monobjc.dll",
+ "OpenMetaverse.Rendering.Meshmerizer.dll",
+ "OpenMetaverse.StructuredData.dll",
+ "OpenMetaverse.dll",
+ "OpenMetaverseTypes.dll",
+ "PrimMesher.dll",
+ "RadSpeechLin.dll",
+ "RadSpeechMac.dll",
+ "RadSpeechWin.dll",
+ "Tao.OpenGl.dll",
+ "Tao.Platform.Windows.dll",
+ "Tools.dll",
+ "XMLRPC.dll",
+ "fmodex-dotnet.dll",
+ "fmodex.dll",
+ "log4net.dll",
+ "openjpeg-dotnet-x86_64.dll",
+ "openjpeg-dotnet.dll",
+ "OpenCyc.dll",
+ "IKVM.",
+ "OpenTK",
+ "zlib.net.dll",
+ "SmartThreadPool",
+ });
+
+ /// <summary>List of file extensions that could potentially hold plugins</summary>
+ public static readonly List<string> AllowedPluginExtensions = new List<string>(new string[]
+ {
+ ".cs",
+ ".dll",
+ ".exe"
+ });
+
List<PluginInfo> PluginsLoaded = new List<PluginInfo>();
RadegastInstance instance;
{
lock (PluginsLoaded)
{
- PluginsLoaded.ForEach((PluginInfo info) =>
+ var pluginInfos = PluginsLoaded.FindAll(info => { return info.Plugin == plug.Plugin; });
+
+ foreach (var info in pluginInfos)
+ {
+ AppDomain domain = info.Domain;
+ try { info.Plugin.StopPlugin(instance); }
+ catch (Exception ex) { Logger.Log("ERROR in unloading plugin: " + info.Plugin.GetType().Name + " because " + ex, Helpers.LogLevel.Debug, ex); }
+ PluginsLoaded.Remove(info);
+
+ if (domain != null && PluginsLoaded.Find(dinfo => { return dinfo.Domain == domain; }) == null)
{
- if (info.FileName == plug.FileName || info.Plugin == plug.Plugin)
- {
- try { info.Plugin.StopPlugin(instance); }
- catch (Exception ex) { Logger.Log("ERROR in unloading plugin: " + info.Plugin.GetType().Name + " because " + ex, Helpers.LogLevel.Debug, ex); }
- }
- });
- PluginsLoaded.RemoveAll((PluginInfo info) => { return plug.FileName == info.FileName || info.Plugin == plug.Plugin; });
+ try { AppDomain.Unload(domain); }
+ catch (Exception ex) { Logger.Log("ERROR unloading application domain for : " + plug.FileName + "\n" + ex.Message, Helpers.LogLevel.Debug); }
+ }
+ }
}
}
/// <param name="stratPlugins">Start plugins that are found in the assembly</param>
public void LoadPluginFile(string loadFileName, bool stratPlugins)
{
- if (loadFileName.ToLower().EndsWith(@".cs"))
+ string ext = Path.GetExtension(loadFileName).ToLower();
+ if (ext == ".cs")
{
LoadCSharpScriptFile(loadFileName, stratPlugins);
}
- else if (loadFileName.ToLower().EndsWith(".dll") || loadFileName.ToLower().EndsWith(".exe"))
+ else if (ext == ".dll" || ext == ".exe")
{
try
{
- Assembly assembly = Assembly.LoadFile(loadFileName);
- LoadAssembly(loadFileName, assembly, stratPlugins);
+ LoadAssembly(loadFileName, stratPlugins);
}
catch (BadImageFormatException)
{
/// </summary>
public void ScanAndLoadPlugins()
{
- string dirName = Application.StartupPath;
+ string dirName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
if (!Directory.Exists(dirName)) return;
foreach (string loadFileName in Directory.GetFiles(dirName))
{
+ if (IsUnusedFile(loadFileName))
+ {
+ continue;
+ }
+
LoadPluginFile(loadFileName, false);
}
}
+ private static bool IsUnusedFile(string loadFileName)
+ {
+ if (!AllowedPluginExtensions.Contains(Path.GetExtension(loadFileName).ToLower())) return true;
+ loadFileName = Path.GetFileName(loadFileName).ToLower();
+
+ foreach (string blackList in PluginBlackList)
+ {
+ if (loadFileName.StartsWith(blackList.ToLower()))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
/// <summary>
/// Loads and compiles a plugin from a C# source file
/// </summary>
loParameters.ReferencedAssemblies.Add("Radegast.exe");
loParameters.ReferencedAssemblies.Add("System.dll");
loParameters.ReferencedAssemblies.Add("System.Core.dll");
+ loParameters.ReferencedAssemblies.Add("System.Xml.dll");
loParameters.ReferencedAssemblies.Add("System.Drawing.dll");
loParameters.ReferencedAssemblies.Add("System.Windows.Forms.dll");
/// <param name="loadfilename">File name from which assembly was loaded</param>
/// <param name="assembly">Assembly to scan for supported types</param>
/// <param name="startPlugins">Start plugins found in the assembly after complilation</param>
+ public void LoadAssembly(string loadfilename, bool startPlugins)
+ {
+ LoadAssembly(loadfilename, null, startPlugins);
+ }
+
+
+ /// <summary>
+ /// Scans assembly for supported types and loads it into it's own domain
+ /// </summary>
+ /// <param name="loadfilename">File name from which assembly was loaded</param>
+ /// <param name="startPlugins">Start plugins found in the assembly after complilation</param>
public void LoadAssembly(string loadfilename, Assembly assembly, bool startPlugins)
{
if (null != PluginsLoaded.Find((PluginInfo info) => { return info.FileName == loadfilename; }))
return;
}
+ AppDomain domain = null;
+
+ if (assembly == null)
+ {
+ // Don't load ourselves into a domain
+ if (Path.GetFileName(Assembly.GetEntryAssembly().Location) == Path.GetFileName(loadfilename))
+ {
+ assembly = Assembly.GetEntryAssembly();
+ }
+ else
+ {
+ assembly = Assembly.Load(File.ReadAllBytes(loadfilename));
+ /* Disable creation of domains for now
+ domain = AppDomain.CreateDomain("Domain for: " + loadfilename);
+ var loader = (RemoteLoader)domain.CreateInstanceAndUnwrap("Radegast", "Radegast.RemoteLoader");
+ assembly = loader.Load(loadfilename);
+ */
+ }
+ }
+
+ bool loadedTypesFromAssembly = false;
+
foreach (Type type in assembly.GetTypes())
{
if (typeof(IRadegastPlugin).IsAssignableFrom(type))
IRadegastPlugin plug = null;
ConstructorInfo constructorInfo = type.GetConstructor(new Type[] { typeof(RadegastInstance) });
if (constructorInfo != null)
+ {
plug = (IRadegastPlugin)constructorInfo.Invoke(new[] { instance });
+ }
else
{
constructorInfo = type.GetConstructor(new Type[] { });
if (constructorInfo != null)
+ {
plug = (IRadegastPlugin)constructorInfo.Invoke(new object[0]);
+ }
else
{
Logger.Log("ERROR Constructing Radegast Plugin: " + loadfilename + " because " + type + " has no usable constructor.", Helpers.LogLevel.Debug);
continue;
}
}
+
+ loadedTypesFromAssembly = true;
+
PluginInfo info = new PluginInfo()
{
FileName = loadfilename,
Plugin = plug,
- Started = false
+ Started = false,
+ Domain = domain
};
lock (PluginsLoaded) PluginsLoaded.Add(info);
{
try
{
- instance.CommandsManager.LoadType(type);
+ loadedTypesFromAssembly |= instance.CommandsManager.LoadType(type);
+ loadedTypesFromAssembly |= instance.ContextActionManager.LoadType(type);
}
catch (Exception ex)
{
}
}
}
+
+ if (domain != null && !loadedTypesFromAssembly)
+ {
+ AppDomain.Unload(domain);
+ }
+
+
}
}
+
+ public class RemoteLoader : MarshalByRefObject
+ {
+ public Assembly Load(string loadfilename)
+ {
+ return AppDomain.CurrentDomain.Load(File.ReadAllBytes(loadfilename));
+ }
+ }
+
}