C ++ dll возвращаемая строка повреждена в C # вызывающем, почему? - PullRequest
6 голосов
/ 15 июня 2011

У меня есть приложение C #, которое вызывает C ++ DLL.

В C # у меня есть следующий код:

[DllImport(@"111.dll", CharSet = CharSet.Unicode)]
public extern static String Func1(String arg);
......
String arg = "test text";
String retstring = Func1(arg);

В CPP у меня есть функция, определенная следующим образом:

extern "C"
{
__declspec(dllexport) LPWSTR Func1(LPWSTR arg)
{
          ....
          LPWSTR ret1 = L"1?2?3?4?5";
          LPWSTR ret2 = SomeActualFunction(arg);
          retturn ret1; // return ret2;
     }
}

Если я возвращаю ret1 в Func1 () C ++, все работает нормально.И в окне памяти VS2008 я вижу правильные двоичные файлы Unicode.В C ++ двоичные файлы ret1 равны

"31 00 3f 00 32 00 3f 00 33 00 3f 00 34 00 3f 00 35 00"

, а в C # двоичные файлы retstring равны

"28 67 a3 f7 fe 07 00 00 0a 00 00 00 09 00 00 00 31 00 3f 00 32 00 3f 00 33 00 3f 00 34 00 3f 00 35 00"

.Я думаю, что двоичные файлы C #

"28 67 a3 f7 fe 07 00 00 0a 00 00 00 09 00 00 00"

являются заголовками типа System.String.

Более того, если я добавлю следующую строку непосредственно перед возвратом в коде CPP, я смогу получить правильную повторную строку в коде C #:

ret2 = L"1?2?3?4?5";

Но когда я возвращаю ret2 в DLL C ++, возвращаемая строка ret inC # кажется поврежден.Двоичные файлы в C ++ DLL, на мой взгляд, являются правильными Unicode.Но двоичные файлы в коде C #:

"28 67 a3 f7 fe 07 00 00 0a 00 00 00 09 00 00 00 dd dd dd dd dd dd dd dd dd dd dd dd ....".

Я могу только заметить, что ret2 длиннее, чем ret1 - у ret2 несколько сотен WCHAR.

Есть идеи?Спасибо заранее.

Ответы [ 7 ]

8 голосов
/ 15 июня 2011

Я бы всегда использовал для этого BSTR, поскольку это делает прозрачной ответственность за распределение / освобождение памяти.

C ++

#include <comutil.h>
BSTR GetSomeText()
{
    return ::SysAllocString(L"Greetings from the native world!");
}

C #

[DllImport(@"test.dll", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.BStr)]
private static extern string GetSomeText();

Вы не говорите, как распределяются ваши строки, но как только вы используете динамически распределенную строку, вам нужно решить эту проблему. Отличительной особенностью BSTR является то, что он использует общий распределитель COM, который позволяет маршаллеру C # освобождать строку с тем же распределителем, что и код C ++, который ее выделил.

5 голосов
/ 15 июня 2011
[DllImport(@"111.dll", CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.LPStr)]
public extern static String Func1(String arg);

или

[DllImport(@"111.dll", CharSet = CharSet.Unicode)]
public extern static IntPtr Func1(String arg);
// In your calling code
string result = Marshal.PtrToStringUni(Func1("somestring"));
1 голос
/ 15 июня 2011

Является ли ваш func __cdecl или __stdcall соглашением о вызовах? IIRC, по умолчанию для C # __stdcall, но по умолчанию для C ++ __cdecl. Попробуйте добавить CallingConvention=CallingConvention.Cdecl.


Другая возможность: поскольку вы говорите, что возвращение статической строки работает, является ли указатель, возвращаемый SomeActualFunction, все еще действительным? Если бы он указывал на локальный буфер в этой функции, он больше не действителен после возврата из функции.

0 голосов
/ 25 сентября 2013

Вот ссылка для вашей справки, если вам нужно упорядочить другие типы данных ОБНОВЛЕННАЯ ССЫЛКА ДЛЯ .NET 4.0 https://msdn.microsoft.com/en-us/library/sak564ww(v=vs.100).aspx

0 голосов
/ 25 сентября 2013

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

In C #

using System.Runtime.InteropServices;

[DllImport(
    @"C:\Users\Ron\Documents\Visual Studio 2013\ etc. ...(The whole path)... my.dll,
    CallingConvention = CallingConvention.Cdecl
    )]
[return: MarshalAs(UnmanagedType.BStr)]
public static extern string cpp_brand_files(string home_dir, string xml_lic);

И звонок позже в C #:

      string a_str = cpp_brand_files(home_dir, xml_license);

В C ++ DLL:

  #using <mscorlib.dll>

   using namespace System;

   extern "C" __declspec(dllexport)
   wchar_t* cpp_brand_files(char* home_dir, char* xml_lic);

И далее в C ++:

  wchar_t* cpp_brand_files(char* home_dir, char* xml_lic)
  {

      BSTR samp = ::SysAllocString( L"This is the long, long string." );
      return samp;
  }

И ТАКЖЕ, скомпилируйте DLL C ++ с / cli (Common Language Runtime Support) в свойствах конфигурации. Щелкните правой кнопкой мыши проект C ++, затем Свойства -> Свойства конфигурации -> Общие -> Поддержка времени выполнения общего языка.

Так что я думаю, что это то, что заставляет его работать (возвращая строку).

Я использую Visual Studio 2013 RC. Эксперты, пожалуйста, прокомментируйте, что лишнее или отсутствует.

0 голосов
/ 15 июня 2011

Я загрузил пример кода в SkyDrive (http://cid -48a119f5ed65483e.office.live.com / self.aspx / .Public / MarshalString.zip). Он демонстрирует, как передать управляемую строку в неуправляемый код и как с ней работать.

0 голосов
/ 15 июня 2011

Итак, если вы вернете указатель на жестко запрограммированную строку L "1? 2? 3? 4? 5", все в порядке. Но если вы вернете SomeActualFunction, ответ будет неверным. Может быть, код C ++ неверен? Как работает SomeActualFunction? Например, он может возвратить LPWSTR из некоторого выделенного стека объекта, который уничтожен в это время. Попробуйте сначала проверить это с клиентом C ++.

...