Безопасно ли хранить указатели C ++ в C #? - PullRequest
6 голосов
/ 14 августа 2011

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

void SetName(char* name, int idx)

Но теперь я думаю, так какЯ работаю с указателями, я не мог отправить в C # адрес самого указателя, тогда в коде я мог бы сделать что-то вроде этого:

void SetName(char*name, int ptr)
{
   ((TypeName*)ptr)->name = name;
}

Очевидно, что это быстрая версия того, что я получаю (ивероятно, не скомпилируется).

Будет ли гарантированно сохраняться адрес указателя в C ++ таким, чтобы я мог безопасно хранить его адрес в C #, или это было бы слишком нестабильным или опасным по какой-то причине?

Ответы [ 4 ]

7 голосов
/ 14 августа 2011

В C # вам не нужно использовать указатель здесь, вы можете просто использовать простую строку C #.

[DllImport(...)]
extern void SetName(string name, int id);

Это работает, потому что стандартное поведение строк в p / invoke заключается в использованииMarshalAs(UnmanagedType.LPStr), который преобразуется в символ C-стиля *.Вы можете пометить каждый аргумент в объявлении C # явно, если для него требуется какой-то другой способ маршалинга, например, [MarshalAs(UnmanagedType.LPWStr)], для аргумента, который использует 2-байтовую строку символов.

Единственная причина для использованияуказатели - это если вам нужно сохранить доступ к данным, на которые вы указали после вызова функции.Даже в этом случае вы можете использовать out параметры большую часть времени.

Вы можете p / вызывать практически все, не требуя указателей вообще (и, следовательно, не требуя небезопасного кода, который требует привилегированного выполнения в некоторых средах).

2 голосов
/ 14 августа 2011

Да, нет проблем. Собственное распределение памяти никогда не перемещается, поэтому сохранение указателя в IntPtr на стороне C # - это хорошо. Вам нужна какая-то закрученная функция, которая возвращает этот указатель, тогда

[DllImport("something.dll", CharSet = CharSet.Ansi)]
void SetName(IntPtr vector, string name, int index);

Который намеренно лжет об этой функции C ++:

void SetName(std::vector<std::string>* vect, const char* name, int index) {
    std::string copy = name;
    (*vect)[index] = copy;
}

Обратите внимание на использование new в коде C ++, вам нужно скопировать строку. Переданный аргумент name указывает на буфер, выделенный маршаллером pinvoke, и действителен только в течение тела функции. Ваш оригинальный код не может работать. Если вы намереваетесь возвращать указатели на элементы вектора <>, тогда будьте очень осторожными. Вектор перераспределяет свой внутренний массив при добавлении элементов. Такой возвращенный указатель станет недействительным, и вы будете повреждать кучу, когда будете использовать его позже. Точно то же самое происходит с C # List <>, но без риска зависания указателей.

1 голос
/ 14 августа 2011

C # GC перемещает вещи, но куча C ++ ничего не перемещает - указатель на выделенный объект гарантированно остается действительным, пока вы не delete его.Лучшая архитектура для этой ситуации - просто отправить указатель на C # в виде IntPtr, а затем вернуть его обратно в C ++.

Это, безусловно, невероятно лучшая идея, чем невероятно БАД, УЖАСНОЕ целочисленное приведение, которое выпошел туда.

1 голос
/ 14 августа 2011

Я думаю, что он стабилен, пока вы не командуете кодом C ++ и не знаете, что он делает, и другие разработчики, работающие над тем же кодом, тоже знают об этой опасности.

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

Привет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...