How to release COM objects in Silverlight 4 - silverlight

When using COM Interop with Office (usually Excel), I always carefully ensure I call Marshal.ReleaseComObject on every reference, to avoid the problem where Excel doesn't quit as described in this KB article.
How can I ensure Excel quits when I use Interop from an OOB Silverlight application (with AutomationFactory.CreateObject)?
Silverlight doesn't have a Marshal.ReleaseComObject method and even calling GC.Collect and GC.WaitForPendingFinalizers doesn't help.
Surely Microsoft hasn't added this feature to Silverlight without a mechanism to release COM references? This seems to me to be a showstopper for automating out-of-process COM servers such as Excel.
A surprising ommission, all the more so as Pete Brown in section 5.5 of his book "Silverlight 4 in Action" goes as far as to say about AutomationFactory.CreateObject, that:
The primary intent of this feature is to allow automation of other applications, including Microsoft Office.
UPDATE in response to Hans' comments.
I'm not convinced the "silent assassin" problem exists in typical automation of Office apps. A common use might look something like the following, which I've seen used repeatedly in WinForms applications without ever coming across the "poisoned RCW" described in the article linked by Hans:
Create an Excel.Application instance
Open or create a workbook
Write data to the workbook
Show Excel if all went well, close the workbook and call Application.Quit if not.
Call Marshal.ReleaseComObject to release all Excel object references.
Failing to call Marshal.ReleaseComObject as recommended by Hans will leave multiple copies of Excel.exe running, as described in the KB article mentioned above - highly undesirable.
UPDATE 2
The sample I'm using to repro this is a sample from the source code for Pete Brown's book Silverlight 4 in action, there's a download link on this page. The sample solution AutomatingExcel is in Ch05.zip / 5.03. To repro:
Make sure no instances of Excel are running
Run AutomatingExcel sample
An Excel workbook is opened
Close Excel
Observe with Task Manager that Excel is still running.
Setting all the dynamic variables to null and calling GC.Collect() seems to work as pointed out in AnthonyWJones's answer.
UPDATE 2
Otaku's answer is what I was looking for - by wrapping references in a using statement the COM references are released without the need to call GC.Collect. A bit of experimentation shows that it's more tolerant of failing to dispose every single reference, unlike the standard Marshal.ReleaseComObject solution described in the KB article referenced above.
It would be interesting to have an authoritative take on exactly what must be disposed to ensure that all Excel references are released.

You could implement the IDisposable interface. The best example of this I've seen is at http://csfun.blog49.fc2.com/blog-entry-79.html. The blog entry is in Japanese, but open in Chrome and let Google do the page translation for you if you don't read Japanese.
Also, if you just want the source code example of the COM wrapper directly you can download the sample app it comes in: SilverOffice.

Take look at this code:-
private void Button_Click(object sender, RoutedEventArgs e)
{
dynamic app = AutomationFactory.CreateObject("Excel.Application");
dynamic book = app.Workbooks.Add();
dynamic sheet = app.ActiveSheet();
sheet = null;
book.Close(false);
book = null;
app.Quit();
app = null;
GC.Collect();
}
The Excel process appears and then disappears. Remove the GC and the Excel process will continue. Do you get the same if you copy this code verbatim? If so then it would suggest that somewhere in your code a reference to an excel object remains reachable from one of the thread stacks or static fields.
Do you ever hold an excel object in a field (as opposed to a local variable)?
Do you hold an excel object in what appears to be variable but is referenced from a dynamic delegate or lambda that is used as an event handler?
Are you attaching event handlers to long-lived objects from an object that has a short lifespan? If so are you ensuring you detach from those handlers properly?
Many of these things can catch devs out into leaving what they think are objects ready for GC but the GC finds them accessible and therefore not candidates for collection.
If the code above doesn't behave the same then we a looking for another issue entirely. I'm using Office 2007 on Server 2008 R2 from the latest SL 4 runtime. However if we are having a varition because of setup then we're on very shakey ground.
Edit
With some testing this appears to be effective:-
private void Button_Click(object sender, RoutedEventArgs e)
{
using (dynamic app = AutomationFactory.CreateObject("Excel.Application"))
{
using (dynamic book = app.Workbooks.Add())
{
using (dynamic sheet = app.ActiveSheet())
{
}
book.Close();
}
app.Quit();
};
GC.Collect();
}
However leave off the GC and you will end up with unwanted Excel processes left running eventually.

I would consider building the Excel file in a WCF service. You can do all the clean up there. Send to bytes to your Silverlight app and use a SaveFileDialog to send them to the user.
Silverlight has a restriction on access to the client file system. Manipulating an Excel file on the client's system, or even assuming the client has Excel installed seems like a violation of this.

by wrapping references in a using statement the COM references are released
Please note: using statements are just syntactic sugar for try/catch/finally with Dispose() in the finally clause.
Also, most applications don't allow for using statements in this case, because creation and cleanup of COM objects are distributed among various places/methods.
The critical line which is needed here reads:
((IDisposable)_excel).Dispose(); // Release COM Interface
This assumes:
dynamic _excel = AutomationFactory.CreateObject("Excel.Application");

Related

Can i recreate a CompositionContainer?

i have a wpf application with login window before displaying the mainwindow.
i use mef to load all modules/parts. before the mainwindow start i check the user login data against the parts which i display then. the parts a Shared and NonShared.
[ImportMany]
private IEnumerable<Lazy<IComponent, IComponentMetadata>> _components;
[ImportMany("Resourcen", typeof(ResourceDictionary))]
private IEnumerable<ResourceDictionary> _importResourcen;
var catalog = new AggregateCatalog();
catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
catalog.Catalogs.Add(new DirectoryCatalog(AppDomain.CurrentDomain.BaseDirectory));
_mefcontainer = new CompositionContainer(catalog);
_mefcontainer.ComposeParts(somepartwithaSharedExport, this);
this all works fine. but now i tried the "relogin".
_mefcontainer.Dispose();
_mefcontainer = null;
//here the stuff that works from above
first i thought it works, but it seems that the parts i create the first time still exist in memory and i have no chance to "kill" them. so i got OutOfMemory Exception when i relogin enough times.
that why i use this approach now
System.Diagnostics.Process.Start(Application.ResourceAssembly.Location);
App.ShutDown();
i dont feel happy with this.
is there a way to cleanup the Compositioncontainer and create a new one?
You could try to call _mefcontainer.RemovePart(somepartwithaSharedExport). More details here: http://mef.codeplex.com/wikipage?title=Parts%20Lifetime
For the non-shared part you can call CompositionContainer.ReleaseExport:
_mefcontainer.ReleaseExport(nonSharedExport);
For more info have a try the sample code from this answer.
As far as I know, the shared parts cannot be released without disposing the container. If you go with that path, then you will also have to make sure that no references to these objects are kept to allow for the GC to collect them. The documentation reference from mrtig's answer provides a lot of useful details concerning the lifetime of parts and you should probably study it along with the answer by weshaggard to a similar question. It also explains what happens to disposable parts.

WinForms ReportViewer hangs the app WPF

I've been experiencing some problems with the ReportViewer. Basically the code is the following:
public void Display(object dataSource, ReportViewer viewer)
{
currentDs = dataSource as MyTypes;
if (currentDs != null)
{
var param = new LinkedList<ReportParameter>();
param.AddFirst(new ReportParameter("Title", "Title"));
viewer.ProcessingMode = ProcessingMode.Local;
viewer.LocalReport.ReportEmbeddedResource = ReportName;
viewer.LocalReport.EnableExternalImages = true;
viewer.LocalReport.DataSources.Add(new ReportDataSource(DataSourceName + "_Header", currentDs.Header));
viewer.LocalReport.DataSources.Add(new ReportDataSource(DataSourceName + "_Footer", currentDs.Footer));
viewer.LocalReport.DataSources.Add(new ReportDataSource(DataSourceName + "_Lines", currentDs.Lines));
viewer.LocalReport.SetParameters(param);
viewer.RefreshReport();
}
}
The problem is that sometimes it generates the rdlc report but others it just hangs the application. Specifically, it stays in viewer.RefreshReport(); forever.
The corresponding parameters and datasets are correctly set on the rdlc file.
Has anyone experienced a similar behavior?
Thanks in advance.
I suppose you have your viewer configured correctly.
Does the first time generation usually succeed? You could try calling viewer.Reset() after each report generation, that might solve some compounding issues.
There is a post from 2006 that on the surface appears to be relevant and might give you a workaround, although I have to admit I haven't done this myself because it would be almost impossible for me to recreate the problem you're seeing and so it would be equally impossible to see if any workaround would fix it. It's not ideal, but I'm hoping there no such thing as useless information :-)
The relevant info is at the bottom of the linked thread and for the sake of clarity, I've quoted it below.
The ReportViewer control is broken up into two components - the report objects (exposed as .ServerReport and .LocalReport) and the UI (the actual ReportViewer class). The report objects store all of the state about your report. The UI component simply calls into them to get the information it needs to display the report, parameter prompts, toolbar, etc.
The UI component is not thread-safe, just like most controls. But the report objects are thread safe. When you call RefreshReport, the viewer uses a background thread to call Render on the report object. But this is not the only time that the control calls into the report object. The first time it needs to compile the report definition (local report) or create a session (server report) can take a long time. If this "first time" is during RefreshReport, it will happen on the background thread. But as you have seen, it can happen at other times. Populating the parameters UI, for example, requires calling GetParameters, which also requires this startup penalty.
If you want to guarantee this "first time" penalty is on a background thread, then after setting the report path and other connection/data source information, call GetParameters from a background thread.
This seems to suggest that you make some calls to your ReportViewer always from a background thread rather than from the UI thread, so that any delays are not affecting the UI. It sounds like a bit of a bodged design to me though!
Is this of any help to you?
I may have fixed it. This page helped me figure it out: http://ikriv.com/dev/dotnet/MysteriousHang.html
So, it seems that a) report viewer needs to be created on the UI thread, and that b) that wasn't always happening reliably (see link for details). The report viewer is a .NET 2 component, and the behavior documented in the link applies to .NET 2.
To fix it, I employed the following tricks:
first trick: name the UI thread at start up
second trick: force a handle early on, then get a reference to the synchronization context
third trick: use the thread name and sync context to effect the invoke required -> invoke idiom.
If it all holds together, I will come back and post w/ more details.
I encountered the issue in Windows XP with .Net 4.0 and Report Viewer 2010. Yes the OS is end of support but Windows Embedded POSReady 2009 (Windows XP SP3), it's still in the extended support until April 2019.
To workaround the hang, call deprecated API to render the report
rptviewer.LocalReport.ExecuteReportInSandboxAppDomain();
and when exit or rerun the report e.g. report's parameters changed, the report need to clean up properly by
rptviewer.LocalReport.DataSources.Clear();
rptviewer.LocalReport.ReportEmbeddedResource = null;
rptviewer.LocalReport.Dispose();
// release sandbox is required to prevent the lock up, see below note on display report
rptviewer.LocalReport.ReleaseSandboxAppDomain();
// clean up report viewer
rptviewer.Clear();
rptviewer.Reset();
Note on Windows 7, 8, 8.1 and 10. The hang issue is not occurred.

Synchronizing Asynchronous request handlers in Silverlight environment

For our senior design project my group is making a Silverlight application that utilizes graph theory concepts and stores the data in a database on the back end. We have a situation where we add a link between two nodes in the graph and upon doing so we run analysis to re-categorize our clusters of nodes. The problem is that this re-categorization is quite complex and involves multiple queries and updates to the database so if multiple instances of it run at once it quickly garbles data and breaks (by trying to re-insert already used primary keys). Essentially it's not thread safe, and we're trying to make it safe, and that's where we're failing and need help :).
The create link function looks like this:
private Semaphore dblock = new Semaphore(1, 1);
// This function is on our service reference and gets called
// by the client code.
public int addNeed(int nodeOne, int nodeTwo)
{
dblock.WaitOne();
submitNewNeed(createNewNeed(nodeOne, nodeTwo));
verifyClusters(nodeOne, nodeTwo);
dblock.Release();
return 0;
}
private void verifyClusters(int nodeOne, int nodeTwo)
{
// Run analysis of nodeOne and nodeTwo in graph
}
All copies of addNeed should wait for the first one that comes in to finish before another can execute. But instead they all seem to be running and conflicting with each other in the verifyClusters method. One solution would be to force our front end calls to be made synchronously. And in fact, when we do that everything works fine, so the code logic isn't broken. But when it's launched our application will be deployed within a business setting and used by internal IT staff (or at least that's the plan) so we'll have the same problem. We can't force all clients to submit data at different times, so we really need to get it synchronized on the back end. Thanks for any help you can give, I'd be glad to supply any additional information that you could need!
I wrote a series to specifically address this situation - let me know if this works for you (sequential asynchronous workflows):
Part 2 (has a link back to the part1):
http://csharperimage.jeremylikness.com/2010/03/sequential-asynchronous-workflows-part.html
Jeremy
Wrap your database updates in a transaction. Escalate to a table lock if necessary

"The calling thread must be STA, because many UI components require this." Error in WPF?

I am creating a xps document as below.
Assembly assembly = Assembly.GetExecutingAssembly();
//read embedded xpsDocument file
Stream helpStream = assembly.GetManifestResourceStream(resourceNameOfContext);
if (helpStream != null)
{
Package package = Package.Open(helpStream);
string inMemoryPackageName = "memorystream://" + topicName + ".xps";
Uri packageUri = new Uri(inMemoryPackageName);
//Add package to PackageStore
PackageStore.AddPackage(packageUri, package);
docXps = new XpsDocument(package, CompressionOption.Maximum, inMemoryPackageName);
}
return docXps;
When i am trying to get docXps.GetFixedDocumentSequence();
I am getting the above error. Can anyone help?
Thanks,
Your problem has nothing to do with the code surrounding the creation or use of the XPS document. It has everything to do with what thread you are running under.
You will receive the The calling thread must be STA, because many UI components require this error whenever any of the following are attempted on a MTA thread:
You construct any object derived from FrameworkElement (including Controls and Panels)
You construct any object derived from BitmapEffect
You construct any object derived from TextComposition
You construct any object derived from HwndSource
You access the current InputManager
You access the primary KeyboardDevice, StylusDevice, or TabletDevice
You attempt to change the focus on a FrameworkContentElement
You provide mouse, keyboard or IME input to any control that accepts text input
You make WPF content visible or update its layout
You manipulate the visual tree in such a way as to cause a re-evaluation for rendering
Several other changes, mostly having to do with display and input
For example, I received this error last year when I tried to deserialize some XAML that contained <Button> and other WPF objects from within a WCF service. The problem was simple to solve: I just switch to a STA thread to do the processing.
Obviously most work with XPS documents will trigger one or more of the above conditions. In your case I suspect that GetFixedDocumentSequence ends up using TextComposition or one of its subclasses.
No doubt the my solution of switching to a STA thread will also work for you, but first you need to figure out how your code that works with XpsDocuments is getting executed run from a MTA thread. Normally any code from from the GUI (eg a button press) is automatically run in a STA thread.
Is it possible that your code that manipulates XPS Documents may be being executed without a GUI? From a user-created thread? From a WCF service or a web service? From an ASPX page? Track that down and you'll probably find your solution.
If that doesn't work, let us know the details of the path through which GetFixedDocumentSequence is called, so we can diagnose it. The directly surrounding code isn't nearly as important as the call stack and how it is originally being invoked. If it is hard to explain you probably should add a call stack to prevent misunderstandings and help us diagnose the problem further, or tell you how to start a STA thread in your particular situation.
Is your code trying to access the xps doc from a background thread? If this is the case, you'll want to use the dispatcher. Info on that here.
If this doesn't help, could you post the code where you're actually calling GetFixedDocumentSequence()?

Can I get the instances of alive objects of a certain type in C#?

This is a C# 3.0 question. Can I use reflection or memory management classes provided by .net framework to count the total alive instances of a certain type in the memory?
I can do the same thing using a memory profiler but that requires extra time to dump the memory and involves a third party software. What I want is only to monitor a certain type and I want a light-weighted method which can go easily to unit tests. The purpose to count the alive instances is to ensure I don't have any expected living instances that cause "memory leak".
Thanks.
To do it entirely within the application you could do an instance-counter, but it would need to be explicitly coded and managed inside each class--there's no silver bullet that I'm aware of to let you query the framework from within the executing code to see how many instances are alive.
What you're asking for is really the domain of a profiler. You can purchase one or build your own, but it requires your application to run as a child process of the profiler. Rolling your own isn't an easy undertaking, by the way.
If you want to consider the instance counter it would have to be something like:
public class MyClass : IDisposable
public MyClass()
{
++ClassInstances;
}
public void Dispose()
{
--ClassInstances;
}
private static new object _ClassInstancesLock;
private static int _ClassInstances;
private static int ClassInstances
{
get
{
lock (_ClassInstancesLock)
{
return _ClassInstances
}
}
}
This is just a really rough sample, no tests for compilation; 0% guarantee for thread-safety (critical for this type of approach) and it leaves the door wide open for Dispose to be called, the instance counter to decrement, but for the object not to properly GC. To diagnose that bundle of joy you'll need, you guessed it, a professional profiler--or at least windbg.
Edit: I just noticed the very last line of your question and needed to say that my above approach, as shoddy and failure-prone as it is, is almost guaranteed to deceive and lie to you about the true number of instances if you're experiencing a leak. The best tool, IMO, for attacking these problems is ANTS Memory Profiler. Version 5 is a double-edge in that they broke Performance and Memory profiler into two seperate SKUs (used to be bundled together) but Memory Profiler 5.0 is absolutely lightning fast. Profiling these problems used to be slow as molases, but they've gotten around it somehome.
Unless this is for a personal project with 0 intent of redistribution you should invest the few hundred dollars needed for ANTS--but by all means use it's trial period first. It's a great tool for exactly this kind of analysis.
The only way I see to do this is without any form of instrumentation to use the CLR Profiling API to track object lifetimes. I'm not aware of any APIs available to the managed code to do the same thing, and, so far as I know, CLR doesn't keep the list of live objects anywhere (so even with profiler API you have to create the data structures for that yourself).
VB.NET has a feature where it lets you track objects in debugger, but it actually emits additional code specifically for that (which basically registers all created objects in internal list of weak references). You could do that as well, using e.g. PostSharp to post-process your assemblies.

Resources