gpt4 book ai didi

c# - 在 Delphi 中使用 C# DLL 只使用第一个函数参数

转载 作者:太空宇宙 更新时间:2023-11-03 18:26:33 25 4
gpt4 key购买 nike

我使用 C# DLL Export (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 已正确传输)- 返回值从字符串更改为 bool 值:

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# 以接受它填充的调用者分配的缓冲区。那将需要 StringBuilder在 C# 方面。并传递缓冲区的长度。
  3. 更改 C# 以使用 string 类型的输出参数, 编码为 UnmanagedType.BStr .映射到 WideString在德尔福。

调用者分配缓冲区的问题是要求调用者知道要分配多大的缓冲区。

BStr/WideString 的细微差别是 Delphi 的 ABI 与 Microsoft 的不兼容,参见 Why can a WideString not be used as a function return value for interop?您可以通过将字符串作为 out 返回来解决此问题参数而不是函数返回值。

返回 C# string , 编码为 LPWStr , 映射到 PWideChar , 让您完成调用 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

关于c# - 在 Delphi 中使用 C# DLL 只使用第一个函数参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33252249/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com