COM对象的创先争优互操作清理
比方说,我有一个组件,它是做与工作簿对象的东西,在某处该方法的身体我有打电话给另一个类的一些方法的中间。
例如:
Lets say that I have one component which is doing something with Workbook object and somewhere in the middle of that method body I have call to some method of another class. For example:
public class MainComponent
{
public void MyMainMethod()
{
OtherComponent otherComponent = new OtherComponent();
Workbook document;
// some work with workbook object
// working with document and worksheet objects.
otherComponent.MethodCall(document);
// some work with workbook object and it's worksheets.
foreach(Worksheet sheet in document.Workheets)
// do something with sheet
}
}
public class OtherComponent
{
public void MethodCall(Workbook document)
{
string worksheetNames = "";
foreach(Worksheet sheet in document.Worksheets)
worksheetNames += sheet.Name;
Console.WriteLine(worksheetNames);
}
}
和在otherComponent.MethodCall(文件);我使用的文件,我迭代通过它的工作表。
And in that otherComponent.MethodCall(document); I'm using document and I'm iterating through it's worksheets.
修改要在问题更具体。我应该otherComponent.MethodCall(文件),或不叫ReleaseComObject的文件上和工作表?
EDIT TO be more concrete on question. Should I call ReleaseCOMObject on document and on Worksheets in otherComponent.MethodCall(document) or not?
我从来没有真正对我应该如何管理这个非托管代码的任何好解释。
我会很感激,如果有人可以给我讲解一下。
I never really had any good explanation on how should I manage this unmanaged code. I would really appreciate if someone could explain this to me.
你必须释放所有本地对象手动在你创建它们的范围。当通过自动化使用Office应用程序,不依赖垃圾收集器来清理这些对象 - 即使它得到它的权利,它可能需要时间才能踢,你可能最终与临时对象持有其他对象的引用你认为已经消失了。
You'll have to release all local objects manually in the scope where you create them. When using Office applications through Automation, don't rely on the garbage collector to clean up these objects - even if it gets it right, it may take time for it to kick in and you may end up with temporary objects holding references to other objects that you think are already gone.
的这是一个有点相关的问题与如果您尝试从与Excel被隐藏您的应用程序运行Excel可能适用于你更多的细节。
This is a somewhat related question with more details that may apply to you if you try to run Excel from your application with Excel being hidden.
这是给你绝对相关的部分是这样的:
The part that's definitely relevant to you is this:
- 裹使用Excel中的每一个功能
在try..catch
块捕捉到任何可能的例外。 - 始终明确通过调用释放所有Excel对象
元帅.ReleaseComObject()
,然后尽快设置您的变量空
因为你不需要他们。始终在最后
块释放这些对象,以确保发生故障的Excel的方法调用不会导致悬空COM对象。 - 当发生错误的时候,请关闭您使用Excel的实例。这不太可能,你可以从Excel相关的错误中恢复的时间越长你保持实例,时间越长,它使用的资源。
- 当您退出Excel,请务必警惕的代码对递归调用 - 如果你的异常处理程序尝试关闭Excel的,而你的代码已被关停的Excel的过程中,你会死的Excel实例结束
- 呼叫
GC.Collect的()
和GC.WaitForPendingFinalizers()
调用Application.Quit(后右)
的方法,以确保在.NET Framework释放所有的Excel COM对象立即
- Wrap every single function that uses Excel in a
try..catch
block to capture any possible exception. - Always explicitly release all Excel objects by calling
Marshal.ReleaseComObject()
and then setting your variables tonull
as soon as you don't need them. Always release these objects in afinally
block to make sure that a failed Excel method call won't result in a dangling COM object. - When an error happens, close the Excel instance that you're using. It's not likely that you can recover from Excel-related errors and the longer you keep the instance, the longer it uses resources.
- When you quit Excel, make sure that you guard that code against recursive calls - if your exception handlers try to shut down Excel while your code is already in the process of shutting down Excel, you'll end up with a dead Excel instance.
- Call
GC.Collect()
andGC.WaitForPendingFinalizers()
right after calling theApplication.Quit()
method to make sure that the .NET Framework releases all Excel COM objects immediately.
修改:这是你增加了更多的细节,你的问题后,的
在 otherComponent
你不需要释放工作簿
和文件
的对象。这两个对象在这暗示着第一个目的是所有者的第一个对象创建。因为它是拥有顶层Excel对象(假设你也有一个应用
对象的地方),你的第一个对象可以调用的第一个对象 otherComponent
,传递工作簿
和文件
,然后返回,清理它们。如果你从来没有使用任何这些对象在你的 MainComponent
,那么你应该建立在 otherComponent
Excel的相关对象和清理它们在那里。
In otherComponent
you don't need to release the Workbook
and Document
objects. These two objects are created in your first object which implies that the first object is the owner. Since it's the first object that owns your top-level Excel objects (assuming you also have an Application
object somewhere), your first object can call otherComponent
, pass in Workbook
and Document
and then on return, clean them up. If you never use any of these objects in your MainComponent
, then you should create the Excel-related objects inside otherComponent
and clean them up there.
通过COM互操作,应该创建尽可能靠近,你需要他们,并明确只要你可以释放他们的地方你的COM对象。这是Office应用程序尤其如此
With COM interop, you should create your COM objects as close to the place where you need them and release them explicitly as soon as you can. This is especially true for Office applications.
我做了这个类使用COM对象更容易使:这个包装是一次性的,所以你可以使用使用 (...)
您的COM对象 - 当使用
范围已经过去,包装释放COM对象
I made this class to make using COM objects easier: this wrapper is disposable, so you can use using(...)
with your COM objects - when the using
scope is over, the wrapper releases the COM object.
using System;
using System.Runtime.InteropServices;
namespace COMHelper
{
/// <summary>
/// Disposable wrapper for COM interface pointers.
/// </summary>
/// <typeparam name="T">COM interface type to wrap.</typeparam>
public class ComPtr<T> : IDisposable
{
private object m_oObject;
private bool m_bDisposeDone = false;
/// <summary>
/// Constructor
/// </summary>
/// <param name="oObject"></param>
public ComPtr ( T oObject )
{
if ( oObject == null )
throw ( new ArgumentNullException ( "Invalid reference for ComPtr (cannot be null)" ) );
if ( !( Marshal.IsComObject ( oObject ) ) )
throw ( new ArgumentException ( "Invalid type for ComPtr (must be a COM interface pointer)" ) );
m_oObject = oObject;
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="oObject"></param>
public ComPtr ( object oObject ) : this ( (T) oObject )
{
}
/// <summary>
/// Destructor
/// </summary>
~ComPtr ()
{
Dispose ( false );
}
/// <summary>
/// Returns the wrapped object.
/// </summary>
public T Object
{
get
{
return ( (T) m_oObject );
}
}
/// <summary>
/// Implicit cast to type T.
/// </summary>
/// <param name="oObject">Object to cast.</param>
/// <returns>Returns the ComPtr object cast to type T.</returns>
public static implicit operator T ( ComPtr<T> oObject )
{
return ( oObject.Object );
}
/// <summary>
/// Frees up resources.
/// </summary>
public void Dispose ()
{
Dispose ( true );
GC.SuppressFinalize ( this );
}
/// <summary>
/// Frees up resurces used by the object.
/// </summary>
/// <param name="bDispose">When false, the function is called from the destructor.</param>
protected void Dispose ( bool bDispose )
{
try
{
if ( !m_bDisposeDone && ( m_oObject != null ) )
{
Marshal.ReleaseComObject ( m_oObject );
m_oObject = null;
}
}
finally
{
m_bDisposeDone = true;
}
}
}
}