Маршалинг интерфейса Interop COM(-isch) приводит к исключению AccessViotlationException при простом вызове
Я пытаюсь написать управляемую библиотеку взаимодействия для стандартного плагина C++. Эта нативная библиотека C++ использует COM-совместимый дизайн интерфейса. Тем не менее, это НЕ делает какие-либо вещи регистрации класса. Как и в случае с COM, все интерфейсы являются производными от IUnknown (называется FUnknown, но в любом случае те же 3 метода).
Я написал простое консольное приложение на C++, которое загружает мой управляемый тестовый плагин и извлекает начальный (корневой) интерфейс (шаблон фабрики объектов - во многом как com) через экспортируемый метод. Я использую стороннюю реализацию атрибута кода DllExport - кажется, она работает нормально. Тестовое приложение C++ использует LoadLibrary/GetProcAddress и успешно извлекает ссылку на интерфейс. Я могу установить точку останова в моей управляемой экспортируемой функции, и она получит ожидаемый результат.
Затем тестовое приложение C++ вызывает AddRef для интерфейса IUnknown (часть) и возвращает 2 - как и следовало ожидать. Обратите внимание, что мое определение управляемого интерфейса (контрагент) НЕ происходит от IUnknown -или включают эти методы. Я бы сказал, что это означает, что управляемая магия маршалинга вмешалась и предоставила CCW.
Затем тестовое приложение C++ вызывает простой метод на заводском интерфейсе - тот, который просто возвращает int32 - и который также прибывает в управляемую реализацию (точка останова получает удар), но когда этот метод возвращает, он генерирует исключение AccessViolationException - где-то в управляемом неуправляемый переход.
class IPluginFactory : public FUnknown
{
public:
// removed other methods before and after this one
virtual int32 PLUGIN_API countClasses () = 0;
};
Int32 - это #define, а PLUGIN_API определяется как __stdcall - насколько я могу судить, COM-совместимость.
Управляемое представление этого интерфейса я определил следующим образом:
[ComImport]
[Guid("same guid as in C++ file")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IPluginFactory
{
[return: MarshalAs(UnmanagedType.I4)]
Int32 CountClasses();
}
Управляемая реализация этого метода просто возвращает жестко закодированное число (1).
Я перепробовал многие вещи (так много, что я даже не могу вспомнить их все), и в настоящее время не могу понять, как решить эту проблему или в чем проблема.
Буду признателен за любую оказанную помощь. Thanx!
РЕДАКТИРОВАТЬ: Запросить подробную информацию о FUnknown:
class FUnknown
{
public:
virtual tresult PLUGIN_API queryInterface (const TUID iid, void** obj) = 0;
virtual uint32 PLUGIN_API addRef () = 0;
virtual uint32 PLUGIN_API release () = 0;
};
1 ответ
Мы не можем видеть, как выглядит FUnknown. Он должен быть идентичен IUnknown, чтобы позволить взаимодействию работать. CLR автоматически вызовет AddRef, Release и QueryInterface. И очень важно, что в FUnknown есть ровно три метода, если FUnknown имеет более или менее, то вы в конечном итоге вызовете совершенно неправильный метод, когда ваш код C# вызывает CountClasses(). Действительно хороший способ вызвать AVE.
Одна проблема, которую мы можем видеть, состоит в том, что CountClasses несовместимы с COM, методы должны возвращать Int32, который является HRESULT. Код ошибки, значение которого не равно нулю, автоматически вызывает исключение в программе на C#. Поддерживаются несовместимые сигнатуры методов, необходимо использовать атрибут. Как это:
[PreserveSig]
int CountClasses();
В противном случае этой ошибки недостаточно для объяснения AVE. Несоответствие интерфейса - ваш вероятный враг. В этом случае вам нужна оболочка C++/CLI.