2010-07-06 2 views
1

У меня есть метод C++ plus, который генерирует значение. Я вызываю этот метод из приложения C#.
метод C++, как это:знак * в управляемом коде?

extern "C" REGISTRATION_API char * generate(char dIn[],char dOut[]) 

Метод generate возвращает массив символов (sOut[]=returnvalue; return sOut;)

Теперь я звоню этот метод из моего C# приложения:

[DllImport("mydll.dll")] 
static extern string generate(string sIn, string sOut); 

Как вы можете видеть, тип возврата в C# равен string. Случается, что возвращаемое значение в C# неверно и оно повреждено. (Значение правильно внутри метода generate, но всякий раз, когда я называю это из C#, чтобы извлечь его, я получаю некоторые ошибочные значения.)

Это нормально, что мой метод в C# имеет string возвращаемое значение в то время как в C++ это char*? Пожалуйста, поделитесь своими комментариями, это срочно, спасибо.

+1

Действительно ли вы 'char *' представляют строку (ANSI/Unicode?) Или последовательность байтов? Если строка, используйте StringBuilder, иначе байт [] в декларации 'static extern'. – dtb

ответ

1

Этот метод нельзя назвать безопасным и из собственного приложения на C++. Он возвращает указатель на массив в фрейме стека. Любой вызов другой функции перезапишет этот стек стека и испортит массив.

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

Вам придется перепроектировать функцию C++. Вы должны дать ему аргументы, которые позволяют вызывающему передать свой собственный буфер, который будет заполнен результатом. Например:

extern "C" void __stdcall generate(const char* input, char* output, int outputSize) 

вызова этого в C# код путем пропускания StringBuilder для выходного аргумента, правильно инициализирован с достаточной пропускной способностью, чтобы получить строку. Аргумент outputSize гарантирует, что функция C++ может избежать записи за конец буфера и испортить кучу мусора. Не игнорируйте его.

3

К сожалению, char* может быть несколько неоднозначным. (Буквально это указатель на один символ, где вы можете или не сможете получить другие символы, разыменовывая указатель, как массив). Обычно это указатель на строку с одиночным байтом с нулевым завершением ANSI-кодирование (что Microsoft называет LPStr). По умолчанию P/Invoke маршалирует экземпляры System.String как тип BStr, который похож на строку Unicode Pascal (с длиной в начале). Вам нужно будет использовать MarshalAs attribute, чтобы явно указать, как вы хотите, чтобы строки, упорядоченные между управляемым и неуправляемым кодом.

Скорее всего, вы хотите, чтобы объявить его следующим образом:

[DllImport("mydll.dll")] 
[return: MarshalAs(UnmanagedType.LPStr)] 
static extern string generate(
    [MarshalAs(UnmanagedType.LPStr)] string sIn, 
    [MarshalAs(UnmanagedType.LPStr)] out string sOut); 

Однако ваша функция подписи сбивает с толку: он возвращение строка, или это установив выходную переменную? Возможно, вам придется настроить объявление P/Invoke в соответствии с вашими потребностями.

2

Это нормально, что мой метод в C# имеет возвращаемое значение строку в C++ это полукокса *?

Это действительно зависит.

Если этот метод С ++ выделяет память для строки и ожидает, что вызывающий абонент освободит ее, у вас возникнут проблемы ... большие проблемы. Вы можете попробовать позвонить Marshal.FreeHGlobal, но нет никакой гарантии того, что метод C++ фактически выделил память в глобальной куче. В большинстве API-интерфейсов вы возвращаете указатель обратно в один из своих методов, чтобы освободить память.

Также возможно, что char * указывает на память, которую не нужно освобождать. В этом случае использование string должно быть прекрасным.

Вы можете вернуть декларацию C# IntPtr, а затем вызвать один из методов Marhsal.PtrToStringXXX.

Смежные вопросы