Почему мне не нужно предоставлять строковый буфер при вызове C # DLL?

Иногда невозможно получить доступ к источнику по умолчанию для gem, https://rubygems.org/.

Он должен быть удален командой:

gem sources -r https://rubygems.org/

И тогда новый источник должен добавлен:

gem sources -a https://ruby.taobao.org/

Обновить кеш:

gem sources -u

Вы можете проверить источники с помощью:

gem sources

Наконец, вы можете установить cocoapods:

sudo gem install cocoapods
0
задан Hugh Myron 13 July 2018 в 06:21
поделиться

3 ответа

Как возвращается значение независимо от используемого языка. Он определяется только вызывающей сигнатурой функции. Поэтому я мог написать следующий источник DLL в C ++:

#include <windows.h>

BOOL WINAPI DllMain(
  _In_ HINSTANCE hinstDLL,
  _In_ DWORD     fdwReason,
  _In_ LPVOID    lpvReserved
)
{
    return TRUE;
}

__declspec(dllexport) char *function1()
{
    char *foo = new char[100];
    // do stuff to fill in foo
    return foo;
}

__declspec(dllexport) void function2(char *out, int maxOutSize)
{
    // do stuff to fill in out
}

, который является полностью законным, и определяет точки входа C ++ с сигнатурами, которые точно соответствуют обеим функциям, перечисленным в вопросе OP.

Обратите внимание, что это использует C ++ new[] для выделения памяти для function1(), поэтому необходимо будет ее освободить в какой-то момент, используя delete[].

В том же ключе, если память выделенные CLR в случае C #, вам нужно либо передать его обратно в C # DLL, чтобы он был правильно выпущен, либо найти безопасный способ уничтожить его на C ++, предполагая, что такая операция даже возможна.

Я буду на 100% честным здесь и скажу, что у меня нет никакой подсказки, если C ++ runtime и C # CLR будут хорошо играть вместе с распределением памяти. Таким образом, может быть, что единственный вариант - передать его обратно на C # для освобождения.

- Изменить -

Я собираюсь связать этот вопрос также для дальнейшей ссылки , поскольку он имеет дело с другим взглядом на ту же проблему, и, в частности, принятый ответ содержит в себе чрезвычайно полезную информацию.

Передача строк из C # в C ++ DLL и обратно - минимальный пример

1
ответ дан dgnuff 17 August 2018 в 13:33
поделиться
  • 1
    Я просто прочитал, что один шаблон использует, так это то, что C ++ (или любой вызывающий) вызовет некоторую функцию в C # (или любую DLL), чтобы освободить память. Спасибо за ответ, я собираюсь прочитать его снова утром, чтобы переварить еще несколько – Hugh Myron 13 July 2018 в 07:05
  • 2
    @HughMyron Да. Основываясь на большом опыте, это будет самая мудрая вещь: используйте язык, на который выделяется память, чтобы освободить его. За исключением вызова C free() из C ++, я был бы очень подозрительным к любой попытке, которая использует среду выполнения одного языка для выделения памяти, а также для запуска другого языка для ее освобождения. – dgnuff 13 July 2018 в 07:09

Я предполагаю, что причина такова:

Когда вы вызываете c # api из c ++, вы вызываете код, управляемый виртуальной машиной CLR, поэтому распределение памяти автоматически управляется самой виртуальной машиной, что позволяет вам не беспокоиться о распределении буфера вручную.

1
ответ дан Nicklaus Brain 17 August 2018 в 13:33
поделиться
  • 1
    Спасибо за ответ. Мне нужно больше посмотреть, как работают адресные пространства – Hugh Myron 13 July 2018 в 07:06

Я не понимаю, как C # callle может выделить память, где C ++ вызываемый не смог?

Вы ищете неправильную проблему , Проблема не в том, «как C # может выделить память». Любой может выделить память ... И слишком много способов :-) Проблема всегда «как вы освободите память?», Потому что освобождение памяти так же важно, как выделение памяти (не освобождение памяти приведет к памяти утечки, которые сделают ваше занятие памятью программы пройденным шаром, а освобождение его с помощью неправильного распределителя не освободит его как минимум (см. предыдущую точку) и в худшем случае приведет к сбою программы ).

Теперь ОС (например, Windows) дает вам некоторые распределители (например, CoTaskMemAlloc), которые может использовать любой пользователь, но проблема в том, что обычно в C / C ++ вы используете, Используйте malloc / new. И проблема здесь: эти распределители могут быть «разными» (отдельными) между двумя dll: две библиотеки dll, скомпилированные в C / C ++ (но, например, с разными версиями Visual C ++ или в режиме отладки vs release или один с использованием среды выполнения как dll, а другая, связанная непосредственно с исполняемой средой или скомпилированная с разными компиляторами C), будет иметь различные malloc (и различные new в C ++). Имея различные malloc / new, они будут иметь free / delete, так что free одной dll не может освободить память другой dll.

Теперь ... Используя эту подпись:

void MyCPPDllFunction(char *out, int maxOutSize);

вызывающий должен выделить память ... Таким образом, вызывающий пользователь знает, как освободить память ... Нет утечки памяти: -)

С помощью этой сигнатуры:

char *MyCPPDllFunction();

вызываемому приходится выделять память ... А теперь как может вызывающий ее освободить? Вызов может экспортировать другой метод:

void FreeMemoryAllocatedByMe(void *ptr)
{
    free(ptr);
}

, после чего вызывающий абонент вызовет его, и все будет решено, потому что free будет вызываемым free.

Теперь ... В общем случае, когда объекты (и строки) C # -маршалов, он использует CoTaskMemAlloc для выделения памяти, и ожидает, что если он получит память, он должен освободить ее с помощью CoTaskMemFree , Итак, в примере C ++ -> C # C ++ должен

CoTaskMemFree(output);
2
ответ дан xanatos 17 August 2018 в 13:33
поделиться
Другие вопросы по тегам:

Похожие вопросы: