Я использую обширный существующий COM-API (может быть, Outlook, но это не так) в .NET(С#). Я сделал это, добавив "COM-ссылку" в Visual Studio, поэтому все "волшебство" выполняется за кулисами (т.е. Мне не нужно вручную запускать tlbimp).
В то время как COM API теперь можно "легко" использовать из .NET, он не очень дружелюбен к .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);
}
}
}
Одной из ближайших проблем с этим подходом является то, что вы можете легко получить несколько экземпляров ComObject для одного и того же основного объекта "native COM". Например, при перечислении:
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)
.
Но теперь проблема заключается в том, что Словарь < > сохранит оба объекта "живыми", чтобы они никогда не собирались собирать мусор; это, вероятно, плохо для COM-объекта. И теперь все начинает запутываться...
Похоже, что часть решения использует WeakReference в некотором роде. Есть также рекомендации об использовании IDisposable, но это не выглядит очень дружественным .NET, чтобы иметь дело с Dispose() на каждом один объект. И затем там различные обсуждения, когда/если ReleaseComObject должен быть вызван. Существует также code над http://codeproject.com, который использует последнее связывание, но я доволен API-интерфейсом, зависящим от версии.
Итак, на данный момент я не совсем уверен, что это лучший способ для продолжения. Я бы хотел, чтобы мой собственный .NET API был как можно более ".NET" (возможно, даже встраивал сборку Interop с .NET 4.0) и не имел необходимости использовать эвристику, как правило "двух точек".
Одна вещь, которую я думал о попытке создания проекта ATL, скомпилировать с флагом /clr и использовать поддержку COM компилятора С++ (Product:: ComObjectPtr, созданная с помощью #import), а не .NET. RCWS. Конечно, я обычно скорее код в С#, чем С++/CLI...