Как передать адрес объектов, созданных в C # List, в C ++ dll? - PullRequest
3 голосов
/ 14 июня 2011

Я искал во всем Google, чтобы найти ответы на мои вопросы, но не совсем понимаю, что я нашел.У меня есть некоторые объекты, которые создаются и хранятся в C # List после использования System.IO для чтения некоторых текстовых файлов.После этого я хочу отправить ссылки (используя указатели const) на каждый из этих объектов во внутренние классы в C ++ dll, чтобы он мог использовать их для вычисления некоторых алгоритмов.

Вот несколько простых примеров (нефактический код) того, что я делаю:

Класс C #:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class SimpleClass
{
    [MarshalAs(UnmanagedType.LPStr)]
    public string           Name;
    public float            Length;
}

с соответствующей структурой C:

struct SimpleClass
{
    const char* Name;
    float Length;
};

, хранящейся в

List<SimpleClass> ItemList;

после разбора некоторых текстовых файлов.

Затем вызов следующей функции dll:

C #:

[DllImport("SimpleDLL")]
public static extern void AddSimpleReference(SimpleClass inSimple);

C:

void AddSimpleReference(const SimpleClass* inSimple)
{
   g_Vector.push_back(inSimple); // g_Vector is a std::vector<const SimpleClass* > type
}

Я попробовал следующее:

for(int i=0; i<ItemList.Count;++i)
{
    SimpleClass theSimpleItem = ItemList[i];
    AddSimpleReference(theSimpleItem);
}

Первоначально я думал, что будет легко получить фактическую ссылку / адрес, просто используя оператор присваивания, так как классы в C # передаются по ссылке, но получаетсячто класс C ++ всегда помещает одно и то же значение адреса (значение адреса временной ссылки) в контейнер вместо фактического адреса.Как получить фактические адреса объектов и отправить их в C ++ DLL, чтобы у них был доступ только для чтения к объектам C #?

ОБНОВЛЕНИЕ : Извините тех, кто опубликовал ответы с небезопаснымкоды.Я забыл упомянуть, что код C # фактически используется в качестве скрипта в игровом движке Unity, который не позволяет использовать небезопасные коды.

Ответы [ 3 ]

6 голосов
/ 14 июня 2011

Сначала вам нужно изменить подпись взаимодействия, чтобы взять указатель (и, следовательно, сделать его небезопасным).

[DllImport("SimpleDLL")]
public unsafe static extern void AddSimpleReference(SimpleClass* inSimple);

Тогда, поскольку ГХ может свободно перемещать объекты в памяти по своему усмотрению, вам потребуется закрепить объект в памяти на все время , когда вам потребуется его адрес на неуправляемой стороне. Для этого вам понадобится оператор fixed:

SimpleClass theSimpleItem = ItemList[i];
unsafe
{
    fixed(SimpleClass* ptr = &theSimpleItem)
    {
        AddSimpleReference(ptr);
    }
}

Это сработало бы, если бы AddSimpleReference использовал указатель, а затем отбросил его. Но вы храните указатель в std::vector на потом. Это не сработает, потому что указатель, вероятно, станет недействительным из-за того, что GC перемещает исходный элемент куда-то еще, как только выполнение покидает блок fixed.

Чтобы решить эту проблему, вам нужно закрепить предметы, пока вы не закончите с ними. Для этого вам может потребоваться прибегнуть к типу GCHandle.

// Change the interop signature, use IntPtr instead (no "unsafe" modifier)
[DllImport("SimpleDLL")]
public static extern void AddSimpleReference(IntPtr inSimple);

// ----
for(int i=0; i<ItemList.Count;++i)
{
    SimpleClass theSimpleItem = ItemList[i];
    // take a pinned handle before passing the item down.
    GCHandle handle = GCHandle.Alloc(theSimpleItem, GCHandleType.Pinned);
    AddSimpleReference(GCHandle.ToIntPtr(handle));
    // probably a good idea save this handle somewhere for later release
}

// ----
// when you're done, don't forget to ensure the handle is freed
// probably in a Dispose method, or a finally block somewhere appropriate
GCHandle.Free(handle);

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

1 голос
/ 14 июня 2011

Несмотря на то, что я думаю, что это не очень хорошая идея, взгляните на небезопасный код и закрепление памяти. Здесь - хорошее начало для MSDN.

исправлено и небезопасно ключевые слова, вероятно, то, что вы должны искать.

0 голосов
/ 14 июня 2011

Вы не можете. C # GC будет перемещать объекты для удовольствия. Ваши адреса выйдут за рамки.

...