I understand that I should release COM objects when using interop. Are things a bit different when developing and Add-In, Excel for example? Here is a loop I have and I was curious to know if the Marshal.ReleaseComObject is necessary?
foreach (var sheet in results.Sheets)
{
var newSheet = workbook.AddSheet();
newSheet.SetSheetTabColor(sheet.TabColor);
newSheet.SetSheetName(sheet.TabName);
newSheet.Cells.SetFont("Calibri", 8);
newSheet.FreezeRow(1);
var endRow = sheet.Data.GetUpperBound(0) + 1;
var endColumn = sheet.Data.GetUpperBound(1) + 1;
var writeRange = newSheet.SetWriteRange(1, 1, endRow, endColumn);
writeRange.Value2 = sheet.Data;
newSheet.AutoFitColumns();
newSheet.RemoveColumn(1);
Marshal.ReleaseComObject(newSheet);
}
Also, I created a library with extension methods. One example is workbook.AddSheet() AddSheet looks like this:
public static Worksheet AddSheet(this Microsoft.Office.Interop.Excel.Workbook workbook)
{
var sheets = workbook.Sheets;
return sheets.Add(After: sheets[sheets.Count]);
}
Since I am accessing sheets from workbook.Sheets, do I have to release of this object? If so where since I am returning a Worksheet? I can't release before I return?
This may be a dumb question, but if the Marshal.ReleaseComObject was not necessary in the foreach scope, does it hurt even if it is still there?
Marshal.ReleaseComObject is used only if you need to control the lifetime of an COM object in timely manner, or in specific order. For casual usage of COM objects i would not advice you to use this method at all.
Check here
This method is used to explicitly control the lifetime of a COM object
used from managed code. You should use this method to free the
underlying COM object that holds references to resources in a timely
manner or when objects must be freed in a specific order.
About your second question
Since I am accessing sheets from workbook.Sheets, do I have to release
of this object? If so where since I am returning a Worksheet? I can't
release before I return?
The COM implementation in .NET is using reference counting mechanism to detect if object is used or not, so NO you don't have to release anything explicitly. The framework is resposible for this.
NOTE:
Use the approriate .Close(for Workbook), .Quit(for Application) methods for proper release of resurces. When you use .Quit() over the application object you will close the excel process in windows (so this will release all resource), and .Close() over a Workbook to release file locks .. etc over specific excel file.
Related
I am developing a VSTO addin where now I am looking to optimize it. In my code, I does something
public class ExtraOrdinaryClass
{
public ExtraOrdinaryClass(Excel.Worksheet someGoodSheet)
{
tSheetName = someGoodSheet.Name;
tDesignSheet = someGoodSheet;
}
}
I just got to know I should release all the COM Objects, but I am searching for a proper way to release the someGoodSheet object in a proper way. I suspect if I do something like below is efficient
public class ExtraOrdinaryClass
{
public ExtraOrdinaryClass(Excel.Worksheet someGoodSheet)
{
tSheetName = someGoodSheet.Name;
tDesignSheet = someGoodSheet;
Marshal.ReleaseComObject(someGoodSheet);
someGoodSheet = null;
}
}
Can anyone help me if I am doing it effectively and tell me when parameter objects are collected by garbage collector?
I just got to know I should release all the COM Objects, but I am searching for a proper way to release the someGoodSheet object in a proper way
It is not necessary to worry about releasing COM objects because you are writing a VSTO add-in. VSTO add-in are in-process COM libraries loaded by the COM application, in this case Excel. The COM object represented by the Excel worksheet was created by Excel and so it has ownership. Attempting to manually release (via Marshal.ReleaseComObject) or reduce the reference count inordinately when you still have a managed reference to it (as in your second example), may crash Excel and/or your application.
Had you been writing a stand-alone process which say launched Excel via COM and fiddled with a few COM objects, then yes, you would need to ensure COM objects are released appropriately.
i.e. do not use this in your constructor:
Marshal.ReleaseComObject(someGoodSheet);
Because you are essentially saying to COM "I'm finished with it" and yet you are not because you still have a managed referenced to it via tDesignSheet. COM/Excel may choose to get rid of the worksheet from under you. The next time you go to access tDesignSheet you may encounter an exception.
Calling Marshal.ReleaseComObject on a parameter that is copied to a field is sort of like using a Font after you called Dispose() - both will lead to the undesirable situation where an obliterated object is accessed. (in the case of COM I assume the reference count reached 0)
Also it is not necessary to call this (because you have a reference to the same object anyway):
someGoodSheet = null;
Your original code (shown below) is fine:
public class ExtraOrdinaryClass
{
public ExtraOrdinaryClass(Excel.Worksheet someGoodSheet)
{
tSheetName = someGoodSheet.Name; // you arguably don't need this (just read tDesignSheet.Name)
tDesignSheet = someGoodSheet;
}
}
and tell me when parameter objects are collected by garbage collector?
I would say that in this case the .NET parameter would not be collected because now you have an additional reference to it in tDesignSheet inside ExtraOrdinaryClass. You would either need to set tDesignSheet to null and wait for the next time the GC runs, whenever that is.
Even if the .NET object is collected, .NET I suspect is smart enough to know that the COM object may still be used by other native COM clients such as Excel itself.
So, just because your .NET now-disposed objects no longer refer to the COM object, you may find the COM object could well be still active. e.g. the worksheet is still open.
I am in the process of writing an application in C# which will open an Excel spreadsheet (2007, for now) via interop, do some magic, then close. The "magic" part is non-trivial, so this application will contain many references to many COM objects spawned by Excel.
I have written this kind of application before (too many times, in fact) but I've never found a comfortable, "good smell" approach to interacting with COM objects. The problem is partly that, despite significant study, I still don't perfectly understand COM and partly that the interop wrappers hide much that probably shouldn't be hidden. The fact that there are so many different, conflicting suggestions from the community only makes matters worse.
In case you can't tell from the title, I've done my research. The title alludes to this post:
How do I properly clean up Excel interop objects?
First asked in 2008, the advice was really helpful and solid at the time (especially the "Never use 2 dots with com objects" bit) but now seems out of date. In March of 2010, the Visual Studio team posted a blog article warning fellow programmers that Marshal.ReleaseComObject [is] Considered Dangerous. The article referred to two articles, cbrumme's WebLog > ReleaseComObject and The mapping between interface pointers and runtime callable wrappers (RCWs), suggesting that people have been using ReleaseComInterop incorrectly all along (cbrumme: "If you are a client application using a modest number of COM objects that are passed around freely in your managed code, you should not use ReleaseComObject").
Does anyone have an example of a moderately complex application, preferably using multiple threads, that is able to successfully navigate between memory leaks (Excel continues running in the background after the application has closed) and InvalidComObjectExceptions? I'm looking for something which will allow a COM object to be used outside of the context in which it was created but can still be cleaned up once the application is finished with it: a hybrid of memory management strategies which can effectively straddle the managed/unmanaged divide.
A reference to an article or tutorial that discusses a correct approach to this problem would be a much appreciated alternative. My best Google-fu efforts have returned the apparently incorrect ReleaseComInterop approach.
UPDATE:
(This is not an answer)
I discovered this article not long after posting:
VSTO and COM Interop by Jake Ginnivan
I've been able to implement his strategy of wrapping COM objects in "AutoCleanup" classes via an extension method, and I'm pretty happy with the result. Though it does not provide a solution to allow COM objects to cross the boundaries of the context in which they were created and still makes use of the ReleaseComObject function, it does at least provide a neat and easy-to-read solution.
Here's my implementation:
class AutoCleanup<T> : IDisposable {
public T Resource {
get;
private set;
}
public AutoCleanup( T resource ) {
this.Resource = resource;
}
~AutoCleanup() {
this.Dispose();
}
private bool _disposed = false;
public void Dispose() {
if ( !_disposed ) {
_disposed = true;
if ( this.Resource != null &&
Marshal.IsComObject( this.Resource ) ) {
Marshal.FinalReleaseComObject( this.Resource );
} else if ( this.Resource is IDisposable ) {
( (IDisposable) this.Resource ).Dispose();
}
this.Resource = null;
}
}
}
static class ExtensionMethods {
public static AutoCleanup<T> WithComCleanup<T>( this T target ) {
return new AutoCleanup<T>( target );
}
}
did you now the NetOffice concept for COM proxy management?
NetOffice use wrapper classes for com proxies and the IDisposable pattern.
NetOffice keep the parent->child relationship for proxies. dispose a worksheet and all created childs from the instance(cells, styles), etc. was also disposed. you can also use a special event or static property to observe the count of open proxies in your application.
just take a look in this documentation snippet:
http://netoffice.codeplex.com/wikipage?title=Tec_Documentation_English_Management
you find some showstopper projects for com proxy management in the tutorials folder
All of these questions:
Excel 2007 Hangs When Closing via .NET
How to properly clean up Excel interop objects in C#
How to properly clean up interop objects in C#
struggle with the problem that C# does not release the Excel COM objects properly after using them. There are mainly two directions of working around this issue:
Kill the Excel process when Excel is not used anymore.
Take care to explicitly assign each COM object used to a variable first and to guarantee that eventually, Marshal.ReleaseComObject is executed on each.
Some have stated that 2 is too tedious and there is always some uncertainty whether you forget to stick to this rule at some places in the code. Still 1 seems dirty and error-prone to me, also I guess that in a restricted environment trying to kill a process could raise a security error.
So I've been thinking about solving 2 by creating another proxy object model which mimics the Excel object model (for me, it would suffice to implement the objects I actually need). The principle would look as follows:
Each Excel Interop class has its proxy which wraps an object of that class.
The proxy releases the COM object in its finalizer.
The proxy mimics the interface of the Interop class.
Any methods that originally returned a COM object are changed to return a proxy instead. The other methods simply delegate the implementation to the inner COM object.
Example:
public class Application
{
private Microsoft.Office.Interop.Excel.Application innerApplication
= new Microsoft.Office.Interop.Excel.Application innerApplication();
~Application()
{
Marshal.ReleaseCOMObject(innerApplication);
innerApplication = null;
}
public Workbooks Workbooks
{
get { return new Workbooks(innerApplication.Workbooks); }
}
}
public class Workbooks
{
private Microsoft.Office.Interop.Excel.Workbooks innerWorkbooks;
Workbooks(Microsoft.Office.Interop.Excel.Workbooks innerWorkbooks)
{
this.innerWorkbooks = innerWorkbooks;
}
~Workbooks()
{
Marshal.ReleaseCOMObject(innerWorkbooks);
innerWorkbooks = null;
}
}
My questions to you are in particular:
Who finds this a bad idea and why?
Who finds this a gread idea? If so, why hasn't anybody implemented/published such a model yet? Is it only due to the effort, or am I missing a killing problem with that idea?
Is it impossible/bad/error-prone to do the ReleaseCOMObject in the finalizer? (I've only seen proposals to put it in a Dispose() rather than in a finalizer - why?)
If the approach makes sense, any suggestions to improve it?
Is it impossible/bad/dangerous to do the ReleaseCOMObject in the destructor? (I've only seen proposals to put it in a Dispose() rather than in a destructor - why?)
It is recommended not to put your clean up code in the finalizer because unlike the destructor in C++ it is not called deterministically. It might be called shortly after the object goes out of scope. It might take an hour. It might never be called. In general if you want to dispose unmanaged objects you should use the IDisposable pattern and not the finalizer.
This solution that you linked to attempts to work around that problem by explicitly calling the garbage collector and waiting for the finalizers to complete. This is really not recommended in general but for this particular situation some people consider it to be an acceptable solution due to the difficulty of keeping track of all the temporary unmanaged objects that get created. But explicitly cleaning up is the proper way of doing it. However given the difficulty of doing so, this "hack" may be acceptable. Note that this solution is probably better than the idea you proposed.
If instead you want to try to explicitly clean up, the "don't use two dots with COM objects" guideline will help you to remember to keep a reference to every object you create so that you can clean them up when you're done.
We use the LifetimeScope class that was described in the MSDN magazine. Using it properly cleans up objects and has worked great with our Excel exports. The code can be downloaded here and also contains the magazine article:
http://lifetimescope.codeplex.com/SourceControl/changeset/changes/1266
Look at my project MS Office for .NET. There is solved problem with referencich wrapper objects and native objects via native VB.NET late-binding ability.
What I'd do:
class ScopedCleanup<T> : IDisposable where T : class
{
readonly Action<T> cleanup;
public ScopedCleanup(T o, Action<T> cleanup)
{
this.Object = o;
this.cleanup = cleanup;
}
public T Object { get; private set; }
#region IDisposable Members
public void Dispose()
{
if (Object != null)
{
if(cleanup != null)
cleanup(Object);
Object = null;
GC.SuppressFinalize(this);
}
}
#endregion
~ScopedCleanup() { Dispose(); }
}
static ScopedCleanup<T> CleanupObject<T>(T o, Action<T> cleanup) where T : class
{
return new ScopedCleanup<T>(o, cleanup);
}
static ScopedCleanup<ComType> CleanupComObject<ComType>(ComType comObject, Action<ComType> actionBeforeRelease) where ComType : class
{
return
CleanupObject(
comObject,
o =>
{
if(actionBeforeRelease != null)
actionBeforeRelease(o);
Marshal.ReleaseComObject(o);
}
);
}
static ScopedCleanup<ComType> CleanupComObject<ComType>(ComType comObject) where ComType : class
{
return CleanupComObject(comObject, null);
}
Usage case. Note the call to Quit, which seems to be necessary to make the process end:
using (var excel = CleanupComObject(new Excel.Application(), o => o.Quit()))
using (var workbooks = CleanupComObject(excel.Object.Workbooks))
{
...
}
For what it's worth, the Excel Refresh Service on codeplex uses this logic:
public static void UsingCOM<T>(T reference, Action<T> doThis) where T : class
{
if (reference == null) return;
try
{
doThis(reference);
}
finally
{
Marshal.ReleaseComObject(reference);
}
}
Question:
I want to ask a question in response to Mike Rosenblum's answer to this question. The question was about cleaning up Excel interop objects. Several solutions where suggested (e.g. wrappers, not using more than one dot, killing the excel process), but I liked Mike Rosenblum's solution to this problem the most (lengthy article about the topic).
What it basically says is that you don't worry too much about all the references floating around. You just keep some main ones (like ApplicationClass, Workbook and Worksheet). You first invoke garbage collection to clean up all the objects floating around and then explicitly clean up the main references you still have by calling Marshal.FinalReleaseComObject (in reverse order of importance).
Now I have two questions about this.
First: How do I determine to which objects I need to keep a reference? In Mike Rosenblum's example he only keeps Ranges, Worksheets, Workbooks and ApplicationClasses.
Second: If there are more objects, how do I determine the order of cleaning them up (i.e. the "order of importance")?
Thanks in advance.
Update 1:
It has been suggested by MattC that for the order, the only thing that is important is that the app is released last. Although in my reference the following sentence:"You should also release your named references in reverse order of importance: range objects first, then worksheets, workbooks, and then finally your Excel Application object." implies that there is more ordering.
nobugz Suggests that setting everything to null and then doing garbage collection will suffice, but that seems to contradict the following quote from Mike Rosenblum's article:"You would think, then, that you can set all your variables = Nothing and then call GC.Collect() at the end, and this does work sometimes. However, Microsoft Office applications are sensitive to the order in which the objects are released and, unfortunately, setting your variables = Nothing and then calling GC.Collect() does not guarantee the release order of the objects."
Update 2:
Some extra info:
In my own application, I do a lot of things with a chart. I am setting a lot of properties etc. As I understand, there are many places where I create new COM objects. I tried to make sure I never use double dots, and I tried to call Marshal.FinalReleaseComObject on all objects that I am finished with. I didn't use the wrapper approach because it would introduce a lot of nesting.
EXCEL.exe did not close after my app finished its work. But... it did close when I told my app to do the same work again. Of course a new EXCEL.exe opened which did not close. Now I have removed alllll the Marshal.FinalReleaseComObject calls and the app works exactly the same. The EXCEL.exe stays, until I tell my app to redo the work, but then a new EXCEL.exe starts and stays.
EDIT: Also when I tell my app to do other non-COM related work, after a while the EXCEL.exe disappears, but now no new EXCEL.exe appears.
Not sure what conclusions I can draw from this...
You should have no trouble finding possible live references in your code, they will be fields in your class(es). Or local variables in the cleanup method, that's unlikely. The list provided in the link are just objects that you most likely will store in a field. There could be others, they'll keep Excel just as alive as the Application object.
I don't think I'd recommend the jackhammer approach as advocated in the link, it just hides a potential life reference to an RCW that wraps a dead COM interface. A best you'll have a possibly permanent leak to the RCW object, at worst it crashes your program with an exception when it accidentally references the object. Bugz, to be sure, just not ones you'll easily discover. All you have to do is set your references to null, order doesn't matter, then collect.
I've had a similar problem this is what my catch and finally look like for clean up. I hope it helps.
.......
oWB._SaveAs(strCurrentDir +
strFile, XlFileFormat.xlWorkbookNormal, null, null, false, false, XlSaveAsAccessMode.xlShared, false, false, null, null);
sumsheet.Activate();
oWB.Close(null, null, null);
oXL.Workbooks.Close();
oXL.Quit();
}
catch (Exception theException)
{
theException.ToString();
}
#region COM Object Cleanup
finally
{
// Cleanup
GC.Collect();
GC.WaitForPendingFinalizers();
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(oRng);
//System.Runtime.InteropServices.Marshal.FinalReleaseComObject(sumSheet);
//System.Runtime.InteropServices.Marshal.FinalReleaseComObject(oSheet);
//oWB.Close(null, null, null);
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(oWB);
oXL.Quit();
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(oXL);
}
#endregion
EDIT
If you noticed I've commented out the sumSheet + oSheet(Which are my worksheets) because it wasn't needed. This code has solid for me with no problems. I've found by rearranging the order I've gotten errors.
I think as long as application is last you can release them in any order (as long as they aren't null).
Then do a GC.Collect to finally kill the excel.exe process.
I'm currently writing about dynamic typing, and I'm giving an example of Excel interop. I've hardly done any Office interop before, and it shows. The MSDN Office Interop tutorial for C# 4 uses the _Worksheet interface, but there's also a Worksheet interface. I've no idea what the difference is.
In my absurdly simple demo app (shown below) either works fine - but if best practice dictates one or the other, I'd rather use it appropriately.
using System;
using System.Linq;
using Excel = Microsoft.Office.Interop.Excel;
class DynamicExcel
{
static void Main()
{
var app = new Excel.Application { Visible = true };
app.Workbooks.Add();
// Can use Excel._Worksheet instead here. Which is better?
Excel.Worksheet workSheet = app.ActiveSheet;
Excel.Range start = workSheet.Cells[1, 1];
Excel.Range end = workSheet.Cells[1, 20];
workSheet.get_Range(start, end).Value2 = Enumerable.Range(1, 20)
.ToArray();
}
}
I'm trying to avoid doing a full deep-dive into COM or Office interoperability, just highlighting the new features of C# 4 - but I don't want to do anything really, really dumb.
(There may be something really, really dumb in the code above as well, in which case please let me know. Using separate start/end cells instead of just "A1:T1" is deliberate - it's easier to see that it's genuinely a range of 20 cells. Anything else is probably accidental.)
So, should I use _Worksheet or Worksheet, and why?
If I recall correctly -- and my memory on this is a bit fuzzy, it has been a long time since I took the Excel PIA apart -- it's like this.
An event is essentially a method that an object calls when something happens. In .NET, events are delegates, plain and simple. But in COM, it is very common to organize a whole bunch of event callbacks into interfaces. You therefore have two interfaces on a given object -- the "incoming" interface, the methods you expect other people to call on you, and the "outgoing" interface, the methods you expect to call on other people when events happen.
In the unmanaged metadata -- the type library -- for a creatable object there are definitions for three things: the incoming interface, the outgoing interface, and the coclass, which says "I'm a creatable object that implements this incoming interface and this outgoing interface".
Now when the type library is automatically translated into metadata, those relationships are, sadly, preserved. It would have been nicer to have a hand-generated PIA that made the classes and interfaces conform more to what we'd expect in the managed world, but sadly, that didn't happen. Therefore the Office PIA is full of these seemingly odd duplications, where every creatable object seems to have two interfaces associated with it, with the same stuff on them. One of the interfaces represents the interface to the coclass, and one of them represents the incoming interface to that coclass.
The _Workbook interface is the incoming interface on the workbook coclass. The Workbook interface is the interface which represents the coclass itself, and therefore inherits from _Workbook.
Long story short, I would use Workbook if you can do so conveniently; _Workbook is a bit of an implementation detail.
If you look at the PIA assembly (Microsoft.Office.Interop.Excel) in Reflector, the Workbook interface has this definition ...
public interface Workbook : _Workbook, WorkbookEvents_Event
Workbook is _Workbook but adds events. Same for Worksheet (sorry, just noticed you were not talking about Workbooks) ...
public interface Worksheet : _Worksheet, DocEvents_Event
DocEvents_Event ...
[ComVisible(false), TypeLibType((short) 0x10), ComEventInterface(typeof(DocEvents),
typeof(DocEvents_EventProvider))]
public interface DocEvents_Event
{
// Events
event DocEvents_ActivateEventHandler Activate;
event DocEvents_BeforeDoubleClickEventHandler BeforeDoubleClick;
event DocEvents_BeforeRightClickEventHandler BeforeRightClick;
event DocEvents_CalculateEventHandler Calculate;
event DocEvents_ChangeEventHandler Change;
event DocEvents_DeactivateEventHandler Deactivate;
event DocEvents_FollowHyperlinkEventHandler FollowHyperlink;
event DocEvents_PivotTableUpdateEventHandler PivotTableUpdate;
event DocEvents_SelectionChangeEventHandler SelectionChange;
}
I would say it's best bet to use Worksheet, but that's the difference.
Classes and Interfaces for Internal
Use Only
Avoid directly using any of the
following classes and interfaces,
which are used internally and are
typically not used directly.
Class/Interface : Examples
classid Class : ApplicationClass
(Word or Excel), WorksheetClass
(Excel)
classid Events x _SinkHelper :
ApplicationEvents4_SinkHelper (Word), WorkbookEvents_SinkHelper (Excel)
_classid : _Application (Word or Excel), _Worksheet (Excel)
classid Events x : ApplicationEvents4
(Word), AppEvents (Excel)
I classid Events x :
IApplicationEvents4 (Word), IAppEvents (Excel)
http://msdn.microsoft.com/en-gb/library/ms247299(office.11).aspx
edit: (re: formatting of this answer) cannot correctly format an escaped underscore followed immediately by italic text. Shows correctly in preview but broken when posted
edit2: works if you make the underscore itself italic which is conceptually horrible but looks the same I suppose
I have seen and written quite a bit of C# / Excel COM Interop code over the last few years and I've seen Worksheet used in almost every case. I have never seen anything definitive from Microsoft on the subject.
MSDN shows that the Worksheet interface simply inherits from the _Worksheet and DocEvents_Event interfaces. It would seem that one simply provides the events that a worksheet object might raise in additional to everything else. As far as I can see, Worksheet doesn't provide any other members of its own. So yeah, you might as well just go with using the Worksheet interface in all cases, since you don't lose anything by it, and potentially might need the events it exposes.