CEF4Delphi application can't run two instances
Asked Answered
S

1

6

I have a Delphi application with an embedded CEF browser and it has stopped working since I updated if from CEF 117.1.4 and Chromium 117.0.5938.92 to CEF 123.0.12 and Chromium 123.0.6312.107.

With CEF 117 I can run two instances of the application with no issue, but now it fails on the second instance startup:

begin
  GlobalCEFApp := TCefApplication.Create;

  InicializaCef;

  // Reducir el número de locales a un mínimo
  GlobalCEFApp.LocalesRequired := 'ca,de,en-GB,en-US,es-419,es,fr,it,pt-BR,pt-PT';
  GlobalCEFApp.SetCurrentDir := True;
  GlobalCEFApp.LocalesDirPath := 'locales';

  Application.Initialize;
  Application.Title := 'QBrowser';
  Application.CreateForm(TMainForm, MainForm);
  test := GlobalCEFApp.StartMainProcess;
  if test then
    Application.Run;

  GlobalCEFApp.Free;
  GlobalCEFApp := nil;
end.

GlobalCEFApp.StartMainProcess is now returning False.

Is there some new configuration value I'm overlooking?

Semibreve answered 21/6 at 11:26 Comment(1)
Why not asking its author?Unyielding
J
8

CEF changed the way it initializes and now it checks if another app is running with the same RootCache setting. This feature was added in CEF 120.1.8.

If GlobalCEFApp.Cache and GlobalCEFApp.RootCache are empty then the default platform specific directory will be used. In the case of Windows: %AppData%\Local\CEF\User Data\.

Use of the default directory is not recommended in production applications. Multiple application instances writing to the same GlobalCEFApp.RootCache directory could result in data corruption.

There are two ways to avoid this:

  1. Implement GlobalCEFApp.OnAlreadyRunningAppRelaunch to be notified when a new app instance is starting and open a new tab or child form with a web browser.
  2. Use a different GlobalCEFApp.RootCache directory for each app instance.

Read the documentation for all the details (search for TCefApplicationCore as type) about:

  • GlobalCEFApp.OnAlreadyRunningAppRelaunch:

    Implement this function to provide app-specific behavior when an already running app is relaunched with the same TCefSettings.root_cache_path value. For example, activate an existing app window or create a new app window. command_line will be read-only. Do not keep a reference to command_line outside of this function. Return true (1) if the relaunch is handled or false (0) for default relaunch behavior. Default behavior will create a new default styled Chrome window.

    To avoid cache corruption only a single app instance is allowed to run for a given TCefSettings.root_cache_path value. On relaunch the app checks a process singleton lock and then forwards the new launch arguments to the already running app process before exiting early. Client apps should therefore check the cef_initialize() return value for early exit before proceeding.

    This function will be called on the browser process UI thread.

  • GlobalCEFApp.RootCache:

    The root directory for installation-specific data and the parent directory for profile-specific data. All TCefSettings.cache_path and ICefRequestContextSettings.cache_path values must have this parent directory in common. If this value is empty and TCefSettings.cache_path is non-empty then it will default to the TCefSettings.cache_path value. Any non-empty value must be an absolute path. If both values are empty then the default platform-specific directory will be used (~/.config/cef_user_data directory on Linux, ~/Library/Application Support/CEF/User Data directory on MacOS, AppData\Local\CEF\User Data directory under the user profile directory on Windows). Use of the default directory is not recommended in production applications (see below).

    Multiple application instances writing to the same root_cache_path directory could result in data corruption. A process singleton lock based on the root_cache_path value is therefore used to protect against this. This singleton behavior applies to all CEF-based applications using version 120 or newer. You should customize root_cache_path for your application and implement ICefBrowserProcessHandler.OnAlreadyRunningAppRelaunch, which will then be called on any app relaunch with the same root_cache_path value.

    Failure to set the root_cache_path value correctly may result in startup crashes or other unexpected behaviors (for example, the sandbox blocking read/write access to certain files).

  • GlobalCEFApp.Cache:

    The directory where data for the global browser cache will be stored on disk. If this value is non-empty then it must be an absolute path that is either equal to or a child directory of TCefSettings.root_cache_path. If this value is empty then browsers will be created in "incognito mode" where in-memory caches are used for storage and no profile-specific data is persisted to disk (installation-specific data will still be persisted in root_cache_path). HTML5 databases such as localStorage will only persist across sessions if a cache path is specified. Can be overridden for individual ICefRequestContext instances via the ICefRequestContextSettings.cache_path value. When using the Chrome runtime any child directory value will be ignored and the "default" profile (also a child directory) will be used instead.

Jelsma answered 21/6 at 13:38 Comment(5)
The docs say "function" but TOnAlreadyRunningAppRelaunchEvent is a procedure. Windows' AppData folder of the current user can be reached by using the environment variable %AppData% (works also in the Explorer).Unyielding
@salvador-díaz-fau I followed your instructions and now I'm running successfully several instances. On startup I create a random name for a folder to use as cache: if CreateDir(UniqueCache) then GlobalCEFApp.RootCache := UniqueCache; But, as I'm also using GlobalCEFApp.SingleProcess := False this ends creating several folders. Is there a way to create only one temporary cache? Where should I delete the cache folder?Implication
You can create unique cache directories concatenating the process ID. For example, "c:\rootcache" + inttostr(MyCurrentProcessID). Then you can delete unused cache directories periodically.Luanaluanda
Where should I delete the temporary cache? The app is creating several folders but I'm only able to delete one of them. I tried GlobalCEFApp.OnBrowserDestroyed but it's not being called.Implication
If all your app instances create rootcache directories with the process ID then you can get a list of all the process IDs of your application and compare those values with the rootcache directory names. If a rootcache directory has an unused process ID then you can safely delete it at any time, from anywhere.Luanaluanda

© 2022 - 2024 — McMap. All rights reserved.