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

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

问题描述

我在.NET(C#)中使用了大量现有的COM API(可能是Outlook,但不是)。我这样做通过在Visual Studio中添加一个COM引用,所有的魔术是在幕后完成的(即,我不必手动运行 tlbimp )。



虽然COM API现在可以从.NET轻松使用,但它不是非常友好的。例如,没有泛型,事件是奇怪的,像 IPicture 等。因此,我想创建一个使用现有COM API实现的本机.NET API。



简单的第一遍可能是

 命名空间Company.Product {
class ComObject {
public readonly global :: Product.ComObject Handle; //nativeCOM对象
public ComObject(global :: Product.ComObject handle){
if(handle == null)throw new ArgumentNullException(handle);
Handle = handle;
}

//编辑:nobugz的建议
public override int GetHashCode(){
return Handle.GetHashCode();
}
public override bool Equals(object obj){
return Handle.Equals(obj);
}
}
}

此方法的一个直接问题是,对于同一底层的本地COM对象,您可以轻松地结束多个实例 ComObject 。例如,当进行枚举时:

  IEnumerable< Company.Product.Item>项目{
get {
foreach(global :: Item项目在Handle.Items)
yield return New Company.Product.Item(item);
}
}

这在大多数情况下可能是意想不到的。修复此问题可能类似于

  namespace Company.Product {
class ComObject {
public readonly global: :Product.ComObject句柄; //nativeCOM对象
静态字典< 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(item)
但现在的问题是字典<> 将保持这两个对象活着,所以他们永远不会被垃圾收集;这对COM对象很可能是坏的。现在事情开始变得麻烦...



它看起来像解决方案的一部分使用 WeakReference 。还有有关使用 IDisposable的建议 然后是/ if的各种讨论应调用 ReleaseComObject 。在代码 .comrel =nofollow noreferrer> http://codeproject.com 使用晚期绑定,但我对一个版本相关的API感到高兴。



所以,在这一点上,我不知道什么是最好的办法。我希望我的本地.NET API尽可能像.NET一样(甚至可能嵌入Interop程序集与.NET 4.0)和w / o必须使用启发式的两点规则。 p>

我想到的一件事是创建一个ATL项目,使用 / clr 标志编译并使用C ++的编译器COM支持产品:: ComObjectPtr 由 #import 创建),而不是.NET RCW。当然,我通常会 C#/ C#中的代码C#/

into .NET是垃圾收集器在不同的线程上运行的事实,COM对象的最终发布将经常(总是?)从该线程调用。



Microsoft故意打破了COM线程模型规则,其中声明了使用公寓线程对象,所有方法必须从同一个线程调用。



对于一些COM库,这不是一个大问题,但对其他人来说,这是一个巨大的问题 - 特别是需要释放其析构器中的资源的库。



需要注意的事项...


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).

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);
       }
   }
}

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;             
       }
   }
}

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...

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.

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.

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...

解决方案

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.

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.

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.

Something to be aware of...

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

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