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...
Пока на собственное сообщение не было ответов, его можно удалить.