Как предотвратить AccessViolationException при возврате строки из C ++ в C # в 64-битной Windows? - PullRequest
8 голосов
/ 02 мая 2011

Я использую стороннюю проприетарную DLL, исходный код которой мне не доступен.Однако код оболочки, который был сгенерирован автоматически с помощью SWIG 1.3.39, доступен для меня.Код оболочки состоит из файла C ++, который компилируется (с использованием некоторых заголовков, описывающих DLL) в DLL, и проекта C #, который делает вызовы PInvoke к DLL-оболочке C ++.

Согласно моей интерпретации документации поставщикаЯ скомпилировал все в решении как x86 или x64, в зависимости от целевой платформы.Поставщик предоставляет как 32-битные, так и 64-битные версии проприетарной DLL, и я гарантировал, что использую правильную версию для данной сборки.Моя машина 32-битная.Тестирование x86-версии моего приложения на моей машине, в релизной или отладочной сборках, похоже, работает нормальноОднако в 64-разрядной версии приложение работает в режиме отладки, но в System.AccessViolationException в режиме выпуска происходит сбой.

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

Кажется, что AccessViolationException возникает при первом возврате (или попытке возврата) строки любой реальной длины из оболочки C ++.Вот код C #, вызывающий проблемы:

// In one file of the C# wrapper:
public string GetKey()
{
    // swigCPtr is a HandleRef to an object already created
    string ret = csWrapperPINVOKE.mdMUHybrid_GetKey(swigCPtr);
    return ret;
}

// In the csWrapperPINVOKE class in another file in the C# wrapper:
[DllImport("csWrapper.dll", EntryPoint="CSharp_mdMUHybrid_GetKey")]
public static extern StringBuilder mdMUHybrid_GetKey(HandleRef jarg1);

И код C ++ из обертки C ++:

SWIGEXPORT char * SWIGSTDCALL CSharp_mdMUHybrid_GetKey(void * jarg1) {
  char * jresult ;
  mdMUHybrid *arg1 = (mdMUHybrid *) 0 ;
  char *result = 0 ;

  arg1 = (mdMUHybrid *)jarg1; 
  result = (char *)(arg1)->GetKey();
  jresult = SWIG_csharp_string_callback((const char *)result); 
  return jresult;
}

SWIGEXPORT уже был определен как __declspec(dllexport).При отладке я обнаружил, что SWIG_csharp_string_callback, определенный как:

/* Callback for returning strings to C# without leaking memory */
typedef char * (SWIGSTDCALL* SWIG_CSharpStringHelperCallback)(const char *);
static SWIG_CSharpStringHelperCallback SWIG_csharp_string_callback = NULL;

, был задан делегату (в оболочке C #):

static string CreateString(string cString) {
  return cString;
}

Я пытался возиться с этимкод для использования таких конструкций, как Marshal.PtrToStringAut безрезультатно.Как устранить неполадки и / или устранить эту проблему?

1 Ответ

3 голосов
/ 03 мая 2011

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

Обычный подход - выделить память с помощью LocalAlloc, который затем можно освободить из управляемого кода, используя Marshal.FreeHGlobal.Таким образом, вам больше не нужны (глупые и явно не функциональные) SWIG_csharp_string_callback и CreateString.

C ++ код :

SWIGEXPORT HLOCAL SWIGSTDCALL CSharp_mdMUHybrid_GetKey(mdMUHybrid* jarg1)
{
    char const* const str = jarg1->GetKey();
    std::size_t const len = std::strlen(str);
    HLOCAL const result = ::LocalAlloc(LPTR, len + 1u);
    if (result)
        std::strncpy(static_cast<char*>(result), str, len);
    return result;
}

Код C # :

// In one file of the C# wrapper:
public string GetKey()
{
    return csWrapperPINVOKE.mdMUHybrid_GetKey(swigCPtr);
}

// ...

public static class csWrapperPINVOKE
{
    // ...

    [DllImport("csWrapper.dll")]
    private static extern IntPtr CSharp_mdMUHybrid_GetKey(HandleRef jarg1);

    public static string mdMUHybrid_GetKey(HandleRef jarg1)
    {
        var ptr = CSharp_mdMUHybrid_GetKey(jarg1);
        try
        {
            return Marshal.PtrToStringAnsi(ptr);
        }
        finally
        {
            Marshal.FreeHGlobal(ptr);
        }
    }
}

Кроме того, этот крошечный фрагмент кода C ++, который вы показали, является отвратительной реликвией C-with-classes;если это представитель остальной части кодовой базы, то просто, вау ...: - /

...