C # вызывает DLL файл Delphi

2

В последнее время мне нужно использовать DLL-библиотеку Delphi в моем проекте на С#, и у меня есть поиск некоторых ответов, но все остальное терпит неудачу. Имя DLL было modelDLL.dll, которому нужен другой файл DLL (я уже поместил эти два файла в папку отладки)

Код Delphi

type
TCharStr=array[0..599] of char;

Использование Delphi для вызова DLL работает нормально (код следующий), однако я не знаю конкретных комментариев в файле DLL. Относительный код Delphi выглядит следующим образом:

procedure TMainDLLForm.PedBitBtnClick(Sender: TObject);
var
  fileName:TCharStr;
begin

        OpenDataFileDlg.InitialDir:= GetCurrentDir;
        OpenDataFileDlg.Title:='load model file';
        OpenDataFileDlg.Filter := 'model_A[*.mdl]|*.mdl|model_T[*.mdr]|*.mdr';
        if OpenDataFileDlg.Execute then
        begin
           StrPCopy(FileName,OpenDataFileDlg.FileName);
           tmpD:=NIRSAModelForPred(graphyData,dataLength,FileName,targetName);
        end;  
       if compareText(fileExt,'.MDR')=0 then
       begin
         memo1.Lines.Add('model_T: '+ExtractFileName(FileName));
         memo1.Lines.Add(Format('Result: %10s:%0.0f',[targetName,tmpD]));
       end;
       memo1.Lines.Add('--------------');
       memo1.Lines.Add(trim(NIRSAPretreatInfor(FileName)));// calling this function
       memo1.Lines.Add('--------------');
       memo1.Lines.Add(trim(NIRSAModelInfor(FileName)));
end;

Мой код С# был следующим: "Попытка чтения или записи защищенной памяти. Это часто указывает на то, что другая память повреждена". ошибка.

[MarshalAs(UnmanagedType.LPStr, SizeConst = 600)]
    public string fileName;

    [DllImport(@"modelDLL.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
    [return: MarshalAs(UnmanagedType.LPStr, SizeConst = 600)]
    public static extern string NIRSAPretreatInfor(ref string fileName);

    private void preCalcButton_Click(object sender, EventArgs e)
    {
        OpenFileDialog dialog = new OpenFileDialog();
        dialog.Multiselect = false;
        if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
        {
            fileName = dialog.FileName;
            string result = NIRSAPretreatInfor(ref fileName);
            modelInfoTextBox.Text = result;
        }
    
    }

Итак, кто-нибудь может дать мне несколько советов? Ваш ответ будет оценен!

PS: Delphi версия: 7.0

импортировать код DLL:

    implementation
       function  
NIRSAModelForPred(Data:TGraphyData;dataLength:integer;ModelFileName:TCharStr;var targetName:TCharStr):double;stdcall;external 'modelDLL.dll';
       function  NIRSAModelInfor(ModelFileName:TCharStr):TCharStr;stdCall;external 'modelDLL.dll';
       function  NIRSAPretreatInfor(ModelFileName:TCharStr):TCharStr;stdCall;external 'modelDLL.dll';

Теперь я изменил CharSet = CharSet.Auto на CharSet = CharSet.Ansi и снова появилось сообщение об ошибке.

The call to the PInvoke "NIRSAPre!NIRSAPre.Form1::NIRSAPretreatInfor" function causes the stack to be asymmetric.
  • 0
    Я не вижу, куда вы экспортируете метод Delphi. Проверьте эту ссылку, она дает некоторое представление о том, что вы можете попробовать. stackoverflow.com/a/38219260/3516555
  • 0
    Вы должны показать описание функции в DLL (особенно соглашение о вызовах) и указать версию Delphi (это Unicode?)
Показать ещё 15 комментариев
Теги:
dllimport

1 ответ

4
Лучший ответ

Самая значительная проблема (есть несколько проблем) заключается в том, что код Delphi работает с массивами символов фиксированной длины, которые нелегко маршалировать. Маршалер С# не имеет типа, который точно соответствует этим вещам. Проблема в том, что массив символов Delphi фиксированной длины не завершается нулем, если он является полной длиной массива.

Другая проблема - это набор символов. Delphi 7 char - это 8-битный тип ANSI. Вы выступаете в роли CharSet.Auto который является 16-битным UTF-16 на платформах, отличных от Windows 9x. Я уверен, что вы не работаете на Windows 9x.

Последняя проблема связана с ABI для больших типов, используемых в качестве возвращаемых значений. В Delphi ABI реализованы такие вещи, как дополнительный (скрытый) параметр var. Так

function NIRSAPretreatInfor(ModelFileName: TCharStr): TCharStr; 
  stdCall; external 'modelDLL.dll';

фактически реализован как:

procedure NIRSAPretreatInfor(ModelFileName: TCharStr; var ReturnValue: TCharStr); 
  stdCall; external 'modelDLL.dll';

Для правильного выполнения этого вам нужно будет обрабатывать строки вручную. Начнем с некоторых вспомогательных методов:

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);
}

Они касаются того факта, что массивы символов Delphi фиксированной длины не должны заканчиваться нулем.

Как только это произойдет, объявление p/invoke выглядит так:

[DllImport(@"modelDLL.dll", CallingConvention = CallingConvention.StdCall)]
public extern static void NIRSAPretreatInfor(byte[] ModelFileName, byte[] ReturnValue);

Назовите это так:

byte[] outputBytes = NewDelphiTCharStr();
NIRSAPretreatInfor(ToDelphiTCharStr("foo"), outputBytes);
string output = FromDelphiTCharStr(outputBytes);
  • 0
    Это действительно хорошее решение для обхода неадекватного дизайна DLL.
  • 0
    Спасибо за ваш ответ. В настоящее время программа может работать, но поступающие данные были пустыми, что означает, что каждый размер outputBytes был (байт) 0, я не знаю, что делает это условие. Я просто заменил foo на путь к файлу ( fileName ).
Показать ещё 6 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню