Как создать самосинхронизирующийся класс C ++ / CLI? - PullRequest
1 голос
/ 19 декабря 2011

Я создаю оболочку .NET вокруг библиотеки C ++ (у меня нет доступа к источнику), используя C ++ / CLI.Библиотека C ++ должна вызывать делегат .NET, написанный на C ++ / CLI.Я назначаю функцию обратного вызова для классов библиотеки C ++, используя Marshal :: GetFunctionPointerForDelegate.Однако когда эта функция вызывается обратно с неуправляемой стороны, я должен убедиться, что функция делегата .NET все еще находится в той же области памяти.

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

Как бы я это спроектировал?Согласно этой ссылке, http://msdn.microsoft.com/en-us/library/18xa23yk%28v=VS.100%29.aspx, вы не можете иметь внутренние ~ = закрепленные указатели в качестве членов объекта.Это означает, что можно создать закрепленную ссылку на указатель в виде статической или глобальной переменной.

Так что я хочу сделать что-то подобное любому из этих двух, но не могу заставить его скомпилировать / работать.

public ref class UserClass{
void createDotNetCPPWrapperClass()
{
    m_class = gcnew DotNetCPPWrapperClass;
}

DotNetCPPWrapperClass^ m_class

};


public ref class DotNetCPPWrapperClass{

static pin_ptr<DotNetCPPWrapperClass^> pinnedSelf;

DotNetCPPWrapperClass()
{
    pinnedSelf = this;
}
};

ИЛИ

public ref class UserClass{

void createDotNetCPPWrapperClass()
{
    m_class = gcnew DotNetCPPWrapperClass;
    m_class->setupImportantStuff();
}

DotNetCPPWrapperClass^ m_class

};


public ref class DotNetCPPWrapperClass{

static pin_ptr<DotNetCPPWrapperClass^> pinnedSelf;

DotNetCPPWrapperClass(){}

void setupImportantStuff()
{
    pinnedSelf = this;
}

};

Ответы [ 2 ]

0 голосов
/ 19 декабря 2011

GCHandle решит вашу проблему.

public ref class Wrapper
{

GChandle thisHandle;
public:
  Wrapper() 
  {
    thisHandle = GCHandle.Alloc(this, GCHandleType::Normal);
  }

  ~Wrapper() // Dispose
  {
    if(thisHandle.IsAllocated)
      thisHandle.Free;
  }

  !Wrapper() // Finalize
  {
    //Native resource releasing
  }
}

Теперь есть несколько моментов, с которыми нужно быть осторожным.

  1. Вы не хотите GCHandle.Alloc() что-то закрепить в течение длительного времени. Как только вы закрепите объект, GC не сможет собрать объекты, которые выровнены перед закрепленным объектом в памяти, потому что адрес закрепленного объекта не может быть изменен. Так что сбор мусора становится бессмысленным. Обратите внимание, что я выделяю ручку с GCHandleType::Normal, чтобы сделать ее не только неснимаемой, но и подвижной.
  2. Я не знаю, пытается ли GC собрать объект, у которого есть выделенный дескриптор, и вызвать финализатор этого объекта. ИМО это не имеет смысла, потому что дескриптор не позволяет объекту собирать GC. Поэтому очень важно, чтобы вы вызвали оператор delete в C ++ / Cli и вызвали Dispose() в C # и освободили дескриптор. Если вы забудете их вызвать, весь объект станет утечкой памяти.
0 голосов
/ 19 декабря 2011
public class MyClass {

  private GCHandle gch;

  public MyClass() {
    gch=GCHandle.Alloc(this, GCHandleType.Pinned);
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...