简体   繁体   English

将COM接口传递回库的正确方法是什么?

[英]What is the correct way to pass a COM Interface back to a library?

I get an System.InvalidCastException runtime error when trying to pass an interface as a parameter from .NET to a DLL. 尝试将接口作为参数从.NET传递到DLL时,我收到System.InvalidCastException运行时错误。 The code fails on the .NET side and never makes it over to Delphi side. 代码在.NET端失败,永远不会转移到Delphi端。

Construct a simple ActiveX library dll in Delphi. 在Delphi中构造一个简单的ActiveX库dll。 Two Automation Objects: MyContent and MyContainer . 两个自动化对象: MyContentMyContainer MyContainer has single method Add that takes an IBase interface that is inherited by IMyContent . MyContainer有单个方法Add ,它接受由IMyContent继承的IBase接口。 IBase is an IDispatch IBase是一个IDispatch

[
  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;
  };

};

The implementation of the classes does not matter. 类的实现无关紧要。 To keep the sample minimal the implementation of Add can be left blank. 为了使样本保持最小, Add的实现可以留空。 Here is the Container 这是容器

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.

... and the Content ......和内容

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.

Register the library and add a reference to a console application. 注册库并添加对控制台应用程序的引用。 The following code will produce the runtime error 以下代码将产生运行时错误

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);


        }
    }
}

If I create a class in .NET that implements IBase I can pass interface back successfully. 如果我在.NET中创建一个实现IBase的类,我可以成功地传回接口。

Here is the StackTrace from .NET 这是.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()

Your problem has nothing to do with how the interface is passed to your COM DLL. 您的问题与接口传递给COM DLL的方式无关。

The error System.InvalidCastException is raised simply because casting from IMyContent to IMyBase fails on the Delphi implementation side. 引发错误System.InvalidCastException只是因为在Delphi实现端从IMyContentIMyBase失败。 The reason is that strictly speaking your implementation does not directly implement IMyBase interface (even though it implements the derived IMyContent interface). 原因是严格来说,您的实现并不直接实现IMyBase接口(即使它实现了派生的IMyContent接口)。 You can try it: 你可以试试:

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

Your implementing class must implement IMyBase explicitly for the casting to work: 您的实现类必须显式实现IMyBase才能使转换工作:

明确实现所有接口

which will change the declaration of your implementing class: 这将改变您的实现类的声明:

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

which in turn will make the casting work for any COM client code (Delphi, C++, .NET, etc.) 这反过来会使任何COM客户端代码(Delphi,C ++,.NET等)的转换工作

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM