I am trying to convert a project that uses dcef3 to CEF4Delphi. I was able to fix most of the issues I encountered, but this one I am not able to: I am using a custom TCefResourceHandler to load some app-specific resources. I have distilled the main part in a simple demo, which loads a browser (via TChromiumWindow component), intercepts the resource loading and tries to load the resource from a custom resource handler. The app consists of a form, edit control with a sample url (that doesn't matter, as we load the same html no matter what page is requested) and a button.
When I click the button, sometimes it works, but most of the times, I get an access violation error:
Project ResourceHandlerTest.exe raised exception class $C0000005 with message 'access violation at 0x11ac91d3: read of address 0xfeef00b6'.
Even if it works the first time, almost always the second click produces the same error as above.
It looks like that there is some (probably memory related) problem with the custom resource handler, but I can't find what could it be. The same code works as expected in dcef3 (2704).
I have the following code (using Rad Studio XE6):
program ResourceHandlerTest;
uses
Vcl.Forms,
uCEFApplication,
Unit1 in 'Unit1.pas' {Form1};
{$R *.res}
{$SetPEFlags IMAGE_FILE_LARGE_ADDRESS_AWARE}
begin
GlobalCEFApp := TCefApplication.Create;
if GlobalCEFApp.StartMainProcess then
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TForm1, Form1);
Application.Run;
end;
GlobalCEFApp.Free;
end.
unit StreamCefResourceHandlerUnit;
interface
uses
System.Classes, System.SysUtils, uCEFTypes, uCEFInterfaces, uCEFResourceHandler;
type
StreamCefResourceHandler = class(TCefResourceHandlerOwn)
private
status_ : integer;
statusText_ : string;
mimeType_ : string;
dataStream_ : TStream;
protected
function ProcessRequest(const request: ICefRequest;
const callback: ICefCallback): Boolean; override;
procedure GetResponseHeaders(const response: ICefResponse;
out responseLength: Int64; out redirectUrl: ustring); override;
function ReadResponse(const dataOut: Pointer; bytesToRead: Integer;
var bytesRead: Integer; const callback: ICefCallback): Boolean; override;
public
constructor Create(const browser: ICefBrowser; const frame: ICefFrame;
const schemeName: ustring; const request: ICefRequest; stream: TStream; mimeType: string); reintroduce; virtual;
destructor Destroy; override;
end;
implementation
constructor StreamCefResourceHandler.Create(const browser: ICefBrowser; const frame: ICefFrame;
const schemeName: ustring; const request: ICefRequest; stream: TStream; mimeType: string);
begin
inherited Create(browser, frame, schemeName, request);
dataStream_ := stream;
mimeType_ := mimeType;
end;
destructor StreamCefResourceHandler.Destroy;
begin
dataStream_.Free;
inherited;
end;
function StreamCefResourceHandler.ProcessRequest(const request: ICefRequest; const callback: ICefCallback) : Boolean;
begin
status_ := 200;
statusText_ := 'OK';
dataStream_.Seek(0, soFromBeginning);
callback.Cont;
result := true;
end;
procedure StreamCefResourceHandler.GetResponseHeaders(const response: ICefResponse; out responseLength: Int64; out redirectUrl: ustring);
begin
dataStream_.Seek(0, soFromBeginning);
response.Status := status_;
response.StatusText := statusText_;
if mimeType_ <> '' then
response.MimeType := mimeType_;
responseLength := dataStream_.Size;
end;
function StreamCefResourceHandler.ReadResponse(const dataOut: Pointer; bytesToRead: Integer;
var bytesRead: Integer; const callback: ICefCallback): Boolean;
begin
bytesRead := dataStream_.Read(DataOut^, BytesToRead);
Result := true;
callback.Cont;
end;
end.
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, uCEFWindowParent, uCEFChromiumWindow, uCEFChromium, uCEFInterfaces,
uCEFTypes, StreamCefResourceHandlerUnit, uCefMiscFunctions, uCEFStreamReader, Vcl.StdCtrls;
type
TForm1 = class(TForm)
ChromiumWindow1: TChromiumWindow;
Edit1: TEdit;
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Chromium1GetResourceHandler(Sender: TObject; const browser: ICefBrowser;
const frame: ICefFrame; const request: ICefRequest; out Result: ICefResourceHandler);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
if not Assigned(ChromiumWindow1.ChromiumBrowser.OnGetResourceHandler) then
ChromiumWindow1.ChromiumBrowser.OnGetResourceHandler := Chromium1GetResourceHandler;
ChromiumWindow1.LoadURL(Edit1.Text);
end;
procedure TForm1.Chromium1GetResourceHandler(Sender: TObject; const browser: ICefBrowser;
const frame: ICefFrame; const request: ICefRequest; out Result: ICefResourceHandler);
var
fileStream: TFileStream;
stream: TStream;
begin
stream := TStringStream.Create('<!DOCTYPE html><html><body><p>test</p></body></html>', TEncoding.UTF8, false);
result := StreamCefResourceHandler.Create(browser, frame, '', request, stream, CefGetMimeType('html'));
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
ChromiumWindow1.CreateBrowser();
end;
end.