Loading assemblies in another domain gets the assembly loaded in the default domain too!?!?!?

D

DeveloperX

Hi

I'm trying to achieve a scenario where I have c# files that are compiled
dynamically, the assemblies are then loaded in a different AppDomain, I call
a simple method from the object, and then unload the AppDomain to release
the lock on the assemly files (so to I can compile the code again if it has
been modified). However, I've encountered a problem, whereby the assembly is
also loaded in the default AppDomain!

I have a few classes (see below):
1) ScriptManager - basically keeps a list of scripts and loads and return
plugins to main program where methods are called on the assemblies
2) PlugIn              - Loaded assembly
3) CodeCompiler - Compiles the code files into dlls

My problem lies in the ScriptManager class on the call to GetScript() with
the piece of code :
 plug = new PlugIn(sDLLPath, ScriptDomain);
At this point, the assembly (which was loaded in the other domain), gets
loaded in the default AppDomain too! I have checked in the PlugIn class,
it's fine there but only when the object is returned to ScriptManager that
this happens.

Can anyone please tell me if i'm doing something wrong? Or how to resolve
the issue?

Thanks
Jeff

Code :
==========================  Call from main program
===============================
ScriptManager m_ScriptMgr;

PlugIn ScriptTimeAdjuster;

for (int i = 0; i < 5; i++)

{

m_ScriptMgr = new ScriptManager();

ScriptTimeAdjuster = m_ScriptMgr.GetScript("TR_TimeAdjuster");

if (ScriptTimeAdjuster != null)

{

ScriptTimeAdjuster.UnloadPlugin();

}

ScriptTimeAdjuster = null;

m_ScriptMgr.ClearAllScripts();

}

===========================  PlugIn class
=======================================
public class PlugIn
{

    private Type _type;

    private Object _obj;

    private ObjectHandle _objHandle;

    private AppDomain _domain;

    public readonly string Name;

public PlugIn(string file, AppDomain domain)

{

    Name = file;

    _domain = domain;

    LoadPlugin(file);

}

public bool LoadPlugin(string file)

{

    bool retour = false;

    try

    {

        Assembly ass = _domain.Load("TR_TimeAdjuster",_domain.Evidence);

        _type = ass.GetType("Scripts.TR_TimeAdjuster");

        _obj = ass.CreateInstance(_type.FullName);

        retour = true;

    }

    catch (Exception ex)

    {retour = false;}

    return (retour);

}

/// <summary>

/// Execute a method in the plugin

/// </summary>

public object ExecuteMethod(string MethodName, Nullable<BindingFlags> Flags,
Binder Binder, object[] MethodParameters)

{

    BindingFlags InvokeFlags = BindingFlags.InvokeMethod;

    Binder InvokeBinder = Binder;

    try

    {

        if (_type == null || _obj == null) return null;

        if (Flags != null) InvokeFlags = (BindingFlags) Flags;

        return _type.InvokeMember(MethodName, InvokeFlags, Binder,_obj,
MethodParameters);

    }

    catch (Exception){return null;}

}
}

===========================  ScriptManager class
=======================================
class ScriptManager

{

Hashtable m_Scripts;

List<string> m_ScriptFiles;

string m_sScriptPath;

AppDomain ScriptDomain;

PlugIn plug;

#region Properties

public string ScriptPath

{

get { return m_sScriptPath; }

set { m_sScriptPath = value; }

}

#endregion

#region Constructors

public ScriptManager()

{

    ScriptDomain = AppDomain.CreateDomain("ScriptDomain");

    ScriptPath = String.Empty;

    InitScriptTable();

}

#endregion

#region Procedures

#region Private

/// <summary>

/// Initialise script list

/// </summary>

private void InitScriptTable()

{

string sDLLPath, sScriptDir;

m_ScriptFiles = new List<string>();

m_Scripts = new Hashtable();

// List of script files - Add as required

m_ScriptFiles.Add("TR_TimeAdjuster.cs");

sScriptDir =
Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location),
ScriptPath);

try

{

foreach (string CustomScript in m_ScriptFiles)

{

// Compile and store resource in hashtable

if (File.Exists(Path.Combine(sScriptDir, CustomScript)))

{

sDLLPath = Path.Combine(sScriptDir, String.Concat(CustomScript.Substring(0,
CustomScript.LastIndexOf('.')), ".dll"));

CodeCompile cc = new CodeCompile();

Path.Combine(sScriptDir, CustomScript),

sDLLPath,

CustomScript.Substring(0, CustomScript.LastIndexOf('.'))));

bool res = cc.Compile(Path.Combine(sScriptDir, CustomScript), sDLLPath,
CustomScript.Substring(0, CustomScript.LastIndexOf('.')));

if (res == false)

{

//Add Empty script path in list

m_Scripts.Add(CustomScript.Substring(0, CustomScript.LastIndexOf('.')),
String.Empty);

}

else

{

//Add path of dll

m_Scripts.Add(CustomScript.Substring(0, CustomScript.LastIndexOf('.')),
sDLLPath);

}
}

else

m_Scripts.Add(CustomScript.Substring(0, CustomScript.LastIndexOf('.')),
String.Empty);

}
}

catch (Exception ex )

{throw ex;}

}

#endregion

#region Public

/// <summary>

/// Gets the compiled script for use

/// </summary>

public PlugIn GetScript(string ScriptName)

{

string sDLLPath;

// Script table is empty

if (m_Scripts.Count == 0)

{

InitScriptTable();

}

if (m_Scripts.ContainsKey(ScriptName))

{

sDLLPath = (string)(m_Scripts[ScriptName]);

plug = new PlugIn(sDLLPath, ScriptDomain);

return  plug ;

}

// No script of such name available

return null;

}

public void ClearAllScripts()

{

try

{

m_Scripts.Clear();

plug = null;

AppDomain.Unload(ScriptDomain);

}

catch (Exception ex)

{throw ex;}

}

#endregion

#endregion



}- Hide quoted text -

- Show quoted text -

Yep, that happens if you are marshalling calls across domain
boundaries incorrectly.

There's a little detail here which may help. It's quite a good read
anyway, but search for accessor which is the first time he describes
the problem:

http://www.codeproject.com/KB/cs/dynamicpluginmanager.aspx
 
J

Jeff

Hi

I'm trying to achieve a scenario where I have c# files that are compiled
dynamically, the assemblies are then loaded in a different AppDomain, I call
a simple method from the object, and then unload the AppDomain to release
the lock on the assemly files (so to I can compile the code again if it has
been modified). However, I've encountered a problem, whereby the assembly is
also loaded in the default AppDomain!

I have a few classes (see below):
1) ScriptManager - basically keeps a list of scripts and loads and return
plugins to main program where methods are called on the assemblies
2) PlugIn - Loaded assembly
3) CodeCompiler - Compiles the code files into dlls

My problem lies in the ScriptManager class on the call to GetScript() with
the piece of code :
plug = new PlugIn(sDLLPath, ScriptDomain);
At this point, the assembly (which was loaded in the other domain), gets
loaded in the default AppDomain too! I have checked in the PlugIn class,
it's fine there but only when the object is returned to ScriptManager that
this happens.

Can anyone please tell me if i'm doing something wrong? Or how to resolve
the issue?

Thanks
Jeff

Code :
========================== Call from main program
===============================
ScriptManager m_ScriptMgr;

PlugIn ScriptTimeAdjuster;

for (int i = 0; i < 5; i++)

{

m_ScriptMgr = new ScriptManager();

ScriptTimeAdjuster = m_ScriptMgr.GetScript("TR_TimeAdjuster");

if (ScriptTimeAdjuster != null)

{

ScriptTimeAdjuster.UnloadPlugin();


}

ScriptTimeAdjuster = null;

m_ScriptMgr.ClearAllScripts();

}

=========================== PlugIn class
=======================================
public class PlugIn
{

private Type _type;

private Object _obj;

private ObjectHandle _objHandle;

private AppDomain _domain;

public readonly string Name;

public PlugIn(string file, AppDomain domain)

{

Name = file;

_domain = domain;

LoadPlugin(file);

}

public bool LoadPlugin(string file)

{

bool retour = false;

try

{

Assembly ass = _domain.Load("TR_TimeAdjuster",_domain.Evidence);


_type = ass.GetType("Scripts.TR_TimeAdjuster");


_obj = ass.CreateInstance(_type.FullName);

retour = true;


}

catch (Exception ex)

{retour = false;}


return (retour);

}

/// <summary>

/// Execute a method in the plugin

/// </summary>

public object ExecuteMethod(string MethodName, Nullable<BindingFlags> Flags,
Binder Binder, object[] MethodParameters)

{

BindingFlags InvokeFlags = BindingFlags.InvokeMethod;

Binder InvokeBinder = Binder;

try

{

if (_type == null || _obj == null) return null;

if (Flags != null) InvokeFlags = (BindingFlags) Flags;

return _type.InvokeMember(MethodName, InvokeFlags, Binder, _obj,
MethodParameters);

}

catch (Exception){return null;}

}

}

=========================== ScriptManager class
=======================================
class ScriptManager

{



Hashtable m_Scripts;

List<string> m_ScriptFiles;

string m_sScriptPath;

AppDomain ScriptDomain;

PlugIn plug;



#region Properties



public string ScriptPath

{

get { return m_sScriptPath; }

set { m_sScriptPath = value; }

}

#endregion

#region Constructors

public ScriptManager()

{

ScriptDomain = AppDomain.CreateDomain("ScriptDomain");

ScriptPath = String.Empty;

InitScriptTable();

}

#endregion

#region Procedures

#region Private

/// <summary>

/// Initialise script list

/// </summary>

private void InitScriptTable()

{

string sDLLPath, sScriptDir;

m_ScriptFiles = new List<string>();

m_Scripts = new Hashtable();

// List of script files - Add as required

m_ScriptFiles.Add("TR_TimeAdjuster.cs");

sScriptDir =
Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location),
ScriptPath);

try

{

foreach (string CustomScript in m_ScriptFiles)

{

// Compile and store resource in hashtable

if (File.Exists(Path.Combine(sScriptDir, CustomScript)))

{

sDLLPath = Path.Combine(sScriptDir, String.Concat(CustomScript.Substring(0,
CustomScript.LastIndexOf('.')), ".dll"));

CodeCompile cc = new CodeCompile();

Path.Combine(sScriptDir, CustomScript),

sDLLPath,

CustomScript.Substring(0, CustomScript.LastIndexOf('.'))));

bool res = cc.Compile(Path.Combine(sScriptDir, CustomScript), sDLLPath,
CustomScript.Substring(0, CustomScript.LastIndexOf('.')));

if (res == false)

{

//Add Empty script path in list

m_Scripts.Add(CustomScript.Substring(0, CustomScript.LastIndexOf('.')),
String.Empty);

}

else

{



//Add path of dll

m_Scripts.Add(CustomScript.Substring(0, CustomScript.LastIndexOf('.')),
sDLLPath);

}

}

else

m_Scripts.Add(CustomScript.Substring(0, CustomScript.LastIndexOf('.')),
String.Empty);

}

}

catch (Exception ex )

{throw ex;}

}

#endregion

#region Public

/// <summary>

/// Gets the compiled script for use

/// </summary>



public PlugIn GetScript(string ScriptName)

{

string sDLLPath;


// Script table is empty

if (m_Scripts.Count == 0)

{

InitScriptTable();

}

if (m_Scripts.ContainsKey(ScriptName))

{

sDLLPath = (string)(m_Scripts[ScriptName]);

plug = new PlugIn(sDLLPath, ScriptDomain);

return plug ;

}


// No script of such name available

return null;

}

public void ClearAllScripts()

{

try

{

m_Scripts.Clear();

plug = null;

AppDomain.Unload(ScriptDomain);

}

catch (Exception ex)

{throw ex;}

}

#endregion

#endregion

}
 
J

Jeff

Thanks for the response. Very interesting article indeed. And similar to
what i want to achieve. I'll try it out :)

========================================================================

"DeveloperX" <[email protected]> a écrit dans le message de (e-mail address removed)...
Hi

I'm trying to achieve a scenario where I have c# files that are compiled
dynamically, the assemblies are then loaded in a different AppDomain, I
call
a simple method from the object, and then unload the AppDomain to release
the lock on the assemly files (so to I can compile the code again if it
has
been modified). However, I've encountered a problem, whereby the assembly
is
also loaded in the default AppDomain!

I have a few classes (see below):
1) ScriptManager - basically keeps a list of scripts and loads and return
plugins to main program where methods are called on the assemblies
2) PlugIn - Loaded assembly
3) CodeCompiler - Compiles the code files into dlls

My problem lies in the ScriptManager class on the call to GetScript() with
the piece of code :
plug = new PlugIn(sDLLPath, ScriptDomain);
At this point, the assembly (which was loaded in the other domain), gets
loaded in the default AppDomain too! I have checked in the PlugIn class,
it's fine there but only when the object is returned to ScriptManager that
this happens.

Can anyone please tell me if i'm doing something wrong? Or how to resolve
the issue?

Thanks
Jeff

Code :
========================== Call from main program
===============================
ScriptManager m_ScriptMgr;

PlugIn ScriptTimeAdjuster;

for (int i = 0; i < 5; i++)

{

m_ScriptMgr = new ScriptManager();

ScriptTimeAdjuster = m_ScriptMgr.GetScript("TR_TimeAdjuster");

if (ScriptTimeAdjuster != null)

{

ScriptTimeAdjuster.UnloadPlugin();

}

ScriptTimeAdjuster = null;

m_ScriptMgr.ClearAllScripts();

}

=========================== PlugIn class
=======================================
public class PlugIn
{

private Type _type;

private Object _obj;

private ObjectHandle _objHandle;

private AppDomain _domain;

public readonly string Name;

public PlugIn(string file, AppDomain domain)

{

Name = file;

_domain = domain;

LoadPlugin(file);

}

public bool LoadPlugin(string file)

{

bool retour = false;

try

{

Assembly ass = _domain.Load("TR_TimeAdjuster",_domain.Evidence);

_type = ass.GetType("Scripts.TR_TimeAdjuster");

_obj = ass.CreateInstance(_type.FullName);

retour = true;

}

catch (Exception ex)

{retour = false;}

return (retour);

}

/// <summary>

/// Execute a method in the plugin

/// </summary>

public object ExecuteMethod(string MethodName, Nullable<BindingFlags>
Flags,
Binder Binder, object[] MethodParameters)

{

BindingFlags InvokeFlags = BindingFlags.InvokeMethod;

Binder InvokeBinder = Binder;

try

{

if (_type == null || _obj == null) return null;

if (Flags != null) InvokeFlags = (BindingFlags) Flags;

return _type.InvokeMember(MethodName, InvokeFlags, Binder, _obj,
MethodParameters);

}

catch (Exception){return null;}

}
}

=========================== ScriptManager class
=======================================
class ScriptManager

{

Hashtable m_Scripts;

List<string> m_ScriptFiles;

string m_sScriptPath;

AppDomain ScriptDomain;

PlugIn plug;

#region Properties

public string ScriptPath

{

get { return m_sScriptPath; }

set { m_sScriptPath = value; }

}

#endregion

#region Constructors

public ScriptManager()

{

ScriptDomain = AppDomain.CreateDomain("ScriptDomain");

ScriptPath = String.Empty;

InitScriptTable();

}

#endregion

#region Procedures

#region Private

/// <summary>

/// Initialise script list

/// </summary>

private void InitScriptTable()

{

string sDLLPath, sScriptDir;

m_ScriptFiles = new List<string>();

m_Scripts = new Hashtable();

// List of script files - Add as required

m_ScriptFiles.Add("TR_TimeAdjuster.cs");

sScriptDir =
Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location),
ScriptPath);

try

{

foreach (string CustomScript in m_ScriptFiles)

{

// Compile and store resource in hashtable

if (File.Exists(Path.Combine(sScriptDir, CustomScript)))

{

sDLLPath = Path.Combine(sScriptDir,
String.Concat(CustomScript.Substring(0,
CustomScript.LastIndexOf('.')), ".dll"));

CodeCompile cc = new CodeCompile();

Path.Combine(sScriptDir, CustomScript),

sDLLPath,

CustomScript.Substring(0, CustomScript.LastIndexOf('.'))));

bool res = cc.Compile(Path.Combine(sScriptDir, CustomScript), sDLLPath,
CustomScript.Substring(0, CustomScript.LastIndexOf('.')));

if (res == false)

{

//Add Empty script path in list

m_Scripts.Add(CustomScript.Substring(0, CustomScript.LastIndexOf('.')),
String.Empty);

}

else

{

//Add path of dll

m_Scripts.Add(CustomScript.Substring(0, CustomScript.LastIndexOf('.')),
sDLLPath);

}
}

else

m_Scripts.Add(CustomScript.Substring(0, CustomScript.LastIndexOf('.')),
String.Empty);

}
}

catch (Exception ex )

{throw ex;}

}

#endregion

#region Public

/// <summary>

/// Gets the compiled script for use

/// </summary>

public PlugIn GetScript(string ScriptName)

{

string sDLLPath;

// Script table is empty

if (m_Scripts.Count == 0)

{

InitScriptTable();

}

if (m_Scripts.ContainsKey(ScriptName))

{

sDLLPath = (string)(m_Scripts[ScriptName]);

plug = new PlugIn(sDLLPath, ScriptDomain);

return plug ;

}

// No script of such name available

return null;

}

public void ClearAllScripts()

{

try

{

m_Scripts.Clear();

plug = null;

AppDomain.Unload(ScriptDomain);

}

catch (Exception ex)

{throw ex;}

}

#endregion

#endregion



}- Hide quoted text -

- Show quoted text -

Yep, that happens if you are marshalling calls across domain
boundaries incorrectly.

There's a little detail here which may help. It's quite a good read
anyway, but search for accessor which is the first time he describes
the problem:

http://www.codeproject.com/KB/cs/dynamicpluginmanager.aspx
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top