【问题标题】:How can I use types from a wrapped resource outside of the wrapper?如何使用包装器外部包装资源中的类型?
【发布时间】:2020-12-29 07:15:00
【问题描述】:

我正在尝试包装 COM API,以便其他人可以使用我的包装器,而无需对包装的 API 有任何了解。但是,我在尝试使用 COM API 中的某些项目时遇到了问题。

假设 COM API 有一个方法可以返回在 API 中定义的对象。

IComChildObject IComObject.GetChildObject()

如果我有对定义 COM API 的 dll 的引用,我可以按如下方式轻松使用它...

IComChildObject childObject = myComObjectInstance.GetChildObject();

我遇到的问题是,我需要能够通过包装类使用 IComChildObject,而无需引用 COM API dll。

我试图在我的包装器中创建一个接口,以实现这一点。所以在我的包装项目中,我有一个这样的界面。

public interface ILocalChildObject : IComChildObject{}

然后我在包装器中添加了一个属性,我认为它可以让我的外部代码使用 IComChildObject。

public class ComWrapper
{
    IComObject comObject;

    public ILocalChildObject ComChildObject { get { return comObject.GetChildObject() as ILocalChildObject;}}
}

当我从外部代码运行以下代码时,ChildObject 为空

ILocalChildObject ChildObject = myComWrapper.ComChildObject;

我显然做错了什么,但我对此一头雾水,甚至不知道要在 Google 上搜索什么。

也许我不清楚我要做什么,或者我正试图在这里做一些奇怪的事情。我想以这样的方式创建一个包装类库,使用它的代码不必知道任何关于包装的库。到目前为止,我做得很好,直到我需要在外部代码中使用包装库中的对象。我可以通过在外部项目中引用包装库轻松解决此问题,但我想避免这样做。

基本上,我只需要一种在我的外部代码中使用 IComChildObject 的方法,而无需添加对 COM API dll 的引用。

任何帮助将不胜感激,

【问题讨论】:

  • 如果您的 API 基于 IDispatch,您可以使用 dynamic 关键字(尚未在 .NET 核心上)docs.microsoft.com/en-us/dotnet/csharp/programming-guide/types/… 或 Type.Invoke("name")。如果不是(如果它基于 IUnknown),那么您必须在外部 dll 或 .tlb 中或直接在 C# 代码中声明此接口,以便.NET 运行时调用它。
  • @SimonMourier 感谢您的帮助。我如何知道 API 的基础是什么?我无法控制 API,只能通过 COM 访问它。我唯一可以更改的代码是包装器代码,以及使用包装器的代码。
  • @SimonMourier 谢谢你的帮助。我使用了动态,它起作用了。如果您将您的评论转换为答案,我将接受它作为正确答案。
  • 接口不是包装器,不会隔离互操作程序集引用。你需要一堂课。假设具有 IComChildObject 类型的私有字段的 ChildWrapper。就像您对 ComWrapper 所做的那样。这确实很无聊,当您看到自己必须用单行复制所有子对象属性和方法时,您会开始想知道这可能是什么意思。倾向于了解更多有关您包装的库的信息,但不起作用。

标签: c# com wrapper


【解决方案1】:

如果您的 API 基于 IDispatch,您可以使用 dynamic keyword,如下所示:

dynamic childObject = GetChildObjectSomehow();
childObject.CallAnyMethod() // compile will always succeed, will be resolved at runtime (and failed if there's like a typo error)

注意动态是not available on .NET core for COM objects yet

如果不是(如果它基于IUnknown),那么您必须在外部 dll 或 .tlb 中或直接在 C# 代码中声明此接口,以便.NET 可以调用它运行。您不必使用原始的.dll,如果需要,您可以自己重新定义接口(可能是简化版本)。但是运行时必须知道二进制布局才能调用它。

【讨论】:

  • 这似乎是最简单的解决方案。但是,它不允许我的包装器的使用者使用 IntelliSense 来查看对象的方法和属性。目前这可能是可以接受的,但我将继续寻找一种解决方案,让他们更容易创建代码。再次感谢我们的帮助。
  • @Tim - 要对 COM 对象进行智能感知,这意味着您必须以某种方式拥有 .tlb。你的 COM API 有 .tlb 吗?您可能会这样做,因为您在使用 .dll 时有智能感知工作。在这种情况下,您可以只导出 .tlb 和 .NET 用户可以引用它而不是 .dll。它们只有元数据,没有代码。
【解决方案2】:

正如 Hans Passant 指出的那样,另一种处理方法是将 COM API 对象包装在一个类中。然后您可以通过新对象访问对象内的属性。这种方法的唯一缺点是需要大量输入,因为您必须重新创建要在 COM API 对象中访问的任何属性或方法。

在包装器项目中,您将创建一个包含从 API 返回的对象的类。该类还将具有允许用户通过该类操作 API 对象的属性和方法。

public class LocalChildObject
{
    internal IComChildObject ComChildObject;
    
    public string ChildObjectProperty { get { reutrn ComChildObject.ChildObjectProperty; } set { ComChildObject.ChildObjectProperty = value ;}}
    
    public LocalChildObject(IComChildObject ComChildObject)
    {
        this.ComChildObject = ComChildObject;
    }
}

在本例中,ComChildObject 是从 API 返回的对象。然后ChildObjectProperty类中有一个属性,可以获取或设置ComChildObjectChildObjectProperty

然后在我的主包装类中,我可以有一个属性来返回这个新对象(其中包含 API COM 对象)。

public class Wrapper
{
    public LocalChildObject GetLocalChildObject { get { return new LocalChildObject(ComObject.GetChildComObject());}}
}

然后在外部代码中我可以通过新的包装对象对对象进行更改

LocalChildObject localObject = myWrapperInstance.GetLocalChildObject;
localObject.ChildObjectProperty = "A new string";

此方法需要重新创建要通过包装器公开的所有属性和方法,但是,它确实允许使用包装器的用户使用 IntelliSense。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-12-29
    • 2020-09-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多