Issues passing data from DLL to Application

前端 未结 3 1194
我寻月下人不归
我寻月下人不归 2021-02-10 14:35

I\'m a bit puzzled as to how Pointers should be properly used in my scenario. I have a DLL with some embedded resources in it. I expose a function in this DLL which passes binar

3条回答
  •  小鲜肉
    小鲜肉 (楼主)
    2021-02-10 15:01

    On the DLL side, GetResource() is reading the resource data into a local array and not copying it into the buffer that is passed to the function. Assigning the local array to the Buffer pointer does not copy the data being pointed at.

    On the app side, BitBtn1Click() is not allocating any memory for GetResource() to write the resource data into. Even if it were, you are not writing the buffer into the TMemoryStream correctly. Even if you were, you are not loading the TMemoryStream into the TPicture correctly.

    You have a couple of approaches you can take to fix the Buffer issue:

    1) have GetResource() allocate a buffer and return it to the app, then have the app pass the buffer back to the DLL when finished to free it:

    library ResDLL;
    
    {$R *.dres}
    
    uses
      System.SysUtils,
      System.Classes,
      Winapi.Windows;
    
    {$R *.res}
    
    function GetResourceData(const ResName: PChar; var Buffer: Pointer;
      var Length: Integer): Bool; stdcall;
    var
      S: TResourceStream;
      L: Integer;
      Data: Pointer;
    begin
      Result := False;
      try
        S := TResourceStream.Create(HInstance, UpperCase(ResName), RT_RCDATA);
        try
          L := S.Size;
          GetMem(Data, L);
          try
            S.ReadBuffer(Data^, L);
            Buffer := Data;
            Length := L;
          except
            FreeMem(Data);
            raise;
          end;
          Result := True;
        finally
          S.Free;
        end;
      except
      end;
    end;
    
    procedure FreeResourceData(Buffer: Pointer); stdcall;
    begin
      try
        FreeMem(Buffer);
      except
      end;
    end;
    
    exports
      GetResourceData,
      FreeBufferData;
    
    begin
    end.
    

    .

    unit uMain;
    
    interface
    
    uses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
      Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.Buttons, Vcl.ExtCtrls;
    
    type
      TForm1 = class(TForm)
        BitBtn1: TBitBtn;
        Image1: TImage;
        procedure BitBtn1Click(Sender: TObject);
      private
      public
      end;
    
    var
      Form1: TForm1;
    
    implementation
    
    uses
      Vcl.Imaging.jpeg;
    
    {$R *.dfm}
    
    function GetResourceData(const ResName: PChar; var Buffer: Pointer;
      var Length: Integer): Bool; stdcall; external 'ResDLL.dll';
    
    procedure FreeResourceData(Buffer: Pointer); stdcall; external 'ResDLL.dll';
    
    procedure TForm1.BitBtn1Click(Sender: TObject);
    var
      Buffer: Pointer;
      Size: Integer;
      S: TMemoryStream;
      JPG: TJPEGImage;
    begin
      if GetResourceData('SOMERESOURCE', Buffer, Size) then
      begin
        try
          S := TMemoryStream.Create;
          try
            S.WriteBuffer(Buffer^, Size);
            S.Position := 0;
            JPG := TJPEGImage.Create;
            try
              JPG.LoadFromStream(S);
              Image1.Picture.Assign(JPG);
            finally
              JPG.Free;
            end;
          finally
            S.Free;
          end;
        finally
          FreeResourceData(Buffer);
        end;
      end else begin
        raise Exception.Create('Problem calling DLL');
      end;
    end;
    
    end.
    

    2) have the app query the DLL for the size of the resource, then allocate a buffer and pass it to the DLL to fill in:

    library ResDLL;
    
    {$R *.dres}
    
    uses
      System.SysUtils,
      System.Classes,
      Winapi.Windows;
    
    {$R *.res}
    
    function GetResourceData(const ResName: PChar; Buffer: Pointer;
      var Length: Integer): Bool; stdcall;
    var
      S: TResourceStream;
      L: Integer;
      Data: Pointer;
    begin
      Result := False;
      try
        S := TResourceStream.Create(HInstance, UpperCase(ResName), RT_RCDATA);
        try
          L := S.Size;
          if Buffer <> nil then
          begin
            if Length < L then Exit;
            S.ReadBuffer(Buffer^, L);
          end;
          Length := L;
          Result := True;
        finally
          S.Free;
        end;
      except
      end;
    end;
    
    exports
      GetResourceData;
    
    begin
    end.
    

    .

    unit uMain;
    
    interface
    
    uses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
      Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.Buttons, Vcl.ExtCtrls;
    
    type
      TForm1 = class(TForm)
        BitBtn1: TBitBtn;
        Image1: TImage;
        procedure BitBtn1Click(Sender: TObject);
      private
      public
      end;
    
    var
      Form1: TForm1;
    
    implementation
    
    uses
      Vcl.Imaging.jpeg;
    
    {$R *.dfm}
    
    function GetResourceData(const ResName: PChar; Buffer: Pointer;
      var Length: Integer): Bool; stdcall; external 'ResDLL.dll';
    
    procedure TForm1.BitBtn1Click(Sender: TObject);
    var
      Buffer: array of Byte;
      Size: Integer;
      S: TMemoryStream;
      JPG: TJPEGImage;
    begin
      if GetResourceData('SOMERESOURCE', nil, Size) then
      begin
        SetLength(Buffer, Size);
        if GetResourceData('SOMERESOURCE', @Buffer[0], Size) then
        begin
          S := TMemoryStream.Create;
          try
            S.WriteBuffer(Buffer[0], Size);
            S.Position := 0;
            // alternatively, use TBytesStream, or a custom
            // TCustomMemoryStream derived class, to read
            // from the original Buffer directly so it does
            // not have to be copied in memory...
    
            JPG := TJPEGImage.Create;
            try
              JPG.LoadFromStream(S);
              Image1.Picture.Assign(JPG);
            finally
              JPG.Free;
            end;
          finally
            S.Free;
          end;
          Exit;
        end;
      end;
      raise Exception.Create('Problem calling DLL');
    end;
    
    end.
    

    Or:

    library ResDLL;
    
    {$R *.dres}
    
    uses
      System.SysUtils,
      System.Classes,
      Winapi.Windows;
    
    {$R *.res}
    
    function GetResourceData(const ResName: PChar; Buffer: Pointer;
      var Length: Integer): Bool; stdcall;
    var
      S: TResourceStream;
      L: Integer;
      Data: Pointer;
    begin
      Result := False;
      if (Buffer = nil) or (Length <= 0) then Exit;
      try
        S := TResourceStream.Create(HInstance, UpperCase(ResName), RT_RCDATA);
        try
          L := S.Size;
          if Length < L then Exit;
          S.ReadBuffer(Buffer^, L);
          Length := L;
          Result := True;
        finally
          S.Free;
        end;
      except
      end;
    end;
    
    function GetResourceSize(const ResName: PChar): Integer; stdcall;
    var
      S: TResourceStream;
    begin
      Result := 0;
      try
        S := TResourceStream.Create(HInstance, UpperCase(ResName), RT_RCDATA);
        try
          Result := S.Size;
        finally
          S.Free;
        end;
      except
      end;
    end;
    
    exports
      GetResourceData,
      GetResourceSize;
    
    begin
    end.
    

    .

    unit uMain;
    
    interface
    
    uses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
      Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.Buttons, Vcl.ExtCtrls;
    
    type
      TForm1 = class(TForm)
        BitBtn1: TBitBtn;
        Image1: TImage;
        procedure BitBtn1Click(Sender: TObject);
      private
      public
      end;
    
    var
      Form1: TForm1;
    
    implementation
    
    uses
      Vcl.Imaging.jpeg;
    
    {$R *.dfm}
    
    function GetResourceData(const ResName: PChar; Buffer: Pointer;
      var Length: Integer): Bool; stdcall; external 'ResDLL.dll';
    
    function GetResourceSize(const ResName: PChar): Integer; stdcall; external 'ResDLL.dll';
    
    procedure TForm1.BitBtn1Click(Sender: TObject);
    var
      Buffer: array of Byte;
      Size: Integer;
      S: TMemoryStream;
      JPG: TJPEGImage;
    begin
      Size := GetResourceSize('SOMERESOURCE');
      id Size > 0 then
      begin
        SetLength(Buffer, Size);
        if GetResourceData('SOMERESOURCE', @Buffer[0], Size) then
        begin
          S := TMemoryStream.Create;
          try
            S.WriteBuffer(Buffer[0], Size);
            S.Position := 0;
            JPG := TJPEGImage.Create;
            try
              JPG.LoadFromStream(S);
              Image1.Picture.Assign(JPG);
            finally
              JPG.Free;
            end;
          finally
            S.Free;
          end;
          Exit;
        end;
      end;
      raise Exception.Create('Problem calling DLL');
    end;
    
    end.
    

提交回复
热议问题