On the DLL side, GetResource() reads the resource data into a local array and does not copy it to the buffer that is passed to the function. Assigning a local array to a Buffer pointer does not copy the data it points to.
On the application side, BitBtn1Click() does not allocate any memory for GetResource() to write resource data. Even so, you are not correctly writing the buffer to TMemoryStream . Even if you were, you would not load the TMemoryStream into TPicture correctly.
You have several approaches that you can take to fix a buffer problem:
1) have GetResource() allocate a buffer and return it to the application, after which the application passes the buffer back to the DLL at the end 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) ask the application to request a DLL for the size of the resource, then allocate a buffer and pass it to the DLL to fill:
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.