【问题标题】:C# calling dll file of DelphiC#调用Delphi的dll文件
【发布时间】:2018-10-24 02:16:17
【问题描述】:

最近,我需要在我的 C# 项目中使用 Delphi DLL,我已经搜索了一些答案,但是一切都失败了。 DLL的名字是modelDLL.dll,需要另外一个DLL文件(我已经把这两个文件放到debug文件夹里了)

德尔福代码

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

使用Delphi调用DLL可以正常工作(代码如下),但是,我不知道DLL文件中的具体cmets。 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: 德尔福版本: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 方法。查看此链接,它可以让您深入了解您可能想要尝试的内容。 stackoverflow.com/a/38219260/3516555
  • 你必须在DLL中显示函数描述(尤其是调用约定)并指定Delphi版本(是Unicode吗?)
  • 感谢您的回复,我现在无法告诉您DLL中的功能描述(我稍后会发布)并且Delphi的版本是7。但是DLL的提供者给了我一个简短的功能描述:
  • (使用C): Char NIRSAPretreatInfor (char *ModelFileName); 输入参数: char ModelFileName[600]; / 模型文件名,扩展名*.mdl或*.mdr */ 返回值:array char str [600],描述内容;
  • 字符集不是主要问题。我会写一个答案,希望能解释一下。

标签: c# delphi dllimport


【解决方案1】:

最重要的问题(有多个问题)是Delphi 代码使用固定长度的字符数组,这些字符数组不容易编组。 C# marshaler 没有与这些东西完全匹配的类型。问题是,如果 Delphi 固定长度字符数组是数组的全长,则它不会以空值结尾。

另一个问题是字符集。 Delphi 7 char 是 8 位 ANSI 类型。您编组为CharSet.Auto,它在 Windows 9x 以外的平台上是 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);

【讨论】:

  • 这确实是解决DLL设计不足的好方法。
  • 感谢您的回复。目前程序可以运行,但是传入的数据是空的,也就是说outputBytes的每个大小都是(byte)0,不知道是什么原因造成的。我只是用文件路径(fileName )替换了foo
  • 我的答案中的代码有效。我已经测试过了。您可以通过使用单个函数制作一个测试 DLL 来自己测试它。像我一样向自己证明数据在两个方向上都正确传递。
  • 很抱歉给您添麻烦了。我没有用别的DLL测试,但是发现了一个奇怪的现象,原来的输入参数byte[] ModelFileName包含了一些正确的返回数据(不是全部),不知道是什么原因造成的?我认为编码可能会导致它?
  • 只返回第一行内容,下一行内容应该是中文描述(看不懂的文字不显示)。另一方面,如果delphi dll接受数组(如double[][]),我是否可以只使用C# double[,]作为变量来调用这个函数(其他类型相同,如Integer对应int)?感谢您的时间和工作!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-05
相关资源
最近更新 更多