在DLL方面,GetResource()
将资源数据读入本地数组,并没有将其复制到传递给函数的缓冲区中。将本地数组分配给Buffer
指针并不会复制指向的数据。
在应用程序方面,BitBtn1Click()
未为GetResource()
分配任何内存以写入资源数据。即使有,你也没有正确地将缓冲区写入TMemoryStream
中。即使你这样做了,你也没有正确地将TMemoryStream
加载到TPicture
中。
你可以采取以下几种方法来解决缓冲区问题:
1) 让GetResource()
分配一个缓冲区并将其返回给应用程序,然后当应用程序完成时让应用程序将缓冲区传回DLL以释放它:
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) 让应用程序查询DLL资源的大小,然后分配一个缓冲区并将其传递给DLL进行填充:
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;
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.
或者:
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.
发生了意外的内存泄漏。未预期泄漏的中型和大型块的大小为227628
。 - Jerry DodgeFreeBuffer()
,否则该示例中不应该有泄漏。 您需要传递与GetResource()
返回的相同指针,以便由分配它的同一内存管理器释放它。 - Remy Lebeau