Проблема отслеживания ссылки на собственную ссылку — C# на C++/CLI на C++ AccessViolationException

Я пытаюсь написать оболочку C++/CLI, используя VS2010 для API C++, для которого у меня есть доступ только к файлам заголовков и файлу dll/lib.

У меня возникла проблема с всплывающей резервной копией собственной ссылки из собственного кода в ссылку отслеживания в коде C#. Я получаю SystemAccessViolation. Код выглядит следующим образом:

Неуправляемые собственные заголовки C++ для собственных функций, которые возвращают ссылку на создание, а также на изменение пользовательского объекта:

class ManageCustomObject;
    _declspec(dllimport) errorCode CreateCustomObject(CustomObject& customObject);
    _declspec(dllimport) errorCode ModifyCustomObject(CustomObject& customObject);

Важно отметить, что CustomObject имеет только частные конструкторы:

CustomObject(const CustomObject&) { }
CustomObject& operator=(const CustomObject&) { return *this; }

Теперь мой метод слоя C++/CLI выглядит так:

public ref class WrapperManageCustomObject {
public:
        errorCode WrapperCreateCustomObject(CustomObject% customObject)
        {
            return CreateCustomObject(customObject);
       }

        errorCode WrapperModifyCustomObject(CustomObject% customObject)
        {
            return ModifyCustomObject(customObject);
        }
};

И оболочка для CustomObject выглядит так:

public class WrapperCustomObject : public CustomObject {
    public:
        WrapperCustomObject(const CustomObject&) {};
};

В C# код выглядит следующим образом:

WrapperCustomObject wrapperCustomObject;
WrapperManageCustomObject wrapperManageCustomObject = new WrapperManageCustomObject();

long result;

// this works great
result = wrapperManageCustomObject.CreateCustomObject(ref wrapperCustomObject);

// can utilize wrapperCustomObject here with other native functions no problem, so long as I don't try to modify it...
...

// this throws an AccessViolationException was unhandled message
// essentially I am modifying / returning a different native customObject (though I'm not 100% sure what it does as I do not have access to the code)
result = wrapperManageCustomObject.ModifyCustomObject(ref wrapperCustomObject);

Как вы можете видеть в моих комментариях, попытка изменить этот объект с помощью ссылки выдает «AccessViolationException was unhandled: Попытка чтения или записи защищенной памяти. Это часто указывает на то, что другая память повреждена».

Теперь, если я исключаю C# и отслеживающую ссылку из этого уравнения и делаю все это в C++ или C++/CLI (пока отсутствует отслеживающая ссылка), все работает нормально. Я предполагаю, что это как-то связано с собственным кодом, пытающимся получить доступ к обновлению ссылки, которая сейчас находится в управляемой земле.

Есть ли способ обойти это, чтобы по существу заставить этот код «работать» изнутри С#? Мне не известно, как передать нативную ссылку на C#.


person codemouse    schedule 05.08.2011    source источник
comment
Как вы должны создать CustomObject в земле C++, если у него есть частный конструктор и нет фабричных функций? У вас уже должен быть экземпляр CustomObject, чтобы вызвать вспомогательную функцию CreateCustomObject.   -  person Adam Rosenfield    schedule 06.08.2011
comment
Как вы заставили это скомпилировать? Вы возвращаете собственный указатель объекта, но код C# обрабатывает его как управляемую оболочку. Да, большой бум, когда вы его используете. Указатель собственного объекта должен быть закрытым членом оболочки.   -  person Hans Passant    schedule 06.08.2011
comment
Стью – Я могу сделать это и как выходной параметр, но не более того. Параметры ссылки - это вещь С#.   -  person codemouse    schedule 06.08.2011
comment
Ганс - C++/CLI позволяет использовать ссылку для отслеживания. Хотите верьте, хотите нет, но это действительно работает.   -  person codemouse    schedule 06.08.2011
comment
Адам, вам не нужно создавать CustomObject в C++, если функция уже возвращает ссылку на один из них. Это означает, что я предполагаю, что файл .cpp, который я не вижу, находится там, где создается и хранится экземпляр. Вам просто нужен контейнер для хранения этой ссылки. то есть: CustomObject customObject; перед вызовом этой функции.   -  person codemouse    schedule 06.08.2011
comment
@ Брайан: здесь я согласен с Гансом; Я не знаю, почему вы думаете, что наличие ссылок на отслеживание меняет достоверность его заявления. Кроме того, обращаясь к людям, добавляйте к их именам префикс @, чтобы они получали уведомления в своем почтовом ящике SO.   -  person ildjarn    schedule 06.08.2011
comment
@ildjarn, хорошо. Согласованный. Однако даже если бы я сделал что-то вроде private: CustomObject _customObject; в общедоступном классе WrapperCustomObject (без наследования класса от собственного базового типа) я все еще получаю ту же ошибку. Принимая во внимание, что если я делаю это полностью на неуправляемом С++, все работает нормально. Ссылка отслеживания, которую я использую, чтобы указать на экземпляр класса-оболочки, вызывает рвоту.   -  person codemouse    schedule 06.08.2011
comment
@Brian: Идиоматический подход состоит в том, чтобы сделать это private: CustomObject* _customObject; и реализовать реализацию удаления и финализатор, чтобы убедиться, что он очищен.   -  person ildjarn    schedule 06.08.2011
comment
@ildjarn: опять же, согласен. это все, что я сделал бы, если бы у меня был надлежащий доступ к исходному коду C++ dll, но у меня его нет. в то же время, это все обходные пути, насколько я могу успешно работать.   -  person codemouse    schedule 09.08.2011


Ответы (1)


Управляемые ссылки абсолютно не похожи на собственные ссылки. Управляемые ссылки больше похожи на собственные указатели. Насколько мне известно, вы не можете делать что-то вроде public class WrapperCustomObject : public CustomObject (где CustomObject — собственный класс) и ожидать, что это сработает. Когда вы создаете управляемый класс, вы сообщаете CLR, что ваш класс будет вести себя хорошо в условиях перемещения в памяти. Если CustomObject вообще что-то делает с указателями, это уже не будет правдой. CustomObject не использует управляемые ссылки, и поэтому среда выполнения не может знать, где обновлять клиентов, если/когда этот экземпляр класса перемещается.

Хуже того, ваша оболочка позволяет создавать экземпляры этого класса, минуя фабричный метод. Следовательно, если фабричный метод CreateCustomObject делает что-то важное с возвращаемым вами объектом, вы нарушаете контракт кода, который вы вызываете, который просит вас создавать экземпляры этого объекта только с помощью фабричного метода.

person Billy ONeal    schedule 05.08.2011
comment
Как ни странно, вы действительно можете получить открытый класс WrapperCustomObject как управляемый производный класс для наследования от открытого CustomObject (неуправляемого собственного класса), и он действительно будет работать. Однако, как вы указали, и я согласен, ограничения могут быстро возрасти, и я согласен с тем, что вы говорите. - person codemouse; 06.08.2011
comment
Возможно, то, что я сейчас ищу, - это любой возможный способ обойти эту методологию, чтобы что-то работало правильно. Кажется, я не могу найти никакого способа сделать что-то полезное, когда CreateCustomObject возвращает этот объект только по ссылке - и это единственный способ, которым я могу эффективно создать или сохранить его. Работать со ссылками очень сложно — вы не можете хранить их вообще, за исключением потенциально статического единственного экземпляра C++, ИЛИ, как я продемонстрировал, отслеживаемой ссылки на него, хранящейся через Managed C#. - person codemouse; 06.08.2011
comment
@Brian: Нет, на самом деле это не работает - то, что оно компилируется, не означает, что оно не дает сбоев во время выполнения (и на самом деле это так). Он компилируется, потому что класс C++ может быть без состояния или может быть без указателя, и в этом случае он будет работать. Но если вы не знаете реализацию класса, на него нельзя положиться. Что касается сложности работы со ссылками, то они сложны только в том случае, если вы попытаетесь думать о собственных ссылках как об управляемых ссылках. Они не имеют ничего общего друг с другом. Если вам нужна управляемая ссылка, создайте ее. - person Billy ONeal; 06.08.2011
comment
@Brian: вы не можете превратить управляемую ссылку в собственную ссылку или наоборот. Период. Если вам нужна оболочка, вам действительно нужно создать оболочку и предоставить управляемые эквиваленты для собственных методов. - person Billy ONeal; 06.08.2011
comment
согласованный. Все мои исследования показали, что невозможно получить собственную ссылку из управляемой/отслеживаемой ссылки. Тем не менее, я все еще отмечаю, что когда-то наружу ссылка отслеживания из C++/CLI может работать — ДАЖЕ обратно внутрь неуправляемого кода, если целевой объект ссылки не изменяется в неуправляемом коде — поскольку неуправляемый код не может получить доступ к управляемому коду. куча. Согласен также, что дальше - это зависит от созданного экземпляра этого класса без сохранения состояния. Решение здесь состоит в том, чтобы хранить любые нативные ссылки только в нативном коде — не привлекайте ссылки для отслеживания. - person codemouse; 09.08.2011
comment
не может изменить базовый код. если скомпилированный код создает там экземпляр и передает его по ссылке, что ж... я подчиняюсь торгам этого кода. :) - person codemouse; 09.08.2011
comment
@Codemouse: вы можете просто скопировать цель ссылки. Или сохранить указатель на него. Или с ним в качестве собственной ссылки любым другим способом, которым можно оперировать собственными ссылками. Вы просто не можете выполнять такую ​​работу изнутри C#. - person Billy ONeal; 09.08.2011