如何在 Release() 上处理 NET COM 互操作对象 [英] How to dispose of a NET COM interop object on Release()

查看:18
本文介绍了如何在 Release() 上处理 NET COM 互操作对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个用托管代码 (C++/CLI) 编写的 COM 对象.我在标准 C++ 中使用该对象.
当 COM 对象被释放时,如何强制我的 COM 对象的析构函数立即被调用?如果这不可能,请调用我的 COM 对象上的 Release() 调用 MyDispose() 方法?

我声明对象的代码(C++/CLI):

<上一页>[Guid("57ED5388-blahblah")][接口类型(ComInterfaceType::InterfaceIsIDispatch)][ComVisible(真)]公共接口类 IFoo{无效 Doit();};[Guid("417E5293-blahblah")][ClassInterface(ClassInterfaceType::None)][ComVisible(真)]公共参考类 Foo : IFoo{上市:无效我的处置();~Foo() {MyDispose();}//这永远不会被调用!Foo() {MyDispose();}//这由垃圾收集器调用.virtual ULONG Release() {MyDispose();}//这永远不会被调用虚空 Doit();};

我使用对象的代码(原生 C++):

<上一页>#import "..\Debug\Foo.tlb"...Bar::IFoo setup(__uuidof(Bar::Foo));//这个对象来自 .tlb.设置.Doit();设置->释放();//显式释放,实际上没有必要,因为 Bar::IFoo 的析构函数将调用 Release().

如果我在我的 COM 对象上放置一个析构方法,它就永远不会被调用.如果我放置一个终结器方法,它会在垃圾收集器处理它时被调用.如果我明确调用我的 Release() 覆盖,它永远不会被调用.

我真的很喜欢这样,当我的原生 Bar::IFoo 对象超出范围时,它会自动调用我的 .NET 对象的处置代码.我想我可以通过重写 Release() 来做到这一点,如果对象计数 = 0,则调用 MyDispose().但显然我没有正确覆盖 Release(),因为我的 Release() 方法从未被调用过.

显然,我可以通过将 MyDispose() 方法放在接口中并要求使用我的对象的人在 Release() 之前调用 MyDispose() 来实现这一点,但如果 R​​elease() 只是清理了对象.

是否可以在释放 COM 对象时强制立即调用 .NET COM 对象的析构函数或其他方法?

在这个问题上谷歌搜索让我得到很多点击,告诉我调用 System.Runtime.InteropServices.Marshal.ReleaseComObject(),但当然,这就是你告诉 .NET 释放 COM 对象的方式.我希望 COM Release() 处理一个 .NET 对象.

解决方案

实际上,当最后一个引用被释放时,无论是 Dispose(或者我应该说 ~Foo)还是 Release 都不会从 COM 客户端调用.它根本没有实施.这里有一些想法如何做到这一点.

http://blogs.msdn.com/oldnewthing/archive/2007/04/24/2252261.aspx#2269675

但即使作者也不建议使用该方法.

如果你也实现了 COM 客户端,最好的选择是查询 IDisposable 并显式调用 Dispose,要请求的 iid 是:

{805D7A98-D4AF-3F0F-967F-E5CF45312D2C}

我能想到的其他选择是实现某种自己的COM 垃圾收集器".COM 创建的每个对象都将放在一个列表中(前提是您的类型的对象只能由 COM 创建 - 我想不出任何方法来区分对象的创建位置).然后您必须定期检查列表,并在每个对象上调用如下内容:

IntPtr iUnk = Marshal.GetIUnknownForObject(@object);int refCount = Marshal.Release(iUnk);如果(参考计数 == 0)@object.Dispose();

但这是一个疯狂的想法.

I have a COM object written in managed code (C++/CLI). I am using that object in standard C++.
How do I force my COM object's destructor to be called immediately when the COM object is released? If that's not possible, call I have Release() call a MyDispose() method on my COM object?

My code to declare the object (C++/CLI):

    [Guid("57ED5388-blahblah")]
    [InterfaceType(ComInterfaceType::InterfaceIsIDispatch)]
    [ComVisible(true)]
    public interface class IFoo
    {
        void Doit();
    };

    [Guid("417E5293-blahblah")]
    [ClassInterface(ClassInterfaceType::None)]
    [ComVisible(true)]
    public ref class Foo : IFoo
    {
    public:
        void MyDispose();
        ~Foo() {MyDispose();} // This is never called
        !Foo() {MyDispose();} // This is called by the garbage collector.
        virtual ULONG Release() {MyDispose();} // This is never called
        virtual void Doit();
    };

My code to use the object (native C++):

#import "..\Debug\Foo.tlb"
...
Bar::IFoo setup(__uuidof(Bar::Foo)); // This object comes from the .tlb.
setup.Doit();
setup->Release(); // explicit release, not really necessary since Bar::IFoo's destructor will call Release().

If I put a destructor method on my COM object, it is never called. If I put a finalizer method, it is called when the garbage collector gets around to it. If I explicitly call my Release() override it is never called.

I would really like it so that when my native Bar::IFoo object goes out of scope it automatically calls my .NET object's dispose code. I would think I could do it by overriding the Release(), and if the object count = 0 then call MyDispose(). But apparently I'm not overriding Release() correctly because my Release() method is never called.

Obviously, I can make this happen by putting my MyDispose() method in the interface and requiring the people using my object to call MyDispose() before Release(), but it would be slicker if Release() just cleaned up the object.

Is it possible to force the .NET COM object's destructor, or some other method, to be called immediately when a COM object is released?

Googling on this issue gets me a lot of hits telling me to call System.Runtime.InteropServices.Marshal.ReleaseComObject(), but of course, that's how you tell .NET to release a COM object. I want COM Release() to Dispose of a .NET object.

解决方案

Actually neither the Dispose (or should i say ~Foo) not the Release will be called from the COM client when the last reference is released. It simply is not implemented. Here is some idea how such a thing could be done.

http://blogs.msdn.com/oldnewthing/archive/2007/04/24/2252261.aspx#2269675

But the method is not advised even by author.

If you implement the COM Client as well the best option would be to query for IDisposable and call Dispose explicitly, iid to request is:

{805D7A98-D4AF-3F0F-967F-E5CF45312D2C}

Other option I can think of is to implement some sort of own "COM Garbage Collector". Each object created by COM would be placed in a list (Provided that objects of your type are only creatable by COM - I cannot think of any method to distinguish from where the object is created). And then you would have to periodically check the list, and on each object call something like this:

IntPtr iUnk = Marshal.GetIUnknownForObject(@object);
int refCount = Marshal.Release(iUnk);
if (refCount == 0)
    @object.Dispose();

but this is some crazy idea.

这篇关于如何在 Release() 上处理 NET COM 互操作对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆