Squeak.ru - шаблоны программирования

Строки в c ++?

Моя проблема в том, что у меня есть некоторые функции в DLL, например, некоторые из этих функций:

#include <string>
using namespace std;
extern "C" __declspec(dllexport) string __cdecl encryption(string s)
{
 return s;
}

Всякий раз, когда я пытаюсь вызвать эту функцию из C #, я использую следующий код:

[DllImport("Packer.dll", EntryPoint = "encryption")]
static extern string encryption(string s);

Я получаю сообщение об ошибке: вызов функции PInvoke Packer привел к разбалансировке стека. Вероятно, это связано с тем, что подпись управляемого PInvoke не соответствует подписи неуправляемого целевого объекта. Убедитесь, что соглашение о вызовах и параметры подписи PInvoke соответствуют целевой неуправляемой подписи.

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

05.10.2011

  • Вот почему библиотеки DLL должны иметь API C, а не C ++. Каждый может согласиться с тем, что такое двоичный интерфейс для const char*. 05.10.2011
  • Вам также необходимо установить объявление вызова в коде C # или изменить соглашение о вызовах C ++. 05.10.2011
  • @ Адам, еле-еле, и добавь Unicode, и ад вырвется наружу :) 05.10.2011

Ответы:


1

std::string нельзя использовать с PInvoke, потому что C ++ не имеет ABI для своих объектов, который требуется для правильной очистки стека, копирования объектов и т. Д. Это одна из самых больших проблем C ++.

Вы должны использовать char* указатели и простые API-интерфейсы C. Проще говоря, PInvoke не работает с C ++.

05.10.2011
  • Это сложнее, чем это - если тип возвращаемого значения его подписи P / Invoke string, тогда среда CLR попытается освободить память, возвращаемую функцией, как если бы это была BSTR. Простое изменение собственного возвращаемого типа не является решением. 05.10.2011
  • Действительно, правильный способ C сделать это - передать предварительно выделенный буфер вместе с его размером в функцию и позволить ему записать в буфер для последующего чтения. Не позволяйте C выделять память за вас, это плохие новости. 05.10.2011
  • @ildjarn, это верно, и точка зрения Джерри Коффина о stdcall показывает другую проблему, но все же самая большая проблема здесь - это попытка вернуть объект C ++. 05.10.2011
  • Недостаточно просто вернуть любой вид предварительно выделенной памяти из функции P / Invoke-d. Это должно быть сделано очень специфическим способом (CoTaskMemAlloc), поэтому это совместимо с тем, как P / Invoke будет пытаться освободить эту память. 06.10.2011
  • @Branko На самом деле, если вы маршалируете char* как IntPtr, а затем используете Marshal.PtrToStringAnsi() для создания System.String, вы добьетесь «простого возврата указателя на буфер» без необходимости вызывать CoTaskMemAlloc :) 06.10.2011
  • Или, что еще проще, создайте StringBuffer и позвольте фреймворку творить чудеса! 06.10.2011
  • P / Invoke работает с C ++ DLL, это служба вызова платформы, она не ограничивается функцией стиля C. 20.04.2013
  • @dave PInvoke просто вызывает экспортированные функции, он не предоставляет ничего, что могло бы помочь вам с C ++. Например, попробуйте перехватить с ним исключение C ++. 20.04.2013
  • Если вы экспортируете класс C ++ в C ++ DLL, нечистые виртуальные методы класса будут экспортированы, все экспортированные методы могут быть вызваны, даже неэкспортированные виртуальные методы класса C ++ могут быть вызваны из C #. Вы можете поймать исключение C ++ при вызове PInvoking метода C ++, единственный недостаток улавливания SEH-выражения из C # заключается в том, что среда выполнения .NET сообщает об этом только как об общем SEHException без каких-либо подробностей об исключении C ++, созданном с использованием ключевых слов throw. Если вы используете RaiseException, эта информация будет отправлена ​​обратно в .NET. 20.04.2013

  • 2

    Как я уверен, вы уже знаете, что C ++ std::string на самом деле является шаблоном. Только при создании экземпляра шаблона (в данном случае как std::basic_string<char>) точное расположение объектов этого типа и сигнатуры методов определяются компилятором C ++.

    К сожалению, только рассматриваемый компилятор C ++ имеет доступ ко всей необходимой информации (например, исходному коду шаблона) для принятия подобных решений. Вот почему функции, не относящиеся к C, такие как шаблоны, обычно не могут быть «перенесены» даже между разными компиляторами C ++, не говоря уже о C ++ и C #.

    Кроме того, имена C ++ обычно "искажаются" специфическим для компилятора C ++ способом.

    Вам нужно будет изменить подпись вашего собственного метода, чтобы он стал "чистой" функцией C:

    • Убедитесь, что имя C ++ не искажается, объявив функцию как extern "C" (вы уже это делаете).
    • Используйте параметр char* вместо std::string.
    • Верните результат char* вместо std::string, но будьте очень осторожными как ты это делаешь.
    • Убедитесь, что ваш DllImportAttribute.CallingConvention совпадает с __cdecl, __stdcall или __fastcall в вашем C.
    05.10.2011
  • Я уже пробовал это, но получаю эту ошибку: попытка чтения или записи в защищенную память. Это часто указывает на то, что другая память повреждена. 06.10.2011
  • конечно, объявление C #: статическое внешнее шифрование строки (string s); C ++: extern C __declspec (dllexport) char * encryption (char * s) {return test; } 06.10.2011
  • @ user959615 return "test" это проблема. Пожалуйста, перейдите по ссылке в моем ответе, чтобы узнать почему. 06.10.2011
  • @ user959615 Кроме того, DllImportAttribute.CallingConvention по умолчанию Winapi, что, в свою очередь, соответствует соглашению StdCall, так что здесь тоже есть несоответствие. 06.10.2011
  • Извините, похоже, я не понимаю, но спасибо всем за вашу помощь Я собираюсь узнать больше о P / Invoke и c ++, снова спасибо всем за вашу помощь 06.10.2011

  • 3

    Проблема здесь в том, что вы используете класс STL string, который C # не умеет маршалировать. У вас есть два варианта:

    1. провести рефакторинг вашего кода C ++ для работы с char * буферами. Или напишите обертку, или перегрузку, или что-нибудь, что использует char * вместо string.
    2. Напишите оболочку C ++ / CLI для ваших функций C ++, которая использует System::String и вызывает внутренние версии строк STL.
    05.10.2011

    4

    Если память не изменяет, если вы не укажете иное, P / Invoke предполагает, что соглашение о вызовах равно __stdcall. В таком случае изменение __cdecl на __stdcall должно решить первую проблему. Как указывает @Adam Rosenfield, вам, вероятно, также нужно передать и вернуть char const *, а не string. C # и C ++ почти наверняка имеют несколько разные представления о том, что представляет собой строка.

    05.10.2011
  • Возможно, вместо этого было бы лучше изменить соглашение о вызовах объявления C #. 05.10.2011
  • Я получаю эту ошибку: не удается найти точку входа с именем «шифрование» в DLL «Packer.dll». Изменить: я собираюсь попробовать, как сказал Джефф Меркадо. Изменить 2: попытался сделать это из объявления C #, но я получил это. Вызов функции PInvoke 'Packer' разбалансировал стек. Вероятно, это связано с тем, что подпись управляемого PInvoke не соответствует подписи неуправляемого целевого объекта. Убедитесь, что соглашение о вызовах и параметры подписи PInvoke соответствуют целевой неуправляемой подписи. 05.10.2011
  • @ user959615: да, по умолчанию stdcall добавит суффикс, указывающий размер аргументов, чтобы он превратился в encryption@4. Вы можете устранить это с помощью компоновщика файла определения модуля. 05.10.2011
  • @JeffMercado: Да, любой из них, конечно, должен работать - они просто должны совпадать. 05.10.2011
  • Новые материалы

    Угловая структура архитектуры
    Обратите внимание, что эта статья устарела, я решил создать новую с лучшей структурой и с учетом автономных компонентов: https://medium.com/@marekpanti/angular-standalone-architecture-b645edd0d54a..

    «Данные, которые большинство людей используют для обучения своих моделей искусственного интеллекта, поставляются со встроенным…
    Первоначально опубликовано HalkTalks: https://hacktown.com.br/blog/blog/os-dados-que-a-maioria-das-pessoas-usa-para-treinar-seus-modelos-de-inteligencia-artificial- ja-vem-com-um-vies-embutido/..

    Сильный ИИ против слабого ИИ: различия парадигм искусственного интеллекта
    В последние годы изучению и развитию искусственного интеллекта (ИИ) уделяется большое внимание и прогресс. Сильный ИИ и Слабый ИИ — две основные парадигмы в области искусственного интеллекта...

    Правильный способ добавить Firebase в ваш проект React с помощью React Hooks
    React + Firebase - это мощная комбинация для быстрого и безопасного создания приложений, от проверки концепции до массового производства. Раньше (знаете, несколько месяцев назад) добавление..

    Создайте API с помощью Python FastAPI
    Создание API с помощью Python становится очень простым при использовании пакета FastAPI. После установки и импорта вы можете создать приложение FastAPI и указать несколько конечных точек. Каждой..

    Веселье с прокси-сервером JavaScript
    Прокси-серверы JavaScript — это чистый сахар, если вы хотите создать некоторую общую логику в своих приложениях, чтобы облегчить себе жизнь. Вот один пример: Связь клиент-сервер Мы..

    Получить бесплатный хостинг для разработчиков | Разместите свой сайт за несколько шагов 🔥
    Статические веб-сайты — это веб-страницы с фиксированным содержанием и его постоянным содержанием. Но теперь статические сайты также обрабатывают динамические данные с помощью API и запросов...