我正在创建一个 COM 接口(interface),它应该允许在 Visual Basic 脚本中使用 For Each,在 C++ 中使用 IEnumVariant。问题是我不希望 C++ 客户端应用程序需要导入 mscorlib.tlb。

到目前为止,我的界面是:

[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface ICars : System.Runtime.InteropServices.ComTypes.IEnumVARIANT
{
    int Count { get; }
}

[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
public class Cars : ICars
{
    int ICars.Count => throw new NotImplementedException();

    int IEnumVARIANT.Next(int celt, object[] rgVar, IntPtr pceltFetched)
    {
        throw new NotImplementedException();
    }

    int IEnumVARIANT.Skip(int celt)
    {
        throw new NotImplementedException();
    }

    int IEnumVARIANT.Reset()
    {
        throw new NotImplementedException();
    }

    IEnumVARIANT IEnumVARIANT.Clone()
    {
        throw new NotImplementedException();
    }
}

TlbExp 吐出这段代码:
// Generated .IDL file (by the OLE/COM Object Viewer)
//
// typelib filename: carsIEnumerator.tlb

[
     uuid(3BBCEAA2-9498-48BF-8053-1CEFB3C1C86F),
     version(1.0),
     custom(90883F05-3D28-11D2-8F17-00A0C9A6186D,  "ClassLibraryIEnumerator, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")

 ]
 library ClassLibraryIEnumerator
 {
     // TLib :     // TLib : mscorlib.dll : {BED7F4EA-1A96-11D2-8F08-00A0C9A6186D}
importlib("mscorlib.tlb");
// TLib : OLE Automation : {00020430-0000-0000-C000-000000000046}
importlib("stdole2.tlb");

// Forward declare all types defined in this typelib
interface ICars;

[
  odl,
  uuid(ABD2A9E4-D5C5-3ED9-88AF-4C310BD5792D),
  version(1.0),
  dual,
  oleautomation,
  custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, "ClassLibraryIEnumerator.ICars")

]
interface ICars : IDispatch {
    [id(0x60020000), propget]
    HRESULT Count([out, retval] long* pRetVal);
};

我怎样才能避免这种情况?

即使我只有我的自定义接口(interface)和一个类(不使用任何 .NET 类型),引用仍然存在。

最佳答案

IEnumVARIANT 类型声明必须来自某个地方。它不是每个编译器都知道的像 int 这样的标准类型。如果您自己编写 IDL,那么您将使用 #import "oaidl.idl" 来包含定义。但这不能在 .NET 中工作,因为类型库导出器不使用 IDL。所以它来自一个导出商确实知道的地方,mscorlib.tlb

解决方法是将接口(interface)声明放在您自己的代码中,而不是在 mscorlib 中使用。从 Reference Source 或这个复制/粘贴它:

[Guid("00020404-0000-0000-C000-000000000046")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
[ComImport]
public interface IEnumVARIANT
{
    [PreserveSig]
    int Next(int celt, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0), Out] object[] rgVar, IntPtr pceltFetched);

    [PreserveSig]
    int Skip(int celt);

    [PreserveSig]
    int Reset();

    IEnumVARIANT Clone();
}

并在您的 ICars 声明中使用 YourNamespace.IEnumVARIANT。

声明您自己的枚举器接口(interface)类型也是一种解决方案,IEnumVARIANT 不会赢得任何奖品。你可以放弃那些没人用过的古怪方法,你可以让它类型安全。如果您也控制客户端代码或者不必在脚本语言中保留 foreach,那么这是一个可接受的替代方案。考虑:
[ComVisible(true)]
public interface ICarEnumerator {
    ICar Next();
}

以及 ICars 界面中的 ICarEnumerator GetCars()

最后但并非最不重要的一点是,考虑根本不实现迭代器。只需让它看起来像客户端代码中的数组:
[ComVisible(true)]
public interface ICars
{
    int Count { get; }
    ICar this[int index] { get; }
}

关于C# COM 实现可枚举而不引用 MSCORLIB,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/43408872/

10-10 04:45