.Net Core Вызов виртуальных методов нативных объектов
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 12.11.16 13:37
Оценка:
Возможно ли вызвать виртуальные методы С++ классов из .Net Core.

Я могу вызвать статические и объектные методы через
Marshal.GetDelegateForFunctionPointer с

[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
или
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
например

https://github.com/Marqin/simpleCoreCLRHost/blob/master/Managed.cs

Можно ли в .Net Core использовать
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)

Или другой интерфейс для доступа к виртуальным методам С++ класса, без методов IUncknown напрямую?

Да и как там в .Net Core C++/CLI ?
и солнце б утром не вставало, когда бы не было меня
Отредактировано 12.11.2016 13:53 Serginio1 . Предыдущая версия .
Re: .Net Core Вызов виртуальных методов нативных объектов
От: rm822 Россия  
Дата: 12.11.16 19:40
Оценка: +1
Короткий ответ — нет.
Ты и в обычном .net их толком не вызовешь без type library или IDispatch потому что
а) для вирт. метода нужно знать индекс в таблице вируальных функций
б) этих таблиц может быть несколько, и надо знать индекс таблицы
в) данные базового класса могут иметь смещение и его тоже нужно знать

Извраты есть, но о них рассказывать нет никакого желания, потому что гомосятина
Re[2]: .Net Core Вызов виртуальных методов нативных объектов
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 12.11.16 21:02
Оценка:
Здравствуйте, rm822, Вы писали:

R>Короткий ответ — нет.

R>Ты и в обычном .net их толком не вызовешь без type library или IDispatch потому что
R>а) для вирт. метода нужно знать индекс в таблице вируальных функций
Ну так описывая класс виртуальными методами мы и описываем VMT
По сути это ничем не отличается от IUnknown
R>б) этих таблиц может быть несколько, и надо знать индекс таблицы
Это как? Интерфейсы и множественное наследование не рассматриваем. Только VMT. При этом на С++ нет проблем их использовать.
R>в) данные базового класса могут иметь смещение и его тоже нужно знать
Данные не интересуют. Интересуют только объект первым полем является ссылка на VMT.
И описание этой VMT на C#. И почему это не сделать для совместимости в .Net Core c нативным кодом?

А контракт можно прописать как то так

[InterfaceType(NativeInterfaceType.VMT)]
interface ICallback
{
    void execute();
}
и солнце б утром не вставало, когда бы не было меня
Отредактировано 13.11.2016 6:23 Serginio1 . Предыдущая версия . Еще …
Отредактировано 12.11.2016 21:13 Serginio1 . Предыдущая версия .
Отредактировано 12.11.2016 21:13 Serginio1 . Предыдущая версия .
Re: .Net Core Вызов виртуальных методов нативных объектов
От: turbocode  
Дата: 12.11.16 21:54
Оценка:
S> Или другой интерфейс для доступа к виртуальным методам С++ класса, без методов IUncknown напрямую?

Нет, нужно завернуть С++ в COM библиотеку.
Re[2]: .Net Core Вызов виртуальных методов нативных объектов
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 13.11.16 06:20
Оценка:
Здравствуйте, turbocode, Вы писали:

S>> Или другой интерфейс для доступа к виртуальным методам С++ класса, без методов IUncknown напрямую?


T>Нет, нужно завернуть С++ в COM библиотеку.

Для .Net Core пойдет?
и солнце б утром не вставало, когда бы не было меня
Re[3]: .Net Core Вызов виртуальных методов нативных объектов
От: turbocode  
Дата: 13.11.16 08:26
Оценка:
T>>Нет, нужно завернуть С++ в COM библиотеку.
S> Для .Net Core пойдет?

Думаю что пойдет при условии что ты согласен потерять переносимость.
Re: .Net Core Вызов виртуальных методов нативных объектов
От: α Российская Империя  
Дата: 13.11.16 09:06
Оценка: 10 (1)
Здравствуйте, Serginio1, Вы писали:

S> Возможно ли вызвать виртуальные методы С++ классов из .Net Core.


В мсдн пишут что в принципе можно, но саму структуру классов с++ надо заранее проектировать под интеграцию с .net. Для остальных случаев рекомендуют оборачивать общение с классами с++ в с-api
Re[4]: .Net Core Вызов виртуальных методов нативных объектов
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 13.11.16 10:03
Оценка:
Здравствуйте, turbocode, Вы писали:

T>>>Нет, нужно завернуть С++ в COM библиотеку.

S>> Для .Net Core пойдет?

T>Думаю что пойдет при условии что ты согласен потерять переносимость.

нет мне нужна кроссплатформенность. На самом деле
interface ICLRRuntimeHost2 : ICLRRuntimeHost


Кроссплатформенный.
https://github.com/dotnet/coreclr/blob/32f0f9721afb584b4a14d69135bea7ddc129f755/src/inc/MSCOREE.IDL


Посмотрю потддерживает ли .Net Core
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
и солнце б утром не вставало, когда бы не было меня
Re[2]: .Net Core Вызов виртуальных методов нативных объектов
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 13.11.16 10:05
Оценка:
Здравствуйте, α, Вы писали:

α>Здравствуйте, Serginio1, Вы писали:


S>> Возможно ли вызвать виртуальные методы С++ классов из .Net Core.


α>В мсдн пишут что в принципе можно, но саму структуру классов с++ надо заранее проектировать под интеграцию с .net. Для остальных случаев рекомендуют оборачивать общение с классами с++ в с-api


Мне как раз и нужно вызывать нативные методы из .Net

Кроссплатформенное использование классов .Net из неуправляемого кода. Или аналог IDispatch на Linux
и солнце б утром не вставало, когда бы не было меня
Re[5]: .Net Core Вызов виртуальных методов нативных объектов
От: turbocode  
Дата: 13.11.16 11:18
Оценка:
S> нет мне нужна кроссплатформенность. На самом деле
S>
S>interface ICLRRuntimeHost2 : ICLRRuntimeHost
S>

S> Кроссплатформенный.
S>https://github.com/dotnet/coreclr/blob/32f0f9721afb584b4a14d69135bea7ddc129f755/src/inc/MSCOREE.IDL
S> Посмотрю потддерживает ли .Net Core
S>
S>[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
S>


А чем тебе это поможет?
Я не слышал чтобы в .Net Core была возможность создавать кроссплатформенные COM объекты (да и еще на С++). P.S. Было бы неплохо конечно, но думаю этого мы никогда не дождемся.
Re[6]: .Net Core Вызов виртуальных методов нативных объектов
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 13.11.16 11:27
Оценка:
Здравствуйте, turbocode, Вы писали:

S>> нет мне нужна кроссплатформенность. На самом деле

S>>
S>>interface ICLRRuntimeHost2 : ICLRRuntimeHost
S>>

S>> Кроссплатформенный.
S>>https://github.com/dotnet/coreclr/blob/32f0f9721afb584b4a14d69135bea7ddc129f755/src/inc/MSCOREE.IDL
S>> Посмотрю потддерживает ли .Net Core
S>>
S>>[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
S>>


T>А чем тебе это поможет?

T>Я не слышал чтобы в .Net Core была возможность создавать кроссплатформенные COM объекты (да и еще на С++). P.S. Было бы неплохо конечно, но думаю этого мы никогда не дождемся.

Так суть COM это VMT. А она по сути кроссплатформенна.
А вот насчет кроссплатформенного использования ICLRRuntimeHost2 то здесь https://github.com/Marqin/simpleCoreCLRHost/blob/master/simpleCoreCLRHost.cpp

используют статические функции

хотя можно и так
http://www.fancy-development.net/hosting-net-core-clr-in-your-own-process
и солнце б утром не вставало, когда бы не было меня
Re[7]: .Net Core Вызов виртуальных методов нативных объектов
От: turbocode  
Дата: 13.11.16 11:58
Оценка:
S> Так суть COM это VMT. А она по сути кроссплатформенна.

С каких это пор? Можно примеры таких С++ COM объектов?
P.S. Я не спорю что MS мог для своих нужд это сделать, но для общего использования ничего подобного нету.
Re[8]: .Net Core Вызов виртуальных методов нативных объектов
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 13.11.16 12:55
Оценка:
Здравствуйте, turbocode, Вы писали:

S>> Так суть COM это VMT. А она по сути кроссплатформенна.


T>С каких это пор? Можно примеры таких С++ COM объектов?

T>P.S. Я не спорю что MS мог для своих нужд это сделать, но для общего использования ничего подобного нету.

Ну я тебе показал ICLRRuntimeHost2.
Так суть интерфейса это VMT. А классы с виртуальными методами кроссплатформенны.
В 1С это прекрасно работает

class IInitDoneBase
{
public:
    virtual ~IInitDoneBase() {}
    /// Initializes component
    /**
     *  @param disp - 1C:Enterpise interface
     *  @return the result of
     */
    virtual bool ADDIN_API Init(void* disp) = 0;
    /// Sets the memory manager
    /*
     * @param mem - pointer to memory manager interface.
     *  @return the result of
     */
    virtual bool ADDIN_API setMemManager(void* mem) = 0;

    /// Returns component version
    /**
     *  @return - component version (2000 - version 2)
     */
    virtual long ADDIN_API GetInfo() = 0;

    /// Uninitializes component
    /**
     *  Component here should release all consumed resources.
     */
    virtual void ADDIN_API Done() = 0;

};
///////////////////////////////////////////////////////////////////////////
/**
 *  The given interface defines methods that are intented to be used by the Platform
 */
/// Interface describing extension of language.



где

#ifndef  ADDIN_API
#ifndef __linux__
#define ADDIN_API __stdcall
#else
//#define ADDIN_API __attribute__ ((__stdcall__))
#define ADDIN_API
#endif //__linux__
#endif //ADDIN_API
и солнце б утром не вставало, когда бы не было меня
Re[9]: .Net Core Вызов виртуальных методов нативных объектов
От: turbocode  
Дата: 13.11.16 13:21
Оценка:
S> Ну я тебе показал ICLRRuntimeHost2.
Это внутренности, для себя MS может сделать какой то кроссплатформенный обрубок COM, но это не значит что ты сможешь этим воспользоваться.

S> Так суть интерфейса это VMT. А классы с виртуальными методами кроссплатформенны.

S> В 1С это прекрасно работает

[example skiped]

Не знаю что там в 1С но в твоем примере COM-ом и не пахнет.
Re[10]: .Net Core Вызов виртуальных методов нативных объекто
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 13.11.16 14:08
Оценка:
Здравствуйте, turbocode, Вы писали:



T>Не знаю что там в 1С но в твоем примере COM-ом и не пахнет.

COM это те же виртуальные методы. Та же VMT (vtbl).

https://msdn.microsoft.com/ru-ru/library/5hhehwba.aspx
http://www.developing.ru/com/iunknown.html
и солнце б утром не вставало, когда бы не было меня
Отредактировано 13.11.2016 14:45 Serginio1 . Предыдущая версия .
Re[11]: .Net Core Вызов виртуальных методов нативных объекто
От: turbocode  
Дата: 13.11.16 15:04
Оценка:
T>>Не знаю что там в 1С но в твоем примере COM-ом и не пахнет.
S> COM это те же виртуальные методы. Та же VMT (vtbl).
S>https://msdn.microsoft.com/ru-ru/library/5hhehwba.aspx
S>http://www.developing.ru/com/iunknown.html

И чем тебе это поможет? Инструмента создавать кроссплатформенные С++ COM объекты нету так как MFC/ATL не кроссплатформенны.
Re[12]: .Net Core Вызов виртуальных методов нативных объекто
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 13.11.16 15:09
Оценка:
Здравствуйте, turbocode, Вы писали:

T>>>Не знаю что там в 1С но в твоем примере COM-ом и не пахнет.

S>> COM это те же виртуальные методы. Та же VMT (vtbl).
S>>https://msdn.microsoft.com/ru-ru/library/5hhehwba.aspx
S>>http://www.developing.ru/com/iunknown.html

T>И чем тебе это поможет? Инструмента создавать кроссплатформенные С++ COM объекты нету так как MFC/ATL не кроссплатформенны.


VMT кроссплатформенна. Мне не нужен COM как таковой. Мне нужен контракт на основе VMT между нативным и манагед кодом
и солнце б утром не вставало, когда бы не было меня
Re[13]: .Net Core Вызов виртуальных методов нативных объекто
От: turbocode  
Дата: 13.11.16 15:18
Оценка:
S>VMT кроссплатформенна. Мне не нужен COM как таковой. Мне нужен контракт на основе VMT между нативным и манагед кодом

Не будет COM-а не будет и контракта, точнее бинарной совместимости. Но если тебе не нужно строить объектные модели то как сказали тебе ниже сделай себе сишную обертку и не парся.
Re[14]: .Net Core Вызов виртуальных методов нативных объекто
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 13.11.16 16:43
Оценка:
Здравствуйте, turbocode, Вы писали:

S>>VMT кроссплатформенна. Мне не нужен COM как таковой. Мне нужен контракт на основе VMT между нативным и манагед кодом


T>Не будет COM-а не будет и контракта, точнее бинарной совместимости. Но если тебе не нужно строить объектные модели то как сказали тебе ниже сделай себе сишную обертку и не парся.


В топике я и так использую
Marshal.GetDelegateForFunctionPointer

с
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]

или
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]


Интересно существует ли возможность использовать интерфейсы или виртуальные методы?
По сути то это несложно если соблюдать тип вызова и VMT
Я и не особо парюсь.
и солнце б утром не вставало, когда бы не было меня
Re[15]: .Net Core Вызов виртуальных методов нативных объекто
От: turbocode  
Дата: 13.11.16 17:26
Оценка: 10 (1)
S> Интересно существует ли возможность использовать интерфейсы или виртуальные методы?
Напрямую нет, только: 1. С-style обертка; 2. COM; 3. грязный хак;
Re[3]: .Net Core Вызов виртуальных методов нативных объектов
От: α Российская Империя  
Дата: 14.11.16 08:01
Оценка:
Здравствуйте, Serginio1, Вы писали:

α>>В мсдн пишут что в принципе можно, но саму структуру классов с++ надо заранее проектировать под интеграцию с .net. Для остальных случаев рекомендуют оборачивать общение с классами с++ в с-api


S> Мне как раз и нужно вызывать нативные методы из .Net


Всё равно все сводится к соответствующей подготовке кода на С++ к interop/pinvoke. А это либо как уже тут сказали — COM, либо обертка в виде С-plain-API, либо вот такие фокусы — https://blogs.msdn.microsoft.com/vcblog/2008/12/08/inheriting-from-a-native-c-class-in-c/
Re[7]: .Net Core Вызов виртуальных методов нативных объектов
От: Lexey Россия  
Дата: 14.11.16 10:31
Оценка:
Здравствуйте, Serginio1, Вы писали:

S> Так суть COM это VMT. А она по сути кроссплатформенна.


С каких это пор реализации VMT стали кроссплатформенными?
Re[8]: .Net Core Вызов виртуальных методов нативных объектов
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 14.11.16 11:00
Оценка:
Здравствуйте, Lexey, Вы писали:

L>Здравствуйте, Serginio1, Вы писали:


S>> Так суть COM это VMT. А она по сути кроссплатформенна.


L>С каких это пор реализации VMT стали кроссплатформенными?

http://rsdn.org/forum/dotnet/6608772.1
Автор: Serginio1
Дата: 13.11.16


Реально работает на линукс
и солнце б утром не вставало, когда бы не было меня
Re[4]: .Net Core Вызов виртуальных методов нативных объектов
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 14.11.16 11:43
Оценка:
Здравствуйте, α, Вы писали:

α>Здравствуйте, Serginio1, Вы писали:


α>>>В мсдн пишут что в принципе можно, но саму структуру классов с++ надо заранее проектировать под интеграцию с .net. Для остальных случаев рекомендуют оборачивать общение с классами с++ в с-api


S>> Мне как раз и нужно вызывать нативные методы из .Net


α>Всё равно все сводится к соответствующей подготовке кода на С++ к interop/pinvoke. А это либо как уже тут сказали — COM, либо обертка в виде С-plain-API, либо вот такие фокусы — https://blogs.msdn.microsoft.com/vcblog/2008/12/08/inheriting-from-a-native-c-class-in-c/




Это видел. Решил поэксперементировать.

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


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


и сделал статический метод

public static void CallInterface(ICallback cb)
        {
            cb.execute();

        }


В С++ определил класс
class ICallback :public IUnknown
    {
    public:
        
        virtual HRESULT __stdcall execute();


        // Унаследовано через IUnknown
        virtual HRESULT __stdcall QueryInterface(REFIID riid, void ** ppvObject) override;

        virtual ULONG __stdcall AddRef(void) override;

        virtual ULONG __stdcall Release(void) override;

    };


и реализацию

    HRESULT ICallback::execute()
    {
        
        wprintf_s(L" %s\n", L"ICallback from.Net");
        return NOERROR;
    }

    HRESULT ICallback::QueryInterface(REFIID riid, void ** ppvObject)
    {
        if (!ppvObject)
            return E_INVALIDARG;
        *ppvObject = NULL;
    //    if (riid == IID_IUnknown)
    //    {
            // Increment the reference count and return the pointer.
            *ppvObject = (LPVOID)this;
            return NOERROR;
  //        }
        return E_NOINTERFACE;
    }


    ULONG ICallback::AddRef(void)
    {
        return 1;
    }

    ULONG ICallback::Release(void)
    {
        return 0;
    }


Описание вызывемого метода из .Net Core

typedef void(STDMETHODCALLTYPE *ManagedRunCallback)(ICallback*);


Получаю ссылку на метод и вызываю
if (!CreateDelegate(domainId, L"CallInterface", (INT_PTR*)&pRunCallback)) return false;

        ICallback* cb = new ICallback();
        pRunCallback(cb);


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

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

И метод не вызывается.

Если же вернуть IProvideClassInfo сылку на this то метод вызывается и в консоли виден текст, но завершается ошибкой доступа памяти
и солнце б утром не вставало, когда бы не было меня
Re[5]: .Net Core Вызов виртуальных методов нативных объектов
От: α Российская Империя  
Дата: 14.11.16 11:58
Оценка:
Здравствуйте, Serginio1, Вы писали:

S> В С++ определил класс

S>class ICallback : public IUnknown

А упорядочить методы тут разве не надо? Т.е. сперва методы IUnknown, потом твой execute(). Что-то я уже подзабыл
PS А, это ж класс, а не интерфейс. Перепутал
Отредактировано 14.11.2016 12:00 α . Предыдущая версия .
Re[6]: .Net Core Вызов виртуальных методов нативных объектов
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 14.11.16 12:18
Оценка:
Здравствуйте, α, Вы писали:

α>Здравствуйте, Serginio1, Вы писали:


S>> В С++ определил класс

S>>class ICallback : public IUnknown

α>А упорядочить методы тут разве не надо? Т.е. сперва методы IUnknown, потом твой execute(). Что-то я уже подзабыл

α>PS А, это ж класс, а не интерфейс. Перепутал

так упорядочено по наследованию. IUnknown

вообще структура.

Переделал на
struct ICallback : IUnknown
    {
    public:
        
        


        // Унаследовано через IUnknown
        virtual HRESULT __stdcall QueryInterface(REFIID riid, void ** ppvObject) override;

        virtual ULONG __stdcall AddRef(void) override;

        virtual ULONG __stdcall Release(void) override;

        virtual HRESULT __stdcall execute();

    };


Эффект тот же.

Вызвано исключение по адресу 0x52684880 (coreclr.dll) в TestNetObjectToNative.exe: 0xC0000005: нарушение прав доступа при чтении по адресу 0x963E0B26.


Подозреваю из-за IProvideClassInfo.

Я на С++ пишу раз в год.
и солнце б утром не вставало, когда бы не было меня
Re[7]: .Net Core Вызов виртуальных методов нативных объектов
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 15.11.16 07:37
Оценка:
Здравствуйте, Serginio1, Вы писали:

Переделал

[ComImport, Guid("F2DAEB5F-228F-47AA-93B2-E81D46194FA8"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
   public interface ICallback
    {
        void execute(int value);
    }


и вызоа

 public static void CallInterface(ICallback cb)
        {
            cb.execute(555);

        }

И на С++

HRESULT __stdcall ICallback::execute(int value)
    {
        
        wprintf_s(L"ICallback from.Net %d\n", value);
        return NOERROR;
    }


В итоге value отлично от 555
Значит вызывается единственный метод IProvideClassInfo GetClassInfo

Попробую проверку на GUID/ Но смотрел вроде нет его.
и солнце б утром не вставало, когда бы не было меня
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
Re[6]: .Net Core Вызов виртуальных методов нативных объектов
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 16.11.16 08:15
Оценка:
Здравствуйте, pilgrim_, Вы писали:
Огромнейшее спасибо!


Проблема была в прямом использовании ICallback

Но вот куча

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



То есть вместо

public static void CallInterface(ICallback cb)
        {
            cb.execute();

        }



Нужно использовать


      public static void CallInterface(IntPtr cb)
        {

            var cb2 = Marshal.GetObjectForIUnknown(cb)  as ICallback;

            cb2?.execute(555);

        }


Но вот на 1 вызов куча QueryInterface, AddRef и Release это конечно
и солнце б утром не вставало, когда бы не было меня
Re: .Net Core Вызов виртуальных методов нативных объектов
От: ajanov  
Дата: 16.11.16 08:19
Оценка:
Здравствуйте, Serginio1, Вы писали:


S> Возможно ли вызвать виртуальные методы С++ классов из .Net Core.


S> Да и как там в .Net Core C++/CLI ?


Компилятор C++/CLI берет все заботы на себя
Re[2]: .Net Core Вызов виртуальных методов нативных объектов
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 16.11.16 08:27
Оценка:
Здравствуйте, ajanov, Вы писали:

A>Здравствуйте, Serginio1, Вы писали:



S>> Возможно ли вызвать виртуальные методы С++ классов из .Net Core.


S>> Да и как там в .Net Core C++/CLI ?


A>Компилятор C++/CLI берет все заботы на себя


А какие опции под .Net Core? Просто я в С++ новичок
и солнце б утром не вставало, когда бы не было меня
Re[3]: .Net Core Вызов виртуальных методов нативных объектов
От: ajanov  
Дата: 16.11.16 08:57
Оценка:
Здравствуйте, Serginio1, Вы писали:

S>Здравствуйте, ajanov, Вы писали:


A>>Здравствуйте, Serginio1, Вы писали:



S>>> Возможно ли вызвать виртуальные методы С++ классов из .Net Core.


S>>> Да и как там в .Net Core C++/CLI ?


A>>Компилятор C++/CLI берет все заботы на себя


S> А какие опции под .Net Core? Просто я в С++ новичок


Не заметил, что про .Net Core речь идет Прошу прощения. Боюсь, что и про C++/CLI наврал
Re[4]: .Net Core Вызов виртуальных методов нативных объектов
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 16.11.16 09:53
Оценка:
Здравствуйте, ajanov, Вы писали:

S>> А какие опции под .Net Core? Просто я в С++ новичок


A>Не заметил, что про .Net Core речь идет Прошу прощения. Боюсь, что и про C++/CLI наврал

Да нет на C++/CLI это легко
https://blogs.msdn.microsoft.com/junfeng/2006/05/20/sample-mixing-unmanaged-c-ccli-and-c-code/
http://stackoverflow.com/questions/15598445/is-it-possible-to-run-unmanaged-c-normally-from-a-managed-c-cli-project
и солнце б утром не вставало, когда бы не было меня
Отредактировано 16.11.2016 9:57 Serginio1 . Предыдущая версия .
Re[5]: .Net Core Вызов виртуальных методов нативных объектов
От: ajanov  
Дата: 16.11.16 10:10
Оценка: 10 (1)
Здравствуйте, Serginio1, Вы писали:

S>Здравствуйте, ajanov, Вы писали:


S>>> А какие опции под .Net Core? Просто я в С++ новичок


A>>Не заметил, что про .Net Core речь идет Прошу прощения. Боюсь, что и про C++/CLI наврал

S> Да нет на C++/CLI это легко
S>https://blogs.msdn.microsoft.com/junfeng/2006/05/20/sample-mixing-unmanaged-c-ccli-and-c-code/
S>http://stackoverflow.com/questions/15598445/is-it-possible-to-run-unmanaged-c-normally-from-a-managed-c-cli-project

Если все как в .Net Framework — то вперед, даже существующие библиотеки не нужно адаптировать, чтобы их использовать в .Net. В C++ проекте настройку General->Common Language Runtime support нужно установить в Common Language Runtime Support (/clr). Полезный раздел о поддержке .Net в C++: https://msdn.microsoft.com/en-us/library/68td296t(v=vs.140).aspx
Re[6]: .Net Core Вызов виртуальных методов нативных объектов
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 16.11.16 10:21
Оценка:
Здравствуйте, ajanov, Вы писали:


A>Если все как в .Net Framework — то вперед, даже существующие библиотеки не нужно адаптировать, чтобы их использовать в .Net. В C++ проекте настройку General->Common Language Runtime support нужно установить в Common Language Runtime Support (/clr). Полезный раздел о поддержке .Net в C++: https://msdn.microsoft.com/en-us/library/68td296t(v=vs.140).aspx


Спасибо! К сожалению у них нет никакого плана для поддержки C ++ / CLI с .NET Core
http://stackoverflow.com/questions/39140014/c-cli-support-in-net-core
и солнце б утром не вставало, когда бы не было меня
Re[7]: .Net Core Вызов виртуальных методов нативных объектов
От: ajanov  
Дата: 16.11.16 12:23
Оценка: +1
Здравствуйте, Serginio1, Вы писали:

S>Здравствуйте, ajanov, Вы писали:



A>>Если все как в .Net Framework — то вперед, даже существующие библиотеки не нужно адаптировать, чтобы их использовать в .Net. В C++ проекте настройку General->Common Language Runtime support нужно установить в Common Language Runtime Support (/clr). Полезный раздел о поддержке .Net в C++: https://msdn.microsoft.com/en-us/library/68td296t(v=vs.140).aspx


S> Спасибо! К сожалению у них нет никакого плана для поддержки C ++ / CLI с .NET Core

S>http://stackoverflow.com/questions/39140014/c-cli-support-in-net-core

Десиплюсплюсизация от Майкрософт
Re[8]: .Net Core Вызов виртуальных методов нативных объектов
От: fddima  
Дата: 16.11.16 12:54
Оценка: 5 (1) +1
Здравствуйте, ajanov, Вы писали:

A>Десиплюсплюсизация от Майкрософт

У C++/CLI столько тараканов, что лучше иметь нормальный ABI (C-like API), чем писать костыли. А для библиотек которые header/template based всё равно только топор поможет. Топором в этом случае я имею ввиду вынос части своей логики в C++ который и сможет быть и скомпилирован эффективно, и вызван (ну и работа будет делаться с минимумом пересечений managed-native границ). Микс же и прозрачные "трамплины" скорости не добавят, в поддержке сложнее, доп знания о диалекте и энвайронменте опять же нужны.
Ну на вскидку — прийдется хорошенько попрыгать прежде чем нативный колбэк будет вызывать код в правильном апп домене, в то время как в интеропе со стороны шарпа (через делегаты и GetFunctionPointer) это достанется бесплатно. Понятно, что всё тоже можно замутить в C++/CLI, но легче из шарпа работать с любыми типами. Лиж бы описания структур были, а потом указатель и вперёд. Но это как раз об ABI.
Re[9]: .Net Core Вызов виртуальных методов нативных объектов
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 16.11.16 13:04
Оценка:
Здравствуйте, fddima, Вы писали:

F> Ну на вскидку — прийдется хорошенько попрыгать прежде чем нативный колбэк будет вызывать код в правильном апп домене, в то время как в интеропе со стороны шарпа (через делегаты и GetFunctionPointer) это достанется бесплатно. Понятно, что всё тоже можно замутить в C++/CLI, но легче из шарпа работать с любыми типами. Лиж бы описания структур были, а потом указатель и вперёд. Но это как раз об ABI.

А можно поподробнее про ABI. Нашел только http://stackoverflow.com/questions/3784389/difference-between-api-and-abi
и солнце б утром не вставало, когда бы не было меня
Re[10]: .Net Core Вызов виртуальных методов нативных объекто
От: fddima  
Дата: 16.11.16 23:06
Оценка:
Здравствуйте, Serginio1, Вы писали:

S> А можно поподробнее про ABI. Нашел только http://stackoverflow.com/questions/3784389/difference-between-api-and-abi

Так там же ж вполне понятно написано. Под ABI понимается любое стабильное соглашение о том как программы взаимодействуют на бинарном уровне. COM в частности — это универсальный такой контракт (интерфейс). Они могут быть и специализированными. Все в итоге выливается в набор экспортируемых вызовов из динамической библиотеки, описания структур и желательно механизм валидации определения версий.
Понятно что для реализации этого нам необходимо предоставить C-like интерфейс, а в дотнете просто юзать PInvoke, при этом мы опускаем остальные детали т.к. обе среды остальное делают за нас (соглашение вызовов, упаковка/выравниевание структур и т.п.).

В том же CEF, например, для того что бы создать объект (реализуемый на стороне C#), я заполняю таблицу с адресами методов которые реализуют конкретные функции, что-то вроде VMT, но на каждый объект своя таблица. А при потреблении объектов — беру адрес функции, делегат и вперед вызывать. Т.е. тонны экспортов из dll нет.

Это и есть "наше" соглашение, используемое для:
1. построения этого самого слоя ABI, т.е. способ которым библиотека экспортирует свой функционал в виде динамической библиотеки (полностью автоматически).
2. построения клиента (C++) для библиотеки (т.е. API внешне выставляется такой же как и "внутри", но на самом деле там самый настоящий интероп с маршаллингом, например, с передачей строк по значению, ну и по ссылке когда надо) (полностью автоматически).
3. построения биндингов для (C#, Go, Python с той или иной степенью автоматизации), но не вокруг этой клиентской C++ обертки, а вокруг этого самого ABI. При этом, разумеется, выпуск патчей не ломает совместимость, хотя такой халявы как в дотнете с символическим связыванием — увы нет — метаданных то нет. Есть один идентификатор (хэш) — если не совпадает — значит другая версия ABI и работать с ней нельзя.

Механизм в целом тот ещё, но по факту довольно удобен, даж Servo к себе на вооружение взяли. Но это ж для демонстрации.

PS: Я возможно не верно понял, чего ты там не понял, или куда ещё подробнее?

UPD2: У WebKit2 на мой взгляд вменяемый интерфейс в этом плане, но правда не без своих таракашек, как и везде.

UPD3: Один из ответов на SO гласит:

ABI: Application Binary Interface

This is how the compiler builds an application.
It defines things (but is not limited to):

— How parameters are passed to functions (registers/stack).
— Who cleans parameters from the stack (caller/callee).
— Where the return value is placed for return.
— How exceptions propagate.


Конечно же это не так (это далеко не всё). Описанное — это соглашение вызова функции (call convention). Соглашение по исключениям... хм, а они есть / могут быть? Да даже C/C++ библиотеки имеют ABI, тот же libc которому следует компилятор (и может таргетиться на разные версии), дабы обеспечить максимальную совместимость. Ну т.е. на уровне того, чего именно финальный экзешник потребляет (в терминах импортов ну и связанных вещей, стуктуры, соглашение вызова и т.п.).

API же — это скорее то, что предоставляет библиотека, и как мы её потребляем в той или иной среде. API например может содержать 2 метода, однако на уровне ABI это будет один метод с параметром.
Отредактировано 16.11.2016 23:39 Mystic Artifact . Предыдущая версия . Еще …
Отредактировано 16.11.2016 23:38 Mystic Artifact . Предыдущая версия .
Отредактировано 16.11.2016 23:30 Mystic Artifact . Предыдущая версия .
Re[11]: .Net Core Вызов виртуальных методов нативных объекто
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 17.11.16 07:07
Оценка:
Здравствуйте, fddima, Вы писали:
Меня вот что интересует.

Передавать статические функции или ThisCall трудоемко.
Проще общаться с нативом в разные стороны через абстрактные классы

Повторюсь http://rsdn.org/Forum/NewMsg.aspx?mid=6608772&amp;edit=1
Автор: Serginio1
Дата: 13.11.16


Так суть интерфейса это VMT. А классы с виртуальными методами кроссплатформенны.
В 1С это прекрасно работает

class IInitDoneBase
{
public:
    virtual ~IInitDoneBase() {}
    /// Initializes component
    /**
     *  @param disp - 1C:Enterpise interface
     *  @return the result of
     */
    virtual bool ADDIN_API Init(void* disp) = 0;
    /// Sets the memory manager
    /*
     * @param mem - pointer to memory manager interface.
     *  @return the result of
     */
    virtual bool ADDIN_API setMemManager(void* mem) = 0;

    /// Returns component version
    /**
     *  @return - component version (2000 - version 2)
     */
    virtual long ADDIN_API GetInfo() = 0;

    /// Uninitializes component
    /**
     *  Component here should release all consumed resources.
     */
    virtual void ADDIN_API Done() = 0;

};
///////////////////////////////////////////////////////////////////////////
/**
 *  The given interface defines methods that are intented to be used by the Platform
 */
/// Interface describing extension of language.



где

#ifndef  ADDIN_API
#ifndef __linux__
#define ADDIN_API __stdcall
#else
//#define ADDIN_API __attribute__ ((__stdcall__))
#define ADDIN_API
#endif //__linux__
#endif //ADDIN_API



Этот вариант кроссплатформен.

Вот хотелось бы и такой вариант и на .Net описать.
Тот же наследник IUnknown это тот же виртуальный класс


class ICallback :public IUnknown
    {
    public:
        
        virtual HRESULT __stdcall execute();


        // Унаследовано через IUnknown
        virtual HRESULT __stdcall QueryInterface(REFIID riid, void ** ppvObject) override;

        virtual ULONG __stdcall AddRef(void) override;

        virtual ULONG __stdcall Release(void) override;

    };


Но при что бы получить ссылку на интерфейс, вызывается куча QueryInterface, AddRef и Release

То есть хотелось бы иметь контракт на уровне абстрактых классов без проверки как это и происходит при

Marshal.GetDelegateForFunctionPointer

с

[UnmanagedFunctionPointer(CallingConvention.ThisCall)]

или
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
и солнце б утром не вставало, когда бы не было меня
Re[12]: .Net Core Вызов виртуальных методов нативных объекто
От: fddima  
Дата: 17.11.16 12:34
Оценка:
Здравствуйте, Serginio1, Вы писали:

S> Передавать статические функции или ThisCall трудоемко.

Что значит передавать? Откуда-куда?

S>Проще общаться с нативом в разные стороны через абстрактные классы

Вот как раз и не проще. Иногда проще через очень даже непрозрачные структуры, где клиент и сервер знает её внутренности. Вообще зависит, от того с какой стороны смотреть и/или какие проблемы решаются. Иногда гораздо проще opaque pointer + вся работа только через статические функции (импорты из длл).

И тут весь этот бред вокруг IUnknown я считаю лишним абсолютно. Далеко не проще через абстрактные.

Я ещё раз делаю акцент: решение зависит только от входящих требований: стабильное ABI, простое, C-совместимое, с или без символическим связыванием имен, пишем ли мы и клиент и сервер и т.п.

S> Так суть интерфейса это VMT. А классы с виртуальными методами кроссплатформенны.

S> В 1С это прекрасно работает
Мне показалось, что в другой ветке же ж разобрались как можно заставить эту кухню работать?
Re[13]: .Net Core Вызов виртуальных методов нативных объекто
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 17.11.16 12:52
Оценка:
Здравствуйте, fddima, Вы писали:

F>Здравствуйте, Serginio1, Вы писали:


S>> Передавать статические функции или ThisCall трудоемко.

F> Что значит передавать? Откуда-куда?
Из натива в .Net Native вестимо http://rsdn.org/forum/dotnet/6609566.1
Автор: Serginio1
Дата: 14.11.16


S>>Проще общаться с нативом в разные стороны через абстрактные классы

F> Вот как раз и не проще. Иногда проще через очень даже непрозрачные структуры, где клиент и сервер знает её внутренности. Вообще зависит, от того с какой стороны смотреть и/или какие проблемы решаются. Иногда гораздо проще opaque pointer + вся работа только через статические функции (импорты из длл).

F> И тут весь этот бред вокруг IUnknown я считаю лишним абсолютно. Далеко не проще через абстрактные.


F> Я ещё раз делаю акцент: решение зависит только от входящих требований: стабильное ABI, простое, C-совместимое, с или без символическим связыванием имен, пишем ли мы и клиент и сервер и т.п.


S>> Так суть интерфейса это VMT. А классы с виртуальными методами кроссплатформенны.

S>> В 1С это прекрасно работает
F> Мне показалось, что в другой ветке же ж разобрались как можно заставить эту кухню работать?

Да все это работает и на уровне статических методов (ThisCall суть таже). При использовании IUnknown идет куча ненужных вызовов.
Кроме того я хочу эти абстрактные C++ классы с stdcall вызовами использовать в .Net Core
Там мне приходится передавать ссылки на каждый метод.

public static void SetDelegate(IntPtr ДляВыделенияПамяти, IntPtr ДляВызоваОшибки, IntPtr ДляВызоваВнешнегоСобытия)
        {
            ВыделитьПямять = Marshal.GetDelegateForFunctionPointer<ВыделитьПамятьDelegate>(ДляВыделенияПамяти);
            ИнформацияОбОшибке = Marshal.GetDelegateForFunctionPointer<ИнформацияОбОшибкеDelegate>(ДляВызоваОшибки);
            ВнешнееСобытие1С = Marshal.GetDelegateForFunctionPointer<ВнешнееСобытие1СDelegate>(ДляВызоваВнешнегоСобытия);
        }


Конечно можно и по VMT получить адреса методов и через соглашение ThisCall.
Например http://www.cyberforum.ru/post4437036.html


Но хотоелось бы аналогов интерфейсов. При этом использовать только ссылку на объект, первым полем которого является ссылка на VMT.
Это просто удобнее при описании и при выполнении.
и солнце б утром не вставало, когда бы не было меня
Отредактировано 17.11.2016 13:22 Serginio1 . Предыдущая версия .
Re[13]: .Net Core Вызов виртуальных методов нативных объекто
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 18.11.16 10:29
Оценка:
Здравствуйте, fddima, Вы писали:

Попробовал с VMT
Определил методы

        [UnmanagedFunctionPointer(CallingConvention.Winapi)]
        internal delegate void ВиртуальныйМетодОбъектаСDelegate(IntPtr self, int Число);

        [UnmanagedFunctionPointer(CallingConvention.ThisCall)]
        internal delegate void ВиртуальныйМетодОбъекта2Delegate(IntPtr self, int Число1, int Число2);



Создал функцию для получения адреса метода

   public static IntPtr ПолучитьАдресВиртуальногоМетода(IntPtr Объект, int ИндексВТаблицеВиртуальныхМетодов )
        {
            int размерIntPtr = Marshal.SizeOf<IntPtr>();

            // Первым полем объекта идет ссылка на VMT
            // Прочитаем её
            var АдресVMT = Marshal.ReadIntPtr(Объект);
            //  получим адресс метода по смещению в VMT
            var АдресМетодаVMT = АдресVMT + ИндексВТаблицеВиртуальныхМетодов * размерIntPtr;
            var АдресМетода = Marshal.ReadIntPtr(АдресМетодаVMT);

            return АдресМетода;
        }



Для интерфеса вроде работает

 public static void CallInterface(IntPtr cb)
        {

            var cb2 = Marshal.GetObjectForIUnknown(cb)  as ICallback;

            cb2?.execute(555);

          
            // метод execute в VMT идет 4 (QueryInterface,Addref,Release)
            // а индекс равен 3 
           
            var АдресМетода = ПолучитьАдресВиртуальногоМетода(cb,3);
            // Получим делегат по дресу
            var execute = Marshal.GetDelegateForFunctionPointer<ВиртуальныйМетодОбъектаСDelegate>(АдресМетода);

            // И вызовем метод
            execute(cb, 666);



        }


Надо будет с параметрами проверить.
Но вот с ThisCall проблема.

Определил класс

struct TestThisCall {
    public:
        virtual void __stdcall execute(int value1, int value2);

    };
    
    typedef void(STDMETHODCALLTYPE *ManagedRunCallback2)(TestThisCall*);



и метод

void TestThisCall::execute(int value1, int value2)
    {
        wprintf_s(L"sizeof(int) %d\n", sizeof(int));
        wprintf_s(L"TestThisCall from.Net value1 %d\n", value1);
        wprintf_s(L"Adress this %d\n", this);

        wprintf_s(L"TestThisCall from.Net value2 %d\n", value2);
    }



В .Net

 public static void CallInterface2(IntPtr ttc)
        {

            
            var АдресМетода = ПолучитьАдресВиртуальногоМетода(ttc, 0);
            // Получим делегат по дресу
            var execute = Marshal.GetDelegateForFunctionPointer<ВиртуальныйМетодОбъекта2Delegate>(АдресМетода);

            // И вызовем метод
            execute(ttc, ttc.ToInt32(), 777);
        }


И вызов


ManagedRunCallback  pRunCallback;
        ManagedRunCallback2 pRunCallback2;
                if (!CreateDelegate(domainId, L"CallInterface", (INT_PTR*)&pRunCallback)) return false;
        if (!CreateDelegate(domainId, L"CallInterface2", (INT_PTR*)&pRunCallback2)) return false;

        

        ICallback* cb = new ICallback();
        pRunCallback(cb);

        TestThisCall* ttc = new TestThisCall();
        pRunCallback2(ttc);


Вот при вызове CallInterface2

параметры поменяны местами, а переданное первым параметром значение не соотвествует переданному.

Сейчас проверю без __stdcall , но чего то маловато CallingConvention
Да если убрать __stdcall то все работает правильно.
и солнце б утром не вставало, когда бы не было меня
Отредактировано 18.11.2016 10:32 Serginio1 . Предыдущая версия .
Re[12]: .Net Core Вызов виртуальных методов нативных объекто
От: pilgrim_ Россия  
Дата: 18.11.16 11:10
Оценка: 10 (1)
Здравствуйте, Serginio1, Вы писали:

S> Так суть интерфейса это VMT. А классы с виртуальными методами кроссплатформенны.


Выделенное в общем случае неверно, тебе уже несколько раз об этом сказали, и зависит от используемого компилятора и ABI который он использует для бинарного представления (vtable layout) виртуальных методов (явных- указанных в описании класса, и не только — добавляемых компилятором) класса для целевой (там где будет выполняться код) платформы.

Конкретно: популярные компиляторы C++ vc/gcc/clang используют след. ABI:
vc/windows — Windows ABI
gcc (начиная с какой-то версии)/linux — Itanium ABI
clang — на windows — Windows ABI ,на linux — Itanium ABI.

В Windows ABI описание класса с чисто-виртуальными функциями (интерфейс) в точности соотв. структуре с указателем на таблицу содержащую указатели на методы — основа COM:
C++ :
struct IFace
{
    virtual void Method0() = 0;
    virtual void Method1() = 0;
};


C:
struct IFace;
struct IFaceVtbl
{
    void (*Method0)(IFace* _this);
    void (*Method1)(IFace* _this);
}

struct IFace
{
    IFaceVtbl *vtblPtr;
};


В Itanium ABI не так — http://mentorembedded.github.io/cxx-abi/abi.html#vtable-construction, для нашего случая:

The virtual table contains offset-to-top and RTTI fields followed by virtual function pointers. There is one function pointer entry for each virtual function declared in the class, in declaration order, with any implicitly-defined virtual destructor pair last.


Вот что выдает gcc на IUnknown:

Vtable for IUnknown
IUnknown::_ZTV8IUnknown: 5u entries
0 (int (*)(...))0
4 (int (*)(...))(& _ZTI8IUnknown)
8 (int (*)(...))__cxa_pure_virtual
12 (int (*)(...))__cxa_pure_virtual
16 (int (*)(...))__cxa_pure_virtual


Соотв. если делать ручной маршалинг, все это нужно учитывать.

S> Но при что бы получить ссылку на интерфейс, вызывается куча QueryInterface, AddRef и Release


Это делается 1 раз при создании RCW (Runtime Callable Wrapper), и будь уверен — очень быстро
Re[14]: .Net Core Вызов виртуальных методов нативных объекто
От: pilgrim_ Россия  
Дата: 18.11.16 11:21
Оценка:
Здравствуйте, Serginio1, Вы писали:

S>Попробовал с VMT

S>Определил методы

Вот ты неуемный ты бы для начала проверил, работает ли COM-интероп на linux — ведь это же твоя цель?
И если работает — тему можно закрывать, COM-интеропа тебе хватит за глаза.
Или ты заради любви к искусству?

S>
S>        [UnmanagedFunctionPointer(CallingConvention.Winapi)]
S>        internal delegate void ВиртуальныйМетодОбъектаСDelegate(IntPtr self, int Число);

S>        [UnmanagedFunctionPointer(CallingConvention.ThisCall)]
S>        internal delegate void ВиртуальныйМетодОбъекта2Delegate(IntPtr self, int Число1, int Число2);
S>


CallingConvention.Winapi — тот же stdcall
CallingConvention.ThisCall — зачем тебе это?


S> Создал функцию для получения адреса метода


S>
S>   public static IntPtr ПолучитьАдресВиртуальногоМетода(IntPtr Объект, int ИндексВТаблицеВиртуальныхМетодов )
S>        {
S>            int размерIntPtr = Marshal.SizeOf<IntPtr>();

S>            // Первым полем объекта идет ссылка на VMT
S>            // Прочитаем её
S>            var АдресVMT = Marshal.ReadIntPtr(Объект);
S>            //  получим адресс метода по смещению в VMT
S>            var АдресМетодаVMT = АдресVMT + ИндексВТаблицеВиртуальныхМетодов * размерIntPtr;
S>            var АдресМетода = Marshal.ReadIntPtr(АдресМетодаVMT);

S>            return АдресМетода;
S>        }


S>Для интерфеса вроде работает


Я вот тут
Автор: pilgrim_
Дата: 22.06.16
тебе намекал, а ты сделал вид что понял весь хардкор с Marshal.ReadIntPtr за тебя сделает рантайм.

S> параметры поменяны местами, а переданное первым параметром значение не соотвествует переданному.


Используй stdcall как в native коде, так и в делегате на C#, и все будет норм.
Re[15]: .Net Core Вызов виртуальных методов нативных объекто
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 18.11.16 11:54
Оценка:
Здравствуйте, pilgrim_, Вы писали:

_>Здравствуйте, Serginio1, Вы писали:


S>>Попробовал с VMT

S>>Определил методы

_>Вот ты неуемный ты бы для начала проверил, работает ли COM-интероп на linux — ведь это же твоя цель?

_>И если работает — тему можно закрывать, COM-интеропа тебе хватит за глаза.
_>Или ты заради любви к искусству?

И то и другое. Да ине нужны мне методы IUknown. Я итак передаю нужныю ссылку, а подсчет ссылок если мне нужно организую сам.

Для приведения к интерфейсу при вызове одного метода идет куча QueryInterface Addref,Release
http://rsdn.org/forum/dotnet/6611240.1
Автор: pilgrim_
Дата: 15.11.16


S>>
S>>        [UnmanagedFunctionPointer(CallingConvention.Winapi)]
S>>        internal delegate void ВиртуальныйМетодОбъектаСDelegate(IntPtr self, int Число);

S>>        [UnmanagedFunctionPointer(CallingConvention.ThisCall)]
S>>        internal delegate void ВиртуальныйМетодОбъекта2Delegate(IntPtr self, int Число1, int Число2);
S>>


_>CallingConvention.Winapi — тот же stdcall

_>CallingConvention.ThisCall — зачем тебе это?


Вот CallingConvention.ThisCall то работает для

struct TestThisCall {
    public:
        virtual int  execute(int value1, int value2);

    };




S>> Создал функцию для получения адреса метода


S>>
S>>   public static IntPtr ПолучитьАдресВиртуальногоМетода(IntPtr Объект, int ИндексВТаблицеВиртуальныхМетодов )
S>>        {
S>>            int размерIntPtr = Marshal.SizeOf<IntPtr>();

S>>            // Первым полем объекта идет ссылка на VMT
S>>            // Прочитаем её
S>>            var АдресVMT = Marshal.ReadIntPtr(Объект);
S>>            //  получим адресс метода по смещению в VMT
S>>            var АдресМетодаVMT = АдресVMT + ИндексВТаблицеВиртуальныхМетодов * размерIntPtr;
S>>            var АдресМетода = Marshal.ReadIntPtr(АдресМетодаVMT);

S>>            return АдресМетода;
S>>        }


S>>Для интерфеса вроде работает


_>Я вот тут
Автор: pilgrim_
Дата: 22.06.16
тебе намекал, а ты сделал вид что понял весь хардкор с Marshal.ReadIntPtr за тебя сделает рантайм.


S>> параметры поменяны местами, а переданное первым параметром значение не соотвествует переданному.


_>Используй stdcall как в native коде, так и в делегате на C#, и все будет норм.


Проблема в том, что ThisCall и stdcall не совместимы.
То есть я должен определить как
virtual int  execute(int value1, int value2);
и солнце б утром не вставало, когда бы не было меня
Отредактировано 18.11.2016 12:00 Serginio1 . Предыдущая версия .
Re[16]: .Net Core Вызов виртуальных методов нативных объекто
От: pilgrim_ Россия  
Дата: 18.11.16 13:46
Оценка:
Здравствуйте, Serginio1, Вы писали:

S>Здравствуйте, pilgrim_, Вы писали:


_>>Здравствуйте, Serginio1, Вы писали:


S>>>Попробовал с VMT

S>>>Определил методы

_>>Вот ты неуемный ты бы для начала проверил, работает ли COM-интероп на linux — ведь это же твоя цель?

_>>И если работает — тему можно закрывать, COM-интеропа тебе хватит за глаза.
_>>Или ты заради любви к искусству?

S> И то и другое. Да ине нужны мне методы IUknown. Я итак передаю нужныю ссылку, а подсчет ссылок если мне нужно организую сам.


Не забывай про связку native -> managed iface, временем жизни managed объекта на стороне managed тоже ручками придется управлять.

S> Для приведения к интерфейсу при вызове одного метода идет куча QueryInterface Addref,Release

S>http://rsdn.org/forum/dotnet/6611240.1
Автор: pilgrim_
Дата: 15.11.16


забей и смирись
Автор: pilgrim_
Дата: 18.11.16



_>>CallingConvention.Winapi — тот же stdcall

_>>CallingConvention.ThisCall — зачем тебе это?


S> Вот CallingConvention.ThisCall то работает для


_>Используй stdcall как в native коде, так и в делегате на C#, и все будет норм.


S> Проблема в том, что ThisCall и stdcall не совместимы.


Внимательно смотри выделенное, stdcall (как и cdecl) — это C-стандарт соглашений о вызове, с одинаковой реализацией разными компиляторами,
тогда как thiscall — compiler specific — https://en.wikipedia.org/wiki/X86_calling_conventions#thiscall .
Тебя все еще интересует linux?
Re[17]: .Net Core Вызов виртуальных методов нативных объекто
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 18.11.16 13:51
Оценка:
Здравствуйте, pilgrim_, Вы писали:


S>> Проблема в том, что ThisCall и stdcall не совместимы.


_>Внимательно смотри выделенное, stdcall (как и cdecl) — это C-стандарт соглашений о вызове, с одинаковой реализацией разными компиляторами,

_>тогда как thiscall — compiler specific — https://en.wikipedia.org/wiki/X86_calling_conventions#thiscall .
_>Тебя все еще интересует linux?

Да волнует. Но компилятор у меня будет один. Опять же я тебе давал ссылку на структуры применяемые в 1С.
Там используется виртуальные методы stdcall.
А вот как это в .Net использовать?

Кстати объявив метод


 [UnmanagedFunctionPointer(CallingConvention.Winapi)]
        internal delegate void ВиртуальныйМетодОбъектаСDelegate(IntPtr self, int Число);

У меня метод вызвался как ThisCall по stdcall

 public static void CallInterface(IntPtr cb)
        {

            var cb2 = Marshal.GetObjectForIUnknown(cb)  as ICallback;

            cb2?.execute(555);

          
            // метод execute в VMT идет 4 (QueryInterface,Addref,Release)
            // а индекс равен 3 
           
            var АдресМетода = ПолучитьАдресВиртуальногоМетода(cb,3);
            // Получим делегат по дресу
            var execute = Marshal.GetDelegateForFunctionPointer<ВиртуальныйМетодОбъектаСDelegate>(АдресМетода);

            // И вызовем метод
            execute(cb, 666);



        }



То есть первый параметр пошел в регисты. Сейчас проверю с несколькими параметрами
и солнце б утром не вставало, когда бы не было меня
Отредактировано 18.11.2016 13:57 Serginio1 . Предыдущая версия .
Re[16]: .Net Core Вызов виртуальных методов нативных объекто
От: pilgrim_ Россия  
Дата: 18.11.16 14:00
Оценка: 15 (1)
_>>Здравствуйте, Serginio1, Вы писали:

S>>>Попробовал с VMT

S>>>Определил методы

_>>Вот ты неуемный ты бы для начала проверил, работает ли COM-интероп на linux — ведь это же твоя цель?

_>>И если работает — тему можно закрывать, COM-интеропа тебе хватит за глаза.
_>>Или ты заради любви к искусству?

S> И то и другое. Да ине нужны мне методы IUknown. Я итак передаю нужныю ссылку, а подсчет ссылок если мне нужно организую сам.


S> Для приведения к интерфейсу при вызове одного метода идет куча QueryInterface Addref,Release

S>http://rsdn.org/forum/dotnet/6611240.1
Автор: pilgrim_
Дата: 15.11.16


Ну ежели упорный, вот смотри примерный вариант ручного маршалинга, а затем сравни его с автоматическим
Автор: pilgrim_
Дата: 15.11.16
(не забывая про разные C++ ABI)

  C++ native не-COM интерфейс
#include <stdio.h>
#include <cassert>


class ICallbackNotCom
{
public:
    virtual bool __stdcall release() = 0;
    virtual bool __stdcall execute() = 0;
    virtual bool __stdcall dispatch(ICallbackNotCom* other) = 0;
};

static void trace(const char* message)
{
    fprintf(stderr, "%s\n", message);
    //std::cout << message << std::endl;
}

class UnmanagedCallbackNotCom : public ICallbackNotCom
{
    static constexpr const char* Signature = "INTEROP";

    ICallbackNotCom* _other;
    const char* _sig;

private:
    void CheckSignature() const
    {
        assert(_sig == Signature);
    }

public:
    UnmanagedCallbackNotCom(ICallbackNotCom* other) : _other(other)
    {
        trace("ctor UnmanagedCallbackNotCom");
        _sig = Signature;
    }

    ~UnmanagedCallbackNotCom()
    {
        CheckSignature();
        trace("dtor UnmanagedCallbackNotCom");
    }


    //ICallbackNotCom

    bool __stdcall release() override
    {
        CheckSignature();
        trace("UnmanagedCallbackNotCom.release");
        delete this;
        return true;
    }

    bool __stdcall execute() override
    {
        CheckSignature();
        trace("UnmanagedCallbackNotCom.execute");
        _other->execute();
        return true;
    }

    bool __stdcall dispatch(ICallbackNotCom* other) override
    {
        CheckSignature();
        trace("UnmanagedCallbackNotCom.dispatch");
        if (other == nullptr) return false;

        _other->dispatch(other);

        return true;
    }
};

extern "C" __declspec(dllexport) ICallbackNotCom* __stdcall UnmanagedWrapCallbackNotCom(ICallbackNotCom* other)
{
    auto ucb = new UnmanagedCallbackNotCom(other);
    return ucb;
}



  C# .NET хост — ручной маршалинг интерфейсов в натив и обратно

    class Program
    {
        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();
            }
        }

        [StructLayout(LayoutKind.Sequential)]
        public class NativeCallbackShim
        {
            [StructLayout(LayoutKind.Sequential)]
            public sealed class Vtbl
            {
                [UnmanagedFunctionPointer(CallingConvention.StdCall)]
                public delegate bool ReleaseHandler(IntPtr self);

                [UnmanagedFunctionPointer(CallingConvention.StdCall)]
                public delegate bool ExecuteHandler(IntPtr self);

                [UnmanagedFunctionPointer(CallingConvention.StdCall)]
                public delegate bool DispatchHandler(IntPtr self, IntPtr other);

                //GCC (Itanium ABI) specific
                //public IntPtr VTableEntry0;
                //public IntPtr VTableEntry1;

                public ReleaseHandler Release;
                public ExecuteHandler Execute;
                public DispatchHandler Dispatch;
            }

            protected IntPtr _vtblPtr;

            protected NativeCallbackShim()
            {
            }

            public NativeCallbackShim(IntPtr thisPtr)
            {
                var iface = Marshal.PtrToStructure<NativeCallbackShim>(thisPtr);
                _vtblPtr = iface._vtblPtr;
            }

            public static Vtbl GetVtbl(IntPtr thisPtr)
            {
                var iface = Marshal.PtrToStructure<NativeCallbackShim>(thisPtr);
                var vtbl = iface.GetVtbl();
                return vtbl;
            }

            public Vtbl GetVtbl() => Marshal.PtrToStructure<Vtbl>(_vtblPtr);
        }

        public sealed class NativeCallback : ICallback, IDisposable
        {
            private IntPtr _nativeCallbackPtr;
            private readonly NativeCallbackShim.Vtbl _vtbl;

            public NativeCallback(IntPtr callbackPtr)
            {
                _nativeCallbackPtr = callbackPtr;
                _vtbl = NativeCallbackShim.GetVtbl(callbackPtr);
            }

            public void Execute()
            {
                CheckDisposedState();
                _vtbl.Execute(_nativeCallbackPtr);
            }

            public void Dispatch(ICallback other)
            {
                CheckDisposedState();
                using (var shim = new Managed2NativeCallbackShim(other))
                {
                    _vtbl.Dispatch(_nativeCallbackPtr, shim.ToPointer());
                }
            }

            public void Dispose()
            {
                if (_nativeCallbackPtr != IntPtr.Zero)
                {
                    _vtbl.Release(_nativeCallbackPtr);
                    _nativeCallbackPtr = IntPtr.Zero;
                }
            }

            private void CheckDisposedState()
            {
                if (_nativeCallbackPtr == IntPtr.Zero)
                {
                    throw new ObjectDisposedException(nameof(NativeCallback));
                }
            }
        }

        [StructLayout(LayoutKind.Sequential)]
        public sealed class Managed2NativeCallbackShim : NativeCallbackShim, ICallback, IDisposable
        {
            [StructLayout(LayoutKind.Sequential)]
            private struct NativeInterfaceShim
            {
                public IntPtr vtblPtr;
            }

            private readonly Vtbl _vtbl; //note: keep reference to vtbl!
            private IntPtr _thisPtr;
            private readonly ICallback _callback;

            public Managed2NativeCallbackShim(ICallback cb)
            {
                _callback = cb;
                //note: keep reference to vtbl!
                _vtbl = new Vtbl
                {
                    Release = _ => true,
                    Execute = _ =>
                    {
                        Execute();
                        return true;
                    },
                    Dispatch = (_, other) =>
                    {
                        Dispatch(new NativeCallback(other));
                        return true;
                    }
                };

                var vtblSize = Marshal.SizeOf(_vtbl);
                _vtblPtr = Marshal.AllocHGlobal(vtblSize);
                Marshal.StructureToPtr(_vtbl, _vtblPtr, false);
            }

            public IntPtr ToPointer()
            {
                CheckDisposedState();
                if (_thisPtr == IntPtr.Zero)
                {
                    var shim = new NativeInterfaceShim { vtblPtr = _vtblPtr};
                    _thisPtr = Marshal.AllocHGlobal(Marshal.SizeOf(shim));
                    Marshal.StructureToPtr(shim, _thisPtr, false);
                }
                return _thisPtr;
            }

            public void Execute()
            {
                CheckDisposedState();
                _callback.Execute();
            }

            public void Dispatch(ICallback other)
            {
                CheckDisposedState();
                _callback.Dispatch(other);
            }

            public void Dispose()
            {
                if (_thisPtr != IntPtr.Zero)
                {
                    Marshal.FreeHGlobal(_thisPtr);
                    _thisPtr = IntPtr.Zero;
                }

                if (_vtblPtr != IntPtr.Zero)
                {
                    Marshal.FreeHGlobal(_vtblPtr);
                    _vtblPtr = IntPtr.Zero;
                }
            }

            private void CheckDisposedState()
            {
                if (_vtblPtr == IntPtr.Zero)
                {
                    throw new ObjectDisposedException(nameof(Managed2NativeCallbackShim));
                }
            }
        }

        [DllImport("Win32Project1.dll", CallingConvention = CallingConvention.StdCall)]
        static extern IntPtr UnmanagedWrapCallbackNotCom(IntPtr callback);


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

            TestNotComCallback();
        }


        static void TestNotComCallback()
        {
            var managedCallback = new ManagedCallback();
            var managedCallbackShim = new Managed2NativeCallbackShim(managedCallback);
            managedCallbackShim.Execute();

            var nativeCallbackPtr = UnmanagedWrapCallbackNotCom(managedCallbackShim.ToPointer());
            var nativeCallback = new NativeCallback(nativeCallbackPtr);

            try
            {
                nativeCallback.Execute();
                nativeCallback.Dispatch(managedCallbackShim);

                nativeCallback.Dispatch(nativeCallback);
            }
            finally
            {
                managedCallbackShim.Dispose();
                nativeCallback.Dispose();
            }

            GC.KeepAlive(managedCallbackShim);
        }

    }



  выхлоп
Project NotComInterop (.NETCoreApp,Version=v1.0) was previously compiled. Skipping compilation.
Press Enter key to continue...
ManagedCallback.Execute
ctor UnmanagedCallbackNotCom
UnmanagedCallbackNotCom.execute
ManagedCallback.Execute
UnmanagedCallbackNotCom.dispatch
ManagedCallback.Dispatch
ManagedCallback.Execute
UnmanagedCallbackNotCom.dispatch
ManagedCallback.Dispatch
UnmanagedCallbackNotCom.execute
ManagedCallback.Execute
UnmanagedCallbackNotCom.release
dtor UnmanagedCallbackNotCom
Re[17]: .Net Core Вызов виртуальных методов нативных объекто
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 18.11.16 14:19
Оценка:
Здравствуйте, pilgrim_, Вы писали:
Спасибо!
Я вот только непонял, почему при stdcall первый параметр идет в регистры?
Или this при stdcall идет первым параметром?

Я к чему. У меня полно структур http://rsdn.org/Forum/NewMsg.aspx?mid=6608772&amp;edit=1
Автор: Serginio1
Дата: 13.11.16

И мне хотелось бы работать с ними напрямую!
Оргомное спасибо!

Кончно было бы замечательно, что бы в .Net Core предусмотрели автоматический привязку.
и солнце б утром не вставало, когда бы не было меня
Отредактировано 18.11.2016 14:46 Serginio1 . Предыдущая версия . Еще …
Отредактировано 18.11.2016 14:28 Serginio1 . Предыдущая версия .
Re[18]: .Net Core Вызов виртуальных методов нативных объекто
От: pilgrim_ Россия  
Дата: 18.11.16 15:45
Оценка: 9 (1)
Здравствуйте, Serginio1, Вы писали:

S> Я вот только непонял, почему при stdcall первый параметр идет в регистры?


Не идет, все через стэк, справа-налево — https://en.wikipedia.org/wiki/X86_calling_conventions#stdcall
Re[19]: .Net Core Вызов виртуальных методов нативных объекто
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 18.11.16 18:30
Оценка:
Здравствуйте, pilgrim_, Вы писали:

_>Здравствуйте, Serginio1, Вы писали:


S>> Я вот только непонял, почему при stdcall первый параметр идет в регистры?


_>Не идет, все через стэк, справа-налево — https://en.wikipedia.org/wiki/X86_calling_conventions#stdcall

Спасибо! Вот под старость лет узнал Еще раз огромное Спасибо!
и солнце б утром не вставало, когда бы не было меня
Re[20]: .Net Core Вызов виртуальных методов нативных объекто
От: pilgrim_ Россия  
Дата: 18.11.16 19:27
Оценка: 3 (1)
Здравствуйте, Serginio1, Вы писали:

S> Спасибо! Вот под старость лет узнал Еще раз огромное Спасибо!


Не за что, посмотри еще вот это — https://github.com/mono/CppSharp , по описанию мощная штука, проверил — не без нюансов, но работает!
Позволяет вмешиваться в процесс генерации interop-кода.

  Вот для такого заголовочного файла:
class ICallbackNotCom
{
public:
    virtual bool __stdcall release() = 0;
    virtual bool __stdcall execute() = 0;
    virtual bool __stdcall dispatch(ICallbackNotCom* other) = 0;
};

extern "C" __declspec(dllexport) ICallbackNotCom* __stdcall UnmanagedWrapCallbackNotCom(ICallbackNotCom* other);


  Сгенерила вот такую адскую штуку:
    public unsafe abstract partial class ICallbackNotCom : IDisposable
    {
        [StructLayout(LayoutKind.Explicit, Size = 4)]
        public partial struct __Internal
        {
            [FieldOffset(0)]
            public global::System.IntPtr vfptr_ICallbackNotCom;

            //[SuppressUnmanagedCodeSecurity]
            //[DllImport("NotComCallback.Native.dll", CallingConvention = global::System.Runtime.InteropServices.CallingConvention.ThisCall,
            //    EntryPoint="??0ICallbackNotCom@@QAE@XZ")]
            //internal static extern global::System.IntPtr ctor_0(global::System.IntPtr instance);

            //[SuppressUnmanagedCodeSecurity]
            //[DllImport("NotComCallback.Native.dll", CallingConvention = global::System.Runtime.InteropServices.CallingConvention.ThisCall,
            //    EntryPoint="??0ICallbackNotCom@@QAE@ABV0@@Z")]
            //internal static extern global::System.IntPtr cctor_1(global::System.IntPtr instance, global::System.IntPtr _0);
        }

        public global::System.IntPtr __Instance { get; protected set; }

        protected int __PointerAdjustment;
        public static readonly System.Collections.Concurrent.ConcurrentDictionary<IntPtr, ICallbackNotCom> NativeToManagedMap = new System.Collections.Concurrent.ConcurrentDictionary<IntPtr, ICallbackNotCom>();
        protected void*[] __OriginalVTables;

        protected bool __ownsNativeInstance;

        public static ICallbackNotCom __CreateInstance(global::System.IntPtr native, bool skipVTables = false)
        {
            return new ICallbackNotComInternal(native.ToPointer(), skipVTables);
        }

        public static ICallbackNotCom __CreateInstance(ICallbackNotCom.__Internal native, bool skipVTables = false)
        {
            return new ICallbackNotComInternal(native, skipVTables);
        }

        protected ICallbackNotCom(void* native, bool skipVTables = false)
        {
            if (native == null)
                return;
            __Instance = new global::System.IntPtr(native);
            __OriginalVTables = new void*[] { ((__Internal*) native)->vfptr_ICallbackNotCom.ToPointer() };
        }

        protected ICallbackNotCom()
        {
            __Instance = Marshal.AllocHGlobal(4);
            __ownsNativeInstance = true;
            NativeToManagedMap[__Instance] = this;
            //__Internal.ctor_0((__Instance + __PointerAdjustment));
            SetupVTables(GetType().FullName == "NotComCallback.ICallbackNotCom");
        }

        protected ICallbackNotCom(ICallbackNotCom _0)
        {
            __Instance = Marshal.AllocHGlobal(4);
            __ownsNativeInstance = true;
            NativeToManagedMap[__Instance] = this;
            if (ReferenceEquals(_0, null))
                throw new global::System.ArgumentNullException("_0", "Cannot be null because it is a C++ reference (&).");
            var __arg0 = _0.__Instance;
            //__Internal.cctor_1((__Instance + __PointerAdjustment), __arg0);
            SetupVTables(GetType().FullName == "NotComCallback.ICallbackNotCom");
        }

        public void Dispose()
        {
            Dispose(disposing: true);
        }

        public virtual void Dispose(bool disposing)
        {
            ICallbackNotCom __dummy;
            NativeToManagedMap.TryRemove(__Instance, out __dummy);
            ((__Internal*) __Instance)->vfptr_ICallbackNotCom = new global::System.IntPtr(__OriginalVTables[0]);
            if (__ownsNativeInstance)
                Marshal.FreeHGlobal(__Instance);
        }

        public abstract bool Execute();

        public abstract bool Dispatch(ICallbackNotCom other);

        public abstract bool Release
        {
            get;
        }

        #region Virtual table interop

        // bool __stdcall release() = 0
        private static Delegates.Func_bool_IntPtr _Release_0DelegateInstance;

        private static bool _Release_0DelegateHook(global::System.IntPtr instance)
        {
            if (!NativeToManagedMap.ContainsKey(instance))
                throw new global::System.Exception("No managed instance was found");

            var __target = (ICallbackNotCom) NativeToManagedMap[instance];
            if (__target.__ownsNativeInstance)
                __target.SetupVTables();
            var __ret = __target.Release;
            return __ret;
        }

        // bool __stdcall execute() = 0
        private static Delegates.Func_bool_IntPtr _Execute_0DelegateInstance;

        private static bool _Execute_0DelegateHook(global::System.IntPtr instance)
        {
            if (!NativeToManagedMap.ContainsKey(instance))
                throw new global::System.Exception("No managed instance was found");

            var __target = (ICallbackNotCom) NativeToManagedMap[instance];
            if (__target.__ownsNativeInstance)
                __target.SetupVTables();
            var __ret = __target.Execute();
            return __ret;
        }

        // bool __stdcall dispatch(ICallbackNotCom* other) = 0
        private static Delegates.Func_bool_IntPtr_IntPtr _Dispatch_0DelegateInstance;

        private static bool _Dispatch_0DelegateHook(global::System.IntPtr instance, global::System.IntPtr other)
        {
            if (!NativeToManagedMap.ContainsKey(instance))
                throw new global::System.Exception("No managed instance was found");

            var __target = (ICallbackNotCom) NativeToManagedMap[instance];
            if (__target.__ownsNativeInstance)
                __target.SetupVTables();
            ICallbackNotCom __result0;
            if (other == IntPtr.Zero) __result0 = null;
            else if (ICallbackNotCom.NativeToManagedMap.ContainsKey(other))
                __result0 = (ICallbackNotCom) ICallbackNotCom.NativeToManagedMap[other];
            else __result0 = ICallbackNotCom.__CreateInstance(other);
            var __ret = __target.Dispatch(__result0);
            return __ret;
        }

        private static void*[] __ManagedVTables;
        private static void*[] _Thunks;

        private void SetupVTables(bool destructorOnly = false)
        {
            if (__OriginalVTables != null)
                return;
            var native = (__Internal*) __Instance.ToPointer();

            __OriginalVTables = new void*[] { ((__Internal*) native)->vfptr_ICallbackNotCom.ToPointer() };

            if (destructorOnly)
                return;
            if (_Thunks == null)
            {
                _Thunks = new void*[3];
                _Release_0DelegateInstance += _Release_0DelegateHook;
                _Thunks[0] = Marshal.GetFunctionPointerForDelegate(_Release_0DelegateInstance).ToPointer();
                _Execute_0DelegateInstance += _Execute_0DelegateHook;
                _Thunks[1] = Marshal.GetFunctionPointerForDelegate(_Execute_0DelegateInstance).ToPointer();
                _Dispatch_0DelegateInstance += _Dispatch_0DelegateHook;
                _Thunks[2] = Marshal.GetFunctionPointerForDelegate(_Dispatch_0DelegateInstance).ToPointer();
            }

            if (__ManagedVTables == null)
            {
                __ManagedVTables = new void*[1];
                var vfptr0 = Marshal.AllocHGlobal(3 * 4);
                __ManagedVTables[0] = vfptr0.ToPointer();
                *(void**)(vfptr0 + 0) = _Thunks[0];
                *(void**)(vfptr0 + 4) = _Thunks[1];
                *(void**)(vfptr0 + 8) = _Thunks[2];
            }

            native->vfptr_ICallbackNotCom = new IntPtr(__ManagedVTables[0]);
        }

        #endregion
    }

    public unsafe partial class ICallbackNotComInternal : ICallbackNotCom, IDisposable
    {
        private static void* __CopyValue(ICallbackNotCom.__Internal native)
        {
            var ret = Marshal.AllocHGlobal(4);
            *(ICallbackNotCom.__Internal*) ret = native;
            return ret.ToPointer();
        }

        internal ICallbackNotComInternal(ICallbackNotCom.__Internal native, bool skipVTables = false)
            : this(__CopyValue(native), skipVTables)
        {
            __ownsNativeInstance = true;
            NativeToManagedMap[__Instance] = this;
        }

        internal ICallbackNotComInternal(void* native, bool skipVTables = false)
            : base((void*) null)
        {
            __PointerAdjustment = 0;
            __Instance = new global::System.IntPtr(native);
            __OriginalVTables = new void*[] { ((__Internal*) native)->vfptr_ICallbackNotCom.ToPointer() };
        }

        public override bool Execute()
        {
            var __slot = *(void**) ((IntPtr) __OriginalVTables[0] + 1 * 4);
            var ___Execute_0Delegate = (Delegates.Func_bool_IntPtr) Marshal.GetDelegateForFunctionPointer(new IntPtr(__slot), typeof(Delegates.Func_bool_IntPtr));
            var __ret = ___Execute_0Delegate((__Instance + __PointerAdjustment));
            return __ret;
        }

        public override bool Dispatch(ICallbackNotCom other)
        {
            var __slot = *(void**) ((IntPtr) __OriginalVTables[0] + 2 * 4);
            var ___Dispatch_0Delegate = (Delegates.Func_bool_IntPtr_IntPtr) Marshal.GetDelegateForFunctionPointer(new IntPtr(__slot), typeof(Delegates.Func_bool_IntPtr_IntPtr));
            var __arg0 = ReferenceEquals(other, null) ? global::System.IntPtr.Zero : other.__Instance;
            var __ret = ___Dispatch_0Delegate((__Instance + __PointerAdjustment), __arg0);
            return __ret;
        }

        public override bool Release
        {
            get
            {
                var __slot = *(void**) ((IntPtr) __OriginalVTables[0] + 0 * 4);
                var ___Release_0Delegate = (Delegates.Func_bool_IntPtr) Marshal.GetDelegateForFunctionPointer(new IntPtr(__slot), typeof(Delegates.Func_bool_IntPtr));
                var __ret = ___Release_0Delegate((__Instance + __PointerAdjustment));
                return __ret;
            }
        }
    }

    public unsafe partial class NotComCallback
    {
        public partial struct __Internal
        {
            [SuppressUnmanagedCodeSecurity]
            [DllImport("NotComCallback.Native.dll", CallingConvention = global::System.Runtime.InteropServices.CallingConvention.StdCall,
                EntryPoint="_UnmanagedWrapCallbackNotCom@4")]
            internal static extern global::System.IntPtr UnmanagedWrapCallbackNotCom_0(global::System.IntPtr other);
        }

        public static ICallbackNotCom UnmanagedWrapCallbackNotCom(ICallbackNotCom other)
        {
            var __arg0 = ReferenceEquals(other, null) ? global::System.IntPtr.Zero : other.__Instance;
            var __ret = __Internal.UnmanagedWrapCallbackNotCom_0(__arg0);
            ICallbackNotCom __result0;
            if (__ret == IntPtr.Zero) __result0 = null;
            else if (ICallbackNotCom.NativeToManagedMap.ContainsKey(__ret))
                __result0 = (ICallbackNotCom) ICallbackNotCom.NativeToManagedMap[__ret];
            else __result0 = ICallbackNotCom.__CreateInstance(__ret);
            return __result0;
        }
    }

    namespace Delegates
    {
        [SuppressUnmanagedCodeSecurity, UnmanagedFunctionPointerAttribute(global::System.Runtime.InteropServices.CallingConvention.StdCall)]
        public unsafe delegate bool Func_bool_IntPtr(global::System.IntPtr instance);

        [SuppressUnmanagedCodeSecurity, UnmanagedFunctionPointerAttribute(global::System.Runtime.InteropServices.CallingConvention.StdCall)]
        public unsafe delegate bool Func_bool_IntPtr_IntPtr(global::System.IntPtr instance, global::System.IntPtr arg1);
    }


Все работает как полагается.

ps: я закоментарил только импорт/вызовы ctor, т.к. не импортирую их. вероятно этим можно управлять в процессе генерации интероп-кода. + из метода bool __stdcall release() = 0 почему то сделала get-свойство Release.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.