【问题标题】:Returning string from delphi dll to c#从delphi dll返回字符串到c#
【发布时间】:2021-12-01 18:10:19
【问题描述】:

我正在尝试将加密函数从我们的旧代码中分离到一个我可以从 C# 调用的 dll 中,但是我在让它工作时遇到问题,并且在调用 dll 时我不断遇到访问冲突。

我不确定 AV 发生在哪里,因为当 dll 附加到另一个进程时,delphi 很难达到我的断点。

我昨天在这里使用 David Heffernan 的回答让它工作:Returning a string from delphi dll to C# caller in 64 bit 但是我的成功是短暂的,因为我将字符串参数更改为常规的string's (delphi),发现它不起作用并将它们更改回AnsiString(我们的加密例程需要 Ansi)。因为我改变了这些参数类型。我无法让它再次工作。

这是我的 Delphi 代码:

procedure Encrypt(const Source: AnsiString; const Key: AnsiString; var OutPut:PAnsiChar; const OutputLength: Integer);
var
  EncryptedString, EncodedString: AnsiString;
begin
  EncryptedString := Crypt(Source, Key);
  EncodedString := Encode(EncryptedString);
  if Length(EncodedString) <= OutputLength then
    System.AnsiStrings.StrPCopy(Output, EncodedString);
end;

exports
  Encrypt;

我的 C# 调用者:

[DllImport("AsmEncrypt.dll", CharSet = CharSet.Ansi)]
public static extern void Encrypt(string password, string key, StringBuilder output, int outputlength);
// using like this:
Encrypt(credentials.Password, myKey, str, str.Capacity);

我现在最好的选择是,我已经对 dll 进行了一些参数设置,因为它似乎在到达 OutputDebugStr() 之前就崩溃了,我在 Encrypt() 的第一行放置了

我们将不胜感激所有帮助

【问题讨论】:

  • 确定第三个参数完全错误 - StringBuilder
  • 参考 David Heffernans 在链接帖子中的回答它应该可以工作,我昨天让它工作了吗?你如何建议我传递一个缓冲区供 delphi 写入?
  • 我的意思是 - StringBuilder 是 .NET 类,你不能像在 C# 和本机 Delphi 代码之间那样传递它。
  • 在C#端,你仍然需要为调用定义marshaler。 [DllImport("AsmEncrypt.dll", CharSet = CharSet.Ansi)] public static extern void Encrypt([MarshalAs(UnmanagedType.LPTStr)] string password, [MarshalAs(UnmanagedType.LPTStr)] string key, [MarshalAs(UnmanagedType.LPTStr)] string output, [MarshalAs(UnmanagedType.I4)]int outputlength);

标签: c# string delphi dll interop


【解决方案1】:

将Delphi函数改为

procedure Encrypt(Source, Key, OutPut: PAnsiChar; OutputLength: Integer); stdcall;

为了使这段代码工作。

您可能还应该将长度参数设置为 IN/OUT,以便调用者可以在调用返回后调整字符串构建器对象的大小。这也将允许被调用者向调用者发出任何错误信号,这是您当前设计中的另一个缺陷。

我还必须说,使用AnsiString 作为字节数组会导致失败。现在是您开始正确进行加密的时候了。如果您有文本,则将其编码为具有特定编码的字节数组,通常这意味着 UTF-8。然后将该字节数组加密为另一个字节数组。

【讨论】:

  • 我知道这个解决方案充其量只是粗略的 - 我正在编写的 dll 应该有助于清理我们的遗留代码并运行转换。尝试复制到输出时,我仍然遇到访问冲突...我已经在这里 2 天了,我根本不明白
  • 我刚刚意识到我只有在设置 charset.Ansi 时才得到一个 AV,如果我将它更改为 CharSet.Unicode 它有点工作,除了返回的字符串是错误的
  • 我给您的代码与您问题中的 C# 声明相匹配。如果您仍然有问题,那么他们就没有我们能看到的任何东西。这里有一些建议。在调试此类问题时,请完全专注于手头的问题。例如,不要尝试加密任何东西。尝试连接两个输入字符串并在输出中返回它们。一旦你能做到这一点,那么你就解决了互操作的问题,你可以把它们抛在脑后。
【解决方案2】:

来自this docs page

AnsiString 结构包含一个 32 位长度指示符、一个 32 位引用计数、一个指示每个字符字节数的 16 位数据长度和一个 16 位代码页。

所以AnsiString 不仅仅是一个指向字符数组的指针——它是一个指向编码一堆信息的特殊结构的指针。

但是,.NET 的 P/Invoke 机制将传递一个指向字符数组的指针。 Delphi 将尝试将其解释为指向其特殊 AnsiString 结构的指针,但事情不会顺利。

我认为您将很难在互操作中使用 AnsiString。您最好选择 .NET 和 Delphi 都知道的字符串类型。如果您需要将其转换为 AnsiString,请在 Delphi 中进行。

【讨论】:

  • 我尝试了普通的string 类型,但我得到了完全相同的异常
  • @MattBaech 上次我做了类似的事情(考虑到 Inno Setup,但应该不会有太大不同),我发现的组合是 Pascal 方面的 WideStringBStr .NET 端(请参阅stackoverflow.com/a/53729600/1086121)。还有另一个答案在 Pascal 端使用了PAnsiChar
  • @MattBaech 肯定你不能在 C# 和原生 DLL 之间传递任何类型的类(包括 Delphi 中的隐式类),你只能使用原始类型,对于字符串你应该使用指向字节内存的指针您应该负责将这些字节转换为 C# 和本机 DLL 之间的字符串。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-27
  • 1970-01-01
  • 2018-03-08
  • 2018-02-25
相关资源
最近更新 更多