我使用以下脚本从.Net 6 DLL注册COM对象.\regsvr32.exe .\MyLibrary.comhost.dll 此外,在DLL中,它通过 [DllImport("oleaut32.dll")] internal extern static int RegisterActiveObject([MarshalAs(UnmanagedType.IUnknown)] object punk, ref Guid rclsid, uint dwFlags, ref int pdwRegister); 在我可以使用Marshal.GetActiveObject("MyLibrary.MyClassName")得到这个对象之后,我可以得到这个对象,但是 System.Runtime.InteropServices.COMException: 'Typelib export: Type library is not registered. (Exception from HRESULT: 0x80131165)'当我试图从这个对象调用一个方法时抛出异常.你能帮忙解决这个问题吗?

推荐答案

发生的事情是,微软已经从.NET Core(所有版本)的COM Callable wrapper实现中移除了对ITypeInfo的支持,该对象也实现了IDispatch.https://github.com/dotnet/runtime/issues/86751这里简要讨论了这一点,实际上在文档中也提到了:

enter image description here

与GitHub的讨论似乎暗示的相反,这是一个巨大的突破性变化,会带来很多麻烦,很难诊断,也没有容易的解决方案.

当您try 按对象上的名称(如myadd)调用任何成员时,dynamic代码使用IDispatch::GetTypeInfo检索ITypeInfo引用:

检索对象的类型信息,然后可以使用该信息 获取接口的类型信息.

HRESULT GetTypeInfo( UINT iTInfo, LCID LCID, ITypeInfo**ppTInfo );

现在,让问题更加复杂的是,您的调用代码是.NET框架,并且两者的dynamic实现是不同的:

.NET框架(即使类型信息计数=0也引发):

internal static ITypeInfo GetITypeInfoFromIDispatch(IDispatch dispatch, bool throwIfMissingExpectedTypeInfo)
{
  int errorCode = dispatch.TryGetTypeInfoCount(out var pctinfo);
  Marshal.ThrowExceptionForHR(errorCode); // noooooooo !
  if (pctinfo == 0)
    return null;
  ...
}

这就是最终导致System.Runtime.InteropServices.COMException: 'Typelib export: Type library is not registered. (Exception from HRESULT: 0x80131165)'错误的原因

.NET核心(更新,不那么愚蠢):

internal static ComTypes.ITypeInfo GetITypeInfoFromIDispatch(IDispatch dispatch)
{
    int hresult = dispatch.TryGetTypeInfoCount(out uint typeCount);
    if (typeCount == 0)
    {
        // COM objects should return a type count of 0 to indicate that type info is not exposed.
        // Some COM objects may return a non-success HRESULT when type info is not supported, so
        // we only check the count and not the HRESULT in this case.
        return null;
    }

    Marshal.ThrowExceptionForHR(hresult); // yeeeeeees !
    ...
}

有多种解决方案:

IDispatch based:如果您想继续使用dynamic,您可以使用.NET Core客户端或使用"旧的"反射方式,如下所示(不使用ITypeInfo,直接转到IDispatch GetIDsOfNamesInvoke):

var sum = obj.GetType().InvokeMember(
    "myadd",
    BindingFlags.Public | BindingFlags.InvokeMethod,
    null,
    obj,
    new object[] { 5, 6 });

IUnknown based:由于您已经定义了双ICalculator接口(IDispatch+IUnknown派生),您可以使用它来代替使用后期绑定(IDispatch)方法.

为此,系统必须知道该接口的详细信息,以便能够跨线程/进程/机器进行编组,即:它需要一个.TLB(还请确保您坚持使用already known data types,否则您将需要一个.NET无法访问的代理/存根.dll).因此,您必须1)从.NET代码导出.TLB(类型库文件,您也可以将其嵌入.dll或.exe中),2)使用例如dscom注册它,这是一个非官方工具,可以很好地使用此示例代码(Microsoft还删除了.NET Framework regasm中存在的.TLB生成和注册功能).确保你在x64中构建了所有东西,并在x64中使用dscom(而不是x86),类似于这样:

dscom.exe tlbexport c:\temp\core dll\bin\Debug\net6.0\Library1.dll
dscom.exe tlbregister Library1.tlb

然后像这样更改您的调用.NET Framework代码:

var calc = (ICalculator)Marshal.GetActiveObject("Library1.Calculator");
var res = calc.myadd(5, 6);
TextBox1.Text = res.ToString();

PS:还有一个更复杂的解决方案,那就是在.NET核心宿主端的对象上公开一个包装器,而不是对象本身,它将重新实现IDispatch,但不会在.NET框架调用IDispatch::GetTypeInfo和内部调用对象的IDispatch实现时报告任何错误

Csharp相关问答推荐

使用GeneratedComInterfaceProperty的.NET 8 COM类对于VB 6/SYS或ALEViewer不可见

等待限制选项似乎不适用于频道

C#将参数传递给具有变化引用的变量

如何在Visual Studio代码中更改大括号模式{},用于C#语言

获取具有AutoFaces的所有IOptions对象的集合

为什么在使用动态obj+类obj时会调用串联?

如何在毛伊岛应用程序中完美地同步视图模型和视图的加载?

取决于您的数据量的多个嵌套循环

C#Null判断处理失败

正在try 从Blazor中的API读取JSON

如何在CSharp中将json字符串转换为DataTable?

为什么@rendermode Interactive Auto不能在.NET 8.0 Blazor中运行?

使用CollectionView时在.NET Maui中显示数据时出现问题

Postgres ENUM类型在第一次运行时对Dapper不可见

是否可以从IQueryable T中获取一个IdentyEntry T>

为什么我的自定义Json.net转换器不工作?

Cmd中的&ping.end()";有时会失败,而";ping";总是有效

C#;AvaloniaUI;MVVM;当另一个窗口上的按钮被单击时,如何更新视图图像源?

如何将行添加到DataGrid以立即显示它?

无法停止PowerShell中的低级挂钩(c#挂钩)