在C#中使用Delphi DLL

8
我有一个由Delphi(未知版本)编写的第三方“mystery dll”,在Delphi中有工作示例(2009年以后),迫切需要在我的C#代码中使用该dll,但我几乎没有相关知识来完成此操作。
以下是使用此dll的Delphi示例:
type
TD_Query = function(host: WideString; port : Word;pud,query : WideString):WideString; stdcall;
procedure TForm11.Button6Click(Sender: TObject);
var
   Handle         : LongWord;
   D_Query        : TD_Query;
   sss            : WideString;
begin

 Handle := LoadLibrary('kobrasdk.dll');
 sss:='';
 if Handle <> 0 then
 begin
  @D_Query := GetProcAddress(Handle, 'D_Query');
  sss:=D_Query('host',8201,'pud','query');
  FreeLibrary(Handle);
 end;
end;

这是我尝试用C#解释它的代码:
class Program
{
    [DllImport("C:\\Games\\kobrasdk.dll", CallingConvention = CallingConvention.StdCall,
        CharSet = CharSet.Ansi)]
    [return: MarshalAs(UnmanagedType.LPStr)]
    public static extern string D_Query(string host, ushort port, string pud, string query);


    static void Main(string[] args)
    {
        D_Query("test", 8201, "test", "test");
    }
}

很遗憾,我遇到了一个错误:尝试读取或写入受保护的内存。这通常表明其他内存已损坏。
根据我在白天阅读的内容,我可能在返回类型或参数类型上出了问题。能帮忙吗?

这个 Delphi DLL 是否有使用 ShareMem 的可能性? - Jerry Dodge
很遗憾,这个dll没有可用的文档或源代码。 - Morhem Howk
2
https://dev59.com/uGox5IYBdhLWcg3wOxxi - MBo
1
有一个希望 - 我想在Delphi中创建代理dll,以与CSharp兼容的方式返回数据。 - MBo
1
@MBo 链接的问题是关键。 - David Heffernan
显示剩余4条评论
2个回答

6

Delphi ABI与Microsoft ABI在某些类型上有所不同。Delphi的WideString是一种托管类型(在Delphi术语中),作为返回类型使用的ABI与Microsoft工具不兼容。

Delphi ABI将托管返回类型转换为隐藏的var参数。因此,编译器会将以下内容转换:

function(host: WideString; port: Word; pud, query: WideString): WideString; stdcall;

转换为

procedure(var result: WideString; host: WideString; port: Word; pud, query: WideString); 
  stdcall;

因此,您可以通过导入转换后的函数以C#的形式访问原始的Delphi函数。

[DllImport(@"...", CallingConvention = CallingConvention.StdCall)]
public static extern void My_D_Query(
    [MarshalAs(UnmanagedType.BStr)]
    out string result,
    [MarshalAs(UnmanagedType.BStr)]
    string host,
    ushort port,
    [MarshalAs(UnmanagedType.BStr)]
    string pud,
    [MarshalAs(UnmanagedType.BStr)]
    string query
);

0
我大部分都弄明白了。由于某些原因,C#无法处理WideString返回值。如果您可以访问Delphi源代码,则可能适当地使用过程交换函数,并将返回值作为“out”参数传递。在我的情况下,我无法访问源代码,因此被迫编写代理DLL来实现这一点。 例如上面的“代理”DLL代码:

  type
  TD_Query = function(host : WideString;port : Word;pud,query : WideString):WideString; stdcall;

procedure My_D_Query(host: WideString; port: Word; pud, query: WideString; out return : WideString); stdcall;
var
   Handle: LongWord;
   D_Query : TD_Query;
   sss : WideString;
begin
 Handle := LoadLibrary('kobrasdk.dll');
 sss:='';
 if Handle <> 0 then
 begin
  @D_Query:=GetProcAddress(Handle, 'D_Query');
  sss:=D_Query(host,port,pud,query);
  FreeLibrary(Handle);
 end;
return := sss;
end;

然后是访问它的C#代码:

[DllImport("C:\\MyDll.dll", CallingConvention = CallingConvention.StdCall,
    CharSet = CharSet.Ansi)]
public static extern void My_D_Query(
[MarshalAs(UnmanagedType.BStr)]
        string host,
        int port,
[MarshalAs(UnmanagedType.BStr)]
        string pud,
[MarshalAs(UnmanagedType.BStr)]
        string query,
[MarshalAs(UnmanagedType.BStr)]
        out string result
);

虽然不太美观,但对我来说,这就是答案。


你看到@MBo发布的链接了吗?据我所知,WideString返回值实际上已经是一个输出参数了,应该在C#端声明为这样。我建议先尝试这个方法。 - Rudy Velthuis
不需要代理DLL,请看我的回答。话虽如此,代理DLL是处理棘手导入的干净方式,但在这里并不需要。另外,“Word”映射到“ushort”。 - David Heffernan
@DavidHeffernan,像你建议的那样,我试图摆脱代理DLL,但是我一直收到“尝试读取或写入受保护的内存”错误。顺便将int更改为ushort。 - Morhem Howk
我在回答中犯了一个错误。转换将结果放在第一个参数而不是最后一个参数中。我的更新答案现在可以工作了。抱歉,我记错了这个细节。 - David Heffernan
@DavidHeffernan 谢谢,现在已经接受了您的答案! - Morhem Howk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接