簡體   English   中英

C#+ COM Interop,確定性發布

[英]C# + COM Interop, deterministic release

COM對象通常具有確定性破壞:在釋放最后一個引用時釋放它們。

如何處理C# - COM Interop? 這些類沒有實現IDisposable ,所以我認為無法觸發顯式的IUnknown :: Release。

隨意測試顯示未引用的COM對象被懶惰地收集(即垃圾收集器觸發釋放)。 對於需要被激活釋放的OCM對象,我該怎么辦? (例如持有大型或共享的關鍵資源)?

原始問題:我們有一個C#應用程序大量使用COM庫,它像瘋了一樣泄漏。 似乎問題是“在C ++和C#代碼之間”(我們可以訪問它們),但我們無法確定它。

您可以使用System.Runtime.InteropServices.Marshal類操作COM互操作引用。 具體來說,您可能需要查看Marshal.ReleaseComObject

我們遭受了很多苦難。 最好不要嘗試在.Net運行時加載太多的互操作引用。 此外,如果您需要立即發布內容,可以使用Marshal.ReleaseComObject API。

另一個好的方法是重構您的客戶端代碼以使用互操作代碼周圍的類型安全包裝器 - 如果您的代碼中有一個已知的引用到每個互操作RCW,這將增加interop引用將及時GC的機會。 這個想要避免的主要問題是“太多點”:

foo.bar.quux.xyzzy.groo(); // where foo, bar, quux and xyzzy are all COM references

上面代碼中點之間的每個對象都被有效泄露(可能不是長期運行),因為我們有一個對實例的隱式引用。 您需要為每個實例創建命名引用,以便有機會清理它們:

Foo foo;
Bar bar=foo.bar;
Quux quux=bar.quux;
Xyzzy xyzzy=quux.xyzzy;
xyzzy.groo();

現在可能使用運行時來釋放引用:

ReleaseComObject(xyzzy); // etc...

這是一個相關的(但略有不同)問題 ,但我認為答案非常整潔 - 所以我認為這也有必要在這里添加。

這是一個選項,它使用Expression樹來討論我們的意圖,捕獲每個節點的值 - 允許單個版本:

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 WalkImpl(Expression expr)
    {
        switch (expr.NodeType)
        {
            case ExpressionType.Constant:
                return ((ConstantExpression)expr).Value;
            case ExpressionType.New:
                NewExpression ne = (NewExpression)expr;
                object[] args = ne.Arguments.Select(arg => Walk(arg)).ToArray();
                return ne.Constructor.Invoke(args);
            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();

                }
            default:
                throw new NotSupportedException();
        }
    }
    public void Dispose()
    {
        foreach(object obj in objects) {
            Marshal.ReleaseComObject(obj);
            Debug.WriteLine("Released: " + obj);
        }
        objects.Clear();
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM