Как правильно вернуть char * из неуправляемой DLL в C #? - PullRequest
3 голосов
/ 18 ноября 2009

Подпись функции:

char * errMessage(int err);

Мой код:

[DllImport("api.dll")]       
internal static extern char[] errMessage(int err);
...
char[] message = errMessage(err);

Это возвращает ошибку:

Cannot marshal 'return value': Invalid managed/unmanaged type combination.

Что я делаю не так? Спасибо за любую помощь.

Ответы [ 5 ]

7 голосов
/ 18 ноября 2009

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

Пример:

C

#include <string.h>

int foo(char *buf, int n) {
   strncpy(buf, "Hello World", n);
   return 0;
}

C #

[DllImport("libfoo", EntryPoint = "foo")]
static extern int Foo(StringBuilder buffer, int capacity);

static void Main()
{
    StringBuilder sb = new StringBuilder(100);
    Foo(sb, sb.Capacity);
    Console.WriteLine(sb.ToString());
}

Тест:

Hello World
5 голосов
/ 18 ноября 2009

Это ужасная сигнатура функции, нет никакого способа угадать, как была расположена строка. Вы также не можете освободить память для строки. Если вы объявите возвращаемый тип как «string» в объявлении, тогда маршаллер P / Invoke вызовет CoTaskMemFree () для указателя. Это вряд ли будет уместным. В XP произойдет тихий сбой, но ваша программа выйдет из строя в Vista и Win7.

Вы даже не можете надежно вызвать функцию в неуправляемой программе. Вероятность того, что вы используете правильную версию free (), довольно мала. Все, что вы можете сделать, это объявить его как IntPtr и самостоятельно упорядочить возвращаемое значение с помощью Marshal.PtrToStringAnsi (). Обязательно напишите тестовую программу, которая делает это миллион раз, пока вы наблюдаете это в Taskmgr.exe. Если размер виртуальной машины для программы растет без ограничений, у вас есть утечка памяти, которую вы не можете подключить.

5 голосов
/ 18 ноября 2009

См. этот вопрос . Таким образом, функция должна возвращать IntPtr, и вы должны использовать Marshal.PtrToString *, чтобы преобразовать его в управляемый объект String.

2 голосов
/ 18 ноября 2009

попробуйте это:

[DllImport("api.dll")]
[return : MarshalAs(UnmanagedType.LPStr)]
internal static extern string errMessage(int err);
...
string message = errMessage(err);

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

Редактировать: добавлен атрибут MarshalAs

0 голосов
/ 18 ноября 2009

Попробуйте использовать String на управляемой стороне. Вы можете также установить CharSet на Ansi

...