【问题标题】:What is the correct way to pass a COM Interface back to a library?将 COM 接口传回库的正确方法是什么?
【发布时间】:2016-09-14 17:44:56
【问题描述】:

尝试将接口作为参数从 .NET 传递到 DLL 时,我收到 System.InvalidCastException 运行时错误。代码在 .NET 端失败,并且永远不会在 Delphi 端运行。

在 Delphi 中构造一个简单的 ActiveX 库 dll。两个自动化对象:MyContentMyContainerMyContainer 具有单一方法 Add,它采用由 IMyContent 继承的 IBase 接口。 IBaseIDispatch

[
  uuid(E29018FF-F142-4BAE-B7A4-AE0A8847E930),
  version(1.0)

]
library DelphiComLib
{

  importlib("stdole2.tlb");

  interface IMyBase;
  interface IMyContainer;
  coclass MyContainer;
  interface IMyContent;
  coclass MyContent;


  [
    uuid(07D17021-9E7F-4D7A-B861-59A35EC686A0),
    dual,
    oleautomation
  ]
  interface IMyBase: IDispatch
  {
  };

  [
    uuid(A6AB6F7D-8BEB-459F-A2F8-BC06FF81A45D),
    helpstring("Dispatch interface for MyContainer Object"),
    dual,
    oleautomation
  ]
  interface IMyContainer: IDispatch
  {
    [id(0x000000C9)]
    HRESULT _stdcall Add([in] IMyBase* AMyBase);
  };

  [
    uuid(AB82964C-13D7-423B-9B16-A789D5D30421),
    helpstring("Dispatch interface for MyContent Object"),
    dual,
    oleautomation
  ]
  interface IMyContent: IMyBase
  {
  };

  [
    uuid(DDDF77E5-E6A6-4429-BD4A-D9695E9E6CED),
    helpstring("MyContainer Object")
  ]
  coclass MyContainer
  {
    [default] interface IMyContainer;
  };

  [
    uuid(134BF8B2-30C3-4C37-8F74-3F677808300A),
    helpstring("MyContent Object")
  ]
  coclass MyContent
  {
    [default] interface IMyContent;
  };

};

类的实现无关紧要。为了使样本最小化,Add 的实现可以留空。这是容器

unit Unit1;

{$WARN SYMBOL_PLATFORM OFF}

interface

uses
  ComObj, ActiveX, DelphiComLib_TLB, StdVcl;

type
  TMyContainer = class(TAutoObject, IMyContainer)
  protected
    procedure Add(const AMyBase: IMyBase); safecall;

  end;

implementation

uses ComServ;

procedure TMyContainer.Add(const AMyBase: IMyBase);
begin

end;

initialization
  TAutoObjectFactory.Create(ComServer, TMyContainer, Class_MyContainer,
    ciMultiInstance, tmApartment);
end.

...和内容

unit Unit2;

{$WARN SYMBOL_PLATFORM OFF}

interface

uses
  ComObj, ActiveX, DelphiComLib_TLB, StdVcl;

type
  TMyContent = class(TAutoObject, IMyContent)
  protected

  end;

implementation

uses ComServ;

initialization
  TAutoObjectFactory.Create(ComServer, TMyContent, Class_MyContent,
    ciMultiInstance, tmApartment);
end.

注册库并添加对控制台应用程序的引用。以下代码会产生运行时错误

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DelphiComLib;
namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            IMyContainer container = new MyContainer();
            IMyContent content = new MyContent();
            container.Add(content);


        }
    }
}

如果我在 .NET 中创建一个实现 IBase 的类,我可以成功地传回接口。

这是来自 .NET 的 StackTrace

   at System.StubHelpers.InterfaceMarshaler.ConvertToNative(Object objSrc, IntPtr itfMT, IntPtr classMT, Int32 flags)
   at DelphiComLib.IMyContainer.Add(IMyBase AMyBase)
   at ConsoleApplication2.Program.Main(String[] args) in c:\users\jaspers\documents\visual studio 2015\Projects\ConsoleApplication2\ConsoleApplication2\Program.cs:line 15
   at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
   at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
   at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
   at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()

【问题讨论】:

    标签: c# delphi com delphi-10.1-berlin


    【解决方案1】:

    您的问题与接口如何传递给您的 COM DLL 无关。

    引发错误System.InvalidCastException 仅仅是因为在Delphi 实现方面从IMyContent 转换为IMyBase 失败。原因是严格来说,您的实现并不直接实现IMyBase 接口(即使它实现了派生的IMyContent 接口)。你可以试试:

    var
      MyContent: IMyContent;
      MyBase: IMyBase;
    begin
      MyContent := CoMyContent.Create;
      MyBase := MyContent as IMyBase; // EIntfCastError is raised
    end;
    

    您的实现类必须显式实现IMyBase,才能使强制转换工作:

    这将改变你的实现类的声明:

    type
      TMyContent = class(TAutoObject, IMyContent, IMyBase)
      ...
      end;
    

    这反过来将使转换适用于任何 COM 客户端代码(Delphi、C++、.NET 等)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-12-29
      • 1970-01-01
      • 2017-06-16
      • 2010-11-07
      • 2010-10-17
      • 1970-01-01
      • 2016-12-16
      • 2019-09-14
      相关资源
      最近更新 更多