символ * в управляемом коде? - PullRequest
1 голос
/ 06 июля 2010

У меня есть метод c ++ plus, который генерирует значение.я вызываю этот метод из приложения ac #.
Метод 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*?Пожалуйста, поделитесь своими комментариями, это срочно, спасибо.

Ответы [ 3 ]

3 голосов
/ 06 июля 2010

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

Скорее всего, вы захотите объявить это так:

[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 голосов
/ 06 июля 2010

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

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

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

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

Вы можете сделать так, чтобы объявление C # возвращало IntPtr, а затем вызывало один из Marhsal.PtrToStringXXX методов.

1 голос
/ 06 июля 2010

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

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

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

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

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

...