Информация об изменениях

Сообщение Re[13]: .Net Core Вызов виртуальных методов нативных объекто от 18.11.2016 10:29

Изменено 18.11.2016 10:32 Serginio1

Здравствуйте, 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
Re[13]: .Net Core Вызов виртуальных методов нативных объекто
Здравствуйте, 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 то все работает правильно.