G
Guest
Hi all. I’m looking for some advice on how to completely unload a newly
created (second) AppDomain that calls a dynamic method in a dynamic
assembly&module which is a wrapper for a legacy dll method. It’s basically a
plugin manager but my problem comes when I need to replace, update or delete
the legacy dll because even if I unload the secondary appdomain, a lock on
the dll file continues to exist. I believe this happens because my second app
domain is being treated as a proxy to the first. I attached some sample code
in hopes that someone could please help me out with this problem. It’s been
giving me a headache for weeks and I just can’t get it to work.
// To run it
Void foo()
{
CLocalLoader pInvoke = new CLocalLoader();
pInvoke.DynamicPInvoke("C:\\VAdminHook.dll", "VAdmin_DLLInit",
typeof(bool), new Type[]{ typeof(uint) }, new Object[]{ (uint)0 });
//……..
pInvoke.Dispose();
pInvoke = null;
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect(0);
}
namespace InvokeExternal
{
/// <summary>
/// Local Loader Class
/// </summary>
public class CLocalLoader : IDisposable
{
private AppDomain m_dynamicAppDomain = null;
private CRemoteLoader m_RemoteLoader = null;
public CLocalLoader()
{
LoadUp();
}
~CLocalLoader()
{
dispose( false );
}
public void Dispose()
{
dispose( true );
}
private void dispose( bool disposing )
{
if( disposing )
{
Console.WriteLine("CLocalLoader::Unload AppDomain[{0}]",
AppDomain.CurrentDomain.ToString());
m_RemoteLoader.Dispose();
m_RemoteLoader = null;
AppDomain.Unload(m_dynamicAppDomain);
m_dynamicAppDomain = null;
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
System.GC.Collect( 0 );
}
}
public void LoadUp()
{
try
{
Console.WriteLine("CLocalLoader::LoadUp AppDomain[{0}]",
AppDomain.CurrentDomain.ToString());
// Set up the AppDomainSetup
AppDomainSetup setup = new AppDomainSetup();
setup.ApplicationName = "CLocalLoaderTestApp";
setup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
setup.PrivateBinPath = AppDomain.CurrentDomain.BaseDirectory;
m_dynamicAppDomain = AppDomain.CreateDomain("MySecondDomain", null,
setup);
//CreateInstanceAndUnwrap(CurrentAssemblyName,
Namespace.ClassObject);
m_RemoteLoader = (CRemoteLoader)
m_dynamicAppDomain.CreateInstanceAndUnwrap("InvokeVadminHook",
"InvokeExternal.CRemoteLoader");
}
catch (Exception pError)
{
Console.WriteLine("CLocalLoader::CallDynaMethod EXCEPTION [{0}]
AppDomain[{1}]", pError.Message, AppDomain.CurrentDomain.ToString());
}
}
public object DynamicPInvoke(string dll, string entryPoint, Type
returnType, Type [] parameterTypes, object [] parameterValues)
{
Console.WriteLine("CLocalLoader:ynamicPInvoke AppDomain[{0}]",
AppDomain.CurrentDomain.ToString());
if (m_RemoteLoader != null )
m_RemoteLoader.CallDynaMethod("InvokeExternal.CRemoteLoader.DynaAssembly",
dll, entryPoint, returnType, parameterTypes, parameterValues);
return null;
}
}
//=============================================================================
/// <summary>MBRO Class to Load and Call DynaMethod </summary>
internal class CRemoteLoader : MarshalByRefObject, IDisposable
{
~CRemoteLoader()
{
dispose( false );
}
public void Dispose()
{
dispose( true );
}
private void dispose( bool disposing )
{
if( disposing )
{
Console.WriteLine("CRemoteLoader::dispose AppDomain[{0}]",
AppDomain.CurrentDomain.ToString());
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
System.GC.Collect( 0 );
}
}
public void CallDynaMethod(String strAssemblyName, string dll, string
entryPoint, Type returnType, Type [] parameterTypes, object []
parameterValues)
{
try
{
Console.WriteLine("CRemoteLoader::CallDynaMethod Executing in
AppDomain[{0}]", AppDomain.CurrentDomain.ToString());
AssemblyName asmName = new AssemblyName();
asmName.Name = strAssemblyName;
AssemblyBuilder dynamicAsm =
AppDomain.CurrentDomain.DefineDynamicAssembly(asmName,
AssemblyBuilderAccess.Run);
//Create a temp module in the assembly
ModuleBuilder dynamicMod = dynamicAsm.DefineDynamicModule(
"tempModule");
// Dynamically construct a global PInvoke signature using the input
information
MethodBuilder dynamicMethod =
dynamicMod.DefinePInvokeMethod(entryPoint,
dll,
MethodAttributes.Static | MethodAttributes.Public |
MethodAttributes.PinvokeImpl,
CallingConventions.Standard,
typeof(System.Int32),
parameterTypes,
CallingConvention.Winapi,
CharSet.Ansi);
dynamicMethod.SetImplementationFlags(MethodImplAttributes.PreserveSig |
dynamicMethod.GetMethodImplementationFlags());
// This global method is now complete
dynamicMod.CreateGlobalFunctions();
// Get a MethodInfo for the PInvoke method
MethodInfo mi = dynamicMod.GetMethod(entryPoint);
// Invoke the static method and return an object
Object pRetObj = mi.Invoke(null, parameterValues);
}
catch (Exception pError)
{
Console.WriteLine("CRemoteLoader::CallDynaMethod EXCEPTION [{0}]
AppDomain[{1}]", pError.Message, AppDomain.CurrentDomain.ToString());
throw pError;
}
}
}
}
created (second) AppDomain that calls a dynamic method in a dynamic
assembly&module which is a wrapper for a legacy dll method. It’s basically a
plugin manager but my problem comes when I need to replace, update or delete
the legacy dll because even if I unload the secondary appdomain, a lock on
the dll file continues to exist. I believe this happens because my second app
domain is being treated as a proxy to the first. I attached some sample code
in hopes that someone could please help me out with this problem. It’s been
giving me a headache for weeks and I just can’t get it to work.
// To run it
Void foo()
{
CLocalLoader pInvoke = new CLocalLoader();
pInvoke.DynamicPInvoke("C:\\VAdminHook.dll", "VAdmin_DLLInit",
typeof(bool), new Type[]{ typeof(uint) }, new Object[]{ (uint)0 });
//……..
pInvoke.Dispose();
pInvoke = null;
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect(0);
}
namespace InvokeExternal
{
/// <summary>
/// Local Loader Class
/// </summary>
public class CLocalLoader : IDisposable
{
private AppDomain m_dynamicAppDomain = null;
private CRemoteLoader m_RemoteLoader = null;
public CLocalLoader()
{
LoadUp();
}
~CLocalLoader()
{
dispose( false );
}
public void Dispose()
{
dispose( true );
}
private void dispose( bool disposing )
{
if( disposing )
{
Console.WriteLine("CLocalLoader::Unload AppDomain[{0}]",
AppDomain.CurrentDomain.ToString());
m_RemoteLoader.Dispose();
m_RemoteLoader = null;
AppDomain.Unload(m_dynamicAppDomain);
m_dynamicAppDomain = null;
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
System.GC.Collect( 0 );
}
}
public void LoadUp()
{
try
{
Console.WriteLine("CLocalLoader::LoadUp AppDomain[{0}]",
AppDomain.CurrentDomain.ToString());
// Set up the AppDomainSetup
AppDomainSetup setup = new AppDomainSetup();
setup.ApplicationName = "CLocalLoaderTestApp";
setup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
setup.PrivateBinPath = AppDomain.CurrentDomain.BaseDirectory;
m_dynamicAppDomain = AppDomain.CreateDomain("MySecondDomain", null,
setup);
//CreateInstanceAndUnwrap(CurrentAssemblyName,
Namespace.ClassObject);
m_RemoteLoader = (CRemoteLoader)
m_dynamicAppDomain.CreateInstanceAndUnwrap("InvokeVadminHook",
"InvokeExternal.CRemoteLoader");
}
catch (Exception pError)
{
Console.WriteLine("CLocalLoader::CallDynaMethod EXCEPTION [{0}]
AppDomain[{1}]", pError.Message, AppDomain.CurrentDomain.ToString());
}
}
public object DynamicPInvoke(string dll, string entryPoint, Type
returnType, Type [] parameterTypes, object [] parameterValues)
{
Console.WriteLine("CLocalLoader:ynamicPInvoke AppDomain[{0}]",
AppDomain.CurrentDomain.ToString());
if (m_RemoteLoader != null )
m_RemoteLoader.CallDynaMethod("InvokeExternal.CRemoteLoader.DynaAssembly",
dll, entryPoint, returnType, parameterTypes, parameterValues);
return null;
}
}
//=============================================================================
/// <summary>MBRO Class to Load and Call DynaMethod </summary>
internal class CRemoteLoader : MarshalByRefObject, IDisposable
{
~CRemoteLoader()
{
dispose( false );
}
public void Dispose()
{
dispose( true );
}
private void dispose( bool disposing )
{
if( disposing )
{
Console.WriteLine("CRemoteLoader::dispose AppDomain[{0}]",
AppDomain.CurrentDomain.ToString());
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
System.GC.Collect( 0 );
}
}
public void CallDynaMethod(String strAssemblyName, string dll, string
entryPoint, Type returnType, Type [] parameterTypes, object []
parameterValues)
{
try
{
Console.WriteLine("CRemoteLoader::CallDynaMethod Executing in
AppDomain[{0}]", AppDomain.CurrentDomain.ToString());
AssemblyName asmName = new AssemblyName();
asmName.Name = strAssemblyName;
AssemblyBuilder dynamicAsm =
AppDomain.CurrentDomain.DefineDynamicAssembly(asmName,
AssemblyBuilderAccess.Run);
//Create a temp module in the assembly
ModuleBuilder dynamicMod = dynamicAsm.DefineDynamicModule(
"tempModule");
// Dynamically construct a global PInvoke signature using the input
information
MethodBuilder dynamicMethod =
dynamicMod.DefinePInvokeMethod(entryPoint,
dll,
MethodAttributes.Static | MethodAttributes.Public |
MethodAttributes.PinvokeImpl,
CallingConventions.Standard,
typeof(System.Int32),
parameterTypes,
CallingConvention.Winapi,
CharSet.Ansi);
dynamicMethod.SetImplementationFlags(MethodImplAttributes.PreserveSig |
dynamicMethod.GetMethodImplementationFlags());
// This global method is now complete
dynamicMod.CreateGlobalFunctions();
// Get a MethodInfo for the PInvoke method
MethodInfo mi = dynamicMod.GetMethod(entryPoint);
// Invoke the static method and return an object
Object pRetObj = mi.Invoke(null, parameterValues);
}
catch (Exception pError)
{
Console.WriteLine("CRemoteLoader::CallDynaMethod EXCEPTION [{0}]
AppDomain[{1}]", pError.Message, AppDomain.CurrentDomain.ToString());
throw pError;
}
}
}
}