C# 调用 Delphi 的 dll 文件
C# calling dll file of Delphi
最近,我需要在我的 C# 项目中使用一个 Delphi DLL,我搜索了一些答案,但是所有其他方法都失败了。 DLL的名字是modelDLL.dll,需要另外一个DLL文件(我已经把这两个文件放在debug文件夹里了)
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;
我的 C# 代码如下,提示 "Attempted to read or write protected memory. This is often an indication that other memory is corrupt." 错误。
[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 代码使用固定长度的字符数组,这不容易编组。 C# 封送拆收器没有与这些东西完全匹配的类型。问题是 Delphi 固定长度字符数组如果是数组的全长则不会以空值终止。
另一个问题是字符集。 Delphi 7 char
是一个 8 位 ANSI 类型。您编组为 CharSet.Auto
,它是 Windows 9x 以外平台上的 16 位 UTF-16。我相信你不会 运行 Windows 9x.
最后一个问题涉及用作 return 值的大型类型的 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 + '[=12=]');
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 字符数组不需要以 null 结尾的事实。
一旦到位,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);
最近,我需要在我的 C# 项目中使用一个 Delphi DLL,我搜索了一些答案,但是所有其他方法都失败了。 DLL的名字是modelDLL.dll,需要另外一个DLL文件(我已经把这两个文件放在debug文件夹里了)
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;
我的 C# 代码如下,提示 "Attempted to read or write protected memory. This is often an indication that other memory is corrupt." 错误。
[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 代码使用固定长度的字符数组,这不容易编组。 C# 封送拆收器没有与这些东西完全匹配的类型。问题是 Delphi 固定长度字符数组如果是数组的全长则不会以空值终止。
另一个问题是字符集。 Delphi 7 char
是一个 8 位 ANSI 类型。您编组为 CharSet.Auto
,它是 Windows 9x 以外平台上的 16 位 UTF-16。我相信你不会 运行 Windows 9x.
最后一个问题涉及用作 return 值的大型类型的 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 + '[=12=]');
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 字符数组不需要以 null 结尾的事实。
一旦到位,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);