虽然现在可以从.NET轻松地使用COM API,但它并不是非常.NET友好。例如,没有泛型,事件很奇怪,像IPicture这样的特殊情况等。因此,我想创建一个本地的.NET API,使用现有的COM API进行实现。
一个简单的第一步可能是:
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);
}
}
}
这种方法的一个直接问题是,您很容易为同一基础“本机COM”对象创建多个 ComObject 实例。例如,在进行枚举时:
IEnumerable<Company.Product.Item> Items {
get {
foreach (global::Item item in Handle.Items)
yield return new Company.Product.Item(item);
}
}
这在大多数情况下可能是意料之外的。解决这个问题可能看起来像
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(item)
。
但现在问题是Dictionary<>会保持两个对象"活着",所以它们永远不会被垃圾回收;这对COM对象可能是不利的。现在事情开始变得混乱起来...看起来解决方案的一部分以某种方式使用WeakReference。还有建议使用IDisposable,但在每个对象上处理Dispose()似乎并不是很.NET友好。然后有各种讨论关于何时/是否应该调用ReleaseComObject。还有代码在http://codeproject.com上使用延迟绑定,但我满意于一个版本相关的API。
所以,目前我不确定最好的处理方式是什么。我希望我的本地.NET API尽可能像".NET"(甚至可以使用.NET 4.0中的Interop程序集),而无需使用"两个点"规则等启发式方法。
我想尝试的一件事是创建一个ATL项目,使用/clr标志进行编译,并使用C++的编译器COM支持(由#import创建的Product::ComObjectPtr)而不是.NET RCWs。当然,我通常更喜欢用C#编程而不是C++/CLI...