Как настроить функцию C ++, чтобы ее можно было использовать с помощью p / invoke?

Надеюсь, это безумно простой вопрос, но он показывает, что я недостаточно разбираюсь в C ++. Я программист на C #, и в прошлом я много работал с P / Invoke с другими библиотеками C ++ / C. Однако на этот раз я решил сам написать dll-оболочку C ++ (неуправляемую), а затем вызываю свою dll-оболочку из C #.

Проблема, с которой я сразу столкнулся, заключается в том, что я не могу определить функцию C ++, которую можно найти с помощью p / invoke. Я не знаю, каков синтаксис для этого, но вот что я пока пытаюсь:

extern bool __cdecl TestFunc()
{
  return true;
}

Изначально у меня просто было это, но тоже не сработало:

bool TestFunc()
{
  return true;
}

А на стороне C # у меня есть:

    public const string InterfaceLibrary = @"Plugins\TestDLL.dll";

    [DllImport( InterfaceLibrary, CallingConvention = CallingConvention.Cdecl,
        EntryPoint = "TestFunc" ), SuppressUnmanagedCodeSecurity]
    internal static extern bool TestFunc();

Все компилируется, но когда я выполняю этот вызов C # p / invoke, я получаю System.EntryPointNotFoundException: невозможно найти точку входа с именем TestFunc в DLL Plugins \ TestDLL.dll.

Конечно, это должно быть что-то невероятно простое со стороны C ++, синтаксис которого я просто не знаю.


person x4000    schedule 07.10.2009    source источник


Ответы (6)


Вы захотите использовать extern "C", а также __declspec(export), например:

extern "C" _declspec(dllexport)  bool TestFunc()
{
    return true;
}

Для получения полной информации см. MSDN о типах маршаллинга.

person Reed Copsey    schedule 07.10.2009
comment
Отлично, вот и все! Раньше я также пробовал использовать только extern C, но это не сработало. Это не удается, пока не будет добавлен _declspec (dllexport). - person x4000; 08.10.2009

Расширение правильного ответа Рида.

Другая проблема, с которой вы можете столкнуться при раскрытии функции C ++ через PInvoke, - это использование недопустимых типов. PInvoke действительно может поддерживать только маршалинг примитивных типов и простых старых типов структур / классов данных.

Например, предположим, что TestFunc имеет следующую подпись

void TestFunc(std::string input);

Даже добавления extern «C» и __declspec(dllexport) было бы недостаточно, чтобы раскрыть функцию C ++. Вместо этого вам нужно будет создать вспомогательную функцию, которая предоставляет только типы, совместимые с PInvoke, а затем вызывается в основной функции. Например

void TestFunc(const std::string& input) { ... }

extern "C" _declspec(dllexport)  void TestFuncWrapper(char* pInput) {
  std::string input(pInput);
  TestFunc(input);
}
person JaredPar    schedule 07.10.2009
comment
Да, именно по этой причине я пишу dll-оболочку C ++, а не пытаюсь напрямую вызывать все нужные мне функции. Моя цель - создать более простой интерфейс для P / Invoke, сохранив при этом всю необходимую сложность с обратными вызовами, сложными типами и т. Д. На стороне C ++. Хорошие моменты! - person x4000; 08.10.2009
comment
@ x4000, я использовал точно такой же подход раньше. За исключением новой библиотеки DLL, я просто добавил функции оболочки к той же самой DLL. Больше DLL было бы чище. - person JaredPar; 08.10.2009
comment
Первоначальная DLL, которую я упаковываю, принадлежит третьей стороне, поэтому для меня это не вариант. Но вы правы, я думаю, что во многих отношениях проще отделить обертки от содержимого, которое оборачивается, когда это возможно. - person x4000; 08.10.2009

Вы должны открыть эту функцию с помощью extern "C", иначе имя будет искажено.

person Yakeen    schedule 07.10.2009

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

Вы можете указать компилятору C ++ работать как компилятор C, используя extern "C":

extern "C" __declspec(dllexport) bool TestFunc { return true; }

Чтобы вызывать функции из C # с помощью P / Invoke, ваши имена не должны изменяться. Следовательно, вы можете экспортировать функции C в C #. Если вы хотите, чтобы функциональность была реализована на C ++, вы можете написать функцию C, которая просто вызывает функцию C ++, реализующую эту функциональность.

person pyon    schedule 07.10.2009

Сделайте что-нибудь вроде этого:

#define EXPORT extern "C" __declspec(dllexport)

А затем объявите любую функцию с ключевым словом EXPORT, например функцию c ++

BOOL getString(TCHAR* string, DWORD size);

станет

EXPORT BOOL getString(TCHAR* string, DWORD size);

затем самое интересное: перейдите в консоль VS и введите:

dumpbin /EXPORTS <PATH_TO_GENERATED_DLL>

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

person Sorcerer86pt    schedule 26.01.2011

Соберите весь проект с помощью платформы Win32 и соответствующего варианта сборки (например, x86 или x64).

person Navin Pandit    schedule 24.02.2016