在c#中使用Delphi DLL

本文关键字:Delphi DLL | 更新日期: 2023-09-27 17:54:53

我有一个用Delphi(未知版本)编写的第三方"神秘dll", Delphi中的工作示例(过去2009年),迫切需要在我的c#代码中使用该dll,几乎没有关于如何做到这一点的相关知识。

下面是使用dll的Delpi示例:

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");
    }
}

不幸的是,我有一个错误:试图读取或写入受保护的内存。这通常表明其他内存已损坏。

从我白天读到的东西来看,我可能混淆了返回类型或参数类型。帮助吗?

在c#中使用Delphi DLL

对于某些类型,Delphi ABI与Microsoft ABI不同。Delphi WideString是一个托管类型(在Delphi术语中),并且作为返回类型使用与Microsoft工具不兼容的ABI。

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;

因此,您可以通过导入转换后的Delphi函数,从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
);

我基本上明白了。由于我不清楚的一些原因,c#不能处理WideString返回值。如果您有访问delphi源代码的权限,可以将函数与过程交换,并将返回值作为"out"参数传递。在我的例子中,我没有访问源代码的权限,因此我被迫编写代理DLL来实现这一点。例如上面的"proxy" 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
);

它不漂亮,但对我来说,这就是答案。