C#调用Delphi的dll文件

时间:2018-10-24 02:16:17

标签: c# delphi dllimport

最近,我需要在我的C#项目中使用Delphi DLL,并且已经搜索了一些答案,但是所有其他操作都失败了。 DLL的名称是modelDLL.dll,它需要另一个DLL文件(我已经将这两个文件放在debug文件夹中)

Delphi代码

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

使用Delphi调用DLL可以正常工作(下面的代码),但是,我不知道DLL File中的特定注释。 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#代码紧随其后,提示“尝试读取或写入受保护的内存。这通常表明其他内存已损坏。”错误。

[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.

1 个答案:

答案 0 :(得分:4)

最重要的问题(存在多个问题)是Delphi代码与固定长度的字符数组一起使用,这不容易编组。 C#封送拆收器没有与这些内容完全匹配的类型。问题是,如果Delphi定长字符数组是数组的完整长度,则它不会以null终止。

另一个问题是字符集。 Delphi 7 char是8位ANSI类型。在Windows 9x以外的平台上,您以CharSet.Auto的形式编组,这是16位UTF-16。我相信您不会在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);