C # & C ++, ошибка времени выполнения при вызове C ++ dll из C # - PullRequest
3 голосов
/ 17 июня 2010

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

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

На стороне C ++ есть только один метод:

#ifdef DLLWRAPPERWIN32_EXPORTS
#define DLLWRAPPERWIN32_API __declspec(dllexport)
#else
#define DLLWRAPPERWIN32_API __declspec(dllimport)
#endif

#include "NB_DPSM.h"

extern "C" {
 DLLWRAPPERWIN32_API int WriteGenbenchDataWrapper(string fileNameToAnalyze, 
  string parameterFileName,  
  string baseNameToSaveData,
  string logFileName,
  string& message) ;
}

на стороне C #, есть определение,

[DllImport("..\\..\\thirdParty\\cogs\\DLLWrapperWin32.dll")]
public static extern int WriteGenbenchDataWrapper(string fileNameToAnalyze, 
              string parameterFileName,  
              string baseNameToSaveData,
              string logFileName, 
              ref string message);

и вызов:

 string msg = "";
    int returnVal = WriteGenbenchDataWrapper(rawDataFileName, 
                   parameterFileName, outputBaseName, logFileName, ref msg);

Я думаю, что-то не так с последним параметром функции.string& в C ++ должно быть ref string в C #?

EDIT:

Действительно ли нам нужен extern "C"?

EDIT 2:

после того, как я удалил extern "C из dll, Я получил EntryPointNotFoundException.Когда я смотрю на DLL с помощью DLL Export Viewer, я обнаружил, что имя функции «int __cdecl WriteGenbenchDataWrapper (class std :: ...». Нужно ли включать «__cdecl»?

Ответы [ 2 ]

3 голосов
/ 17 июня 2010

Существует несколько правил маршелинга с PInvoke. Для справки Маршелинг между управляемым и неуправляемым

Сначала сосредоточимся на стороне C #. Если вы знали разумный размер сообщения заранее, вы могли бы использовать тип StringBuilder и определить этот размер, например.

[DllImport("DLLWrapperWin32.dll")]
public static extern int WriteGenbenchDataWrapper(string fileNameToAnalyze, 
                                                    string parameterFileName,   
                                                    string baseNameToSaveData,
                                                    string logFileName, 
                                                    StringBuilder message
                                                    int messageLength );

Из сообщения об имени (и других сообщений) видно, что вы не знаете размер заранее, и вы не будете передавать частичное сообщение функции, поэтому, возможно,

[DllImport("DLLWrapperWin32.dll")]
public static extern int WriteGenbenchDataWrapper(in string fileNameToAnalyze, 
                                                    in string parameterFileName,   
                                                    in string baseNameToSaveData,
                                                    in string logFileName, 
                                                    out string message );

Теперь на стороне C / C ++ - чтобы соответствовать второму определению

extern "C" // if this is a C++ file to turn off name mangling for this function only
int WriteGenbenchDataWrapper( char * fileNameToAnalyze, 
                              char * parameterFileName,   
                              char * baseNameToSaveData,
                              char * logFileName, 
                              char ** message ) {
  string internalMessage;
  SomeFunc( internalMessage ); // these functions won't have extern "C" applied
  * message = (char *)::CoTaskMemAlloc(internalMessage.length()+1); 
  strcpy(* message, internalMessage.c_str());
}

Также важно учитывать Unicode / ANSI строки, см. [MarshalAsAttribute (UnmanagedType.LPWSTR)]]

В режиме релиза вы захотите удалить настройки пути разработки ".. \ .. \ thirdParty \ cogs"

1 голос
/ 18 июня 2010

В вашем коде C ++:

Мне всегда нужен был внешний "C".C ++ изменяет имена функций, если вы этого не делаете (искажение необходимо для поддержки перегрузки функций).Экстерьер "C" говорит ему не делать этого.

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

Что касается передачи строкового объекта, я не уверен в этом, я придерживаюсь только использования примитивовдля передачи параметров, поэтому я бы использовал const char * и настроил соответственно в своем коде C ++.

Также я стараюсь избегать передачи по ссылке.Скорее, если мне нужно будет вернуть несколько значений, я настрою серию получателей для обработки этого (const char * возвращает как IntPtr).

В вашем коде C #:

Я использую String для const char *, int для int и так далее.Я полагаю, что у Microsoft есть диаграмма где-то, чтобы сказать вам, что нужно заменить для чего.

При работе с возвращаемой строкой вам необходимо преобразовать ее в ANSI.Это можно сделать с помощью вызова функции Marshal.PtrToStringAnsi ().

Например:

В моем коде C ++:

extern "C" __declspec(dllexport) const char* __stdcall GetCompany(const char *In) {
  return MyGetCompany(In); // Calls the real implementation
}

Inмой код C #:

[DllImport("TheDLL.dll", EntryPoint = "GetCompany")]
private static extern IntPtr privGetCompany(String In);

// Call this one, not the one above:
public String GetProvince(String In)
{
  return Marshal.PtrToStringAnsi(privGetCompany(In));
}

Последнее замечание: если вы работаете на 64-битной машине, конфигурация «Любой ЦП» сделает 64-битный исполняемый файл C #, для которого потребуется 64-бит DLL.Если у вас есть только 32-битная DLL, вам нужно добавить конфигурацию (x86).

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

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