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.

Starting in the main process

Post Reply
Philip Rayment
Posts: 9
Joined: Sun May 13, 2018 5:51 am

Starting in the main process

Post by Philip Rayment »

The instructions for CEF4Delphi say "Since TApplication must only [be] initialized and run in the main process, it's necessary to create GlobalCEFApp and call GlobalCEFApp.StartMainProcess to detect if that is the main process."
I've never programmed for multiple processes nor multiple threads, but while Delphi does allow the programmer to use multiple threads, Googling the topic gives me no indication that Delphi programs typically uses more than one process.

What am I missing here?
User avatar
salvadordf
Posts: 4057
Joined: Thu Feb 02, 2017 12:24 pm
Location: Spain
Contact:

Re: Starting in the main process

Post by salvadordf »

Hi,

Delphi applications don't use more than one thread or process unless you manually add the code to use several threads or processes but this is a special case.

CEF4Delphi is a wrapper for CEF3, which uses Chromium, and Chromium uses several threads and processes to show web pages.
When you add CEF4Delphi to your application, Chromium will create several threads and processes.

If you try to use a Delphi component that creates new threads you don't need to add anything to your app but Chromium also creates new processes using the same EXE.

Let's say that you build your application and Delphi creates an EXE called "MyApplication.exe". Your application may have a DPR file with these contents :

Code: Select all

program MyApplication;

uses
  Vcl.Forms,
  Unit1 in 'Unit1.pas' {Form1};

{$R *.res}

begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.
If you run "MyApplication.exe" you will be executing the code between the begin and end :
  • Application.Initialize;
  • Application.MainFormOnTaskbar := True;
  • Application.CreateForm(TForm1, Form1);
  • Application.Run;
For a normal Delphi application this is totally normal but Chromium will create new processes executing "MyApplication.exe" several times.
Those processes must NOT execute all those "Application..." lines because that would create your forms several times.

That's why it's necessary to add some code that only allows to execute the "Application..." lines in the main process. This is accomplished with "GlobalCEFApp.StartMainProcess".

GlobalCEFApp.StartMainProcess detects if this is the main process and it will return TRUE only in that case, allowing the main form creation. If it detects that this is a subprocess then it will not return immediately. It will do whatever Chromium needs to do in that process (rendering, gpu, etc) and when it finishes then it will return FALSE.

There's a way to tell Chromium to use a different EXE for the subprocesses if you prefer to keep your DPR as clean as possible. Take a look at the SubProcess demo.
Philip Rayment
Posts: 9
Joined: Sun May 13, 2018 5:51 am

Re: Starting in the main process

Post by Philip Rayment »

Thanks Salvador. However, although that helps a bit, it just raises more questions (which I might not have if I understood processes better, sorry).

First, I had wondered what happened if GlobalCEFApp didn't start in the main process—does the application just close without warning, or what? I take it now that you're saying that there are several processes and that one of them will be the main process, so the if test is not so much to see if CEF is in the main process, but whether a particular call to StartMainProcess is in the main process, and that one then processes the normal Application statements.

You mention that if this wasn't done, the forms would be created multiple times. So only the form creation is the issue? Although presumably the .Run procedure would be an issue also. But what about setting MainFormOnTaskbar? Would it matter if that was set in each process? Not that that makes any real difference to me; I'm just asking to see if I understand properly.

But what your explanation seems to be saying seems very counter-intuitive to me. When the Delphi application starts, that creates a new process, which I'll call process A. Then CEF starts several new processes, which I'll call B, C, D, E, etc. And each of them then starts another copy of A, the Delphi application??? Really? Or am I still not understanding?

But a more important question is, what about Initialization sections of units? They get executed before the program even gets to the point of creating the GlobalCEFApp. So do they get executed multiple times? And if so, couldn't that be a problem?
User avatar
salvadordf
Posts: 4057
Joined: Thu Feb 02, 2017 12:24 pm
Location: Spain
Contact:

Re: Starting in the main process

Post by salvadordf »

Chromium is very complex and the official CEF3 project tries to make it easier for all app developers to embed browsers.
CEF4Delphi is a CEF3 wrapper that tries to make it even easier for Delphi/Lazarus developers.

Even with all those layers it's not as easy to use as TWebBrowser or other Delphi components.

If you have Chrome or Chromium installed in your computer open it and load any web page. Then open the Windows Task Manager and you will see something like this :
Image

As you can see Chrome executes Chrome.exe several times to create all the processes. CEF does exactly the same and it will execute your main EXE several times to create all the processes.

If you run the TabBrowser demo then you will see something like this in the task manager when you load a few web pages :
Image

This is very unusual for Delphi developers but it's perfectly normal for any application using CEF to embed a browser.

As you mentioned, it can be tricky to add a browser to an existing Delphi application because the developers have to deal with multiple app instances running at the same time. This can be problematic if that app already has code in the DPR file or if it uses the initialization section for non-trivial code.

In that case, it's recommended to configure CEF to use a different EXE for the subprocesses.

The SubProcess demo shows you how to configure a simple browser to use "subprocess.exe" for the subprocesses. If you build the 2 projects inside the SubProcess demo and run SimpleBrowser.exe then you will see this in the task manager :
Image

This configuration requires much less effort because your application is only executed once.
User avatar
salvadordf
Posts: 4057
Joined: Thu Feb 02, 2017 12:24 pm
Location: Spain
Contact:

Re: Starting in the main process

Post by salvadordf »

Read this post for a detailed explanation about the DPR code if you prefer to use the main EXE for all subprocesses :
https://www.briskbard.com/forum/viewtop ... =302#p1228
Philip Rayment
Posts: 9
Joined: Sun May 13, 2018 5:51 am

Re: Starting in the main process

Post by Philip Rayment »

Okay, that makes things clearer (even it still weird).

So this should work:
  • Put the following code into a unit's initialization section (assuming all this is needed)

Code: Select all

  GlobalCEFApp                 := TCefApplication.Create;
  GlobalCEFApp.MustFreeLibrary := False;
  GlobalCEFApp.FrameworkDirPath     := 'cef';
  GlobalCEFApp.ResourcesDirPath     := 'cef';
  GlobalCEFApp.LocalesDirPath       := 'cef\locales';
  GlobalCEFApp.EnableGPU            := True;      // Enable hardware acceleration
  GlobalCEFApp.DisableGPUCache      := True;      // Disable the creation of a 'GPUCache' directory in the hard drive.
  GlobalCEFApp.cache                := 'cef\cache';
  GlobalCEFApp.cookies              := 'cef\cookies';
  GlobalCEFApp.UserDataPath         := 'cef\User Data';
  • Put the following code into the same unit's finalization section

Code: Select all

FreeAndNil(GlobalCEFApplication);
That would all get executed multiple times, but apart from the last statement (freeing the application), it would be anyway, in the .dpr file. And FreeAndNil avoids problems with that being executed multiple times.
That would leave only the following part:

Code: Select all

If GlobalCEFApp.StartMainProcess then begin ... end;
in the .dpr (plus the normal Delphi .dpr code of course).

Do you see any problems with that?
User avatar
salvadordf
Posts: 4057
Joined: Thu Feb 02, 2017 12:24 pm
Location: Spain
Contact:

Re: Starting in the main process

Post by salvadordf »

Yes, that solution works too.

I just uploaded a new version of the SubProcess demo using that technique.

Now the SubProcess demo uses an external unit called uCEFLoader.pas to create, initialize and destroy GlobalCEFApp.

This demo can even move the GlobalCEFApp.StartMainProcess call to uCEFLoader.pas because it uses an external EXE for the subprocesses.
All you would have to add to the DPR is {$SetPEFlags IMAGE_FILE_LARGE_ADDRESS_AWARE} if you need to load really big images and you don't have compatibility problems with that flag.
Post Reply