Releasing temporary COM objects
Consider the following C# code using a COM object.
MyComObject o = new MyComObject;
try
{
var baz = o.Foo.Bar.Baz;
try
{
// do something with baz
}
finally
{
Marshal.ReleaseComObject(baz);
}
}
finally
{
Marshal.ReleaseComObject(o);
}
This will release the COM objects o
and baz
, but not the temporary objects returnd by o.Foo
and o.Foo.Bar
. This can cause problems, when those objects hold a large amount of unmanaged memory or other resources.
An obvious but ugly solution would be, to clutter the code even more with try-finally
and Marshal.ReleaseComObject
. See C# + COM Interop, deterministic release
As a workaround, I created a helper class
class TemporaryComObjects: IDisposable
{
public C T<C>(C comObject)
{
m_objects.Add(comObject);
return comObject;
}
public void Dispose()
{
foreach (object o in m_objects)
Marshal.ReleaseComObject(o);
}
}
Usage:
using (TemporaryComObjects t = new TemporaryComObjects())
{
MyComObject o = t.T(new MyComObject);
var baz = t.T(t.T(t.T(o.Foo).Bar).Baz);
// do something with baz
}
My questions: Are there potential problems with this code? Has anybody a more elegant solution?
My biggest gripe would be the name, T
; Add
might be more illusrative of the usage. I'd also add where T : class
to the generic method, but the "fluent API" seems usable. I'd also be inclined to flatten the code a bit. I can also see some ways of using the Expression
API to walk an entire tree and capture all the intermediate steps, but it wouldn't be trivial - but imagine:
using(var com = new SomeWrapper()) {
var baz = com.Add(() => new MyComObject().Foo.Bar.Baz);
}
where that is an expression tree and we get the intermediaries automatically.
(also, you could Clear()
or null
the list in Dispose()
)
Like so:
static class ComExample {
static void Main()
{
using (var wrapper = new ReleaseWrapper())
{
var baz = wrapper.Add(
() => new Foo().Bar.Baz);
Console.WriteLine(baz.Name);
}
}
}
class ReleaseWrapper : IDisposable
{
List<object> objects = new List<object>();
public T Add<T>(Expression<Func<T>> func)
{
return (T)Walk(func.Body);
}
object Walk(Expression expr)
{
object obj = WalkImpl(expr);
if (obj != null && Marshal.IsComObject(obj) && !objects.Contains(obj))
{
objects.Add(obj);
}
return obj;
}
object[] Walk(IEnumerable<Expression> args)
{
if (args == null) return null;
return args.Select(arg => Walk(arg)).ToArray();
}
object WalkImpl(Expression expr)
{
switch (expr.NodeType)
{
case ExpressionType.Constant:
return ((ConstantExpression)expr).Value;
case ExpressionType.New:
NewExpression ne = (NewExpression)expr;
return ne.Constructor.Invoke(Walk(ne.Arguments));
case ExpressionType.MemberAccess:
MemberExpression me = (MemberExpression)expr;
object target = Walk(me.Expression);
switch (me.Member.MemberType)
{
case MemberTypes.Field:
return ((FieldInfo)me.Member).GetValue(target);
case MemberTypes.Property:
return ((PropertyInfo)me.Member).GetValue(target, null);
default:
throw new NotSupportedException();
}
case ExpressionType.Call:
MethodCallExpression mce = (MethodCallExpression)expr;
return mce.Method.Invoke(Walk(mce.Object), Walk(mce.Arguments));
default:
throw new NotSupportedException();
}
}
public void Dispose()
{
foreach(object obj in objects) {
Marshal.ReleaseComObject(obj);
Debug.WriteLine("Released: " + obj);
}
objects.Clear();
}
}
The solution of Marc Gravell won't work with .Net 4.+ because of introduction of Dynamic in COM instead of object. Furthermore, when testing with the Excel COM, there is an exception with the constructor saying "Convert not supported" (default of the switch of WalkImpl).
There are others limitations with Expressions as no indexed properties and no optional arguments. Never coded Expression before then I have no idea how to address these problems.
Won't compile or execute:
using (var wrapper = new ComWrapper())
{
var application = wrapper.Add(() => new Excel.Application());
var workbook = wrapper.Add(() => application.Workbooks.Open(@"C:MyExcel.xls"));
Excel.Range range = wrapper.Add(() => workbook.Sheets[1].UsedRange);
string value = wrapper.Add(() => range.Cells[1, 1]).Value2;
}
链接地址: http://www.djcxy.com/p/35646.html
上一篇: 使用Wrapper对象来正确清理Excel互操作对象
下一篇: 释放临时COM对象