В последнее время мне нужно использовать 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.
Самая значительная проблема (есть несколько проблем) заключается в том, что код 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);
outputBytes
был (байт) 0, я не знаю, что делает это условие. Я просто заменил foo
на путь к файлу ( fileName
).