【问题标题】:'InvalidCastException' while trying to cast COM class to interface尝试将 COM 类转换为接口时出现“InvalidCastException”
【发布时间】:2021-02-10 02:11:05
【问题描述】:

我用 C# 写了一个非常简单的 DLL,它看起来像这样:

[InterfaceType(ComInterfaceType.InterfaceIsDual)]
[Guid("EAA4976A-45C3-4BC5-BC0B-E474F4C3C83F")]
[ComVisible(true)]
public interface IComClass1
{
    void Do();
}

[Guid("0D53A3E8-E51A-49C7-944E-E72A2064F933"),
    ProgId("MyComProgId"),
    ComVisible(true)]
public class ComClass1 : IComClass1
{
    void IComClass1.Do()
    {
        System.Windows.Forms.MessageBox.Show("ComClass1 ^^^^^ Test");
    }
}

我的客户端看起来像这样:

[InterfaceType(ComInterfaceType.InterfaceIsDual)]
[Guid("EAA4976A-45C3-4BC5-BC0B-E474F4C3C83F")]
public interface IComClass1
{
    void Do();
}

class Program
{
    static void Main(string[] args)
    {
        var type = Type.GetTypeFromProgID("MyComProgId");
        var com = (IComClass1)Activator.CreateInstance(type); // ERROR: System.InvalidCastException
        com.Do();
    }
}

由于某种原因,此处出现错误:InvalidCastException

为什么会出现错误?我错了吗?我该如何解决?

【问题讨论】:

  • COM 接口需要[InterfaceType(ComInterfaceType.InterfaceIsXXX)] 属性。
  • @SimonMourier 在哪里添加?
  • 作为两个接口声明的属性
  • @SimonMourier 没有帮助。
  • 它确实有帮助,因为 .NET COM 在没有这个的情况下不起作用。但是,由于您是从 .NET 到 .NET,因此它会保持 .NET 调用,并且 COM 不在循环中。在通用 .NET 库或您在两个项目中引用的 TLB 中定义 IComClass1 接口。或者不要在Program中定义IComClass1,用dynamicC#关键字声明com的类型(不再是intellisense)。

标签: c# .net com


【解决方案1】:

由于 .NET 认为 COM 对象是一个 .NET 对象,它无法在 IComClass1 接口的两个定义之间建立等价关系,因此出现了无效强制转换异常。 一种解决方案是在独立于 .idl 文件的 .tlb 文件(类型库)中声明通用 COM 接口。

要创建 .TLB,您可以想象使用 regasm 将 .NET 组件导出为 TLB,不幸的是它可以工作,但生成的 .TLB 不能被 .NET 项目引用(同样,没有 .NET->COM- >.NET)。

不过,这仍然是一个好的开始,因为您可以查看来自 Windows SDK 的带有 OleView 的生成 .TLB,并基本上删除所有表明它是 .NET 导出的 .tlb 的“自定义”IDL 属性。

所以,假设我在 C# 中有这个定义:

namespace ClassLibrary2
{
    [InterfaceType(ComInterfaceType.InterfaceIsDual)]
    [Guid("EAA4976A-45C3-4BC5-BC0B-E474F4C3C83F")]
    [ComVisible(true)]
    public interface IComClass1
    {
        void Do();
    }
}

例如,我编译并运行它(或使用 TlbExp):

c:\MyProject\ClassLibrary2\bin\Debug>tlbexp ClassLibrary2.dll
Microsoft (R) .NET Framework Assembly to Type Library Converter 4.8.3928.0
Copyright (C) Microsoft Corporation.  All rights reserved.

Assembly exported to 'c:\MyProject\ClassLibrary2\bin\Debug\ClassLibrary2.tlb'

这是它在 OleView 中的样子:

如果您尝试从 .NET 项目中引用它,它会说:

---------------------------
Microsoft Visual Studio
---------------------------
A reference to 'c:\MyProject\ClassLibrary2\bin\Debug\ClassLibrary2.tlb' could not be added. Please make sure that the file is accessible, and that it is a valid assembly or COM component.
---------------------------
OK   
---------------------------

恕我直言,这真是愚蠢。无论如何,因此,您必须修改 .tlb。将 OleView 结果复制粘贴到一些 test.idl 文本文件中,删除所有 "custom" idl attributes,如下所示:

[
  uuid("your tlb guid here"),
  version(1.0),

]
library ClassLibrary2 // some name or keep ClassLibrary2
{
    importlib("mscorlib.tlb");
    importlib("stdole2.tlb");

    [
      odl,
      uuid(EAA4976A-45C3-4BC5-BC0B-E474F4C3C83F), // your interface id
      version(1.0),
      dual,
      oleautomation,

    ]
    interface IComClass1 : IDispatch { // if the interface is IDispatch
        [id(0x60020000)]
        HRESULT Do();
    };
};

现在在 test.idl 文件上运行 MIDL compiler

c:\MyProject\ClassLibrary2\ClassLibrary1>midl test.idl
Microsoft (R) 32b/64b MIDL Compiler Version 8.01.0622
Copyright (c) Microsoft Corporation. All rights reserved.
Processing .\test.idl
test.idl
Processing C:\Program Files (x86)\Windows Kits\10\include\10.0.19041.0\um\oaidl.idl
oaidl.idl
Processing C:\Program Files (x86)\Windows Kits\10\include\10.0.19041.0\um\objidl.idl
objidl.idl
Processing C:\Program Files (x86)\Windows Kits\10\include\10.0.19041.0\um\unknwn.idl
unknwn.idl
Processing C:\Program Files (x86)\Windows Kits\10\include\10.0.19041.0\shared\wtypes.idl
wtypes.idl
Processing C:\Program Files (x86)\Windows Kits\10\include\10.0.19041.0\shared\wtypesbase.idl
wtypesbase.idl
Processing C:\Program Files (x86)\Windows Kits\10\include\10.0.19041.0\shared\basetsd.h
basetsd.h
Processing C:\Program Files (x86)\Windows Kits\10\include\10.0.19041.0\shared\guiddef.h
guiddef.h
Processing C:\Program Files (x86)\Windows Kits\10\include\10.0.19041.0\um\oaidl.acf
oaidl.acf

这将创建一个 .TLB,然后您现在可以使用标准 .NET 工具在两个项目中引用。请注意,它包含的类型应该自动嵌入到您的程序集中,无需发送 .tlb 或任何其他二进制文件。

【讨论】:

  • 在客户端出现错误:无法解析 COM 引用“cca3d0bd-33f0-4ffb-8d67-a24d1e596e2e”1.0 版。类型库导入器在类型验证期间遇到错误。尝试在没有类成员的情况下导入。
  • 请在oleview中查看midl生成的新的.tld,它仍然包含customidl属性。
  • 并非所有自定义属性都有问题“DE77BA64-517C-11D1-A2DA-0000F8773CE9”很好(来自 MIDL)。仅必须删除 .NET 属性(如“0F21F359-AB84-41E8-9A78-36D110E6D2F9”或 GUID_ManagedName)。我测试并为我工作得很好。有时,VS 添加参考 UI 不起作用。尝试 tlbimp test2.tlb 创建一个互操作 .dll 并引用它。
  • 很遗憾,我做了很多不同的尝试,都没有成功。
  • 你能把你的idl贴在某个地方吗
猜你喜欢
  • 1970-01-01
  • 2020-10-13
  • 2011-11-15
  • 2019-11-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-21
相关资源
最近更新 更多