jeudi 7 mai 2020

Casting to interface throws an InvalidCastException

I use C++/CLI as middleware between my C++ game server and (future) C# plugins. It's supposed to load all types from the loaded assembly which implement a certain interface called IPlakScript, construct them and return a native interface for them to the server, and it's doing exactly this, except for some reason casting the object to IPlakScript fails with an InvalidCastException. Here's my code:

C++/CLI code:

    public interface class IPlakScript
    {
    public:
        void OnScriptInit(Server^ server);
        void OnScriptShutdown();
    };
    static IScript* LoadGamemodeM(String^ name)
    {
        try {
            auto assembly = Assembly::LoadFrom(name);
            auto types = assembly->GetTypes();
            for each (auto t in types)
            {
                LogWrite("Found some class " + t->Module->FullyQualifiedName);
                LogWrite("IPlakScript mdl " + IPlakScript::typeid->Module->FullyQualifiedName);

                for each (auto i in t->GetInterfaces())
                    LogWrite("IMPLEMENTS: " + i->FullName);

                for each (auto m in t->GetMembers())
                    LogWrite("MEMBER: " + m->Name + " " + m->MemberType.ToString());

                if (t->GetInterface("PlakMP.IPlakScript")) // aka implements IPlakScript
                {
                    LogWrite("Found gamemode class " + t->FullName);
                    Object^ obj = t->GetConstructor(gcnew cli::array<System::Type^, 1> {})->Invoke(gcnew cli::array<System::Object^, 1> {});
                    for each (auto m in t->GetMethods())
                        LogWrite("METHOD: " + m->Name);
                    IPlakScript^ ps = safe_cast<IPlakScript^>(obj); // the exception is thrown here, even though the exact same operation is perfectly fine in C#: why?
                    return new Script(ps);
                    //ps->OnScriptInit(gcnew PlakMP::Server(g_pScriptable));
                }
            }
        }
        catch (Exception^ e)
        {
            String^ description = "PSEsharp exception: " + e->ToString();
            LogWrite(description);
        }
        return nullptr;
    }

C# code:

using System;
using PlakMP;

namespace CSharpGamemodeTest
{
    public class TestGamemode : IPlakScript
    {
        public TestGamemode()
        {
            Console.WriteLine("TestGamemode constructed");

            if (this is IPlakScript) // evaluates to true, thus it's not a C# problem
                Console.WriteLine("I'm PlakScript!");
        }
        public void OnScriptInit(Server server)
        {
            _server = server;

            _server.LogWrite("Hello from C#!");
        }
        public void OnScriptShutdown()
        {
            _server.LogWrite("Goodbye from C#!");
        }
        private Server _server;
    }
}

Log output:

[2020-05-07 09:34:43] Found some class E:\PlakMP\Sources\Release\plakscript\CSharpGamemodeTest.dll
[2020-05-07 09:34:43] IPlakScript mdl E:\PlakMP\Sources\Release\plakscript\PSEsharp.dll
[2020-05-07 09:34:43] IMPLEMENTS: PlakMP.IPlakScript
[2020-05-07 09:34:43] MEMBER: OnScriptInit Method
[2020-05-07 09:34:43] MEMBER: OnScriptShutdown Method
[2020-05-07 09:34:43] MEMBER: GetType Method
[2020-05-07 09:34:43] MEMBER: ToString Method
[2020-05-07 09:34:43] MEMBER: Equals Method
[2020-05-07 09:34:43] MEMBER: GetHashCode Method
[2020-05-07 09:34:43] MEMBER: .ctor Constructor
[2020-05-07 09:34:43] Found gamemode class CSharpGamemodeTest.TestGamemode
TestGamemode constructed
I'm PlakScript!
[2020-05-07 09:34:43] METHOD: OnScriptInit
[2020-05-07 09:34:43] METHOD: OnScriptShutdown
[2020-05-07 09:34:43] METHOD: GetType
[2020-05-07 09:34:43] METHOD: ToString
[2020-05-07 09:34:43] METHOD: Equals
[2020-05-07 09:34:43] METHOD: GetHashCode
[2020-05-07 09:34:43] PSEsharp exception: System.InvalidCastException: Unable to cast object of type 'CSharpGamemodeTest.TestGamemode' to type 'PlakMP.IPlakScript'.
   at PSManagedWrapper.LoadGamemodeM(String name) in E:\PlakMP\Sources\PSEsharp\PSEsharp.cpp:line 123

Admittedly, the code is a quite disgusting mess: but hey - it's my first time working with C++/CLI! Any help would be greatly appreciated, thanks in advance.





Aucun commentaire:

Enregistrer un commentaire