Я использую неуправляемую библиотеку, написанную на C ++. Библиотека имеет управляемую оболочку C ++ (CLI), и я использую библиотеку из управляемого кода. Неуправляемая библиотека (включая оболочку CLI) написана третьей стороной, но у меня есть доступ к исходному коду.
К сожалению, управляемая оболочка плохо работает с доменами приложений. Неуправляемая библиотека создает потоки и обращается к управляемому коду из этих потоков. Это приводит к проблемам, когда управляемый код выполняется в домене приложения не по умолчанию. И мне нужны кросс-доменные вызовы, чтобы можно было проводить модульное тестирование с использованием стандартных инструментов.
Чтобы решить эту проблему, я ввел делегатов в управляемую оболочку и использовал Marshal.GetFunctionPointerForDelegate()
, чтобы получить указатель на функцию, позволяющую успешно выполнять вызовы между доменами AppDomain.
Обычно это работает хорошо, но теперь у меня возникла новая проблема. У меня следующая последовательность событий.
Unmanaged thread ->
Unmanaged code 1 ->
Managed wrapper 1 ->
AppDomain transition (via delegate) ->
Managed wrapper 2 ->
Unmanaged code 2 ->
Я упустил некоторые подробности о том, как библиотека позволяет вам переопределить некоторые функции в управляемом коде выше управляемая обертка 2 , что является главной целью сделать переход от неуправляемого к управляемому в первую очередь.
В конечном итоге неуправляемый код 2 должен будет вернуть неуправляемый объект в неуправляемый код 1 .
Без делегата и содержимого AppDomain управляемая оболочка 2 обернет неуправляемый объект и вернет его в управляемую оболочку 1 , которая затем передаст состояние неуправляемому объекту, используемому неуправляемый код 1 .
К сожалению, мне трудно вернуть управляемый объект через переход AppDomain.
Я подумал, что должен был сделать управляемый объект, передаваемый через границу AppDomain, сериализуемым. Однако это нелегко сделать. Вместо этого я создал простой класс, в котором я могу хранить тип объекта, который я хочу передать, и строку, представляющую состояние объекта. И Type
, и String
легко маршалируются, и, к счастью, я всегда могу создать экземпляр объекта, используя конструктор по умолчанию, а затем инициализировать его из строки:
// Message is the base class of large hierarchy of managed classes.
[Serializable]
// Sorry for the "oldsyntax", but that is what the C++ library uses.
__gc class SerializedMessage {
public:
SerializedMessage(Message* message)
: _type(message->GetType()), _string(message->ToString()) { }
Message* Create() {
Message* message = static_cast<Message*>(Activator::CreateInstance(_type));
message->InitializeFromString(_string);
return message;
}
private:
Type* _type;
String* _string;
};
В управляемой оболочке 2 Я возвращаю SerializedMessage
, а в управляемой оболочке 1 Затем я получаю копию исходного сообщения, вызывая SerializedMessage::Create
. Или, по крайней мере, это было то, чего я надеялся достичь.
К сожалению, переход AppDomain завершается с ошибкой InvalidCastException
с сообщением Невозможно привести объект типа 'SerializedMessage' к типу 'SerializedMessage'.
Я не уверен, что происходит, но сообщение об ошибке может указывать, что к объекту SerializedMessage
обращаются из неправильного домена приложения. Однако весь смысл использования Marshal.GetFunctionPointerForDelegate
заключается в возможности вызова через домены приложений.
Я также пытался извлечь SerializedMessage
из MarshalByRefObject
, но затем я получил и InvalidCastException
с сообщением Невозможно привести объект типа 'System.MarshalByRefObject' к типу 'SerializedMessage'.
Что мне нужно сделать, чтобы иметь возможность передавать управляемый объект обратно из другого домена приложений, когда я вызываю указатель, возвращаемый Marshal.GetFunctionPointerForDelegate()
?
Комментарии к принятому ответу
Правильная часть о том, как сборка была загружена во второй домен приложений.
Код C ++ выполняется в домене приложений по умолчанию, который контролируется модулем модульного тестирования. Управляемая сборка загружается во второй домен приложений, созданный этим модулем тестирования модулей. Я использую Marshal.GetFunctionPointerForDelegate
для включения управляемых вызовов C ++ от первого до второго домена приложений.
Изначально я получил несколько FileNotFoundException
, пытающихся загрузить свою управляемую сборку, и, чтобы обойти это, я скопировал свою управляемую сборку в AppBase программы запуска модульных тестов. Я все еще немного сбит с толку, почему .NET настаивает на загрузке самой сборки, которая выполняется, но решил поработать над этой проблемой позже и просто скопировал отсутствующий файл в качестве кладжи.
К сожалению, это загружает один и тот же тип из двух разных копий одной и той же сборки в двух разных доменах приложений, и я предполагаю, что это является основной причиной InvalidCastException
.
Мой вывод заключается в том, что яневозможно использовать «стандартный» модуль модульных тестов для тестирования управляемой библиотеки C ++, потому что обратные вызовы из этой библиотеки получены с неправильного домена приложений, который я не могу контролировать.