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

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

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

extern bool __cdecl TestFunc()
{
  return true;
}

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

bool TestFunc()
{
  return true;
}

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

    public const string InterfaceLibrary = @"PluginsTestDLL.dll";

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

все компилируется, но когда я выполняю этот вызов c# p / invoke, я получаю систему.EntryPointNotFoundException: не удалось найти точку входа с именем 'TestFunc' в DLL ' ПлагиныTestDLL.в DLL'.

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

6 ответов


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

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

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


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

еще одна проблема, с которой вы можете столкнуться при отображении функции 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);
}

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


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

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

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

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


сделать что-то вроде этого:

#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>

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


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