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

节俭

我使用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)
大卫·赫弗南(David Heffernan)

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

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

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

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

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

调用方分配的缓冲区存在的问题是,要求调用方知道要分配多大的缓冲区。

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

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

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.

输出

foob​​ar

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

Shell函数仅使用第一个参数

PHP:在第一个函数内的另一个函数中使用函数参数吗?

此PHP函数中使用的第一个参数$ con是什么?

将自定义函数应用于每行仅使用参数的第一个值

为什么在调用时不需要在Swift中使用函数的第一个参数名?

何时在GraphQL-resolver函数中使用“ parent”以及何时将“ root”用作第一个参数

使用数组作为参数仅通过第一个元素(JSON RPC)调用PHP中的另一个函数

“仅使用 'destfile' 参数的第一个元素”,循环下载文件

参数'pattern'的长度> 1,并且仅使用第一个元素-GSUB()

参数“替换”的长度> 1,并且仅使用第一个元素

具有参数的SSRS缓存的报告仅使用第一个生成的报告

参数'pattern'的长度> 1,并且仅第一个元素将使用数字grepl

在Ramda管道中使用相同的第一个参数

当第一个参数使用flip时了解map函数

我可以使用成员函数作为EnumWindows的第一个参数吗

正则表达式:查找在函数的第一个参数上使用的属性

在函数中使用时,for循环不会按顺序循环通过第一个变量

在Python中使用explode()函数后,如何保留特定列的第一个值?

使用第一个可选参数进行重载

webpack require。确保使用第一个参数

在第二forEach()中使用第一个$(this)

在节点中,使用Q,使2个函数并行工作,但仅等待第一个函数履行其承诺

如何在方法 LinearRegression().fit(X,Y) 的第一个参数“X”中使用 2 个参数?

我无法使用第一个函数公式

在另一个函数中使用变量,然后在第一个函数中使用该函数作为 if 语句

MySQL在第一个查询中使用2个给定的匹配参数中的1个查询整个表

javascript如何在第二个函数中使用第一个函数输出

在Python中使用C#DLL

在C#Dll中使用OpenFileDialog