Disclosure Statement: This site contains affiliate links, which means that I may receive a commission if you make a purchase using these links. As an eBay Partner, I earn from qualifying purchases.

Debugging Problems and strange behavior

User avatar
Minz3
Posts: 17
Joined: Tue Nov 12, 2019 12:55 pm

Debugging Problems and strange behavior

Post by Minz3 »

Hello,

I'm trying to implement CEF in our ERP System to access a PWA (progressive web application). The program is module based with .bpl's.
The first thing I've tried is to compile a single Demo project. That worked fine. Then I created a new Package and kept it simple, just showing the Form with an edit and a button. Now I started to copy the SimpleBrowser step by step. I realized that the debugging wont work so I checked the .dpr first. It looks like this:

Code: Select all

program Program;
 
uses ...,
  uCEFApplication;

{$R *.RES}

// CEF3 needs to set the LARGEADDRESSAWARE flag which allows 32-bit processes to use up to 3GB of RAM.
{$SetPEFlags $20}

begin
{$IFDEF DEBUG}
  ReportMemoryLeaksOnShutdown := True;
  GlobalCEFApp.SingleProcess := True;
{$ENDIF}
  GlobalCEFApp := TCefApplication.Create;
  if GlobalCEFApp.StartMainProcess then
  begin  
    Application.Initialize;
    Application.Title := 'programtitle';
    Application.CreateForm(TProgramFrame, ProgramModulesFrame);
    Application.Run;
  end;
  GlobalCEFApp.Free;
  GlobalCEFApp := nil;
end.
While this did not helped me, I started with manually logging every procedure and it's behaviors. The logging looks like this:

Code: Select all

procedure TPWAModuleForm.Logging(info : String; name : String);
begin
  {$IFDEF DEBUG}
    AssignFile(LogFile, 'log.txt');
    Append(LogFile);
    if IOResult = 0 then
    begin
      writeln(LogFile, '[' + DateTimeToStr(now) + ']');
      writeln(LogFile, 'Procedure: ' + name);
      writeln(LogFile, 'Message:   ' + info);
      writeln(LogFile, ' ');
      CloseFile(LogFile);
    end;
  {$ENDIF}
end;
and gets called whenever I need it, like inside the "FormShow" procedure. As said before I copied the Simple Browser example.

Code: Select all

procedure TPWAModuleForm.FormShow(Sender: TObject);
begin
  ChromiumWindow1.ChromiumBrowser.OnBeforePopup := Chromium_OnBeforePopup;

  Logging('Chromium_OnBeforePopup: Popups and new Tabs blocked', 'FormShow');

  if not(ChromiumWindow1.CreateBrowser) then
  begin
    Timer1.Enabled := True;

    Logging('Timer1 enabled | !ChromiumWindow1.CreateBrowser: Browser not created yet', 'FormShow');
  end;

end;
Now when I call the module aka. the PWA Form I see the an empty Form only with my Edit and the Button. The browser never gets created. Could it be, that it has something to do with the "external" .dpr and that it doesnt work within .bpl's?

I hope I could explain my problem good enough.
Thanks in advance!

Greets
User avatar
salvadordf
Posts: 4016
Joined: Thu Feb 02, 2017 12:24 pm
Location: Spain
Contact:

Re: Debugging Problems and strange behavior

Post by salvadordf »

Hi,

In case of large applications like ERPs I would use a different EXE for the subprocesses. It's possible that your main application EXE has a complex initialization and perhaps it's only allowing one instance of the app running at the same time.

Use the Subprocess demo as a template for your application and read all the code comments in that demo. You will not need to modify the DPR file because CEF is initialized in the "initialization" section of the uCEFLoader.pas file.

If you need to debug your application you can use the logging functions in CEF. Add these properties before the GlobalCEFApp.StartMainProcess call :

Code: Select all

  GlobalCEFApp.LogFile             := 'debug.log';
  GlobalCEFApp.LogSeverity         := LOGSEVERITY_INFO;
Then call CefDebugLog defined in uCefMiscFunctions.pas to add a text message to the "debug.log" file.

Remember that the latest CEF4Delphi version at GitHub has a known shutdown error. If you need a stable component then use the latest release :
https://github.com/salvadordf/CEF4Delph ... ses/latest
User avatar
Minz3
Posts: 17
Joined: Tue Nov 12, 2019 12:55 pm

Re: Debugging Problems and strange behavior

Post by Minz3 »

Thank you Salvadordf, I will try this immediately and will give you an update. :)
User avatar
Minz3
Posts: 17
Joined: Tue Nov 12, 2019 12:55 pm

Re: Debugging Problems and strange behavior

Post by Minz3 »

Hey,

I've tried some time with the SubProcess Demo now but I got no clue, how these Projects are connected and what the SubProcesses actually do. It seems to be quite empty.

I have taken a closer look to the dpr file and it seems that it initializes the CEF components correctly. But my PWA form actually doesnt recognize anything from the main application. So the call:

Code: Select all

procedure TPWAModuleForm.FormShow(Sender: TObject);
begin
  ChromiumWindow1.ChromiumBrowser.OnBeforePopup := Chromium_OnBeforePopup;
  if not(ChromiumWindow1.CreateBrowser) then
    Timer1.Enabled := True;
end;
ChromiumWindow1.CreateBrowser
Will always return false.

Are there any settings or properties of the main EXE, where I can tell the PWA form that CEF is initialized?

If this option is not available I could try the SubProcess Method but don't I need to implement an EXE to the (Package) .bpl then? Is that even possible?
User avatar
salvadordf
Posts: 4016
Joined: Thu Feb 02, 2017 12:24 pm
Location: Spain
Contact:

Re: Debugging Problems and strange behavior

Post by salvadordf »

Minz3 wrote: Tue Nov 12, 2019 5:32 pm I've tried some time with the SubProcess Demo now but I got no clue, how these Projects are connected and what the SubProcesses actually do. It seems to be quite empty.
As you can see in the following document, Chromium uses several processes for rendering, gpu tasks, etc.
http://www.chromium.org/developers/desi ... chitecture

In order to create a new process Chromium executes an existing EXE file. By default it will try to execute your main EXE again but the "Subprocess" demo shows how to use a different EXE.

You have probably witnessed this with Chrome in the Windows Task Manager when you saw several "Chrome" processes even when you only executed it once.

The SubProcess.dpr file will create a small EXE that will remain executing the GlobalCEFApp.StartSubProcess line to do rendering or any other task until Chromium decides to close that process. Then it will return from the GlobalCEFApp.StartSubProcess call, free GlobalCEFApp and exit.

Read this for more information :
https://www.briskbard.com/index.php?lang=en&pageid=cef
http://www.chromium.org/developers/design-documents
Minz3 wrote: Tue Nov 12, 2019 5:32 pm I have taken a closer look to the dpr file and it seems that it initializes the CEF components correctly. But my PWA form actually doesnt recognize anything from the main application. So the call:

Code: Select all

procedure TPWAModuleForm.FormShow(Sender: TObject);
begin
  ChromiumWindow1.ChromiumBrowser.OnBeforePopup := Chromium_OnBeforePopup;
  if not(ChromiumWindow1.CreateBrowser) then
    Timer1.Enabled := True;
end;
ChromiumWindow1.CreateBrowser
Will always return false.
I would try something easier first.
  • Build SubProcess.dpr "as is" and copy SubProcess.exe to the same directory as the main EXE in your application.
  • Copy the "release" and "resources" directories from the CEF binaries to the same directory as your main EXE. It should look like this :
    Image
  • Add the "uCEFLoader.pas" file to your main application project.
  • Add a button to your application that uses "showmessage" to show the value of GlobalCEFApp.Status, GlobalCEFApp.LibLoaded and GlobalCEFApp.GlobalContextInitialized.
  • Build your application and click the new button.
When you run your application you will see something like this in the Task Manager :
  • myapplication.exe
  • subprocess.exe
  • subprocess.exe
  • subprocess.exe
GlobalCEFApp.LibLoaded and GlobalCEFApp.GlobalContextInitialized should be TRUE.
GlobalCEFApp.Status should be "asInitialized".

All this indicates that your application initializes CEF correctly and it's ready to create browsers.
Minz3 wrote: Tue Nov 12, 2019 5:32 pm Are there any settings or properties of the main EXE, where I can tell the PWA form that CEF is initialized?

If this option is not available I could try the SubProcess Method but don't I need to implement an EXE to the (Package) .bpl then? Is that even possible?
Please, try what I described previously to check that your application has no issues initializing CEF.

After that I need to know how do you need to embed a browser in your application :
  • child popup form with a browser
  • MDI form with a browser
  • tab, frame or other component with a browser inside the main application form
User avatar
Minz3
Posts: 17
Joined: Tue Nov 12, 2019 12:55 pm

Re: Debugging Problems and strange behavior

Post by Minz3 »

I followed your instructions and got this result:

GlobalCEFApp.LibLoaded is TRUE
GlobalCEFApp.GlobalContextInitialized is FALSE

At the moment I've been struggling with the GlobalCEFApp.Status, because I can't find a way to return the status with ShowMessage.
I have tried something like this:

Code: Select all

if (GlobalCEFApp.Status = GlobalCEFApp.Status.asInitialized) then
  strStatus := 'TRUE'
else 
  strStatus := 'FALSE';
I would guess that the status of asInitialized would be also FALSE...

But I've got the SubProcess.exe to run beneath my MainApplication.exe.
After that I need to know how do you need to embed a browser in your application :
child popup form with a browser
MDI form with a browser
tab, frame or other component with a browser inside the main application form
I have to embed the browser inside a vcl form, which gets loaded from the main form.
It should look like this:
Image

Edit:
There are some memory leaks after I implemented the methods:
Image

Again thank you for your help. I am pretty new to this subject and struggle at some points. I will keep reading your documentations and mess a bit around.
User avatar
salvadordf
Posts: 4016
Joined: Thu Feb 02, 2017 12:24 pm
Location: Spain
Contact:

Re: Debugging Problems and strange behavior

Post by salvadordf »

Minz3 wrote: Thu Nov 14, 2019 2:54 pm I followed your instructions and got this result:

GlobalCEFApp.LibLoaded is TRUE
GlobalCEFApp.GlobalContextInitialized is FALSE

At the moment I've been struggling with the GlobalCEFApp.Status, because I can't find a way to return the status with ShowMessage.
I have tried something like this:

Code: Select all

if (GlobalCEFApp.Status = GlobalCEFApp.Status.asInitialized) then
  strStatus := 'TRUE'
else 
  strStatus := 'FALSE';
I would guess that the status of asInitialized would be also FALSE...

But I've got the SubProcess.exe to run beneath my MainApplication.exe.
CEF is not initialized instantly. Sometimes it can take a second or two.
That's why the demos have a timer to call TChromium.CreateBrowser several times until GlobalCEFApp.GlobalContextInitialized ir true.
Minz3 wrote: Thu Nov 14, 2019 2:54 pm But I've got the SubProcess.exe to run beneath my MainApplication.exe.
If this means that your application executed SubProcess.exe automatically and you see 2 or 3 "SubProcess.exe" processes in the Windows task manager that's a good sign.
Minz3 wrote: Thu Nov 14, 2019 2:54 pm I have to embed the browser inside a vcl form, which gets loaded from the main form.
It should look like this:
Image
Now that your application initializes CEF you can create any browser. If you need a browser in a child form then use the code in the ToolBoxBrowser demo.

Read all the code comments in that demo because all the browsers need to be closed following a "destruction sequence" to avoid shutdown errors.
Minz3 wrote: Thu Nov 14, 2019 2:54 pm Edit:
There are some memory leaks after I implemented the methods:
Image
Again thank you for your help. I am pretty new to this subject and struggle at some points. I will keep reading your documentations and mess a bit around.
Check that GlobalCEFApp is only created once and it's freed when all the forms are closed.
User avatar
salvadordf
Posts: 4016
Joined: Thu Feb 02, 2017 12:24 pm
Location: Spain
Contact:

Re: Debugging Problems and strange behavior

Post by salvadordf »

Sorry, I didn't pay attention to the part where you said that you load BPL modules.

It's been some years since the last time I used BPLs but if I remember correctly you will need to use the code in the DLLBrowser demo for your application.
User avatar
Minz3
Posts: 17
Joined: Tue Nov 12, 2019 12:55 pm

Re: Debugging Problems and strange behavior

Post by Minz3 »

Hey,

thanks for the tips!
I implemented the ToolBoxBrowser Demo and so far the form gets created but while debugging I realized that "CreateGlobalCEFApp" will never be called. I don't know why because I did pretty much the same as in the example. Therefore the created form doesn't behave as expected. It can't get closed and when I close the main form, I get an access error: Address 50AAB6AF in Module 'vcl240.bpl', reading of Address 00000280.

Code: Select all

{...}

var
  PWAModuleForm: TPWAModuleForm;

procedure CreateGlobalCEFApp;

implementation

{$R *.dfm}

uses
  MiPWAChildFrm, uCEFApplication;

{...}

procedure CreateGlobalCEFApp;
begin
  GlobalCEFApp                      := TCefApplication.Create;
  GlobalCEFApp.OnContextInitialized := GlobalCEFApp_OnContextInitialized;
end;

{...}
The PWAModuleForm is my main form, whilst the child form is called ChildForm. So at the moment the main application calls the PWAModuleForm which then calls the ChildForm.
User avatar
salvadordf
Posts: 4016
Joined: Thu Feb 02, 2017 12:24 pm
Location: Spain
Contact:

Re: Debugging Problems and strange behavior

Post by salvadordf »

You need to mix the code from the "SubProcess" and "ToolBoxBrowser" demos.

Take the "uCEFLoader.pas" file from the SubProcess demo to initialize CEF. That unit calls CreateGlobalCEFApp in the initialization section of that unit.
You will also need to copy the "SubProcess.dpr" to build "SubProcess.exe"

Then copy all the code from "uMainForm.pas" from the ToolBoxBrowser demo into your application's main form. Add the "uChildForm.pas" file from the ToolBoxBrowser demo to your application's project.

Notice that the ToolBoxBrowser demo uses the GlobalCEFApp.OnContextInitialized event to send a CEFBROWSER_INITIALIZED message to the main form.
CEF can only create browsers when GlobalCEFApp.GlobalContextInitialized is true and that property is set to true by CEF4Delphi just before triggering the GlobalCEFApp.OnContextInitialized event.

However, all CEF initialization is asynchronous and there's a race between the main form creation and the GlobalCEFApp.OnContextInitialized event. To complicate things a little bit more, GlobalCEFApp.OnContextInitialized is executed in a CEF thread and that's why it sends a windows message to the main form instead of calling one of its functions or setting its properties.

This is what's happening when you run your application :
  • Your application will create the main form in the main thread and it will trigger TForm.OnShow when it's ready.
  • CEF will be initialized asynchronously in a different thread and it will trigger GlobalCEFApp.OnContextInitialized when it's ready.
You need both of them to be ready before creating browsers and you can't guarantee which one will finish first in all cases.

Many demos use a simple timer to create the browser and retry a little later if GlobalCEFApp.GlobalContextInitialized is not true yet.

ToolBoxBrowser uses both events (TForm.OnShow and GlobalCEFApp.OnContextInitialized) to enable the user interface only when CEF is initialized.
Post Reply