C#Dll库不会将输出参数值返回给Delphi应用程序

埃里克·费尼·库比洛斯·加西亚

我已经用C#编写了一个带有导出功能的Dll,该功能可以保存文件。

这是C#代码

using RGiesecke.DllExport;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.IO;    

namespace ClassLibrary1
{
    public class Class1
    {
        [DllExport("Funcion", CallingConvention = CallingConvention.StdCall)]
        public static void Funcion(IntPtr pDataIn, Int32 pSize, [Out, MarshalAs(UnmanagedType.I4)] int pArchivo)
        {
            byte[] documento = new byte[pSize];
            Marshal.Copy(pDataIn, documento, 0, pSize);
            File.WriteAllBytes("Document2.pdf", documento);            

            pArchivo = 25;
        }
    }    
}

在Delphi中,我加载了库并调用了导出的函数,它运行良好。

这是Delphi代码

procedure TForm1.Button1Click(Sender: TObject);
var
  lStream     : TMemoryStream;
  lArBytes      : array of Byte;
  lInDocSize : Integer;
  lHndle : THandle;

  Funcion : procedure(pDataIn : array of Byte;
                      pInSize : Integer;
                      var pDocumento : Integer
                      ); stdcall;
begin

  try
    lHndle := LoadLibrary('ClassLibrary1.dll');

    if (lHndle <> 0) then
    begin

      Funcion := GetProcAddress(lHndle, 'Funcion');

      if Assigned(Funcion) then
      begin
        try
          lStream := TMemoryStream.Create;

          lStream.LoadFromFile('Document1.PDF');
          lStream.Position := 0;

          SetLength(lArBytes, lStream.Size);

          lStream.Read(lArBytes[0], lStream.Size);

          lInDocSize := 0;
          Funcion(lArBytes, lStream.Size, lInDocSize);

          Label1.Caption := IntToStr(lInDocSize);

        except on E : Exception do
          begin
            RaiseLastOSError;
            ShowMessage(e.Message);
          end;
        end;
      end;
    end;

  finally
  end;

end;

我的输出参数有错误,它总是返回cero(0)值,无论我为参数分配什么值,它始终具有cero值。

我改变了这样的参数

out int pArchivo

ref int pArchivo

但是当函数完成时,我得到了内存异常。

使用Marshal时,功能可以很好地完成,没有内存错误,但是输出参数值始终为0(0)。

[Out, MarshalAs(UnmanagedType.I4)] int pArchivo

我已经在Stackoverflow的这篇文章中了解了这个问题

将结构数组从C#传递到Delphi

但就我而言,它不起作用

我做错了什么?

我希望你能帮助...非常感谢

雷米·勒博

在Delphi方面,直接声明为的函数参数array of ...称为“开放数组”编译器使用2个参数传递“开放数组” -指向第一个数组元素的指针和数组的高索引(而不是长度!)。这允许调用代码将静态数组或动态数组传递给相同的参数,并且编译器将相应地传递数组数据。

但是,您的.NET代码只希望数组有1个参数-指向第一个数组元素的原始指针。这就是为什么您没有正确获得输出值的原因。您的lStream.Size参数值在.NET代码期望第三个参数到达的地方传递,因此该Size值被误解为.NET代码将其输出pArchivo写入其中的内存地址,因此出现了内存错误。无论如何都无所谓,因为您正在破坏调用堆栈!您最终将4个参数值入堆栈,然后stdcall函数退出时,.NET端在堆栈清理期间仅弹出3个参数值。

您需要通过以下方式更改Funcion变量的声明

  • 保持pDataIn参数声明为array of Byte,但删除显式pInSize参数让编译器隐式传递它:

    Funcion : procedure(pDataIn : array of Byte;
                        //pInSize : Integer;
                        var pDocumento : Integer
                        ); stdcall;
    

    然后,您将不得不更改的调用,Funcion()以为lArBytes数组分配+1个更多的字节,以便编译器将正确的大小值传递给pSize参数:

    SetLength(lArBytes, lStream.Size+1); // <-- +1 here!
    lStream.Read(PByte(lArBytes)^, High(lArBytes)); // <-- no +1 here!
    ...
    Funcion(lArBytes{, High(lArBytes)}, lInDocSize);
    

    不用说,这是不直观,但它应该工作,因为开放数组的行为是众所周知的,虽然它是Delphi编译器的专用实现详细信息。

  • 使用PByte(或仅Pointer)代替array of Byte

    Funcion : procedure(pDataIn : PByte; // <-- here
                        pInSize : Integer;
                        var pDocumento : Integer
                        ); stdcall;
    

    然后,您将必须更改的调用Funcion()以将指针传递给第一个数组元素,并显式传递数组长度:

    SetLength(lArBytes, lStream.Size); // <-- no +1 here!
    lStream.Read(PByte(lArBytes)^, Length(lArBytes)); // <-- or here!
    ...
    Funcion(@lArBytes[0]{or: PByte(lArBytes)}, Length(lArBytes), lInDocSize);
    

    这更加直观,并且更接近.NET代码的预期。

另外,我建议您完全摆脱所有lArBytes变量。您实际上并不需要它。由于.NET代码期望使用指向字节数据的原始指针,因此只需TMemoryStream直接传递数据即可:

procedure TForm1.Button1Click(Sender: TObject);
var
  lStream    : TMemoryStream;
  lInDocSize : Integer;
  lHndle : THandle;

  Funcion : procedure(pDataIn : Pointer;
                      pInSize : Integer;
                      var pDocumento : Integer
                      ); stdcall;
begin

  lHndle := LoadLibrary('ClassLibrary1.dll');
  if (lHndle <> 0) then
  try
    Funcion := GetProcAddress(lHndle, 'Funcion');
    if Assigned(Funcion) then
    begin
      lInDocSize := 0;

      lStream := TMemoryStream.Create;
      try
        lStream.LoadFromFile('Document1.PDF');
        Funcion(lStream.Memory, lStream.Size, lInDocSize);
      finally
        lStream.Free;
      end;

      Label1.Caption := IntToStr(lInDocSize);
    end;
  finally
    FreeLibrary(lHndle);
  end;
end;

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

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

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章