I get an System.InvalidCastException
runtime error when trying to pass an interface as a parameter from .NET to a DLL. The code fails on the .NET side and never makes it over to Delphi side.
Construct a simple ActiveX library dll in Delphi. Two Automation Objects: MyContent
and MyContainer
. MyContainer
has single method Add
that takes an IBase
interface that is inherited by IMyContent
. IBase
is an 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. 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.
Here is the StackTrace from .NET
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.
The error System.InvalidCastException
is raised simply because casting from IMyContent
to IMyBase
fails on the Delphi implementation side. The reason is that strictly speaking your implementation does not directly implement IMyBase
interface (even though it implements the derived IMyContent
interface). 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:
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.)
User contributions licensed under CC BY-SA 3.0