Re[5]: .Net Core Вызов виртуальных методов нативных объектов
От: pilgrim_ Россия  
Дата: 15.11.16 19:00
Оценка: 15 (1)
Здравствуйте, Serginio1, Вы писали:

S>В .Net определил интерфейс



S>
S>[ComVisible(true), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
S>   public interface ICallback
S>    {
S>        void execute();
S>    }
S>


Нужно явно задать id интерфейса (IID — GuidAttribute)..

S> и реализацию

S>
S>    HRESULT ICallback::QueryInterface(REFIID riid, void ** ppvObject)
S>    {
S>        if (!ppvObject)
S>            return E_INVALIDARG;
S>        *ppvObject = NULL;
S>    //    if (riid == IID_IUnknown)
S>    //    {
S>            // Increment the reference count and return the pointer.
S>            *ppvObject = (LPVOID)this; //тут кастовать нужно к требуемому интерфейсу
S>            return NOERROR;
S>  //        }
S>        return E_NOINTERFACE;
S>    }

S>


См. выделенное
-ты должен проверять поддерживаемые тобой интерфейсы (как минимум IUnknown)
-и возвращать правильную ссылку на них (с пом. приведения — чтобы получить правильное смещение на vtable)

S> Так вот перед вызовом execute сначала запрашиваеется QueryInterface с двумя riid IUnknown и IProvideClassInfo

S>Если не дать IProvideClassInfo то начинает запрашивать Idispatch и еще какие то интерфейсы.

Ты должен правильно реагировать на эти запросы — реализуешь — возвращай, нет — досвидания, в твоем случае как минимум 2 интерфейса ты поддерживаешь — IUnknown и твой ICallback

Рабочий пример (хост — .net приложение, в случае хостинга как у тебя из нативного приложения результат должен быть такой же)

  нативный код (dll)
#include <windows.h>
#include <iostream>


//COM sample

class ICallback : public IUnknown
{
public:
    virtual HRESULT __stdcall execute() = 0;
    virtual HRESULT __stdcall dispatch(ICallback* other) = 0;
};


extern "C"  __declspec(dllexport) ICallback* __stdcall UnmanagedWrapCallback(ICallback* cb);

// {FFB46654-083E-486A-94B8-E28B5C01561D}
static const GUID IID_ICallback =
{ 0xffb46654, 0x83e, 0x486a,{ 0x94, 0xb8, 0xe2, 0x8b, 0x5c, 0x1, 0x56, 0x1d } };


class UnmanagedCallback : public ICallback
{
    ULONG _refCount;
    ICallback* _other;
public:
    UnmanagedCallback(ICallback* other) : _refCount(1), _other(other)
    {
        std::cout << "ctor UnmanagedCallback" << std::endl;
        _other->AddRef();
    }

    ~UnmanagedCallback()
    {
        std::cout << "dtor UnmanagedCallback" << std::endl;
        _other->Release();
    }

    //IUnknown
    HRESULT __stdcall QueryInterface(REFIID riid, void ** ppvObject) override
    {
        OLECHAR* strGuid;
        StringFromCLSID(riid, &strGuid);
        std::wcout << L"UnmanagedCallback.QueryInterface. Requested IID: " << strGuid << std::endl;
        CoTaskMemFree(strGuid);
            
        if (ppvObject == nullptr) return E_INVALIDARG;

        if (riid == IID_IUnknown)
        {
            *ppvObject = static_cast<IUnknown*>(this);
            AddRef();
            return S_OK;
        }
        else if (riid == IID_ICallback)
        {
            *ppvObject = static_cast<ICallback*>(this);
            AddRef();
            return S_OK;
        }

        *ppvObject = nullptr;

        return E_NOINTERFACE;
    }

    ULONG __stdcall AddRef(void) override
    {
        std::cout << "UnmanagedCallback.AddRef" << std::endl;
        auto cnt = ++_refCount;
        return cnt;
    }

    ULONG __stdcall Release(void) override
    {
        std::cout << "UnmanagedCallback.Release" << std::endl;
        auto cnt = --_refCount;
        if (cnt == 0)
        {
            delete this;
        }
        return cnt;
    }

    //ICallback

    HRESULT __stdcall execute() override
    {
        std::cout << "UnmanagedCallback.execute" << std::endl;
        _other->execute();
        return S_OK;
    }

    HRESULT __stdcall dispatch(ICallback* other) override
    {
        std::cout << "UnmanagedCallback.dispatch" << std::endl;
        if (other == nullptr) return E_INVALIDARG;

        _other->dispatch(other);

        return S_OK;
    }
};


extern "C" __declspec(dllexport) ICallback* __stdcall UnmanagedWrapCallback(ICallback* other)
{
    auto ucb = new UnmanagedCallback(other);
    return ucb;
}


  ComCallback.cs — .net консольное приложение
    class Program
    {
        #region COM interop

        [ComVisible(true), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        [Guid("FFB46654-083E-486A-94B8-E28B5C01561D")]
        public interface ICallback
        {
            void Execute();

            void Dispatch(ICallback other);
        }

        public class ManagedCallback : ICallback
        {
            public void Execute()
            {
                Console.WriteLine($"{nameof(ManagedCallback)}.{nameof(Execute)}");
            }

            public void Dispatch(ICallback other)
            {
                Console.WriteLine($"{nameof(ManagedCallback)}.{nameof(Dispatch)}");
                other.Execute();
            }
        }

        [DllImport("Win32Project1.dll", CallingConvention = CallingConvention.StdCall)]
        private static extern ICallback UnmanagedWrapCallback(ICallback cb);

        #endregion COM interop


        static void Main()
        {
            Console.WriteLine("Press Enter key to continue...");
            //Console.ReadLine();

            TestComCallback();
        }

        static void TestComCallback()
        {
            var mcb = new ManagedCallback();
            mcb.Execute();

            WrapUnmanaged(mcb);
        }

        static void WrapUnmanaged(ICallback cb)
        {
            var ucb = UnmanagedWrapCallback(cb);

            try
            {
                ucb.Execute();
                ucb.Dispatch(cb);
            }
            finally
            {
                Marshal.ReleaseComObject(ucb);
            }
        }
    }



Проверил на .NET Core dotnet-dev-win-x86.1.0.0-preview2-003131 — все работает как и в "большом" .NET, значит как минимум для windows поддержка COM-interop реализованна.

  выхлоп
Project ComInterop (.NETCoreApp,Version=v1.0) was previously compiled. Skipping compilation.
Press Enter key to continue...
ManagedCallback.Execute
ctor UnmanagedCallback
UnmanagedCallback.QueryInterface. Requested IID: {00000000-0000-0000-C000-000000000046}
UnmanagedCallback.AddRef
UnmanagedCallback.QueryInterface. Requested IID: {B196B283-BAB4-101A-B69C-00AA00341D07}
UnmanagedCallback.AddRef
UnmanagedCallback.QueryInterface. Requested IID: {ECC8691B-C1DB-4DC0-855E-65F6C551AF49}
UnmanagedCallback.QueryInterface. Requested IID: {94EA2B94-E9CC-49E0-C0FF-EE64CA8F5B90}
UnmanagedCallback.QueryInterface. Requested IID: {00000003-0000-0000-C000-000000000046}
UnmanagedCallback.QueryInterface. Requested IID: {00000144-0000-0000-C000-000000000046}
UnmanagedCallback.Release
UnmanagedCallback.QueryInterface. Requested IID: {FFB46654-083E-486A-94B8-E28B5C01561D}
UnmanagedCallback.AddRef
UnmanagedCallback.AddRef
UnmanagedCallback.Release
UnmanagedCallback.Release
UnmanagedCallback.execute
ManagedCallback.Execute
UnmanagedCallback.dispatch
ManagedCallback.Dispatch
ManagedCallback.Execute
UnmanagedCallback.Release
UnmanagedCallback.Release
dtor UnmanagedCallback
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.