繁体   English   中英

在Delphi中使用C#DLL仅使用第一个函数参数

[英]Using a C# DLL in Delphi only uses the first function parameter

我使用C#DLL导出(UnmanagedExports- https: //www.nuget.org/packages/UnmanagedExports)使托管C#DLL可访问诸如Delphi之类的未管理代码。 我的问题是只有第一个功能参数从delphi传输到C#dll:

C#DLL部分

   [DllExport("SomeCall", CallingConvention.StdCall)]
   public static String SomeCall([MarshalAs(UnmanagedType.LPWStr)] String data1, [MarshalAs(UnmanagedType.LPWStr)] String data2)
    { 
             //Data1 is never filled with some string data. 
             String result = WorkWithData(data1);                   
             //Data2 is filled with some string data.
             result += WorkWithData(data2) 
             return result;
    }

Delphi部分(调用部分):

SomeCall: function(data1: PWideChar; data2: PWideChar;): String StdCall;

procedure DoSomeDLLWork(data1: PWideChar; data2: PWideChar);
var 
 dllCallResult: String;
begin
  dllCallResult := SomeCall(data1,data2);
end

在这种情况下的问题是仅填充了data2。 data1永远不会被填充。 我已经尝试过StdCall和Cdecl。

编辑:

以下操作有效(data1和data2正确传输)-返回值从字符串更改为布尔值:

C#(DLL部分):

   [DllExport("SomeCall", CallingConvention.StdCall)]
   public static bool SomeCall([MarshalAs(UnmanagedType.LPWStr)] String data1, [MarshalAs(UnmanagedType.LPWStr)] String data2)

德尔斐(来电显示):

 SomeCall: function(data1: PWideChar; data2: PWideChar;): boolean StdCall;

现在,我必须考虑一个返回值或一个缓冲区,以将结果字符串返回给delphi。

编辑2:

我同意David Heffernan提出的使用out参数的建议:

德尔福:

SomeCall: procedure(data1: PWideChar; data2: PWideChar; var result: PWideChar)StdCall;

C#

   [DllExport("SomeCall", CallingConvention.StdCall)]
   public static bool SomeCall([MarshalAs(UnmanagedType.LPWStr)] String data1, [MarshalAs(UnmanagedType.LPWStr)] String data2, [MarshalAs(UnmanagedType.LPWStr)] out String result)

问题是string返回值。 在Delphi中, string是托管类型。 此外,对此类类型进行了一些不寻常的处理。 实际上,它们在所有其他参数之后作为额外的隐式var参数传递。 C#代码通过寄存器传递返回值。

这意味着C#函数具有2个参数,而Delphi函数具有3个参数。 那就是不匹配的原因。

无论如何,从C#返回一个字符串都会导致指向以null结尾的字符数组的指针被编组。 它当然不会作为Delphi字符串进行封送。

您有一些可用的解决方案:

  1. 不理会C#并将Delphi返回类型更改为PAnsiChar PWideChar ,如果你当元帅的C#返回值LPWStr 您需要通过调用CoTaskMemFree释放指针
  2. 更改C#以接受它填充的调用方分配的缓冲区。 这将需要C#端的StringBuilder 并传递缓冲区的长度。
  3. 更改C#以使用string类型的out参数,将其编组为UnmanagedType.BStr 映射到Delphi中的WideString

调用方分配的缓冲区的问题在于,要求调用方知道要分配的缓冲区大小。

BStr/WideString的细微差别在于Delphi的ABI与Microsoft的不兼容,请参见为什么不能将WideString用作互操作的函数返回值? 您可以通过将字符串作为out参数而不是函数返回值返回来解决此问题。

返回映射为PWideChar ,以LPWStr编组的C# string ,使您LPWStr调用CoTaskMemFree释放内存的任务。 总的来说,我想我会选择这个选项。 这是该方法的示例。

C#

using System.Runtime.InteropServices;
using RGiesecke.DllExport;

namespace ClassLibrary1
{
    public class Class1
    {
        [DllExport]
        [return: MarshalAs(UnmanagedType.LPWStr)]
        public static string Concatenate(
            [MarshalAs(UnmanagedType.LPWStr)] string str1, 
            [MarshalAs(UnmanagedType.LPWStr)] string str2
        )
        {
            return str1 + str2;
        }
    }
}

德尔菲

{$APPTYPE CONSOLE}

uses
  Winapi.ActiveX; // for CoTaskMemFree

const
  dllname = 'ClassLibrary1.dll';

function Concatenate(str1, str2: PWideChar): PWideChar; stdcall; external dllname;

procedure Main;
var
  res: PWideChar;
  str: string;
begin
  res := Concatenate('foo', 'bar');
  str := res;
  CoTaskMemFree(res);
  Writeln(Str);
end;

begin
  Main;
  Readln;
end.

输出量

foobar

不要将string用作结果类型:该类型是Delphi专用的。

最简单的方法是使用BSTR封送处理,它映射Delphi WideString类型。

所以你定义

SomeCall: function(const aFileName, data2: WideString): WideString; StdCall;

可以这样映射:

[DllExport(CallingConvention = CallingConvention.StdCall)]
public static void AddCertificate([MarshalAs(UnmanagedType.BStr)] string data1, [MarshalAs(UnmanagedType.BStr)] string data2,  [MarshalAs(UnmanagedType.BStr)] out string Result);

(在回答之后)技巧是转换作为附加out参数返回的Delphi函数 ,如参数编组的Delphi低层信息所述

您必须封送返回值

像这样:

[DllExport(CallingConvention = CallingConvention.StdCall)]

    [return: MarshalAs(UnmanagedType.LPWStr)]

    public static string AddCertificate([MarshalAs(UnmanagedType.LPWStr)] string aFileName, [MarshalAs(UnmanagedType.LPWStr)] string aPassword)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM