如何包装一个本地.NET类的COM对象? [英] How can I wrap a COM object in a native .NET class?

查看:210
本文介绍了如何包装一个本地.NET类的COM对象?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用的是一个广泛存在的COM API(可能是Outlook中,但它不是)的.NET(C#)。我已经通过添加在Visual Studio中的COM引用,使所有的魔术是在幕后做了这样做(即我没有手动运行 TLBIMP

I'm using an extensive existing COM API (could be Outlook, but it's not) in .NET (C#). I've done this by adding a "COM Reference" in Visual Studio so all the "magic" is done behind the scenes (i.e., I don't have to manually run tlbimp).

虽然COM API现在可以从.NET使用的容易时,它是不很.NET友好。例如,没有泛型,事件很奇怪,喜欢的 <一个奇特href="http://stackoverflow.com/questions/2210594/convert-an-icon-to-ipicture-in-net-4-0">IPicture,等等。所以,我想创建一个使用现有的COM API实现本机.NET API。

While the COM API can now be "easily" used from .NET, it is not very .NET friendly. For example, there are no generics, events are strange, oddities like IPicture, etc. So, I'd like to create a native .NET API that is implemented using the existing COM API.

一个简单的第一阶段可能是

A simple first pass might be

namespace Company.Product {
   class ComObject {
       public readonly global::Product.ComObject Handle; // the "native" COM object
       public ComObject(global::Product.ComObject handle) {
          if (handle == null) throw new ArgumentNullException("handle");
          Handle = handle;
       }

       // EDIT: suggestions from nobugz
       public override int GetHashCode() {
          return Handle.GetHashCode();
       }
       public override bool Equals(object obj) {
          return Handle.Equals(obj);
       }
   }
}

一个直接的问题,这种方法是,你可以轻松地结束了的 ComObject 的对于相同的底层本地COM对象多个实例。例如,做一个枚举时:

One immediate problem with this approach is that you can easily end up with multiple instances of ComObject for the same underlying "native COM" object. For example, when doing an enumeration:

IEnumerable<Company.Product.Item> Items {
   get {
      foreach (global::Item item in Handle.Items)
         yield return new Company.Product.Item(item);
   }
}

这很可能是意想不到的,在大多数情况下。解决这个问题可能类似于

This would probably be unexpected in most situations. Fixing this problem might look like

namespace Company.Product {
   class ComObject {
       public readonly global::Product.ComObject Handle; // the "native" COM object
       static Dictionary<global::Product.ComObject, ComObject> m_handleMap = new Dictionary<global::Product.ComObject, ComObject>();
       private ComObject(global::Product.ComObject handle) {
          Handle = handle;
          handleMap[Handle] = this;
       }
       public ComObject Create(global::Product.ComObject handle) {
          if (handle == null) throw new ArgumentNullException("handle");

          ComObject retval;
          if (!handleMap.TryGetValue(handle, out retval))
              retval = new ComObject(handle);
          return retval;             
       }
   }
}

这看起来更好。枚举变化调用 Company.Product.Item.Create(项目)。 但现在的问题是的词典&LT;> 的将保持这两个对象活着,所以他们永远不会被垃圾收集;这很可能是坏的COM对象。而事情开始天色已渐渐凌乱...

That looks better. The enumerator changes to call Company.Product.Item.Create(item). But now the problem is the Dictionary<> will keep both objects "alive" so they will never be garbage collected; this is likely bad for the COM object. And things start getting messy now...

它看起来像解决方案的是使用的 <一个部分href="http://blogs.msdn.com/jaredpar/archive/2009/03/03/building-a-weakreference-hashtable.aspx">WeakReference某种程度上来说。也有<一个href="http://radio-weblogs.com/0105852/stories/2002/12/21/comInteropNotFundamentallyFlawedButHard.html">suggestions有关使用的的IDisposable 的,但它似乎并不十分.NET友好在所有要处理的的Dispose()的每一个对象。再有就是各种讨论当/如果的 ReleaseComObject的应该叫。还有 code 超过上的 HTTP://$c$cproject.com 使用后期绑定,但我很高兴与版本相关API

It looks like part of the solution is using a WeakReference in some way. There are also suggestions about using IDisposable but it doesn't seem very .NET-friendly at all to have to deal with Dispose() on every single object. And then there's the various discussions of when/if ReleaseComObject should be called. There is also code over on http://codeproject.com that uses late binding, but I'm happy with a version-dependent API.

所以,在这一点上我真的不知道什么是继续进行的最佳方式。我想我的家乡.NET API是为.NET类成为可能(甚至嵌入Interop程序集与.NET 4.0)和W / O不必采用像两个点规则启发。

So, at this point I'm not really sure what is the best way to proceed. I'd like my native .NET API to be as ".NET-like" as possible (maybe even embedding the Interop assembly with .NET 4.0) and w/o having to employ heuristics like the "two dots" rule.

有一件事我想尝试是创建一个ATL项目,在 / CLR 标志编译和使用C ++的编译器COM支持(产品:: ComObjectPtr 的创建人#进口的),而不是.NET的RCW。当然,我一般倒是<一href="http://stackoverflow.com/questions/2185321/what-are-the-advantages-of-doing-100-managed-development-using-c-cli">rather code在C#比C ++ / CLI ...

One thing I thought of trying is to create an ATL project, compile with the /clr flag and use the C++'s compiler COM support (Product::ComObjectPtr created by #import) rather than .NET RCWs. Of course, I'd generally rather code in C# than C++/CLI...

推荐答案

我发现把COM对象到.NET最大的问题是,垃圾收集器在不同的线程和COM的最​​终版本上运行的事实对象往往会(永远?)被称为从那个线程。

The biggest problem I've found with bringing COM objects into .NET is the fact that the garbage collector runs on a different thread and the final release of the COM object will often (always?) be called from that thread.

微软故意在这里打破了COM线程模型规则,其中规定与单元线程对象,所有方法都必须来自同一个线程调用。

Microsoft deliberately broke the COM threading model rules here which state that with apartment threaded objects, all methods must be called from the same thread.

对于某些COM库,这不是什么大不了的事,但对其他人来说是一个很大的问题 - 特别是对于那些需要释放资源的析构函数库

For some COM libraries this is not a big deal, but for others it's a huge problem - particularly for libraries that need to release resources in their destructors.

有什么要注意的?

这篇关于如何包装一个本地.NET类的COM对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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