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

C#Dll库不会将输出参数值返回给Delphi应用程序,c#,delphi,dll,parameter-passing,pass-by-reference,C#,Delphi,Dll,Parameter Passing,Pass By Reference,我用C#编写了一个Dll,其中包含一个保存文件的导出函数 这是C代码 在Delphi中,我加载库并调用导出函数,它工作正常 这是Delphi代码 procedure TForm1.Button1Click(Sender: TObject); var lStream : TMemoryStream; lArBytes : array of Byte; lInDocSize : Integer; lHndle : THandle; Funcion : proc

我用C#编写了一个Dll,其中包含一个保存文件的导出函数

这是C代码

在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)

我在Stackoverflow的这篇文章中读到了这个问题

但就我而言,它不起作用

我做错了什么


我希望你能帮助我。。。非常感谢您

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

但是,您的.NET代码只需要数组的1个参数—指向第一个数组元素的原始指针。这就是为什么您没有正确获得输出值的原因。您的
lStream.Size
参数值在.NET代码期望第三个参数的位置传递,因此
Size
值被误解为.NET代码将其输出
pArchivo
值写入的内存地址,因此内存错误。这一点都不重要,因为您正在破坏调用堆栈!最后将4参数值推送到堆栈上,然后当函数退出时,.NET端的
stdcall
在堆栈清理过程中仅弹出3参数值

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

  • 保持
    pDataIn
    参数声明为
    字节数组
    ,但删除显式
    pInSize
    参数
    。让编译器隐式地传递它:

    Funcion : procedure(pDataIn : array of Byte;
                        //pInSize : Integer;
                        var pDocumento : Integer
                        ); stdcall;
    
    然后,您必须更改
    function()
    的调用,为
    lArBytes
    数组再分配+1个字节,以便编译器将正确的大小值传递给
    pSize
    参数:

    SetLength(lArBytes, lStream.Size+1); // <-- +1 here!
    lStream.Read(PByte(lArBytes)^, High(lArBytes)); // <-- no +1 here!
    ...
    Funcion(lArBytes{, High(lArBytes)}, lInDocSize);
    
    然后必须更改
    function()
    的调用,以传递指向第一个数组元素的指针,并显式传递数组长度:

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

    out int pArchivo是正确的。用那个。另一个错误是由于打开的数组与c#不匹配。嗨,雷米,我已经尝试了你建议的所有更改,所有这些都很好。我选择了最后一种选择,因为这是我所需要的最简单的方式。再次感谢你的帮助和时间!!!
    [Out, MarshalAs(UnmanagedType.I4)] int pArchivo
    
    Funcion : procedure(pDataIn : array of Byte;
                        //pInSize : Integer;
                        var pDocumento : Integer
                        ); stdcall;
    
    SetLength(lArBytes, lStream.Size+1); // <-- +1 here!
    lStream.Read(PByte(lArBytes)^, High(lArBytes)); // <-- no +1 here!
    ...
    Funcion(lArBytes{, High(lArBytes)}, lInDocSize);
    
    Funcion : procedure(pDataIn : PByte; // <-- here
                        pInSize : Integer;
                        var pDocumento : Integer
                        ); stdcall;
    
    SetLength(lArBytes, lStream.Size); // <-- no +1 here!
    lStream.Read(PByte(lArBytes)^, Length(lArBytes)); // <-- or here!
    ...
    Funcion(@lArBytes[0]{or: PByte(lArBytes)}, Length(lArBytes), lInDocSize);
    
    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;