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