[英]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
. 两个自动化对象: MyContent
和MyContainer
。 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实现端从IMyContent
为IMyBase
失败。 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.