Введение:
При вызове функций из Delphi DLL в C#-приложениях часто возникают проблемы, связанные с несоответствием типов данных и соглашений о вызовах. В данной статье рассмотрим, как можно решить проблему, возникшую при использовании функции из Delphi DLL в C# проекте, когда возникает ошибка "Attempted to read or write protected memory" и "The call to the PInvoke function causes the stack to be asymmetric".
Описание проблемы:
Пользователь столкнулся с проблемой при использовании функции из Delphi DLL в C#. Функция NIRSAPretreatInfor вызывается корректно из Delphi-приложения, но вызывает ошибки в C#. Проблема заключается в несоответствии CharSet и возможном некорректном обращении с памятью.
Контекст:
Delphi-код для вызова функции NIRSAPretreatInfor работает корректно. Функция ожидает строку в качестве аргумента, которая должна содержать имя файла. В C#-коде для вызова этой функции используется ref string fileName, но это приводит к ошибкам.
Альтернативный ответ:
Пользователь не видит, как функция экспортируется из Delphi DLL, и упоминается, что Delphi версия 7.0. Описание функции NIRSAPretreatInfor предоставлено провайдером DLL, но оно не полное. Обсуждается проблема с размером символа (1 байт в Delphi 7 против 2 байт в C#) и возможное использование CharSet.Ansi.
Подтвержденный ответ:
Основная проблема заключается в использовании фиксированных по размеру массивов символов в Delphi, которые трудно преобразовать для использования в C#. В C# нет типа, который точно соответствует этим структурам. Также важно учитывать, что Delphi использует ANSI-тип char размером в 8 бит, в то время как CharSet.Auto в C# может быть 16-битным UTF-16. Кроме того, ABI для крупных типов, используемых как возвращаемые значения, в Delphi реализован с дополнительным скрытым параметром типа var.
Решение:
Для корректного маршалинга строк необходимо использовать вспомогательные методы. Пример таких методов представлен ниже:
public const int DelphiTCharStrLength = 600;
public static byte[] NewDelphiTCharStr()
{
return new byte[DelphiTCharStrLength];
}
public static byte[] ToDelphiTCharStr(string value)
{
byte[] result = NewDelphiTCharStr();
byte[] bytes = Encoding.Default.GetBytes(value + '\0');
Buffer.BlockCopy(bytes, 0, result, 0, Math.Min(bytes.Length, DelphiTCharStrLength));
return result;
}
public static string FromDelphiTCharStr(byte[] value)
{
int len = Array.IndexOf(value, (byte)0);
if (len == -1)
len = DelphiTCharStrLength;
return Encoding.Default.GetString(value, 0, len);
}
Используя эти методы, можно изменить объявление P/Invoke следующим образом:
Заключение:
Пользователь столкнулся с проблемой, что возвращаемые значения массивов байтов outputBytes были пустыми, и часть данных возвращалась обратно в входные параметры. Возможные причины и решения включают проблемы с кодировкой и необходимость тестирования с простым тестовым DLL. Для получения дополнительной информации рекомендуется обратиться к тестированному коду.
Проблема заключается в необходимости правильного маршалинга строк и данных между C#-приложением и Delphi DLL, включая учет различий в кодировке и соглашениях о вызовах.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS