From: Arrow on
Wondering if anyone has done this successfully and has any code they can
share ? I have
a non-GUI app that runs 24 / 7, and there has always been a memory leak in
the LocalReport.Render call. In VS 2008 this was around (average) 20k per
report,
with VS 2010, I was hoping this leak would be fixed, instead it's much worse
! ...around
3 meg per report ! Add features without fixing the current problems !
So from searching, the idea of a separate AppDomain came up so I could
unload
Microsoft.ReportViewer.WinForms after so many calls and release the memory.

Basically to have a separate class:

namespace MyApp
{
[ClassInterface(ClassInterfaceType.AutoDual)]
public class LocalReportFactory : MarshalByRefObject
{
public AppDomain LocalAppDomain = null;
public string ErrorMessage = string.Empty;

/// <summary>
/// Creates a new instance of the LocalReportParser in a new
AppDomain
/// </summary>
/// <returns></returns>
public LocalReport CreateLocalReportParser()
{
this.CreateAppDomain(null);
LocalReport parser = null;

// string AssemblyPath =
Assembly.GetExecutingAssembly().Location;
try
{
// parser = (LocalReport)
this.LocalAppDomain.CreateInstanceFrom(AssemblyPath,
typeof(LocalReport).FullName).Unwrap() ;

Type MyLR = typeof(LocalReport);
parser =
(LocalReport)this.LocalAppDomain.CreateInstanceAndUnwrap(MyLR.Assembly.FullName,
MyLR.FullName);

// Assembly RPAssembly =
this.LocalAppDomain.Load("Microsoft.ReportViewer.WinForms, Version=10.0.0.0,
Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
// Type tInvokerType =
RPAssembly.GetType("Microsoft.Reporting.WinForms.LocalReport");
// parser =
(LocalReport)Activator.CreateInstance(tInvokerType);
}
catch (Exception ex)
{
this.ErrorMessage = ex.Message;
}
return parser;
}

public bool CreateAppDomain(string appDomain)
{
if (string.IsNullOrEmpty(appDomain))
appDomain = "LocalReportParser" +
Guid.NewGuid().ToString().GetHashCode().ToString("x");

AppDomainSetup domainSetup = new AppDomainSetup();
// domainSetup.ApplicationName = appDomain;

// *** Point at current directory
// domainSetup.ApplicationBase =
Environment.CurrentDirectory; // AppDomain.CurrentDomain.BaseDirectory;
domainSetup.DisallowBindingRedirects = false;
domainSetup.DisallowCodeDownload = true;

this.LocalAppDomain = AppDomain.CreateDomain(appDomain, null,
domainSetup);

// *** Need a custom resolver so we can load assembly from non
current path
// AppDomain.CurrentDomain.AssemblyResolve += new
ResolveEventHandler(CurrentDomain_AssemblyResolve);
this.LocalAppDomain.AssemblyResolve += new
ResolveEventHandler(CurrentDomain_AssemblyResolve);

return true;
}

/*
Assembly CurrentDomain_AssemblyResolve(object sender,
ResolveEventArgs args)
{
Assembly assembly = null;

try
{
assembly =
System.Reflection.Assembly.Load("Microsoft.ReportViewer.WinForms,
Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
// if (assembly != null)
// return assembly;
}

catch { // ignore load error
}

// *** Try to load by filename - split out the filename of
the full assembly name
// *** and append the base path of the original assembly
(ie. look in the same dir)
// *** NOTE: this doesn't account for special search paths
but then that never
// worked before either.
// string[] Parts = args.Name.Split(',');
// string File =
Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\" +
Parts[0].Trim() + ".dll";

// return System.Reflection.Assembly.LoadFrom(File);

return assembly;
}
*/

Assembly CurrentDomain_AssemblyResolve(object sender,
ResolveEventArgs args)
{

foreach (Assembly LR in AppDomain.CurrentDomain.GetAssemblies())
{
if (
string.Compare(LR.GetName().Name, args.Name, true)
== 0 ||
string.Compare(LR.FullName, args.Name, true) == 0
)
return LR;
}
return null;
}

/// <summary>
///
/// </summary>
public void Unload()
{
if (this.LocalAppDomain != null)
{
AppDomain.Unload(this.LocalAppDomain);
this.LocalAppDomain = null;
}
}
}
}

Then in the main part of my app

LocalReportFactory factory = new LocalReportFactory();
LocalReport report = factory.CreateLocalReportParser();

then when all done I expected after calling:
factory.Unload();

that "Microsoft.ReportViewer.WinForms" would get unloaded, but it doesn't !?
Anyone have an idea why and how I can fix it ...thanks !!!