Короткий ответ — нет.
Ты и в обычном .net их толком не вызовешь без type library или IDispatch потому что
а) для вирт. метода нужно знать индекс в таблице вируальных функций
б) этих таблиц может быть несколько, и надо знать индекс таблицы
в) данные базового класса могут иметь смещение и его тоже нужно знать
Извраты есть, но о них рассказывать нет никакого желания, потому что гомосятина
Re[2]: .Net Core Вызов виртуальных методов нативных объектов
Здравствуйте, rm822, Вы писали:
R>Короткий ответ — нет. R>Ты и в обычном .net их толком не вызовешь без type library или IDispatch потому что R>а) для вирт. метода нужно знать индекс в таблице вируальных функций
Ну так описывая класс виртуальными методами мы и описываем VMT
По сути это ничем не отличается от IUnknown R>б) этих таблиц может быть несколько, и надо знать индекс таблицы
Это как? Интерфейсы и множественное наследование не рассматриваем. Только VMT. При этом на С++ нет проблем их использовать. R>в) данные базового класса могут иметь смещение и его тоже нужно знать
Данные не интересуют. Интересуют только объект первым полем является ссылка на VMT.
И описание этой VMT на C#. И почему это не сделать для совместимости в .Net Core c нативным кодом?
Здравствуйте, turbocode, Вы писали:
S>> Или другой интерфейс для доступа к виртуальным методам С++ класса, без методов IUncknown напрямую?
T>Нет, нужно завернуть С++ в COM библиотеку.
Для .Net Core пойдет?
и солнце б утром не вставало, когда бы не было меня
Re[3]: .Net Core Вызов виртуальных методов нативных объектов
Здравствуйте, Serginio1, Вы писали:
S> Возможно ли вызвать виртуальные методы С++ классов из .Net Core.
В мсдн пишут что в принципе можно, но саму структуру классов с++ надо заранее проектировать под интеграцию с .net. Для остальных случаев рекомендуют оборачивать общение с классами с++ в с-api
Re[4]: .Net Core Вызов виртуальных методов нативных объектов
Здравствуйте, turbocode, Вы писали:
T>>>Нет, нужно завернуть С++ в COM библиотеку. S>> Для .Net Core пойдет?
T>Думаю что пойдет при условии что ты согласен потерять переносимость.
нет мне нужна кроссплатформенность. На самом деле
Здравствуйте, α, Вы писали:
α>Здравствуйте, Serginio1, Вы писали:
S>> Возможно ли вызвать виртуальные методы С++ классов из .Net Core.
α>В мсдн пишут что в принципе можно, но саму структуру классов с++ надо заранее проектировать под интеграцию с .net. Для остальных случаев рекомендуют оборачивать общение с классами с++ в с-api
Мне как раз и нужно вызывать нативные методы из .Net
А чем тебе это поможет?
Я не слышал чтобы в .Net Core была возможность создавать кроссплатформенные COM объекты (да и еще на С++). P.S. Было бы неплохо конечно, но думаю этого мы никогда не дождемся.
Re[6]: .Net Core Вызов виртуальных методов нативных объектов
T>А чем тебе это поможет? T>Я не слышал чтобы в .Net Core была возможность создавать кроссплатформенные COM объекты (да и еще на С++). P.S. Было бы неплохо конечно, но думаю этого мы никогда не дождемся.
S> Так суть COM это VMT. А она по сути кроссплатформенна.
С каких это пор? Можно примеры таких С++ COM объектов?
P.S. Я не спорю что MS мог для своих нужд это сделать, но для общего использования ничего подобного нету.
Re[8]: .Net Core Вызов виртуальных методов нативных объектов
Здравствуйте, 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.
S> Ну я тебе показал ICLRRuntimeHost2.
Это внутренности, для себя MS может сделать какой то кроссплатформенный обрубок COM, но это не значит что ты сможешь этим воспользоваться.
S> Так суть интерфейса это VMT. А классы с виртуальными методами кроссплатформенны. S> В 1С это прекрасно работает
[example skiped]
Не знаю что там в 1С но в твоем примере COM-ом и не пахнет.
Re[10]: .Net Core Вызов виртуальных методов нативных объекто
Здравствуйте, 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 Вызов виртуальных методов нативных объекто
S>VMT кроссплатформенна. Мне не нужен COM как таковой. Мне нужен контракт на основе VMT между нативным и манагед кодом
Не будет COM-а не будет и контракта, точнее бинарной совместимости. Но если тебе не нужно строить объектные модели то как сказали тебе ниже сделай себе сишную обертку и не парся.
Re[14]: .Net Core Вызов виртуальных методов нативных объекто
Здравствуйте, turbocode, Вы писали:
S>>VMT кроссплатформенна. Мне не нужен COM как таковой. Мне нужен контракт на основе VMT между нативным и манагед кодом
T>Не будет COM-а не будет и контракта, точнее бинарной совместимости. Но если тебе не нужно строить объектные модели то как сказали тебе ниже сделай себе сишную обертку и не парся.
Интересно существует ли возможность использовать интерфейсы или виртуальные методы?
По сути то это несложно если соблюдать тип вызова и VMT
Я и не особо парюсь.
и солнце б утром не вставало, когда бы не было меня
Re[15]: .Net Core Вызов виртуальных методов нативных объекто
S> Интересно существует ли возможность использовать интерфейсы или виртуальные методы?
Напрямую нет, только: 1. С-style обертка; 2. COM; 3. грязный хак;
Re[3]: .Net Core Вызов виртуальных методов нативных объектов
Здравствуйте, Serginio1, Вы писали:
α>>В мсдн пишут что в принципе можно, но саму структуру классов с++ надо заранее проектировать под интеграцию с .net. Для остальных случаев рекомендуют оборачивать общение с классами с++ в с-api
S> Мне как раз и нужно вызывать нативные методы из .Net
Здравствуйте, Lexey, Вы писали:
L>Здравствуйте, Serginio1, Вы писали:
S>> Так суть COM это VMT. А она по сути кроссплатформенна.
L>С каких это пор реализации VMT стали кроссплатформенными? http://rsdn.org/forum/dotnet/6608772.1
Здравствуйте, α, Вы писали:
α>Здравствуйте, 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();
}
Здравствуйте, Serginio1, Вы писали:
S> В С++ определил класс S>class ICallback : public IUnknown
А упорядочить методы тут разве не надо? Т.е. сперва методы IUnknown, потом твой execute(). Что-то я уже подзабыл
PS А, это ж класс, а не интерфейс. Перепутал
Здравствуйте, α, Вы писали:
α>Здравствуйте, Serginio1, Вы писали:
S>> В С++ определил класс S>>class ICallback : public IUnknown
α>А упорядочить методы тут разве не надо? Т.е. сперва методы IUnknown, потом твой execute(). Что-то я уже подзабыл α>PS А, это ж класс, а не интерфейс. Перепутал
См. выделенное
-ты должен проверять поддерживаемые тобой интерфейсы (как минимум IUnknown)
-и возвращать правильную ссылку на них (с пом. приведения — чтобы получить правильное смещение на vtable) S> Так вот перед вызовом execute сначала запрашиваеется QueryInterface с двумя riid IUnknown и IProvideClassInfo S>Если не дать IProvideClassInfo то начинает запрашивать Idispatch и еще какие то интерфейсы.
Ты должен правильно реагировать на эти запросы — реализуешь — возвращай, нет — досвидания, в твоем случае как минимум 2 интерфейса ты поддерживаешь — IUnknown и твой ICallback
Рабочий пример (хост — .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 реализованна.
Здравствуйте, ajanov, Вы писали:
A>Здравствуйте, Serginio1, Вы писали:
S>> Возможно ли вызвать виртуальные методы С++ классов из .Net Core.
S>> Да и как там в .Net Core C++/CLI ?
A>Компилятор C++/CLI берет все заботы на себя
А какие опции под .Net Core? Просто я в С++ новичок
и солнце б утром не вставало, когда бы не было меня
Re[3]: .Net Core Вызов виртуальных методов нативных объектов
Здравствуйте, 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 Вызов виртуальных методов нативных объектов
Если все как в .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 Вызов виртуальных методов нативных объектов
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
Здравствуйте, ajanov, Вы писали:
A>Десиплюсплюсизация от Майкрософт
У C++/CLI столько тараканов, что лучше иметь нормальный ABI (C-like API), чем писать костыли. А для библиотек которые header/template based всё равно только топор поможет. Топором в этом случае я имею ввиду вынос части своей логики в C++ который и сможет быть и скомпилирован эффективно, и вызван (ну и работа будет делаться с минимумом пересечений managed-native границ). Микс же и прозрачные "трамплины" скорости не добавят, в поддержке сложнее, доп знания о диалекте и энвайронменте опять же нужны.
Ну на вскидку — прийдется хорошенько попрыгать прежде чем нативный колбэк будет вызывать код в правильном апп домене, в то время как в интеропе со стороны шарпа (через делегаты и GetFunctionPointer) это достанется бесплатно. Понятно, что всё тоже можно замутить в C++/CLI, но легче из шарпа работать с любыми типами. Лиж бы описания структур были, а потом указатель и вперёд. Но это как раз об ABI.
Re[9]: .Net Core Вызов виртуальных методов нативных объектов
Здравствуйте, fddima, Вы писали:
F> Ну на вскидку — прийдется хорошенько попрыгать прежде чем нативный колбэк будет вызывать код в правильном апп домене, в то время как в интеропе со стороны шарпа (через делегаты и GetFunctionPointer) это достанется бесплатно. Понятно, что всё тоже можно замутить в C++/CLI, но легче из шарпа работать с любыми типами. Лиж бы описания структур были, а потом указатель и вперёд. Но это как раз об ABI.
А можно поподробнее про ABI. Нашел только http://stackoverflow.com/questions/3784389/difference-between-api-and-abi
и солнце б утром не вставало, когда бы не было меня
Re[10]: .Net Core Вызов виртуальных методов нативных объекто
Здравствуйте, 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 это будет один метод с параметром.
Так суть интерфейса это 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.
Здравствуйте, Serginio1, Вы писали:
S> Передавать статические функции или ThisCall трудоемко.
Что значит передавать? Откуда-куда?
S>Проще общаться с нативом в разные стороны через абстрактные классы
Вот как раз и не проще. Иногда проще через очень даже непрозрачные структуры, где клиент и сервер знает её внутренности. Вообще зависит, от того с какой стороны смотреть и/или какие проблемы решаются. Иногда гораздо проще opaque pointer + вся работа только через статические функции (импорты из длл).
И тут весь этот бред вокруг IUnknown я считаю лишним абсолютно. Далеко не проще через абстрактные.
Я ещё раз делаю акцент: решение зависит только от входящих требований: стабильное ABI, простое, C-совместимое, с или без символическим связыванием имен, пишем ли мы и клиент и сервер и т.п.
S> Так суть интерфейса это VMT. А классы с виртуальными методами кроссплатформенны. S> В 1С это прекрасно работает
Мне показалось, что в другой ветке же ж разобрались как можно заставить эту кухню работать?
Re[13]: .Net Core Вызов виртуальных методов нативных объекто
Здравствуйте, fddima, Вы писали:
F>Здравствуйте, Serginio1, Вы писали:
S>> Передавать статические функции или ThisCall трудоемко. F> Что значит передавать? Откуда-куда?
Из натива в .Net Native вестимо http://rsdn.org/forum/dotnet/6609566.1
S>>Проще общаться с нативом в разные стороны через абстрактные классы F> Вот как раз и не проще. Иногда проще через очень даже непрозрачные структуры, где клиент и сервер знает её внутренности. Вообще зависит, от того с какой стороны смотреть и/или какие проблемы решаются. Иногда гораздо проще opaque pointer + вся работа только через статические функции (импорты из длл).
F> И тут весь этот бред вокруг IUnknown я считаю лишним абсолютно. Далеко не проще через абстрактные.
F> Я ещё раз делаю акцент: решение зависит только от входящих требований: стабильное ABI, простое, C-совместимое, с или без символическим связыванием имен, пишем ли мы и клиент и сервер и т.п.
S>> Так суть интерфейса это VMT. А классы с виртуальными методами кроссплатформенны. S>> В 1С это прекрасно работает F> Мне показалось, что в другой ветке же ж разобрались как можно заставить эту кухню работать?
Да все это работает и на уровне статических методов (ThisCall суть таже). При использовании IUnknown идет куча ненужных вызовов.
Кроме того я хочу эти абстрактные C++ классы с stdcall вызовами использовать в .Net Core
Там мне приходится передавать ссылки на каждый метод.
Но хотоелось бы аналогов интерфейсов. При этом использовать только ссылку на объект, первым полем которого является ссылка на 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(Объект);
// получим адресс метода по смещению в VMTvar АдресМетода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 проблема.
Здравствуйте, 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++ :
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.
Соотв. если делать ручной маршалинг, все это нужно учитывать.
S> Но при что бы получить ссылку на интерфейс, вызывается куча QueryInterface, AddRef и Release
Здравствуйте, 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> }
тебе намекал, а ты сделал вид что понял весь хардкор с Marshal.ReadIntPtr за тебя сделает рантайм.
S> параметры поменяны местами, а переданное первым параметром значение не соотвествует переданному.
Используй stdcall как в native коде, так и в делегате на C#, и все будет норм.
Re[15]: .Net Core Вызов виртуальных методов нативных объекто
Здравствуйте, pilgrim_, Вы писали:
_>Здравствуйте, Serginio1, Вы писали:
S>>Попробовал с VMT S>>Определил методы
_>Вот ты неуемный ты бы для начала проверил, работает ли COM-интероп на linux — ведь это же твоя цель? _>И если работает — тему можно закрывать, COM-интеропа тебе хватит за глаза. _>Или ты заради любви к искусству?
И то и другое. Да ине нужны мне методы IUknown. Я итак передаю нужныю ссылку, а подсчет ссылок если мне нужно организую сам.
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>> }
тебе намекал, а ты сделал вид что понял весь хардкор с Marshal.ReadIntPtr за тебя сделает рантайм.
S>> параметры поменяны местами, а переданное первым параметром значение не соотвествует переданному.
_>Используй stdcall как в native коде, так и в делегате на C#, и все будет норм.
Проблема в том, что ThisCall и stdcall не совместимы.
То есть я должен определить как
virtual int execute(int value1, int value2);
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, 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
_>>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 Вызов виртуальных методов нативных объекто
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);
}
То есть первый параметр пошел в регисты. Сейчас проверю с несколькими параметрами
и солнце б утром не вставало, когда бы не было меня
_>>Здравствуйте, Serginio1, Вы писали: S>>>Попробовал с VMT S>>>Определил методы _>>Вот ты неуемный ты бы для начала проверил, работает ли COM-интероп на linux — ведь это же твоя цель? _>>И если работает — тему можно закрывать, COM-интеропа тебе хватит за глаза. _>>Или ты заради любви к искусству? S> И то и другое. Да ине нужны мне методы IUknown. Я итак передаю нужныю ссылку, а подсчет ссылок если мне нужно организую сам. S> Для приведения к интерфейсу при вызове одного метода идет куча QueryInterface Addref,Release S>http://rsdn.org/forum/dotnet/6611240.1
Здравствуйте, pilgrim_, Вы писали:
Спасибо!
Я вот только непонял, почему при stdcall первый параметр идет в регистры?
Или this при stdcall идет первым параметром?
Здравствуйте, pilgrim_, Вы писали:
_>Здравствуйте, Serginio1, Вы писали:
S>> Я вот только непонял, почему при stdcall первый параметр идет в регистры?
_>Не идет, все через стэк, справа-налево — https://en.wikipedia.org/wiki/X86_calling_conventions#stdcall
Спасибо! Вот под старость лет узнал Еще раз огромное Спасибо!
и солнце б утром не вставало, когда бы не было меня
Re[20]: .Net Core Вызов виртуальных методов нативных объекто
Здравствуйте, Serginio1, Вы писали: S> Спасибо! Вот под старость лет узнал Еще раз огромное Спасибо!
Не за что, посмотри еще вот это — https://github.com/mono/CppSharp , по описанию мощная штука, проверил — не без нюансов, но работает!
Позволяет вмешиваться в процесс генерации interop-кода.
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() = 0private 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() = 0private 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) = 0private 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.