Releasing temporary COM objects(释放临时 COM 对象)
问题描述
考虑以下使用 COM 对象的 C# 代码.
<上一页><代码>MyComObject o = 新的 MyComObject;尝试{var baz = o.Foo.Bar.Baz;尝试{//用 baz 做一些事情}最后{Marshal.ReleaseComObject(baz);}}最后{Marshal.ReleaseComObject(o);}代码>这将释放 COM 对象 o 和 baz,但不会释放 o.Foo 和 o 返回的临时对象.Foo.Bar.当这些对象拥有大量非托管内存或其他资源时,这可能会导致问题.
一个明显但丑陋的解决方案是,使用 try-finally 和 Marshal.ReleaseComObject 使代码更加混乱.看C# + COM 互操作,确定性发布
作为一种解决方法,我创建了一个辅助类
<上一页><代码>类 TemporaryComObjects:IDisposable{公共 C T<C>(C comObject){m_objects.Add(comObject);返回comObject;}公共无效处置(){foreach(m_objects 中的对象 o)Marshal.ReleaseComObject(o);}}代码>用法:
<上一页><代码>使用 (TemporaryComObjects t = new TemporaryComObjects()){MyComObject o = t.T(new MyComObject);var baz = t.T(t.T(t.T(o.Foo).Bar).Baz);//用 baz 做一些事情}代码>我的问题:此代码是否存在潜在问题?有没有更优雅的解决方案?
我最大的抱怨是名字,T;Add 可能更能说明用法.我还将 where T : class 添加到通用方法中,但流利的 API"似乎可用.我也倾向于稍微扁平化代码.我还可以看到一些使用 Expression API 遍历整个树并捕获所有中间步骤的方法,但它不会是微不足道的 - 但想象一下:
使用(var com = new SomeWrapper()) {var baz = com.Add(() => new MyComObject().Foo.Bar.Baz);}这是一个表达式树,我们会自动获取中介.
(另外,您可以 Clear() 或 null Dispose() 中的列表)
像这样:
静态类 ComExample {静态无效主要(){使用 (var wrapper = new ReleaseWrapper()){var baz = wrapper.Add(() =>新的 Foo().Bar.Baz);Console.WriteLine(baz.Name);}}}类 ReleaseWrapper : IDisposable{列表<对象>对象=新列表<对象>();public T Add<T>(表达式<Func<T>> func){返回 (T)Walk(func.Body);}对象行走(表达式 expr){对象 obj = WalkImpl(expr);if (obj != null && Marshal.IsComObject(obj) && !objects.Contains(obj)){objects.Add(obj);}返回对象;}object[] Walk(IEnumerable<表达式> args){如果(args == null)返回null;return args.Select(arg => Walk(arg)).ToArray();}对象 WalkImpl(表达式 expr){开关(expr.NodeType){案例ExpressionType.Constant:返回 ((ConstantExpression)expr).Value;案例ExpressionType.New:新表达式 ne = (NewExpression)expr;返回 ne.Constructor.Invoke(Walk(ne.Arguments));案例ExpressionType.MemberAccess:MemberExpression me = (MemberExpression)expr;对象目标 = 步行(me.Expression);开关(me.Member.MemberType){案例成员类型.字段:return ((FieldInfo)me.Member).GetValue(target);案例 MemberTypes.Property:return ((PropertyInfo)me.Member).GetValue(target, null);默认:抛出新的 NotSupportedException();}案例ExpressionType.Call:MethodCallExpression mce = (MethodCallExpression)expr;返回 mce.Method.Invoke(Walk(mce.Object), Walk(mce.Arguments));默认:抛出新的 NotSupportedException();}}公共无效处置(){foreach(对象中的对象 obj){Marshal.ReleaseComObject(obj);Debug.WriteLine("已发布:" + obj);}对象.清除();}}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();
}
}
这篇关于释放临时 COM 对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:释放临时 COM 对象
基础教程推荐
- 将事件 TextChanged 分配给表单中的所有文本框 2022-01-01
- 是否可以在 asp classic 和 asp.net 之间共享会话状态 2022-01-01
- 全局 ASAX - 获取服务器名称 2022-01-01
- 经典 Asp 中的 ResolveUrl/Url.Content 等效项 2022-01-01
- 错误“此流不支持搜索操作"在 C# 中 2022-01-01
- 首先创建代码,多对多,关联表中的附加字段 2022-01-01
- 从 VS 2017 .NET Core 项目的发布目录中排除文件 2022-01-01
- 在 VS2010 中的 Post Build 事件中将 bin 文件复制到物 2022-01-01
- 如何动态获取文本框中datagridview列的总和 2022-01-01
- JSON.NET 中基于属性的类型解析 2022-01-01
