Ðаn*_*Ðаn 16 .net c# com interop com-interop
我在.NET(C#)中使用了广泛的现有COM API(可能是Outlook,但它不是).我通过在Visual Studio中添加"COM引用"来完成此操作,因此所有"魔法"都是在幕后完成的(即,我不必手动运行tlbimp).
虽然现在可以从.NET"轻松"使用COM API,但它不是非常友好的.NET.例如,没有泛型,事件很奇怪,奇怪,如IPicture等.所以,我想创建一个使用现有COM API实现的本机.NET 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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
这种方法的一个直接问题是,您可以轻松地为同一个底层"本机COM"对象结束多个ComObject实例.例如,在进行枚举时:
IEnumerable<Company.Product.Item> Items {
get {
foreach (global::Item item in Handle.Items)
yield return new Company.Product.Item(item);
}
}
Run Code Online (Sandbox Code Playgroud)
在大多数情况下,这可能是意外的.修复此问题可能看起来像
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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
那看起来更好.枚举器更改为调用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 RCW.当然,我通常宁愿用C#编写代码而不是C++/CLI ...
Kar*_*rlW 12
你自己不是在处理COM对象.您已经在处理在向项目添加对COM二进制文件的引用时创建的外观.(.NET)将为您生成一个Facade,因此简化了使用COM对象简单地使用常规.NET类的任务.如果您不喜欢为您生成的界面,则应该为现有外观创建一个外观.您不必担心COM错综复杂,因为已经为您完成了(您可能需要担心一些事情,但我认为它们很少而且很远).只需将该类用作常规.net类,因为它正是它的本质,并在出现问题时处理它们.
编辑:您可能遇到的一个问题是不确定的COM对象破坏.在幕后发生的引用计数依赖于垃圾收集,因此您无法确定何时销毁对象.根据您的应用程序,您可能需要对COM对象进行更确定的破坏.为此,您将使用Marshal.ReleaseComObject.如果是这种情况,那么你应该知道这个问题.
对不起,我会发布更多链接,但显然我不能在没有获得10个声望的情况下发布超过1个.
我发现将COM对象引入.NET的最大问题是垃圾收集器在不同的线程上运行,并且COM对象的最终版本通常(总是?)从该线程调用.
微软故意破坏了这里的COM线程模型规则,该规则规定,对于单元线程对象,必须从同一个线程调用所有方法.
对于某些COM库而言,这不是什么大问题,但对于其他库来说,这是一个很大的问题 - 特别是对于需要在析构函数中释放资源的库.
需要注意的事情......
归档时间: |
|
查看次数: |
3125 次 |
最近记录: |